sábado, 15 de noviembre de 2008

¿Qué nos mantiene ocupados?

Pués estamos trabajando en un par de wrappers para ODE:
- ODE++
- OdeManager (for Irrlicht applications)
¿Suena bien? pues sigue leyendo.. :)


El primero, bastante demandado en la comunidad ODE, es un wrapper de ODE en C++. Se acabó ver ode como una colección de funciones aglomeradas en una librería. A través de éste sencillo wrapper pretendemos ordenar todas estas funciones en un conjunto de clases en C++ que nos faciliten su acceso. La principal ventaja que obtenemos es el poder navegar de forma ordenada entre todas estas funciones (ahora métodos :)) a través de la típica característica de autocompletar de los IDE actuales. Además de ahorrarnos el ir mareando de aquí p'allá con los ID de los objetos ODE.

El segundo ODE puede ser útil para todos aquellos que trabajen con Irrlicht como nosotros, ya que apoyándonos sobre el wrapper anterior, estamos implementando un "OdeManager" que encapsule las tareas cotidianas de toda aplicación Irrlicht que haga uso de ODE. Cuando la consideremos mínimamente aceptable la publicaremos, por el momento id abriendo boca con este preview de como sería una aplicación Irr+ODE con un par de esferas: una estática y una dinámica:


#include "irrlicht/irrlicht.h"
#include "OdeManager.h"
#include "IStaticSphereSceneNode.h"
#include "IDynamicSphereSceneNode.h"

using namespace ode;
using namespace irr;

int main()
{
IrrlichtDevice* device = createDevice(video::EDT_OPENGL, core::dimension2di(800,600),16,false);
ISceneManager* scene = device->getSceneManager();
scene->addCameraSceneNodeMaya();

OdeManager* odeMngr = createOdeManager( device );

IStaticSphereSceneNode* staticNode = odeMngr->mAddStaticSphereSceneNode( 10.0 );
IDynamicSphereSceneNode* dynamicNode = odeMngr->mAddDynamicSphereSceneNode( 10.0 );

dynamicNode->getGeom()->setPosition( 0, 25, 0 );
staticNode->getGeom()->setPosition( 0, -25, 0 );

while( device->run() )
{
odeMngr->mStep();

device->getVideoDriver()->beginScene(true, true, video::SColor(0,200,200,200));
device->getSceneManager()->drawAll();
device->getGUIEnvironment()->drawAll();
device->getVideoDriver()->endScene();
}

return 0;
}


Y recordad, un poco de feedback por vuestra parte siempre es agradecido!

Sigue leyendo >

jueves, 6 de noviembre de 2008

Tutorial 5: Irrlicht, ODE, os presento a irrKlang!

Siguiendo la línea de "Los Hola Mundos más Simples" pasamos a añadir a nuestra sencilla escena de la pelota una nueva librería para dotar a esta de sonido: irrKlang.
Librería, como podeis intuir, del mismo equipo que desarrolla Irrlich. Misma filosofía, misma simpleza, mismo potencial.


IrrKlang se distribuye como un proyecto aparte de Irrlicht, por ello tiene su propia web. También tiene diferencias en cuanto a su licencia, ya que si se desea utilizar para uso comercial hay que pagar por la versión Pro. Pero cabe destacar que la única diferencia entre la versión para uso NO comercial y la versión Pro es la posibilidad de compilar irrKlang en forma de librería estática. Como podeis observar en esta comparativa la versión no comercial mantiene las mismas interesantes características que la versión de pago.
Y al igual que irrlicht soportaba una gran cantidad de formatos de modelos 3D, irrklang tiene un buen soporte en formatos de audio (wav, ogg, mp3...). Aunque es importante recordar que MP3 tiene su propia licencia que se debería pagar aparte, es por ello que no está integrado realmente en el núcleo de la librería irrklang, sino que viene en forma de plugin ("ikpMP3.dll") por si te interesara eliminarlo de tu aplicación.
Para acabar esta introducción de la librería, comentar lo que me parece la característica más interesante: Sonido 3D. De forma graciosa el API de irrklang llama "Sonido 2D" al sonido digamos estéreo de toda la vida, y "Sonido 3D" a aquel que está posicionado en un espacio tridimensional y suena de una forma u otra dependiento de un ficticio observador (oyente en este caso ;D) de la escena. Y todo ésto es fácil de integrar en una escena irrlicht gracias al ISceneNode implementado para irrKlang que podemos encontrar en su web: irrKlangSceneNode.

Pero vayamos al grano. Un hola mundo de irrKlang se podría resumir en el siguiente par de líneas:

ISoundEngine* engine = createIrrKlangDevice();
engine->play2D("path/myFile", false);

Y ya tendríamos sonido en nuestra aplicación.
El booleano de play2D es por si queremos la reproducción en loop, así que para tener música de fondo tan sólo hay que ejecutar dicha línea con true.

En nuestro tutorial hemos dotado de sonido a la pelota cuando choca contra el suelo, y para ello hemos hecho uso de ese par de líneas, y además hemos ajustado el volumen según la fuerza del rebote para darle algo de gracia. Veamos como:

Un nuevo include:

#include <irrKlang/irrKlang.h>


Una nueva variable:

ISoundEngine* engine;


Una nueva inicialización de librería:

void irrklang_inicializacion()
{
engine = createIrrKlangDevice();
}


