#if 0 #!/bin/sh FILE=`basename $0 | sed "s/\.[Cc].*$//"` EXT=` basename $0 | sed "s/^.*\.\([Cc].*\)$/\1/"` CC="gcc -g -Wall -Wextra -pedantic -std=c99" if [ -z "$SDL_CONFIG" ]; then SDL_CONFIG="sdl-config" fi CFLAGS=`$SDL_CONFIG --cflags` LFLAGS="`$SDL_CONFIG --libs` -lGL -lGLU -lGLEW -lode" $CC $CFLAGS $FILE.$EXT -o $FILE $LFLAGS exit #endif #include #include #include "SDL.h" #include #include #define WIN_W 400 #define WIN_H 400 #define WIN_W2 (WIN_W/2) #define WIN_H2 (WIN_H/2) #define MAX_CONTACTS 10 typedef struct { dBodyID body; dMass mass; dGeomID geom; } Object; // Not a good idea to use globals, but... :P enum { oCube, oSphere, oComplex, objsCount }; Object objs[objsCount]; dReal masses[objsCount] = {1.0, 2.0, 4.0}; char *names[objsCount] = {"Cube", "Sphere", "Complex"}; float colors[objsCount][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; dSpaceID space; dGeomID plane; dTriMeshDataID triMesh; dReal contactPoints[100][3]; int contactPointsCount = 0; dJointGroupID contactGroup; dWorldID world; float orthoView = 22.0; void drawObject(Object *o) { int type = dGeomGetClass(o->geom); switch (type) { case dSphereClass: case dBoxClass: case dTriMeshClass: { float rad = dGeomSphereGetRadius(o->geom); GLUquadric *q = gluNewQuadric(); gluSphere(q, rad, 8, 8); break; } default: printf(" UNKNOWN OBJECT CLASS!\n"); } } // When two objects are near, this handler is called for each couple of objects // User defined data isn't used void collisionHandler(void *dataUnused, dGeomID o1, dGeomID o2) { // Since here there is just one space, we don't check for geometry type: // they are not spaces dContactGeom contacts[MAX_CONTACTS]; int collisions = dCollide(o1, o2, MAX_CONTACTS, contacts, sizeof(dContactGeom)); printf("%d collision points\n", collisions); // Draw contact points for (int i = 0; i < collisions; ++i) { dGeomID g1 = contacts[i].g1, g2 = contacts[i].g2; if (g1 == g2) continue; float *pos = contacts[i].pos; printf("Copoint %d: %f %f %f\n", i, pos[0], pos[1], pos[2]); contactPoints[contactPointsCount][0] = pos[0]; contactPoints[contactPointsCount][1] = pos[1]; contactPoints[contactPointsCount][2] = pos[2]; contactPointsCount++; char *o1Name = dGeomGetData(g1); char *o2Name = dGeomGetData(g2); const dReal *o1Pos = dGeomGetPosition(g1); const dReal *o2Pos = dGeomGetPosition(g2); printf("Collision between %s (%x) and %s (%x)\n", o1Name, g1, o2Name, g2); } } void nearCallback(void *unused, dGeomID o1, dGeomID o2) { dBodyID body1 = dGeomGetBody(o1); dBodyID body2 = dGeomGetBody(o2); dContact contact[MAX_CONTACTS]; for (int i = 0; i < MAX_CONTACTS; i++) { contact[i].surface.mode = dContactBounce; // Bouncy surface contact[i].surface.bounce = 0.5; contact[i].surface.mu = 100.0; // Friction, but doesn't work :P } int collisions = dCollide(o1, o2, MAX_CONTACTS, &contact[0].geom, sizeof(dContact)); if (collisions) { for (int i = 0; i < collisions; ++i) { dJointID c = dJointCreateContact(world, contactGroup, contact + i); dJointAttach(c, body1, body2); } } } void setViewPersp() { glShadeModel(GL_SMOOTH); glViewport(0, WIN_H2, WIN_W2, WIN_H2); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, WIN_W / WIN_H, 0.01, 300.0); glMatrixMode(GL_MODELVIEW); } void setViewOrtho(int x, int y, int w, int h) { glShadeModel(GL_SMOOTH); glViewport(x, y, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-orthoView, orthoView, -orthoView, orthoView, -orthoView, orthoView); glMatrixMode(GL_MODELVIEW); } void drawEverything() { const dReal *realP; // Drawing bodies for (int i = 0; i < objsCount; ++i) { glPushMatrix(); glColor3fv(colors[i]); realP = dBodyGetPosition(objs[i].body); glTranslatef(realP[0], realP[1], realP[2]); drawObject(&objs[i]); glPopMatrix(); } // Draw plane dVector3 v1, v2, v3; glPushMatrix(); glColor3f(1.0, 1.0, 1.0); realP = dGeomGetPosition(plane); // glTranslatef(realP[0], realP[1], realP[2]); glBegin(GL_TRIANGLES); for (int i = 0; i < 2; ++i) { dGeomTriMeshGetTriangle(plane, i, v1, v2, v3); // printf("Triangle %d: %2.1f %2.1f %2.1f, %2.1f %2.1f %2.1f, %2.1f %2.1f %2.1f\n", i, // v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], v3[0], v3[1], v3[2]); glVertex3fv(v1); glVertex3fv(v2); glVertex3fv(v3); } glEnd(); glPopMatrix(); // Draw contact points glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glPointSize(7.0); glColor3f(1.0, 0.0, 1.0); glBegin(GL_POINTS); for (int i = 0; i < contactPointsCount; ++i) glVertex3fv(contactPoints[i]); glEnd(); glPopAttrib(); } int main(int argc, char **argv) { /* SDL Info and flags */ SDL_Event sdlEv; /* SDL events */ SDL_VideoInfo *sdlVideoInfo; /* Info about the video */ SDL_Surface *screen; /* SDL Screen surface */ Uint32 sdlVideoFlags = SDL_OPENGL; /* Video flags */ int sdlbpp = 0; /* Bits per pixel */ /* Application data */ Uint8 quit; /* Initialize */ if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDL_Init: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } atexit(SDL_Quit); /* Retrive video info */ sdlVideoInfo = (SDL_VideoInfo *)SDL_GetVideoInfo(); /* Set flags based on video info */ if (sdlVideoInfo->hw_available) sdlVideoFlags |= SDL_HWSURFACE; else sdlVideoFlags |= SDL_SWSURFACE; if (sdlVideoInfo->blit_hw) sdlVideoFlags |= SDL_HWACCEL; sdlbpp = sdlVideoInfo->vfmt->BitsPerPixel; /* Start graphic system with OGL */ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); if (!(screen = SDL_SetVideoMode(WIN_W, WIN_H, sdlbpp, sdlVideoFlags))) { fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } // Set a perspective view glShadeModel(GL_SMOOTH); glViewport(0, 0, WIN_W, WIN_H); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, WIN_W / WIN_H, 0.01, 300.0); glMatrixMode(GL_MODELVIEW); // Setup something nice float light0_pos[] = {5.0, 5.0, 10.0, 0.0}; glLightfv(GL_LIGHT0, GL_POSITION, light0_pos); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL); glPointSize(3.0); // Light background to see better glClearColor(0.5, 0.5, 0.5, 0.0); glewInit(); if (GLEW_VERSION_2_0) fprintf(stderr, "INFO: OpenGL 2.0 supported\n"); else fprintf(stderr, "INFO: OpenGL 2.0 not supported\n"); /* Application Initialization */ // Initialize the world world = dWorldCreate(); dWorldSetGravity(world, 0.0, -4.0, 0.0); // Let's create a space space = dSimpleSpaceCreate(0); // Initialize objects for (int i = 0; i < objsCount; ++i) { // Create an object in the world objs[i].body = dBodyCreate(world); // Create a mass with the distribution of a sphere dMassSetSphereTotal(&objs[i].mass, masses[i], 1.0); // Apply the mass to the cube dBodySetMass(objs[i].body, &objs[i].mass); // Translate the body somewhere dBodySetPosition(objs[i].body, (i - 1) * 4.0, 0.0, 0.0); // Set a starting speed dBodySetLinearVel(objs[i].body, (i - 1) * -1.0, 0.0, 0.0); // Create a geometry objs[i].geom = dCreateSphere(space, (i / 3.0) + 1.0); // And givin it some data dGeomSetData(objs[i].geom, names[i]); dGeomSetBody(objs[i].geom, objs[i].body); } // Plane geometry const int indexes[6] = {2, 1, 0, 3, 2, 0}; const dVector3 triVert[4] = { { 10.0, 0.0, 10.0}, {-10.0, 0.0, 10.0}, {-10.0, 0.0, -10.0}, { 10.0, 0.0, -10.0} }; triMesh = dGeomTriMeshDataCreate(); dGeomTriMeshDataBuildSimple(triMesh, triVert, 4, indexes, 6); plane = dCreateTriMesh(space, triMesh, NULL, NULL, NULL); dGeomSetData(plane, "Plane"); dGeomSetPosition(plane, 0, -10.0, 0); // Create a group for joints contactGroup = dJointGroupCreate(0); // maxsize is always 0 int mouseButtonPressed = 0; int running = 1; float sceneRotX = 0.0, sceneRotY = 0.0, sceneTraX = 0.0, sceneTraY = 5.0; /* Main loop */ quit = 0; while (!quit) { while (SDL_PollEvent(&sdlEv)) switch (sdlEv.type) { case SDL_QUIT: quit = 1; break; case SDL_MOUSEBUTTONDOWN: mouseButtonPressed = sdlEv.button.button; switch (mouseButtonPressed) { case SDL_BUTTON_RIGHT: { running = !running; break; } case 4: { orthoView -= 1.0; break; } case 5: { orthoView += 1.0; break; } default: break; } break; case SDL_MOUSEBUTTONUP: mouseButtonPressed = 0; break; case SDL_MOUSEMOTION: if (mouseButtonPressed == SDL_BUTTON_MIDDLE) { sceneRotY += sdlEv.motion.xrel / 2.0; sceneRotX += sdlEv.motion.yrel / 2.0; } if (mouseButtonPressed == SDL_BUTTON_LEFT) { sceneTraY += sdlEv.motion.yrel / 5.0; sceneTraX -= sdlEv.motion.xrel / 5.0; } break; default: break; } // Calculate collisions if (running) { contactPointsCount = 0; // dSpaceCollide(space, 0, collisionHandler); dSpaceCollide(space, 0, nearCallback); } // Drawing glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // View perspective scene glEnable(GL_LIGHTING); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); setViewPersp(); glLoadIdentity(); glTranslatef(sceneTraX, sceneTraY, -20.0); glRotatef(sceneRotX, 1.0, 0.0, 0.0); glRotatef(sceneRotY, 0.0, 1.0, 0.0); drawEverything(); // Fixed camera from sides glDisable(GL_LIGHTING); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Top setViewOrtho(WIN_W2, WIN_H2, WIN_W2, WIN_H2); glLoadIdentity(); glRotatef(90.0, 1.0, 0.0, 0.0); drawEverything(); float *p = dBodyGetPosition(objs[1].body); // Left setViewOrtho(0, 0, WIN_W2, WIN_H2); glLoadIdentity(); glRotatef(90.0, 0.0, 1.0, 0.0); glTranslatef(0.0, -p[1], 0.0); drawEverything(); // Right setViewOrtho(WIN_W2, 0, WIN_W2, WIN_H2); glLoadIdentity(); glTranslatef(0.0, -p[1], 0.0); drawEverything(); // Step if (running) dWorldQuickStep(world, 0.05); // Clear joints if (running) dJointGroupEmpty(contactGroup); SDL_GL_SwapBuffers(); SDL_Delay(20); } dJointGroupDestroy(contactGroup); dSpaceDestroy(space); dWorldDestroy(world); return EXIT_SUCCESS; }