/***********************************************************************/
/* gravedad_colision.cpp. Juan Gonzalez. Mayo 2006 */
/*---------------------------------------------------------------------*/
/* LICENCIA GPL */
/*---------------------------------------------------------------------*/
/* Ejemplo "hola mundo" del motor fisico ODE (Open Dynamics Engine) */
/* Es similar al ejemplo gravedad, pero se ha anadido deteccion de */
/* colisiones. Se crea un mundo virtual, se crea un suelo infinitao, */
/* se situa una "caja" a una cierta altura y se comienza la simulacion */
/* La caja caera hasta que choque con el suelo. La salida del programa */
/* es la posicion de la caja en los diferentes instantes de tiempo */
/*---------------------------------------------------------------------*/
/* Este programa es para consola, no hay representacion grafica en 3D */
/*---------------------------------------------------------------------*/
/* Los datos devueltos se pueden visualizar con Octave/Matlab */
/* Ejemplo de uso: */
/* $ gravedad_colision > func.m */
/* $ octave func.m */
/***********************************************************************/
#include <ode/ode.h>
/*********************************************/
/* Algunas constantes usadas en el programa */
/*********************************************/
//-- Numero maximo de puntos de contacto. Es para la deteccion de
//-- colisiones.
#define MAX_CONTACTS 4
//-- Numero de instantes que queremos simular. Este valor se puede
//-- cambiar
#define TICKS 200
//-- 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;
//-- Contador de cucantos tics de simulacion quedan
static int ticks = TICKS;
/***************************************************************************/
/* 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,4);
//-- 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 los puntos de contacto, que en realidad son articulaciones
//-- Para mas informacion consultar la documentacion de ODE
//-- Aqui se puede especificar el tipo de superficies, los
//-- coeficientes de rozamiento (mu), etc...
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);
}
}
}
/************************************************************************/
/* Realizar un paso de simulacion. Primero se comprueba colisiones. Si */
/* hay alguna, se crean automaticamente (por medio de la funcion */
/* nearCallback, puntos de contactos que en realidad son articulaciones */
/* Despues se realiza un paso de la simulacion. Los objetos rotaran en */
/* contacto rotaran sobre los nuevos puntos de contacto creados. */
/* Finalmente se eliminan. */
/************************************************************************/
static void simLoop()
{
//-- 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 (articulaciones) creados.
dJointGroupEmpty (contactgroup);
}
/*******************/
/* MAIN */
/*******************/
int main (int argc, char **argv)
{
const dReal *pos;
/********************************************************************/
/* 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,-9.81);
//-- 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();
//-- Salida para Octave:
//-- La matriz z es la que se ira rellenando con los valores de la altura
//-- de la caja
printf ("z=[");
/********************************/
/** COMENZAR LA SIMULACION!!!! */
/********************************/
//-- Se haran tantos pasos de simulacion como se indican en la
//-- constante TICKS
for (ticks=TICKS; ticks>0; ticks--) {
//-- Realizar un paso de la simulacion, comprobando colisiones
simLoop();
//-- Leer la altura del objeto e imprimirla
//-- Pos es el vector de posicion, que tiene 3 componentes:
//-- pos[0] --> x; pos[1]--> y; pos[2] --> z
//-- Solo nos interesa la altura a la que esta la caja (pos[2])
pos=dBodyGetPosition(obj.body);
printf ("%f,",pos[2]);
}
//-- Simulacion finalizada:
//-- Imprimir la ultima posicion e
//-- Imprimir comandos octave para sacar grafica
pos=dBodyGetPosition(obj.body);
printf ("%f];\n",pos[2]);
printf ("t=0:1:%d;\n",TICKS);
printf ("plot(t,z);\n");
printf ("grid on;\n");
printf ("pause;\n");
/************************/
/* 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 (apocalipsis?)
dWorldDestroy (world);
return 0;
}