Browse Source

better floors and scene rotation trackball tester

Former-commit-id: 26ef6932b9c2bc05ee388785be6372e4c7348ca8
Alec Jacobson (jalec 11 years ago
parent
commit
f7f20ae33e

+ 3 - 3
examples/ambient-occlusion/example.cpp

@@ -198,17 +198,17 @@ void display()
   glEnable(GL_COLOR_MATERIAL);
   draw_mesh(V,F,N,C);
 
+  pop_object();
 
   // Draw a nice floor
   glPushMatrix();
-  const double floor_offset = -V.col(1).minCoeff();
+  const double floor_offset =
+    -2./bbd*(V.col(1).maxCoeff()-mid(1));
   glTranslated(0,floor_offset,0);
-  glScaled(bbd/2.0,bbd/2.0,bbd/2.0);
   const float GREY[4] = {0.5,0.5,0.6,1.0};
   const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
   draw_floor(GREY,DARK_GREY);
   glPopMatrix();
-  pop_object();
 
   pop_scene();
 

+ 52 - 0
examples/scene-rotation/Makefile

@@ -0,0 +1,52 @@
+
+.PHONY: all
+
+# Shared flags etc.
+include ../../Makefile.conf
+
+all: obj example
+
+.PHONY: example
+
+LIBIGL=../../
+LIBIGL_INC=-I$(LIBIGL)/include
+LIBIGL_LIB=-L$(LIBIGL)/lib -ligl -liglmatlab -liglembree
+
+EIGEN3_INC=-I/opt/local/include/eigen3 -I/opt/local/include/eigen3/unsupported
+
+ANTTWEAKBAR_INC=-I$(LIBIGL)/external/AntTweakBar/include
+ANTTWEAKBAR_LIB=-L$(LIBIGL)/external/AntTweakBar/lib -lAntTweakBar -framework AppKit
+
+MATLAB_INC=-I$(MATLAB)/extern/include/
+MATLAB_LIB=-L$(MATLAB)/bin/maci64 -lmx -lmat -lmex -lstdc++
+
+CARBON_LIB=-framework Carbon
+
+# Use free glut for mouse scrolling
+FREE_GLUT=/opt/local/
+FREE_GLUT_INC=-I$(FREE_GLUT)/include
+FREE_GLUT_LIB=-L$(FREE_GLUT)/lib -lglut
+GLUT_LIB=$(FREE_GLUT_LIB)
+GLUT_INC=$(FREE_GLUT_INC)
+
+INC=$(LIBIGL_INC) $(ANTTWEAKBAR_INC) $(EIGEN3_INC) $(MATLAB_INC) $(GLUT_INC)
+LIB=$(OPENGL_LIB) $(GLUT_LIB) $(ANTTWEAKBAR_LIB) $(LIBIGL_LIB) $(MATLAB_LIB) $(CARBON_LIB)
+
+CPP_FILES=$(wildcard ./*.cpp)
+OBJ_FILES=$(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o))) 
+
+example: obj $(OBJ_FILES)
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -o example $(OBJ_FILES) $(LIB)
+
+obj:
+	mkdir -p obj
+
+obj/%.o: %.cpp
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC)
+
+obj/%.o: %.cpp %.h
+	g++ $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC)
+
+clean:
+	rm -f example.o
+	rm -f example

+ 16 - 0
examples/scene-rotation/README

@@ -0,0 +1,16 @@
+This is a small program that demonstrates various 2D mouse input interfaces for
+control a 3D world-in-hand rotation.
+
+So far this implements:
+  ROTATION_TYPE_IGL_TRACKBALL  Continuous, "wrap-around" trackball, as seen in
+    AntTweakBar.
+  ROTATION_TYPE_BELL_TRACKBALL  De facto "Standard" trackball, limited to 180°
+    rotations.
+  ROTATION_TYPE_TWO_AXIS_VALUATOR   Rotate around drag vector by amount
+    proportional to drag length.
+  ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP  Rotate around fixed up-vector by x
+    drag length and around y drag vector by y drag length. As seen in Maya, 3D
+    Studio Max, etc.
+
+More trackball variantes could be snatched from here:
+  http://opentissue.org/svn/OpenTissue/archieve/grid3d/OpenTissue/trackball/

+ 643 - 0
examples/scene-rotation/example.cpp

@@ -0,0 +1,643 @@
+// Small GLUT application to test different scene rotation paradigms 
+//
+
+#include "trackball.h"
+
+#include <igl/readOBJ.h>
+#include <igl/writeOBJ.h>
+#include <igl/writeOFF.h>
+#include <igl/readWRL.h>
+#include <igl/report_gl_error.h>
+#include <igl/triangulate.h>
+#include <igl/readOFF.h>
+#include <igl/readMESH.h>
+#include <igl/draw_mesh.h>
+#include <igl/draw_floor.h>
+#include <igl/pathinfo.h>
+#include <igl/list_to_matrix.h>
+#include <igl/quat_to_mat.h>
+#include <igl/per_face_normals.h>
+#include <igl/material_colors.h>
+#include <igl/trackball.h>
+#include <igl/snap_to_canonical_view_quat.h>
+#include <igl/REDRUM.h>
+#include <igl/Camera.h>
+#include <igl/ReAntTweakBar.h>
+
+#include <Eigen/Core>
+#include <Eigen/Geometry>
+
+#include <GLUT/glut.h>
+
+#include <Carbon/Carbon.h>
+
+#include <string>
+#include <vector>
+#include <stack>
+#include <iostream>
+
+
+Eigen::MatrixXd V,N;
+Eigen::VectorXd Vmid,Vmin,Vmax;
+double bbd = 1.0;
+Eigen::MatrixXi F;
+struct State
+{
+  igl::Camera camera;
+} s;
+
+// See README for descriptions
+enum ROTATION_TYPE
+{
+  ROTATION_TYPE_IGL_TRACKBALL = 0,
+  ROTATION_TYPE_BELL_TRACKBALL = 1,
+  ROTATION_TYPE_TWO_AXIS_VALUATOR = 2,
+  ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 3,
+  NUM_ROTATION_TYPES = 4,
+} rotation_type;
+
+std::stack<State> undo_stack;
+std::stack<State> redo_stack;
+
+bool is_rotating = false;
+int down_x,down_y;
+igl::Camera down_camera;
+
+int width,height;
+Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0);
+
+#define REBAR_NAME "temp.rbr"
+igl::ReTwBar rebar;
+
+// No-op setter, does nothing
+void TW_CALL no_op(const void * /*value*/, void * /*clientData*/)
+{
+}
+
+void TW_CALL get_camera_rotation(void * value, void *clientData)
+{
+  using namespace std;
+  // case current value to double
+  double * quat = (double *)(value);
+  std::copy(s.camera.rotation,s.camera.rotation+4,quat);
+}
+
+void push_undo()
+{
+  undo_stack.push(s);
+  // Clear
+  redo_stack = std::stack<State>();
+}
+
+void reshape(int width, int height)
+{
+  ::width = width;
+  ::height = height;
+  glViewport(0,0,width,height);
+  // Send the new window size to AntTweakBar
+  TwWindowSize(width, height);
+}
+
+void push_scene()
+{
+  using namespace igl;
+  using namespace std;
+  const double angle = s.camera.angle;
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix();
+  glLoadIdentity();
+  double zNear = 1e-2;
+  double zFar = 100;
+  double aspect = ((double)width)/((double)height);
+  // Amount of scaling needed to "fix" perspective z-shift
+  double z_fix = 1.0;
+  // 5 is far enough to see unit "things" well
+  const double camera_z = 2;
+  // Test if should be using true orthographic projection
+  if(angle == 0)
+  {
+    glOrtho(
+      -0.5*camera_z*aspect,
+      0.5*camera_z*aspect,
+      -0.5*camera_z,
+      0.5*camera_z,
+      zNear,
+      zFar);
+  }else
+  {
+    // Make sure aspect is sane
+    aspect = aspect < 0.01 ? 0.01 : aspect;
+    gluPerspective(angle,aspect,zNear,zFar);
+    z_fix = 2.*tan(angle/2./360.*2.*M_PI);
+  }
+
+  glMatrixMode(GL_MODELVIEW);
+  glPushMatrix();
+  glLoadIdentity();
+  gluLookAt(0,0,camera_z,0,0,0,0,1,0);
+  // Adjust scale to correct perspective
+  glScaled(z_fix,z_fix,z_fix);
+  // scale, pan
+  glScaled( s.camera.zoom, s.camera.zoom, s.camera.zoom);
+  double mat[4*4];
+  quat_to_mat(s.camera.rotation,mat);
+  glMultMatrixd(mat);
+}
+
+void push_object()
+{
+  using namespace igl;
+  glPushMatrix();
+  glScaled(2./bbd,2./bbd,2./bbd);
+  glTranslated(-Vmid(0),-Vmid(1),-Vmid(2));
+}
+
+void pop_object()
+{
+  glPopMatrix();
+}
+
+void pop_scene()
+{
+  glMatrixMode(GL_PROJECTION);
+  glPopMatrix();
+  glMatrixMode(GL_MODELVIEW);
+  glPopMatrix();
+}
+
+// Set up double-sided lights
+void lights()
+{
+  using namespace std;
+  using namespace Eigen;
+  glEnable(GL_LIGHTING);
+  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
+  glEnable(GL_LIGHT0);
+  glEnable(GL_LIGHT1);
+  float WHITE[4] =  {0.8,0.8,0.8,1.};
+  float GREY[4] =  {0.4,0.4,0.4,1.};
+  float BLACK[4] =  {0.,0.,0.,1.};
+  Vector4f pos = light_pos;
+  glLightfv(GL_LIGHT0,GL_AMBIENT,GREY);
+  glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE);
+  glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK);
+  glLightfv(GL_LIGHT0,GL_POSITION,pos.data());
+  pos(0) *= -1;
+  pos(1) *= -1;
+  pos(2) *= -1;
+  glLightfv(GL_LIGHT1,GL_AMBIENT,GREY);
+  glLightfv(GL_LIGHT1,GL_DIFFUSE,WHITE);
+  glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK);
+  glLightfv(GL_LIGHT1,GL_POSITION,pos.data());
+}
+
+void display()
+{
+  using namespace igl;
+  using namespace std;
+  glClearColor(1,1,1,0);
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_NORMALIZE);
+  lights();
+  push_scene();
+  push_object();
+
+  // Set material properties
+  glDisable(GL_COLOR_MATERIAL);
+  glMaterialfv(GL_FRONT, GL_AMBIENT,  GOLD_AMBIENT);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE,  GOLD_DIFFUSE  );
+  glMaterialfv(GL_FRONT, GL_SPECULAR, GOLD_SPECULAR);
+  glMaterialf (GL_FRONT, GL_SHININESS, 128);
+  glMaterialfv(GL_BACK, GL_AMBIENT,  SILVER_AMBIENT);
+  glMaterialfv(GL_BACK, GL_DIFFUSE,  FAST_GREEN_DIFFUSE  );
+  glMaterialfv(GL_BACK, GL_SPECULAR, SILVER_SPECULAR);
+  glMaterialf (GL_BACK, GL_SHININESS, 128);
+
+  
+  draw_mesh(V,F,N);
+  pop_object();
+
+  // Draw a nice floor
+  glPushMatrix();
+  const double floor_offset =
+    -2./bbd*(V.col(1).maxCoeff()-Vmid(1));
+  glTranslated(0,floor_offset,0);
+  const float GREY[4] = {0.5,0.5,0.6,1.0};
+  const float DARK_GREY[4] = {0.2,0.2,0.3,1.0};
+  draw_floor(GREY,DARK_GREY);
+  glPopMatrix();
+
+  pop_scene();
+
+  report_gl_error();
+
+  TwDraw();
+  glutSwapBuffers();
+  glutPostRedisplay();
+}
+
+void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  push_undo();
+  if(wheel == 0)
+  {
+    static double mouse_scroll_y = 0;
+    const double delta_y = 0.125*direction;
+    mouse_scroll_y += delta_y;
+    // absolute scale difference when changing zooms (+1)
+    const double z_diff = 0.01;
+    GLint viewport[4];
+    glGetIntegerv(GL_VIEWPORT,viewport);
+    if(TwMouseMotion(mouse_x, viewport[3] - mouse_y))
+    {
+      TwMouseWheel(mouse_scroll_y);
+    }else
+    {
+      s.camera.zoom *= (1.0+double(direction)*z_diff);
+      const double min_zoom = 0.01;
+      const double max_zoom = 10.0;
+      s.camera.zoom = min(max_zoom,max(min_zoom,s.camera.zoom));
+    }
+  }else
+  {
+    if(!is_rotating)
+    {
+      // Change viewing angle (reshape will take care of adjust zoom)
+      const double a_diff = 1.0;
+      s.camera.angle += double(direction)*a_diff;
+      const double min_angle = 15.0;
+      s.camera.angle = 
+        min(90.0,max(min_angle,s.camera.angle));
+    }
+  }
+}
+
+void mouse(int glutButton, int glutState, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  using namespace Eigen;
+  using namespace igl;
+  bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y);
+  switch(glutButton)
+  {
+    case GLUT_RIGHT_BUTTON:
+    case GLUT_LEFT_BUTTON:
+    {
+      switch(glutState)
+      {
+        case 1:
+          // up
+          glutSetCursor(GLUT_CURSOR_INHERIT);
+          is_rotating = false;
+          break;
+        case 0:
+          if(!tw_using)
+          {
+            push_undo();
+            glutSetCursor(GLUT_CURSOR_CYCLE);
+            // collect information for trackball
+            is_rotating = true;
+            down_camera = s.camera;
+            down_x = mouse_x;
+            down_y = mouse_y;
+          }
+        break;
+      }
+      break;
+    }
+    // Scroll down
+    case 3:
+    {
+      mouse_wheel(0,-1,mouse_x,mouse_y);
+      break;
+    }
+    // Scroll up
+    case 4:
+    {
+      mouse_wheel(0,1,mouse_x,mouse_y);
+      break;
+    }
+    // Scroll left
+    case 5:
+    {
+      mouse_wheel(1,-1,mouse_x,mouse_y);
+      break;
+    }
+    // Scroll right
+    case 6:
+    {
+      mouse_wheel(1,1,mouse_x,mouse_y);
+      break;
+    }
+  }
+}
+
+void mouse_drag(int mouse_x, int mouse_y)
+{
+  using namespace igl;
+  using namespace std;
+  using namespace Eigen;
+
+  if(is_rotating)
+  {
+    glutSetCursor(GLUT_CURSOR_CYCLE);
+    switch(rotation_type)
+    {
+      case ROTATION_TYPE_IGL_TRACKBALL:
+      {
+        // Rotate according to trackball
+        igl::trackball<double>(
+          width,
+          height,
+          2.0,
+          down_camera.rotation,
+          down_x,
+          down_y,
+          mouse_x,
+          mouse_y,
+          s.camera.rotation);
+          break;
+      }
+      case ROTATION_TYPE_BELL_TRACKBALL:
+      {
+        float down_quaternion[4];
+        copy(down_camera.rotation,down_camera.rotation+4,down_quaternion);
+        float new_quaternion[4];
+        
+        const float center_x = ((float)width)/2.0;
+        const float center_y = ((float)height)/2.0;
+        const double speed = 2.0f;
+        const float half_width =   ((float)width)/speed;
+        const float half_height = ((float)height)/speed;
+        
+        ::trackball(new_quaternion,
+          (float)(center_x-down_x)/half_width,
+          (float)(down_y-center_y)/half_height,
+          (float)(center_x-mouse_x)/half_width,
+          (float)(mouse_y-center_y)/half_height);
+        // I think we need to do this because we have z pointing out of the
+        // screen rather than into the screen
+        new_quaternion[2] = -new_quaternion[2];
+        float float_quat[4];
+        add_quats(down_quaternion,new_quaternion,float_quat);
+        copy(float_quat,float_quat+4,s.camera.rotation);
+        break;
+      }
+      case ROTATION_TYPE_TWO_AXIS_VALUATOR:
+      {
+        Quaterniond down_q;
+        copy(down_camera.rotation,down_camera.rotation+4,down_q.coeffs().data());
+        Vector3d axis(mouse_y-down_y,mouse_x-down_x,0);
+        const double speed = 2.0;
+        if(axis.norm() != 0)
+        {
+          Quaterniond q;
+          q = 
+            Quaterniond(
+              AngleAxisd(
+                M_PI*axis.norm()/(double)width*speed/2.0,
+                axis.normalized())) * down_q;
+          q.normalize();
+          copy(q.coeffs().data(),q.coeffs().data()+4,s.camera.rotation);
+        }
+        break;
+      }
+      case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP:
+      {
+        Quaterniond down_q;
+        copy(down_camera.rotation,down_camera.rotation+4,down_q.coeffs().data());
+        Vector3d axis(0,1,0);
+        const double speed = 2.0;
+        Quaterniond q;
+        q = down_q * 
+          Quaterniond(
+            AngleAxisd(
+              M_PI*((double)(mouse_x-down_x))/(double)width*speed/2.0,
+              axis.normalized()));
+        q.normalize();
+        {
+          Vector3d axis(1,0,0);
+          const double speed = 2.0;
+          if(axis.norm() != 0)
+          {
+            q = 
+              Quaterniond(
+                AngleAxisd(
+                  M_PI*(mouse_y-down_y)/(double)width*speed/2.0,
+                  axis.normalized())) * q;
+            q.normalize();
+          }
+        }
+        copy(q.coeffs().data(),q.coeffs().data()+4,s.camera.rotation);
+        break;
+      }
+      default:
+        break;
+    }
+  }
+}
+
+void init_relative()
+{
+  using namespace Eigen;
+  using namespace igl;
+  per_face_normals(V,F,N);
+  Vmax = V.colwise().maxCoeff();
+  Vmin = V.colwise().minCoeff();
+  Vmid = 0.5*(Vmax + Vmin);
+  bbd = (Vmax-Vmin).norm();
+}
+
+
+KeyMap keyStates ;
+bool IS_KEYDOWN( uint16_t vKey )
+{
+  uint8_t index = vKey / 32 ;
+  uint8_t shift = vKey % 32 ;
+  return keyStates[index].bigEndianValue & (1 << shift) ;
+}
+
+void undo()
+{
+  using namespace std;
+  if(!undo_stack.empty())
+  {
+    redo_stack.push(s);
+    s = undo_stack.top();
+    undo_stack.pop();
+  }
+}
+
+void redo()
+{
+  using namespace std;
+  if(!redo_stack.empty())
+  {
+    undo_stack.push(s);
+    s = redo_stack.top();
+    redo_stack.pop();
+  }
+}
+
+void key(unsigned char key, int mouse_x, int mouse_y)
+{
+  using namespace std;
+  GetKeys(keyStates);
+  const bool command_down = IS_KEYDOWN(kVK_Command);
+  const bool shift_down = IS_KEYDOWN(kVK_Shift);
+  switch(key)
+  {
+    // ESC
+    case char(27):
+      rebar.save(REBAR_NAME);
+    // ^C
+    case char(3):
+      exit(0);
+    case 'z':
+    case 'Z':
+      if(command_down)
+      {
+        if(shift_down)
+        {
+          redo();
+        }else
+        {
+          undo();
+        }
+        break;
+      }else
+      {
+        push_undo();
+        igl::snap_to_canonical_view_quat<double>(
+          s.camera.rotation,
+          1.0,
+          s.camera.rotation);
+        break;
+      }
+    default:
+      if(!TwEventKeyboardGLUT(key,mouse_x,mouse_y))
+      {
+        cout<<"Unknown key command: "<<key<<" "<<int(key)<<endl;
+      }
+  }
+  
+}
+
+int main(int argc, char * argv[])
+{
+  using namespace std;
+  using namespace Eigen;
+  using namespace igl;
+  string filename = "../shared/cheburashka.obj";
+  if(argc < 2)
+  {
+    cerr<<"Usage:"<<endl<<"    ./example input.obj"<<endl;
+    cout<<endl<<"Opening default mesh..."<<endl;
+  }else
+  {
+    // Read and prepare mesh
+    filename = argv[1];
+  }
+
+  // print key commands
+  cout<<"[Click] and [drag]  Rotate model using trackball."<<endl;
+  cout<<"[Z,z]               Snap rotation to canonical view."<<endl;
+  cout<<"[⌘ Z]               Undo."<<endl;
+  cout<<"[⇧ ⌘ Z]             Redo."<<endl;
+  cout<<"[^C,ESC]            Exit."<<endl;
+
+  // dirname, basename, extension and filename
+  string d,b,ext,f;
+  pathinfo(filename,d,b,ext,f);
+  // Convert extension to lower case
+  transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
+  vector<vector<double > > vV,vN,vTC;
+  vector<vector<int > > vF,vFTC,vFN;
+  if(ext == "obj")
+  {
+    // Convert extension to lower case
+    if(!igl::readOBJ(filename,vV,vTC,vN,vF,vFTC,vFN))
+    {
+      return 1;
+    }
+  }else if(ext == "off")
+  {
+    // Convert extension to lower case
+    if(!igl::readOFF(filename,vV,vF,vN))
+    {
+      return 1;
+    }
+  }else if(ext == "wrl")
+  {
+    // Convert extension to lower case
+    if(!igl::readWRL(filename,vV,vF))
+    {
+      return 1;
+    }
+  //}else
+  //{
+  //  // Convert extension to lower case
+  //  MatrixXi T;
+  //  if(!igl::readMESH(filename,V,T,F))
+  //  {
+  //    return 1;
+  //  }
+  //  //if(F.size() > T.size() || F.size() == 0)
+  //  {
+  //    boundary_faces(T,F);
+  //  }
+  }
+  if(vV.size() > 0)
+  {
+    if(!list_to_matrix(vV,V))
+    {
+      return 1;
+    }
+    triangulate(vF,F);
+  }
+
+  init_relative();
+
+  // Init glut
+  glutInit(&argc,argv);
+  if( !TwInit(TW_OPENGL, NULL) )
+  {
+    // A fatal error occured
+    fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError());
+    return 1;
+  }
+  // Create a tweak bar
+  rebar.TwNewBar("TweakBar");
+  rebar.TwAddVarCB("camera_rotation", TW_TYPE_QUAT4D, no_op,get_camera_rotation, NULL, "open readonly=true");
+  TwEnumVal RotationTypesEV[NUM_ROTATION_TYPES] = 
+  {
+    {ROTATION_TYPE_IGL_TRACKBALL,"igl trackball"},
+    {ROTATION_TYPE_BELL_TRACKBALL,"bell trackball"},
+    {ROTATION_TYPE_TWO_AXIS_VALUATOR,"two axis valuator"},
+    {ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP,"two a... fixed up"},
+  };
+  TwType RotationTypeTW = 
+    ReTwDefineEnum(
+        "RotationType", 
+        RotationTypesEV, 
+        NUM_ROTATION_TYPES);
+  rebar.TwAddVarRW( "rotation_type", RotationTypeTW, &rotation_type,"keyIncr=] keyDecr=[");
+  rebar.load(REBAR_NAME);
+
+  // Init antweakbar
+  glutInitDisplayString( "rgba depth double samples>=8 ");
+  glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT));
+  glutCreateWindow("upright");
+  glutDisplayFunc(display);
+  glutReshapeFunc(reshape);
+  glutKeyboardFunc(key);
+  glutMouseFunc(mouse);
+  glutMotionFunc(mouse_drag);
+  glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT);
+  glutMainLoop();
+
+  return 0;
+}

