// a simple trackball implementatioon #include #ifdef _WIN32 #include #endif #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "cvec3t.h" #include "cvec4t.h" #include "hmatrix.h" typedef CVec3T Vec3f; typedef CVec4T Vec4f; namespace WindowParams { static int WindowWidth = 800; static int WindowHeight = 600; static int MainWindow; }; Vec3f CameraPosition(5,5,5); Vec3f SphereCenter(0,0,0); float SphereRadius = 2; float ExaminerRotAngle = 0; Vec3f ExaminerRotAxis(0,1,0); HMatrix ExaminerRotation; void Reshape(int width, int height) { WindowParams::WindowWidth = width; WindowParams::WindowHeight = height; glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(40, width/float(height), 1, 10); } void Draw() { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(CameraPosition.x(),CameraPosition.y(),CameraPosition.z(),0,0,0,0,1,0); glClearColor( 0.6, 0.6,0.6,0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); // enable automatic rescaling of normals to unit length glEnable(GL_NORMALIZE); // enable two lights glEnable(GL_LIGHT0); // glEnable(GL_LIGHT1); // directional lights (w=0) along z axis glLightfv(GL_LIGHT0,GL_DIFFUSE, Vec4f(1, 1, 1,1)); glLightfv(GL_LIGHT0,GL_POSITION, Vec4f(0, 0, 1,0)); glLightfv(GL_LIGHT1,GL_DIFFUSE,Vec4f(1, 1, 1,1)); glLightfv(GL_LIGHT1,GL_POSITION, Vec4f(0, 0, -1,0)); glPushMatrix(); glMultMatrixf(ExaminerRotation); glutWireSphere(SphereRadius,10,10); glEnable(GL_LIGHTING); glutSolidTeapot(1.0); glDisable(GL_LIGHTING); glPopMatrix(); glutSwapBuffers(); } Vec3f ScreenToWorld(int windowid, int x, int y) { glutSetWindow(windowid); GLdouble modelview[16]; GLdouble projection[16]; GLint viewport[4]; double world_x, world_y, world_z; // get current modelview, projection and viewport transforms glGetDoublev(GL_MODELVIEW_MATRIX,modelview); glGetDoublev(GL_PROJECTION_MATRIX,projection); glGetIntegerv(GL_VIEWPORT,viewport); // this function computes inverse of VPM and applies it to (x,y,0) to convert from pixel to world coords // this computes the world coordinates of the point on the near plane of the frustum which corresponds to pixel (x,y) gluUnProject(x,y,0,modelview,projection,viewport, &world_x,&world_y,&world_z); return Vec3f(world_x,world_y,world_z); } bool SpherePoint(const Vec3f& center, float r, const Vec3f& pscreen, Vec3f& psphere) { Vec3f v = (pscreen- CameraPosition).dir(); Vec3f d = CameraPosition-center; float ddotv = d.dot(v); float D = ddotv*ddotv-d.dot() +r*r; if (D < 0) return false; float t = -ddotv-sqrt(D); psphere = CameraPosition+v*t; return true; } // a mouse button is pressed or released Vec3f CurrentPsphere; Vec3f NewPsphere; void MouseClick (int button, int state, int x, int y) { y = WindowParams::WindowHeight - y-1; if(state == GLUT_DOWN) { Vec3f psphere; if(SpherePoint(SphereCenter,SphereRadius,ScreenToWorld(WindowParams::MainWindow,x,y),psphere)) { CurrentPsphere = psphere; NewPsphere = psphere; } glutPostRedisplay(); } if(state == GLUT_UP) { CurrentPsphere = NewPsphere; } } void MouseMotion(int x, int y) { y = WindowParams::WindowHeight - y-1; Vec3f psphere; if(SpherePoint(SphereCenter,SphereRadius,ScreenToWorld(WindowParams::MainWindow,x,y),psphere)) { ExaminerRotAxis = cross(CurrentPsphere-SphereCenter, psphere-SphereCenter); ExaminerRotAngle = acos((CurrentPsphere-SphereCenter).dot(psphere-SphereCenter)/SphereRadius/SphereRadius); ExaminerRotation = HMatrix::Rotation(ExaminerRotAngle,ExaminerRotAxis)*ExaminerRotation; CurrentPsphere = psphere; } glutPostRedisplay(); } int main(int argc, char* argv[]) { // initialize glut and parse command-line aguments that glut understands glutInit(&argc, argv); // initialize dislay mode: 4 color components, double buffer and depth buffer glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH); glutInitWindowSize(WindowParams::WindowWidth,WindowParams::WindowHeight); WindowParams::MainWindow = glutCreateWindow("Trackball"); glutDisplayFunc(Draw); glutReshapeFunc(Reshape); // gets called whenever a mouse button is pressed or released glutMouseFunc(MouseClick); glutMotionFunc(MouseMotion); // this is an infinite loop get event - dispatch event which never returns glutMainLoop(); return 0; }