You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
12 KiB
380 lines
12 KiB
/* |
|
|
|
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 <SDL/SDL.h> |
|
#include <gl/glew.h> |
|
#include <vector> |
|
|
|
/*include the base definition for the vertex, face, and meshes */ |
|
#include <vcg/simplex/vertex/base.h> |
|
#include <vcg/simplex/face/base.h> |
|
#include <vcg/complex/complex.h> |
|
|
|
/*include the algorihm that update bounding box and normals*/ |
|
#include <vcg/complex/algorithms/update/bounding.h> |
|
#include <vcg/complex/algorithms/update/normal.h> |
|
|
|
/*include the importer from disk*/ |
|
#include <wrap/io_trimesh/import.h> |
|
|
|
/*include wrapping of the trimesh towards opengl*/ |
|
#include <wrap/gl/trimesh.h> |
|
|
|
/*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 <wrap/gui/trackball.h> |
|
|
|
/* 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<CVertex>, vector<CFace> > {}; |
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////// |
|
// Globals: the mesh, the OpenGL wrapper to draw the mesh and the trackball. |
|
CMesh mesh; |
|
vcg::GlTrimesh<CMesh> 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<vcg::GLW::DMSmooth, vcg::GLW::CMNone,vcg::GLW::TMNone> ();break; |
|
case 1: glWrap.Draw<vcg::GLW::DMWire, vcg::GLW::CMNone,vcg::GLW::TMNone> ();break; |
|
case 2: glWrap.Draw<vcg::GLW::DMFlatWire, vcg::GLW::CMNone,vcg::GLW::TMNone> ();break; |
|
case 3: glWrap.Draw<vcg::GLW::DMHidden, vcg::GLW::CMNone,vcg::GLW::TMNone> ();break; |
|
case 4: glWrap.Draw<vcg::GLW::DMFlat, vcg::GLW::CMNone,vcg::GLW::TMNone> ();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<CMesh>::Open(mesh,argv[1])!=0) { |
|
fprintf(stderr,"Error reading file %s\n",argv[1]); |
|
exit(0); |
|
} |
|
|
|
// Initialize the mesh itself |
|
vcg::tri::UpdateBounding<CMesh>::Box(mesh); // update bounding box |
|
//vcg::tri::UpdateNormals<CMesh>::PerVertexNormalizePerFaceNormalized(mesh); // update Normals |
|
vcg::tri::UpdateNormals<CMesh>::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); |
|
} |
|
|
|
|
|
|
|
|