Y simplemente, en el instante en el que sabemos que se produce una colisión, reproducimos el sonido. Ésto es, dentro del "ode_nearCallback".
Primeros se ha capturado la velocidad lineal del body de la esfera mediante "dBodyGetLinearVel( sphereBody )", ODE siempre devuelve los datos en forma de array, como la velocidad lineal es un vector, esta función nos proporciona un array con (x,y,z). Sólo nos interesa la velocidad vertical, con lo cual: "dBodyGetLinearVel( sphereBody )[1]".
En las siguientes líneas simplemente hemos obtenido el valor absoluto de la velocidad lineal:

dReal verticalVelocity = dBodyGetLinearVel( sphereBody )[1];
if ( verticalVelocity < 0.0 )
verticalVelocity *= -1.0;


Y en el caso de que haya una velocidad lineal mínima en el eje Y, reproducimos el sonido del boing. Para dotar de algo más de realismo el sonido, modificamos el volumen mediante "setSoundVolume" en función de la velocidad. El volumen se ajusta de [0..1], así que simplemente, cuando la fuerza empiece a ser muy débil (<1.0) empazará a disminuir el volumen.

if( verticalVelocity > 0.1 )
{
engine->setSoundVolume( verticalVelocity );
engine->play2D("res/boing.mp3", false);
}


Y poco más!

A nivel de archivos, han sido añadidos al tutorial la dll "irrKlang.dll" y el pluging para mp3 "ikpMP3.dll", la librería estática "libirrKlang.a, libirrKlang.def" (tan sólo son la interfaz a la dll), y la colección de cabeceras en "include\irrKlang".

Ya sabeis, el código en el box bajo el nombre "tutorial_05.zip".

Sigue leyendo >

Completando el Cuarto Tutorial (Irrlicht+ODE): La Masa

Volvemos trás un pequeño letargo con novedades.
Pero antes que nada, retomemos el último tutorial publicado para añadirle un detalle que dejé en el tintero expresamente para evitar marear mucho más la perdiz con todas las nuevas líenas de código que ODE implicaba. Y aquello que ignoré fue la masa de la esfera, o dicho en términos ODE: la masa de un body.


Por la simpleza del tutorial, aplicar la masa no afecta demasiado en los resultados ya obtenidos sin ella, por eso decidí no mencionarla con tal de ganar un poco más en simplicidad de código (ya resulta bastante duro enfrentarse a una librería como ODE la primera vez, sin contar en la casi necesidad de estar mezclada con otra librería como Irrlicht).

Recordando ODE, teníamos Geoms y Bodys para representar la física de nuestros objetos. Los Geoms representaban su caracter geométrico en el espacio, y los bodys su facultad de movimiento. Y es por este movito por lo que son los bodys los encargados de cargar con la masa, ya que ésta se utiliza básicamente para ajustar el centro de gravedad de un objeto y su densidad. Ambas características afectan a la simulación del movimiento, y no a los cálculos de geometría y colisión.

Su aplicación es realmente sencilla. Retomando el último tutorial con nuestra pelotita rebotando contra el suelo, tan sólo hay que añadirle cuatro nuevas líneas de código:


dMass sphereMass;


Una nueva definición de variable. Ésta no es un identificador como lo eran todas las otras variables definidas para ODE, "dMass" es una pequeña estructura de datos que almacena la información nombrada referente a la masa: densidad, centro de gravedad, matriz de inercia y masa total.
Según ODE es un struct tal que así:


typedef struct dMass {
dReal mass; // total mass of the rigid body
dVector4 c; // center of gravity position in body frame (x,y,z)
dMatrix3 I; // 3x3 inertia tensor in body frame, about POR
} dMass;


Wow! ¿Y como se supone que voy a configurar yo esos valores??! Nada que temer, lo hará ODE por nosotros si trabajamos con primitivas geométricas.

El resto de líneas nuevas se encuentra en la función de creacion "ode_creación":


dMassSetSphere( &sphereMass, 1.0f, 10.0f);
dMassAdjust( &sphereMass, 0.25);

dBodySetMass( sphereBody, &sphereMass);


Las dos primeras líneas realizan toda la configuración del struct dMass que acabamos de ver. La primera recibe como parámetro la densidad deseada para nuestro objeto y su radio (por tratarse de una esfera, obviamente), y se encarga de configurar en el dMass las variables 'c' e 'I': el centro de gravedad y su matriz de inercia para nuestra esfera. Ésto se realiza gracias a la función "dMassSetSphere" que sabe como se ajustan dichos valores a un objeto esférico. Por supuesto, disponemos de funciones de ajuste de masa para todas las geometrías primitivas que ODE nos ofrecía (cajas, cilindros, esferas, cápsulas y trimesh).
Según ODE, éste método no ajusta la masa total del objeto (la variable 'mass' del struct dMass), dejando este parámetro ajustable por nosotros mediante la función "dMassAdjust".

Por último ya sólo queda asignar nuestra nueva masa configurada a su respectivo body mediante "dBodySetMass".

La ejecución del tutorial con los valores de masa que he puesto por defecto da un resultado similar al anterior, la pelota rebota prácticamente de la misma forma.
Ahora puedes probar a cambiarle la masa total mediante "dMassAdjust" a 1000.0 unidades por ejemplo, y observarás como nuestra pelota de playa empieza parecerse más bien a una bola de bolos.

Como de costumbre, el código fuente está disponible en el box baje el nombre "tutorial_04b.zip" ;)

Sigue leyendo >