diff --git a/apps/sample/trackball_SDL/trackball_sdl.cpp b/apps/sample/trackball_SDL/trackball_sdl.cpp new file mode 100644 index 00000000..c6454027 --- /dev/null +++ b/apps/sample/trackball_SDL/trackball_sdl.cpp @@ -0,0 +1,380 @@ +/* + +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(false); + 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); +} + + +