+ 422 - 0
examples/scene-rotation/trackball.cpp

@@ -0,0 +1,422 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * Trackball code:
+ *
+ * Implementation of a virtual trackball.
+ * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
+ *   the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
+ *
+ * Vector manip code:
+ *
+ * Original code from:
+ * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
+ *
+ * Much mucking with by:
+ * Gavin Bell
+ */
+#include <math.h>
+#include "trackball.h"
+
+#ifdef WIN32
+	//Disable warning in this file: conversion from 'double' to 'float', possible loss of data
+	#pragma warning(disable:4244)
+#endif
+
+/*
+ * This size should really be based on the distance from the center of
+ * rotation to the point on the object underneath the mouse.  That
+ * point would then track the mouse as closely as possible.  This is a
+ * simple example, though, so that is left as an Exercise for the
+ * Programmer.
+ */
+#define TRACKBALLSIZE  (0.8)
+
+/*
+ * Local function prototypes (not defined in trackball.h)
+ */
+static float tb_project_to_sphere(float, float, float);
+static void normalize_quat(float [4]);
+
+void
+vzero(float *v)
+{
+    v[0] = 0.0;
+    v[1] = 0.0;
+    v[2] = 0.0;
+}
+
+void
+vset(float *v, float x, float y, float z)
+{
+    v[0] = x;
+    v[1] = y;
+    v[2] = z;
+}
+
+void
+vsub(const float *src1, const float *src2, float *dst)
+{
+    dst[0] = src1[0] - src2[0];
+    dst[1] = src1[1] - src2[1];
+    dst[2] = src1[2] - src2[2];
+}
+
+void
+vcopy(const float *v1, float *v2)
+{
+    register int i;
+    for (i = 0 ; i < 3 ; i++)
+        v2[i] = v1[i];
+}
+
+void
+vcross(const float *v1, const float *v2, float *cross)
+{
+    float temp[3];
+
+    temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+    temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+    temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+    vcopy(temp, cross);
+}
+
+float
+vlength(const float *v)
+{
+    return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+}
+
+void
+vscale(float *v, float div)
+{
+    v[0] *= div;
+    v[1] *= div;
+    v[2] *= div;
+}
+
+void
+vnormal(float *v)
+{
+    vscale(v,1.0/vlength(v));
+}
+
+float
+vdot(const float *v1, const float *v2)
+{
+    return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void
+vadd(const float *src1, const float *src2, float *dst)
+{
+    dst[0] = src1[0] + src2[0];
+    dst[1] = src1[1] + src2[1];
+    dst[2] = src1[2] + src2[2];
+}
+
+/*
+ * Ok, simulate a track-ball.  Project the points onto the virtual
+ * trackball, then figure out the axis of rotation, which is the cross
+ * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
+ * Note:  This is a deformed trackball-- is a trackball in the center,
+ * but is deformed into a hyperbolic sheet of rotation away from the
+ * center.  This particular function was chosen after trying out
+ * several variations.
+ *
+ * It is assumed that the arguments to this routine are in the range
+ * (-1.0 ... 1.0)
+ */
+void
+trackball(float q[4], float p1x, float p1y, float p2x, float p2y)
+{
+    float a[3]; /* Axis of rotation */
+    float phi;  /* how much to rotate about axis */
+    float p1[3], p2[3], d[3];
+    float t;
+
+    if (p1x == p2x && p1y == p2y) {
+        /* Zero rotation */
+        vzero(q);
+        q[3] = 1.0;
+        return;
+    }
+
+    /*
+     * First, figure out z-coordinates for projection of P1 and P2 to
+     * deformed sphere
+     */
+    vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
+    vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
+
+    /*
+     *  Now, we want the cross product of P1 and P2
+     */
+    vcross(p2,p1,a);
+
+    /*
+     *  Figure out how much to rotate around that axis.
+     */
+    vsub(p1,p2,d);
+    t = vlength(d) / (2.0*TRACKBALLSIZE);
+
+    /*
+     * Avoid problems with out-of-control values...
+     */
+    if (t > 1.0) t = 1.0;
+    if (t < -1.0) t = -1.0;
+    phi = 2.0 * asin(t);
+
+    axis_to_quat(a,phi,q);
+}
+
+/*
+ *  Given an axis and angle, compute quaternion.
+ */
+void
+axis_to_quat(float a[3], float phi, float q[4])
+{
+    vnormal(a);
+    vcopy(a,q);
+    vscale(q,sin(phi/2.0));
+    q[3] = cos(phi/2.0);
+}
+
+void
+quat_to_axis(float q[4], float axis[3], float &phi) {
+    if (q[3] > 1) 
+        normalize_quat(q); // if w>1 acos and sqrt will produce errors, this cant happen if quaternion is normalised
+    phi = 2 * acos(q[3]);
+    double s = sqrt(1-q[3]*q[3]); // assuming quaternion normalised then w is less than 1, so term always positive.
+    if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
+        // if s close to zero then direction of axis not important
+        axis[0] = q[0]; // if it is important that axis is normalised then replace with x=1; y=z=0;
+        axis[1] = q[1];
+        axis[2] = q[2];
+    } else {
+        axis[0] = q[0] / s; // normalise axis
+        axis[1] = q[1] / s;
+        axis[2] = q[2] / s;
+    }
+}
+
+/*
+ * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
+ * if we are away from the center of the sphere.
+ */
+static float
+tb_project_to_sphere(float r, float x, float y)
+{
+    float d, t, z;
+
+    d = sqrt(x*x + y*y);
+    if (d < r * 0.70710678118654752440) {    /* Inside sphere */
+        z = sqrt(r*r - d*d);
+    } else {           /* On hyperbola */
+        t = r / 1.41421356237309504880;
+        z = t*t / d;
+    }
+    return z;
+}
+
+/*
+ * Given two rotations, e1 and e2, expressed as quaternion rotations,
+ * figure out the equivalent single rotation and stuff it into dest.
+ *
+ * This routine also normalizes the result every RENORMCOUNT times it is
+ * called, to keep error from creeping in.
+ *
+ * NOTE: This routine is written so that q1 or q2 may be the same
+ * as dest (or each other).
+ */
+
+#define RENORMCOUNT 1
+
+void
+add_quats(float q1[4], float q2[4], float dest[4])
+{
+    static int count=0;
+    float t1[4], t2[4], t3[4];
+    float tf[4];
+
+    vcopy(q1,t1);
+    vscale(t1,q2[3]);
+
+    vcopy(q2,t2);
+    vscale(t2,q1[3]);
+
+    vcross(q2,q1,t3);
+    vadd(t1,t2,tf);
+    vadd(t3,tf,tf);
+    tf[3] = q1[3] * q2[3] - vdot(q1,q2);
+
+    dest[0] = tf[0];
+    dest[1] = tf[1];
+    dest[2] = tf[2];
+    dest[3] = tf[3];
+
+    if (++count > RENORMCOUNT) {
+        count = 0;
+        normalize_quat(dest);
+    }
+}
+
+/*
+ * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
+ * If they don't add up to 1.0, dividing by their magnitued will
+ * renormalize them.
+ *
+ * Note: See the following for more information on quaternions:
+ *
+ * - Shoemake, K., Animating rotation with quaternion curves, Computer
+ *   Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
+ * - Pletinckx, D., Quaternion calculus as a basic tool in computer
+ *   graphics, The Visual Computer 5, 2-13, 1989.
+ */
+static void
+normalize_quat(float q[4])
+{
+    int i;
+    float mag;
+
+    mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
+    for (i = 0; i < 4; i++) q[i] /= mag;
+}
+
+/*
+ * Build a rotation matrix, given a quaternion rotation.
+ *
+ */
+void
+build_rotmatrix(float m[4][4], float q[4])
+{
+    m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
+    m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
+    m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
+    m[0][3] = 0.0;
+
+    m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
+    m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
+    m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
+    m[1][3] = 0.0;
+
+    m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
+    m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
+    m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
+    m[2][3] = 0.0;
+
+    m[3][0] = 0.0;
+    m[3][1] = 0.0;
+    m[3][2] = 0.0;
+    m[3][3] = 1.0;
+}
+
+void
+build_rotmatrix_v2(float m[16], float q[4])
+{
+    m[0*4+0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
+    m[0*4+1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
+    m[0*4+2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
+    m[0*4+3] = 0.0;
+
+    m[1*4+0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
+    m[1*4+1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
+    m[1*4+2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
+    m[1*4+3] = 0.0;
+
+    m[2*4+0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
+    m[2*4+1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
+    m[2*4+2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
+    m[2*4+3] = 0.0;
+
+    m[3*4+0] = 0.0;
+    m[3*4+1] = 0.0;
+    m[3*4+2] = 0.0;
+    m[3*4+3] = 1.0;
+}
+
+////Olga
+
+void add_quats_no_renorm(float q1[4], float q2[4], float dest[4])
+{
+    float t1[4], t2[4], t3[4];
+    float tf[4];
+    
+    vcopy(q1,t1);
+    vscale(t1,q2[3]);
+    
+    vcopy(q2,t2);
+    vscale(t2,q1[3]);
+    
+    vcross(q2,q1,t3);
+    vadd(t1,t2,tf);
+    vadd(t3,tf,tf);
+    tf[3] = q1[3] * q2[3] - vdot(q1,q2);
+    
+    dest[0] = tf[0];
+    dest[1] = tf[1];
+    dest[2] = tf[2];
+    dest[3] = tf[3];
+    
+}
+
+void quat_rotate_point(float q[4], const float p1[3], float p2[3])
+{
+    float co[4];
+    co[0] = -q[0];
+    co[1] = -q[1];
+    co[2] = -q[2];
+    co[3] = q[3];
+    
+    float tmp[4];
+    tmp[0] = p1[0];
+    tmp[1] = p1[1];
+    tmp[2] = p1[2];
+    tmp[3] = 0;
+    
+    float tmp1[4];
+    float tmp2[4];
+    add_quats_no_renorm(tmp, co, tmp1);
+
+    add_quats_no_renorm(q, tmp1, tmp2);
+    
+    p2[0] = tmp2[0];
+    p2[1] = tmp2[1];
+    p2[2] = tmp2[2];
+
+}
+////Olga

+ 86 - 0
examples/scene-rotation/trackball.h

@@ -0,0 +1,86 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * trackball.h
+ * A virtual trackball implementation
+ * Written by Gavin Bell for Silicon Graphics, November 1988.
+ */
+
+/*
+ * Pass the x and y coordinates of the last and current positions of
+ * the mouse, scaled so they are from (-1.0 ... 1.0).
+ *
+ * The resulting rotation is returned as a quaternion rotation in the
+ * first paramater.
+ */
+void
+trackball(float q[4], float p1x, float p1y, float p2x, float p2y);
+
+/*
+ * Given two quaternions, add them together to get a third quaternion.
+ * Adding quaternions to get a compound rotation is analagous to adding
+ * translations to get a compound translation.  When incrementally
+ * adding rotations, the first argument here should be the new
+ * rotation, the second and third the total rotation (which will be
+ * over-written with the resulting new total rotation).
+ */
+void
+add_quats(float *q1, float *q2, float *dest);
+
+/*
+ * A useful function, builds a rotation matrix in Matrix based on
+ * given quaternion.
+ */
+void
+build_rotmatrix(float m[4][4], float q[4]);
+
+void
+build_rotmatrix_v2(float m[16], float q[4]);
+
+/*
+ * This function computes a quaternion based on an axis (defined by
+ * the given vector) and an angle about which to rotate.  The angle is
+ * expressed in radians.  The result is put into the third argument.
+ */
+void
+axis_to_quat(float a[3], float phi, float q[4]);
+
+////Olga////
+void quat_to_axis(float q[4], float axis[3], float &phi);
+void add_quats_no_renorm(float q1[4], float q2[4], float dest[4]);
+void quat_rotate_point(float q[4], const float p1[3], float p2[3]);
+////Olga////

+ 3 - 1
include/igl/matlab_format.cpp

@@ -87,7 +87,9 @@ IGL_INLINE Eigen::IOFormat igl::matlab_format()
 }
 
 #ifndef IGL_HEADER_ONLY
-// Explicit template instanciations
+// Explicit template specialization
+// generated by autoexplicit.sh
+template Eigen::WithFormat<Eigen::Matrix<int, 4, 1, 0, 4, 1> > const igl::matlab_format<Eigen::Matrix<int, 4, 1, 0, 4, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<int, 4, 1, 0, 4, 1> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);
 template std::basic_string<char, std::char_traits<char>, std::allocator<char> > const igl::matlab_format<double>(Eigen::SparseMatrix<double, 0, int> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);
 template Eigen::WithFormat<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const igl::matlab_format<Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, std::string);
 template Eigen::WithFormat<Eigen::Array<int, -1, -1, 0, -1, -1> > const igl::matlab_format<Eigen::Array<int, -1, -1, 0, -1, -1> >(Eigen::PlainObjectBase<Eigen::Array<int, -1, -1, 0, -1, -1> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >);