2008-11-12 17:42:24 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
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 */
|
2008-12-19 11:43:36 +01:00
|
|
|
#include <vcg/simplex/vertex/base.h>
|
|
|
|
#include <vcg/simplex/face/base.h>
|
2008-11-12 17:42:24 +01:00
|
|
|
#include <vcg/complex/trimesh/base.h>
|
|
|
|
|
|
|
|
/*include the algorihm that update bounding box and normals*/
|
|
|
|
#include <vcg/complex/trimesh/update/bounding.h>
|
|
|
|
#include <vcg/complex/trimesh/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(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<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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|