/***********************************************************************/ /* gravedad_colision-3D.cpp. Juan Gonzalez. Mayo 2006 */ /*---------------------------------------------------------------------*/ /* LICENCIA GPL */ /* Programa derivado del ejemplo test_boxstack.cpp que viene con las */ /* fuentes del ODE. Por Russell L. Smith */ /*---------------------------------------------------------------------*/ /* Ejemplo "hola mundo" del motor fisico ODE (Open Dynamics Engine) */ /* Es similar al programa gravedad_colision, pero se ha anadido */ /* representacion en 3D, de manera que se puede ver el objeto */ /* cayendo. */ /* Se ha realizado a partir del ejemplo test_boxstack.cpp que viene */ /* en el directorio ode/test de las fuentes del ODE. */ /* Se ha simplificado al maximo y se han anadido comentarios para */ /* que se entienda. */ /*---------------------------------------------------------------------*/ /* Es un ejemplo "hola_mundo" que incluye representacion en 3D. */ /* Se coloca una "caja" a una altura, y al comenzar la simulacion */ /* se puede ver como cae. */ /* Los unicos objetos que hay en el mundo son la caja y un plano */ /* que hace de suelo */ /***********************************************************************/ #include <ode/ode.h> #include <unistd.h> #include "drawstuff.h" //*********************************************************************** // Sobre la libreria DRAWSTUFF. //----------------------------------------------------------------------- // Esta libreria no es parte del motor fisico ODE. Se utiliza para // hacer mas facil el dibujo en 3D // Inicialmente se dibuja una reticula (grid) que tiene 9 puntos. // El punto central (amarillo) es el origen (0,0,0). // El rojo indica la direccion positiva de las Xs // El azul la direccion positiva de las Ys // Estos puntos estan separados una distancia de 1 //************************************************************************ /*********************************************/ /* Algunas constantes usadas en el programa */ /*********************************************/ // Numero maximo de puntos de contacto. Es para la deteccion de // colisiones #define MAX_CONTACTS 4 //-- Estructura que representa los objetos del universo //-- Hay dos tipos de elementos: //-- * Cuerpos (bodys): Tienen la informacion sobre posicion, //-- orientacion, velocidad lineal y velocidad de rotacion //-- * Elementos geometricos: Determinal la forma y se usan para //-- las colisiones //-- Un objeto esta formado por el cuerpo y su geometria (o la //-- composicion de varias geometrias. struct MyObject { dBodyID body; // Cuerpo dGeomID geom; // Elemento geometrico }; //******************************************* // VARIABLES GLOBALES DEL PROGRAMA //******************************************* //-- Identificador para el mundo static dWorldID world; //-- Identificador del espacio (para colisiones) //-- Para detectar las colisiones hay que crear un espacio con los //-- elementos que pueden colisionar. static dSpaceID space; //-- Identificador del grupo de articulaciones de los puntos de contactos //-- Cuando hay una colision, se crean puntos de contacto entre las //-- superficies y actuan como articulaciones: los objetos rotaran con //-- respecto a estos puntos de contacto. static dJointGroupID contactgroup; //-- El objeto que situamos en el mundo: la caja static MyObject obj; /***************************************************************************/ /* CODIGO */ /***************************************************************************/ /********************************************************************/ /* Crear la "caja" */ /* Se define la "caja" usando la API de ODE y se asocia al "mundo" */ /********************************************************************/ void Crear_objeto() { dMass m; //-- Crear el Cuerpo y asociarlo al mundo obj.body = dBodyCreate (world); //-- Establecer la posicion inicial. Se pasan las coordenadas x,y,z //-- En este ejemplo el objeto esta en el origen, a una altura de //-- 4 unidades dBodySetPosition(obj.body, 0,0,3); //-- Establecer la rotacion //-- Cada objeto puede estar rotado. Se crea una matriz de //-- rotacion pasandole como parametros el vector que hace de //-- eje de giro y el angulo que se rota. En este ejemplo se pasa //-- el eje z y se rota '0' grados con respecto a el. //-- Probar este mismo ejemplo con los parametros (R,0,0,1,45) dMatrix3 R; dRFromAxisAndAngle(R,0,0,1,0); dBodySetRotation (obj.body,R); //-- Establecer la masa del cuerpo //-- Hay que especificar la masa total y las dimensiones //-- del cubo. Como masa se toma 0.5 y las dimensiones de la caja //-- son 0.5 x 0.5 x 0.1 dMassSetBoxTotal (&m,0.5,0.5,0.5,0.1); dBodySetMass (obj.body,&m); //-- Crear la geometria: un cubo que se anadira al espacio para detectar //-- las colisiones. Las dimensiones son 0.5 x 0.5 x 0.1 obj.geom = dCreateBox (space,0.5,0.5,0.1); //-- Asociar el cuerpo con la geometria dGeomSetBody (obj.geom,obj.body); } /****************************************************************/ /* Funcion de retrollamada invocada por dSpaceCollide cuando */ /* dos objetos del espacio estan a punto de colisionar */ /* El ODE permite que los usuarios avanzados puedan implementar */ /* su propia rutina de colision. Para los usuarios no expertos */ /* que simplemente quieren una colision estandar, esta es la */ /* rutina que SIEMPRE deberan usar. No es solo valida para este */ /* ejemplo, vale para cualquiera. Pero no esta incluida en la */ /* libreria del ODE para que se pueda adaptar a otras */ /* necesidades. /****************************************************************/ static void nearCallback (void *data, dGeomID o1, dGeomID o2) { int i; //-- Obtener los cuerpos asociados dBodyID b1 = dGeomGetBody(o1); dBodyID b2 = dGeomGetBody(o2); //-- Si ya estan conectados por una articulacion, terminar if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return; //-- Crear contactos dContact contact[MAX_CONTACTS]; for (i=0; i<MAX_CONTACTS; i++) { contact[i].surface.mode = dContactBounce | dContactSoftCFM; contact[i].surface.mu = dInfinity; contact[i].surface.mu2 = 0; contact[i].surface.bounce = 0.1; contact[i].surface.bounce_vel = 0.1; contact[i].surface.soft_cfm = 0.01; } //-- Obtener los puntos de contacto int numc = dCollide (o1,o2,MAX_CONTACTS,&contact[0].geom, sizeof(dContact)); //-- Si hay al menos algun punto de contacto... if (numc!=0) { //-- Para cada punto de contacto crear una articulacion for (i=0; i<numc; i++) { //-- Crear articulacion y meterla en el grupo contactgroup dJointID c = dJointCreateContact (world,contactgroup,&contact[i]); //-- Establecer la artiulacion entre los dos cuerpos dJointAttach (c,b1,b2); } } } //************************************************************** //-- Funcion de retrollamada de comienzo de la simulacion //-- Se utiliza principalmente para establecer el punto de vista //-- inicial (posicion de la camara en el universo) //************************************************************** static void start() { //-- Esta es la posicion de la camara. Se pasan las coordenadas x,y,z static float xyz[3] = {-6.0f,0.3f,2.0f}; //-- Esta es la orientacion de la camara. //-- Las componentes son Pan, Tilt (inclinacion) y Roll //-- El valor (0,0,0) indica que la camara esta apuntando en //-- direccion positiva de las X, paralelamente al suelo. static float hpr[3] = {0.0f,-19.0f,0.0f}; //-- Establecer la posicion de la camara dsSetViewpoint (xyz,hpr); } /**************************************************/ /* FUNCION DE RETROLLAMADA. */ /* Se invoca cuando se pulsa una tecla */ /**************************************************/ static void command (int cmd) { //-- En este ejemplo no se usan las teclas printf ("Tecla pulsada: %c\n",cmd); } /**************************/ /* DIBUJAR una geometria */ /**************************/ void drawGeom (dGeomID g) { int i; const dReal *pos; const dReal *R; dVector3 sides; //-- Leer la posicion y orintacion de la geometria pos = dGeomGetPosition (g); R = dGeomGetRotation (g); //-- Obtener las tres componentes: altura, ancho, largo de la caja dGeomBoxGetLengths (g,sides); //-- Dibujar! dsDrawBoxD (pos,R,sides); } /***************************************************/ /* FUNCION DE RETROLLAMADA */ /* Simulacion de un paso */ /* Se invoca a esta funcion cada vez que hay que */ /* simular un nuevo paso */ /* El argumento pause es 1 si la simulacicon esta */ /* en modo "pause". En ese caso solo hay que */ /* dibujar el universo tal y como esta */ /***************************************************/ static void simLoop (int pause) { if (!pause) { //-- Deteccion de colisiones. Determinar que pares de elementos geometricos //-- estan a punto de colisionar. Se llama a la funcion de retrollamada //-- nearCallback, pasando como argumento los dos elementos. dSpaceCollide (space,0,&nearCallback); //-- Realizar un paso de simulacion dWorldStep(world,0.01); // Eliminar todos los puntos de contacto dJointGroupEmpty (contactgroup); } //-- Dibujar el objeto //-- Establecer el color dsSetColor (1,1,0); //-- Especificar la textura dsSetTexture (DS_WOOD); //-- Dibujar drawGeom (obj.geom); //-- Pequena pausa para no cargar tanto la CPU //-- Si la simulacion va lenta se puede eliminar usleep(100); } /*******************/ /* MAIN */ /*******************/ int main (int argc, char **argv) { /*-------------------------------------------------------------*/ /* Establecer parametros y las funciones de retrollamadas */ /* de la libreria de dibujo */ /*-------------------------------------------------------------*/ dsFunctions fn; //-- Retrollamada de comienzo de la simulacion fn.start = &start; //-- Retrollamada de un paso en la simulacion fn.step = &simLoop; //-- Retrollamada para tecla pulsada fn.command = &command; //-- Otros fn.version = DS_VERSION; fn.stop = 0; fn.path_to_textures = "./textures"; /********************************************************************/ /* Crear el mundo. Es un contenedor de todos los objetos a simular */ /* El mundo no sabe nada de como se dibujan los objetos */ /* En este ejemplo el mundo solo tiene un suelo y una "caja" */ /********************************************************************/ //-- Crear mundo world = dWorldCreate(); //-- Establecer la gravedad (gravedad terrestres: -9.81) dWorldSetGravity (world,0,0,-3.0); //-- Establecer parametro CFM //-- Normalmente se deja siempre a este valor dWorldSetCFM (world,1e-5); //-- Establecer el modo auto-disabled por defecto //-- Cualquier objeto que se encuentre en reposo se deshabilitara //-- y no consumira recursos en la simulacion dWorldSetAutoDisableFlag (world,1); //-- Otros parametros... (consular documentacion) //-- En principio siempre tendran esos valores dWorldSetContactMaxCorrectingVel (world,0.1); dWorldSetContactSurfaceLayer (world,0.001); //-- Crear un espacio. Los espacios contienen los elementos geometricos //-- sobre las que se quiere comprobar si hay colision o no //-- Se utilizan espacios para que la simulacion sea mas rapida space = dHashSpaceCreate (0); //-- Crear un grupo de articulaciones //-- Se utiliza para almacenar los puntos de contacto en una colision contactgroup = dJointGroupCreate (0); //-- Crear un plano infinito y meterlo en el espacio //-- El plano se determina por su ecuacion del tipo: //-- a*x + b*y + c*z = d, donde (a,b,c) es un vector unitario //-- normal a su superficie //-- En este ejemplo se crea el plano con vector (0,0,1), es //-- decir, el plano: z=0 //-- Este plano sera para nosotros el "suelo" dCreatePlane (space,0,0,1,0); //-- Inicializar los objetos del universo a cero memset (&obj,0,sizeof(obj)); //-- Crear la "Caja" y ponerla en el "mundo" Crear_objeto(); /********************************/ /** COMENZAR LA SIMULACION!!!! */ /********************************/ //-- Este es el bucle principal. Llamara a las funciones de //-- retrollamada especificadas //-- Se pasan como parametros las dimensiones de la ventana dsSimulationLoop (argc,argv,400,300,&fn); /************************/ /* FIN DE LA SIMULACION */ /************************/ //-- Destruir el grupo de articulaciones dJointGroupDestroy (contactgroup); //-- Destruir el espacio de colisiones dSpaceDestroy (space); //-- Destruir el mundo con todos sus objetos dWorldDestroy (world); return 0; }