#define GLUT_API_VERSION 4 #include #ifdef _WIN32 #include #endif #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #include "cvec2t.h" #include "cvec3t.h" #include "cvec4t.h" #include "hmatrix.h" typedef CVec2T Vec2f; typedef CVec3T Vec3f; typedef CVec4T Vec4f; typedef void (*DisplayFuncType)(void); namespace WindowParams { static int WindowWidth = 800; static int WindowHeight = 600; static int MainWindow; }; namespace Params { const float WorldWidth = 10*1.33; const float WorldHeight = 10; const Vec3f BackgroundColor(0,0,0); const int TimerStep = 16; // millisec }; const Vec3f red(1,0,0); const Vec3f blue(0,0,1); const Vec3f green(0,1,0); const Vec3f white(1,1,1); class Camera { public: Camera(): Position(10,10,10),ViewCenter(0,0,0),Up(0,1,0), FieldOfView(40),AspectRatio(1.33),Near(1),Far(20),moving(false) {} // glLookAt arguments Vec3f Position; Vec3f ViewCenter; Vec3f Up; // gluPerspective arguments float FieldOfView; float AspectRatio; float Near; float Far; bool moving; }; Camera* perspCamera; Vec3f SphereCenter(0,0,0); float SphereRadius = 5; float ExaminerRotAngle = 0; Vec3f ExaminerRotAxis(0,1,0); HMatrix ExaminerRotation; void Reshape(int width, int height) { WindowParams::WindowWidth = width; WindowParams::WindowHeight = height; // make sure the aspect ratio of the camera in gluPerspective/glFrustum matches the window // aspect ratio perspCamera->AspectRatio = width/float(height); glViewport(0,0,width,height); } // red = X axis, green = Y, blue = Z void drawAxes() { glLineWidth(3.0); float d = min(Params::WorldWidth,Params::WorldHeight); glBegin(GL_LINES); glColor3fv(red); glVertex3f(0,0,0); glVertex3f(d,0,0); glColor3fv(green); glVertex3f(0,0,0); glVertex3f(0,d,0); glColor3fv(blue); glVertex3f(0,0,0); glVertex3f(0,0,d); glEnd(); } void DrawPlanes() { glColor3f(0,1,1); glBegin(GL_POLYGON); glNormal3f(1,0,0); glVertex3f(0,0,0); glVertex3f(0,1,0); glVertex3f(0,1,1); glVertex3f(0,0,1); glEnd(); glColor3f(1,0,1); glBegin(GL_POLYGON); glNormal3f(0,1,0); glVertex3f(0,0,0); glVertex3f(0,0,1); glVertex3f(1,0,1); glVertex3f(1,0,0); glEnd(); glColor3f(1,1,0); glBegin(GL_POLYGON); glNormal3f(0,0,1); glVertex3f(0,0,0); glVertex3f(1,0,0); glVertex3f(1,1,0); glVertex3f(0,1,0); glEnd(); } // all drawing except initializing the buffer and camera setup (projection matrix + lookat) void DrawScene() { 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)); // flip normals for polygons facing away from the screen // this ensures the back facing polygins are lit glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1); glEnable(GL_LIGHTING); glPushMatrix(); glPushMatrix(); glTranslatef(2,2,2); glutSolidTeapot(1.0); glPopMatrix(); glDisable(GL_LIGHTING); // axes and planes are drawn without lighting glPushMatrix(); glScalef(20,20,20); DrawPlanes(); glPopMatrix(); drawAxes(); glPopMatrix(); } void Draw() { glClearColor( 0.6, 0.6,0.6,0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); float fov_angle = perspCamera->FieldOfView; float ar = perspCamera->AspectRatio; float n = perspCamera->Near; float f = perspCamera->Far; // this gluPerspective call is equivalent to glFrustum call below gluPerspective(fov_angle, // field of view in degrees ar, // aspect ratio n, // near distance f); // far distance /* glFrustum( -ar*n*tan(fov_angle/180.0*M_PI/2),// left ar*n*tan(fov_angle/180.0*M_PI/2), // right -n*tan(fov_angle/180.0*M_PI/2), // bottom n*tan(fov_angle/180.0*M_PI/2), // top n,f ); */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( perspCamera->Position.x(), perspCamera->Position.y(),perspCamera->Position.z(), perspCamera->ViewCenter.x(),perspCamera->ViewCenter.y(),perspCamera->ViewCenter.z(), perspCamera->Up.x(),perspCamera->Up.y(),perspCamera->Up.z()); DrawScene(); 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- perspCamera->Position).dir(); Vec3f d = perspCamera->Position-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 = perspCamera->Position+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) { perspCamera->ViewCenter = ScreenToWorld(WindowParams::MainWindow,x,y); } glutPostRedisplay(); } void MouseMotion(int x, int y) { } void MousePassiveMotion(int x,int y) { } void Keyboard(unsigned char key, int x, int y) { if(key == ' ') { // this creates camera with default parameters delete perspCamera; perspCamera = new Camera; } glutPostRedisplay(); } void Animate(int time) { if(perspCamera->moving) { perspCamera->Position += (perspCamera->ViewCenter-perspCamera->Position).dir()*0.06; perspCamera->ViewCenter += (perspCamera->ViewCenter-perspCamera->Position).dir()*0.06; } glutTimerFunc(Params::TimerStep,Animate,0); 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("Camera"); // gets called whenever a mouse button is pressed or released glutMouseFunc(MouseClick); // gets called when a key is pressed glutKeyboardFunc(Keyboard); glutMotionFunc(MouseMotion); glutPassiveMotionFunc(MousePassiveMotion); glutTimerFunc(Params::TimerStep,Animate,0); // register callbacks for main window glutSetWindow(WindowParams::MainWindow); // dummy callback, no part of the main window is ever visible glutDisplayFunc(Draw); glutReshapeFunc(Reshape); perspCamera = new Camera; // this is an infinite loop get event - dispatch event which never returns glutMainLoop(); return 0; }