/* This extends the example in sample/trimesh_sdl.h. It loads the mesh specified in the command line and shows it. Navigate around the object with WASD (doom-like) or arrow modes. (wheel, or PGUP, PGDOWN, moves vertically) [F1] changes rendering style. [SPACE] toggles trackball modes. This example shows: - the use of a trackball in a time-syncronized application like a game. (exactly FPS times every seconds, the trackball is animated, but rendering can occurs fewer times). - how to set a trackball with NavigatorAwsd mode, i.e. to NAVIGATE around or inside the object rather than rotate it in front of the camera. */ #include #include #include /*include the base definition for the vertex, face, and meshes */ #include #include #include /*include the algorihm that update bounding box and normals*/ #include #include /*include the importer from disk*/ #include /*include wrapping of the trimesh towards opengl*/ #include /*include the trackball: NOTE, you the implementation of the trackball is found in the files: wrap/gui/trackball.cpp and wrap/gui/trackmode.cpp. You should include these files in your solution otherwise you'll get linking errors */ #include /* we define our own SDL_TIMER event */ #define SDL_TIMER SDL_USEREVENT // How many FPS? const int FPS = 30; using namespace vcg; using namespace std; /* Definition of our own types for Vertices, Faces, Meshes*/ class CEdge; // dummy prototype never used class CFace; class CVertex : public VertexSimp2< CVertex, CEdge, CFace, vertex::Coord3f, vertex::Normal3f >{}; class CFace : public FaceSimp2< CVertex, CEdge, CFace, face::VertexRef, face::Normal3f > {}; class CMesh : public vcg::tri::TriMesh< vector, vector > {}; //////////////////////////////////////////////////////////////////////////// // Globals: the mesh, the OpenGL wrapper to draw the mesh and the trackball. CMesh mesh; vcg::GlTrimesh glWrap; vcg::Trackball track; /* Internal state of the application */ int drawMode=2; int trackballMode=1; int width =800; int height = 600; vcg::Point3f observerPos(0,0.2,3); // (initial) point of view (object is in 0,0,0) vcg::Point3f objectPos(0,0,0); // (initial) point of view (object is in 0,0,0) /* Simple function that renders a floor */ void RenderFloor(){ glHint(GL_FOG_HINT, GL_NICEST); glDisable(GL_LIGHTING); double K = 500, c=0.7; glBegin(GL_QUADS); glColor3f(c,c,c); glNormal3f(0,1,0); glVertex3d(+K,0,+K); glVertex3d(+K,0,-K); glVertex3d(-K,0,-K); glVertex3d(-K,0,+K); glEnd(); glHint(GL_FOG_HINT, GL_FASTEST); glEnable(GL_LIGHTING); } /* Sets the trackball in a new mode: standard mode (rotate object in front of camera) or Navigation mode (navigate around/inside object) */ void SetTrackball(int mode){ // we define all possible trackModes that we could be using (static) static vcg::PolarMode polarMode; static vcg::SphereMode sphereMode; static vcg::NavigatorWasdMode navigatorMode; static vcg::InactiveMode inactiveMode; static vcg::ScaleMode scaleMode; static vcg::PanMode panMode; static vcg::ZMode zMode; // customize navigation mode... navigatorMode.SetTopSpeedsAndAcc(1.2f,0.6f,6); // this adds a neat human stepping effect navigatorMode.SetStepOnWalk(0.5f,0.015f); track.modes.clear(); track.Reset(); switch (mode) { case 1: // switch to navigator mode track.modes[vcg::Trackball::BUTTON_NONE] = NULL; track.modes[vcg::Trackball::WHEEL] = track.modes[vcg::Trackball::BUTTON_LEFT] = track.idle_and_keys_mode = &navigatorMode; track.inactive_mode = NULL; // nothing to draw when inactive track.SetPosition( observerPos ); break; default: // sweitch to default trackmode -- this is equivalent to a call to track->SetDefault() track.modes[vcg::Trackball::BUTTON_NONE] = NULL; track.modes[vcg::Trackball::BUTTON_LEFT] = &sphereMode; track.modes[vcg::Trackball::BUTTON_LEFT | vcg::Trackball::KEY_CTRL] = track.modes[vcg::Trackball::BUTTON_MIDDLE] = &panMode; track.modes[vcg::Trackball::WHEEL] = track.modes[vcg::Trackball::BUTTON_LEFT | vcg::Trackball::KEY_SHIFT] = &scaleMode; track.modes[vcg::Trackball::BUTTON_LEFT | vcg::Trackball::KEY_ALT] = &zMode; track.modes[vcg::Trackball::BUTTON_MIDDLE | vcg::Trackball::KEY_ALT] = track.idle_and_keys_mode = &sphereMode; track.inactive_mode = &inactiveMode; // draw a sphere when inactive track.SetPosition( objectPos ); } } /* Sets up OpenGL state */ void initGL() { glClearColor(0, 0, 0, 0); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_NORMALIZE); glEnable(GL_FOG); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT); glDisable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); } /* Response to a window resize event. */ void myReshapeFunc(GLsizei w, GLsizei h) { SDL_SetVideoMode(w, h, 0, SDL_OPENGL|SDL_RESIZABLE|SDL_DOUBLEBUF); width=w; height=h; glViewport (0, 0, (GLsizei) w, (GLsizei) h); initGL(); } /* sends a SDL_TIMER event to self (timer callback) */ Uint32 timerCallback(Uint32 interval, void *param){ static SDL_Event e; // the msg e.type = SDL_TIMER; // its content SDL_PushEvent(&e); return interval; } /* sends a redraw event to self */ void sendRedraw() { static SDL_Event e; // the msg e.type = SDL_VIDEOEXPOSE; // its content SDL_PushEvent(&e); } /* clears any "redraw" even still in the event queue */ void drainRedrawEvents(){ static SDL_Event tmp[200]; int eventRemoved = SDL_PeepEvents(tmp,200,SDL_GETEVENT, SDL_EVENTMASK(SDL_VIDEOEXPOSE)); } /* Response to a redraw event: renders the scene */ void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(40, width/(float)height, 0.01, 10); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslate( -observerPos ); track.GetView(); track.Apply(); glPushMatrix(); float d=mesh.bbox.Diag(); glScale(1.5f/d); Point3f p = glWrap.m->bbox.Center(); p[1] = glWrap.m->bbox.min[1]; glTranslate(-p); // the trimesh drawing calls switch(drawMode) { case 0: glWrap.Draw ();break; case 1: glWrap.Draw ();break; case 2: glWrap.Draw ();break; case 3: glWrap.Draw ();break; case 4: glWrap.Draw ();break; default: break; } glPopMatrix(); RenderFloor(); track.DrawPostApply(); SDL_GL_SwapBuffers(); } /* Response to a timer event: animates the status of the game (and the trackball) */ // - retunrs true if anything changed. bool onTimer(){ int res = false; if ( track.IsAnimating() ) { track.Animate(1000/FPS); res = true; } // insert any other animation processing here return res; } /* Helper function: translates SDL codes into VCG codes */ vcg::Trackball::Button SDL2VCG(int code){ switch (code) { case SDL_BUTTON_LEFT: return vcg::Trackball::BUTTON_LEFT; case SDL_BUTTON_MIDDLE: return vcg::Trackball::BUTTON_MIDDLE; case SDL_BUTTON_RIGHT: return vcg::Trackball::BUTTON_RIGHT; case SDLK_RCTRL: case SDLK_LCTRL: return vcg::Trackball::KEY_CTRL; case SDLK_RALT: case SDLK_LALT: return vcg::Trackball::KEY_ALT; case SDLK_LSHIFT: case SDLK_RSHIFT: return vcg::Trackball::KEY_SHIFT; case SDLK_LEFT: case SDLK_a: return vcg::Trackball::KEY_LEFT; case SDLK_RIGHT: case SDLK_d: return vcg::Trackball::KEY_RIGHT; case SDLK_UP: case SDLK_w: return vcg::Trackball::KEY_UP; case SDLK_DOWN: case SDLK_s: return vcg::Trackball::KEY_DOWN; case SDLK_PAGEUP: case SDLK_r: return vcg::Trackball::KEY_PGUP; case SDLK_PAGEDOWN: case SDLK_f: return vcg::Trackball::KEY_PGDOWN; return vcg::Trackball::BUTTON_NONE; } } /* initializes SDL */ bool initSDL(const std::string &str) { if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0 ) { fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); exit(1); } if ( SDL_SetVideoMode(width, height, 0, SDL_OPENGL|SDL_RESIZABLE) == NULL ) { fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError()); SDL_Quit(); exit(2); } SDL_AddTimer(1000/FPS, timerCallback, NULL); SDL_WM_SetCaption(str.c_str(), str.c_str()); myReshapeFunc(width, height); return true; } /* The main event Loop */ int sdlLoop() { bool quit=false; bool redraw_needed = false; // true whan a scene needs a redraw SDL_Event event; while( !quit ) { SDL_WaitEvent(&event); switch( event.type ) { case SDL_QUIT: quit = true; break; case SDL_VIDEORESIZE : myReshapeFunc(event.resize.w,event.resize.h); break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: exit(0); break; case SDLK_F1: drawMode= (drawMode+1)%5; printf("Current Mode %i\n",drawMode); break; case SDLK_HOME: track.Reset(); break; case SDLK_SPACE: trackballMode= (trackballMode+1)%2; printf("Trackball Mode %i\n",drawMode); SetTrackball(trackballMode); break; default: track.ButtonDown( SDL2VCG( event.key.keysym.sym) ); } redraw_needed = true; break; case SDL_KEYUP: track.ButtonUp( SDL2VCG( event.key.keysym.sym) ); break; case SDL_MOUSEBUTTONDOWN: switch(event.button.button) { case SDL_BUTTON_WHEELUP: track.MouseWheel( 1); redraw_needed = true; break; case SDL_BUTTON_WHEELDOWN: track.MouseWheel(-1); redraw_needed = true; break; default: track.MouseDown(event.button.x, (height - event.button.y), SDL2VCG(event.button.button) ); break; } break; case SDL_MOUSEBUTTONUP: track.MouseUp(event.button.x, (height - event.button.y), SDL2VCG(event.button.button) ); break; break; case SDL_MOUSEMOTION: while(SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEMOTIONMASK)); track.MouseMove(event.button.x, (height - event.button.y)); redraw_needed = true; break; case SDL_TIMER: if (onTimer()) redraw_needed = true; if (redraw_needed) sendRedraw(); // justs sends a redraw event! redraw_needed = false; break; case SDL_VIDEOEXPOSE: // any rendering is done ONLY here. display(); drainRedrawEvents(); break; default: break; } } SDL_Quit(); return -1; } int main(int argc, char *argv[]) { // Generic loading of the mesh from disk if(vcg::tri::io::Importer::Open(mesh,argv[1])!=0) { fprintf(stderr,"Error reading file %s\n",argv[1]); exit(0); } // Initialize the mesh itself vcg::tri::UpdateBounding::Box(mesh); // update bounding box //vcg::tri::UpdateNormals::PerVertexNormalizePerFaceNormalized(mesh); // update Normals vcg::tri::UpdateNormals::PerVertexPerFace(mesh); // update Normals // Initialize the wrapper glWrap.m = &mesh; glWrap.Update(); SetTrackball(trackballMode); // we will do exaclty an animation step every 1000/FPS msecs. track.SetFixedTimesteps(true); initSDL("SDL_minimal_viewer"); initGL(); sdlLoop(); exit(0); }