sábado, 14 de junio de 2008

Primer tutorial: el HolaMundo más simple de Irrlicht

Nos ponemos las pilas comenzando con una colección de tutoriales que partan desde lo más básico. Empezamos obviamente con Irrlicht, con un HolaMundo más simple si es posible que el tutorial HelloWorld que acompaña al engine.


Antes que nada, el código y demás está disponible en el box de nuestro blog bajo el nombre de "tutorial_01". Su puesta en marcha debería ser tan sencilla como el tener instalado MinGW en el sistema, y ejecutar make sobre el tutorial, ya que han sido incluidas las cabeceras y librerías de Irrlicht necesarias para funcionar, haciendo así el código más portable y sencillo de poner en marcha al reducir los pre-requisitos del sistema en el que vaya a ser compilado/ejecutado.
Queda pendiente añadirle las librerías de linux y hacerlo así multiplataforma, por ahora sólo incluye lo necesario para ponerlo en marcha bajo MsWindows con MinGW.

Este HolaMundo ha sido pensado para aquellos que vienen (o venimos) de librerías gráficas de bajo nivel como OpenGL o DirectX y estamos acostumbrados a realizar el pintado por pantalla casi de forma manual, pensando siempre en vértices, aristas, normales, etc.. Así que apartaremos de nuestra vista aquellos objetos de Irrlicht más avanzados intentando dejar el mínimo número posible de objetos necesarios.

Empecemos echándole un ojo al código, y luego lo comentamos.
(Se han eliminado los comentarios que el zip del tutorial sí incluye)

#include <irrlicht/irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;

int main()
{
IrrlichtDevice *device = createDevice(
video::EDT_OPENGL,
dimension2d(640, 480), 16,
false, false, false, NULL );

IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();

smgr->addCameraSceneNode( NULL, vector3df(20,30,-40), vector3df(0,5,0) );

while( device->run() )
{
driver->beginScene( true, true, SColor(255,100,101,140) );
smgr->drawAll();
driver->draw3DBox( aabbox3df(0.f,0.f,0.f, 10.f,10.f,10.f) );
driver->endScene();
}

device->drop();
return 0;
}


Lo primero que se observa es que para trabajar con Irrlicht tan solo hay que incluir una cabecera (que por cierto el blog por el momento no visualiza al confundirlo con una etiqueta HTML). Y lo mismo pasa a nivel de linkado, si observas el Makefile verás que tan solo ha habido que enlazar con una librería.

Todo el API de Irrlicht se encuentra bajo el espacio de nombres "irr". Y está estructurado mediante 5 sub-espacios de nombres más: core, scene, video, io, gui.

La primera instrucción que no puede faltar en una aplicación Irrlicht es una llamada a "irr::createDevice". Ésta función en la encargada de inicializar todo el motor gráfico, representado mediante el objeto "IrrlichtDevice" devuelto. Éste objeto es la madre del cordero de toda aplicación Irrlicht.
Vemos que en la incialización estamos definiendo la ventana de render a través del conjunto de parámetros que tiene la llamada:

- "deviceType": el primero de ellos nos permite seleccionar el motor de render utilizado en la aplicación. Irrlicht incluye una buena colección: OpenGL, DirectX8, DirectX9 y 2 implementaciones software propias. No hay excusas para no hacer aplicaciones multiplataforma! Los identificadores de los distintos motores los podemos encontrar en irr::video::EDT_*. "EDT" significa "Enum Device Type", y es muy común encontrarse este tipo de nomenclatura en el API, así que no lo olvides para encontrar por ti mismo aquello que estés buscando en un futuro.

- "windowSize": el tamaño de la ventana de render, como su nombre indica, expresado en pixels como suele ser costumbre. Aquí se espera un objeto irr::core::dimension2d que encapsula el par de valores.

- "bits": la profundidad de color en bits de la ventana. Típicamente "16" o "32". No olvidar que este valor sólo tiene efecto al trabajar a pantalla completa, ya que de otra forma se utiliza el valor con el que tengas configurado el escritorio.

- "fullscreen": un booleano que especifica si queremos la aplicacion a pantalla completa o no.

- "stencilbuffer": mediante un booleano indicamos si queremos hacer uso del "stencilbuffer" para sombras. Un concepto algo avanzado para el objetivo de este primer tutorial.

- "vsync": activamos o desactivamos la sincronización vertical de nuestra tarjeta gráfica. De nuevo solo funcionaría al trabajar a pantalla completa.

- "eventReceiver": un objeto de la clase "EventReceiver" encargado de gestionar todos los eventos que queremos capturar en nuestra aplicación. Ya llegaremos..


Tras crear nuestro dispositivo Irrlicht, extraemos de él dos de los objetos más importante que tiene instanciados. Como son usados frecuentemente, es habitual hacer con una referencia propia para no estar continuamente haciendo "device->getXXX()".
Los nombres de ambos objetos son bastante intuitivos: VideoDriver y SceneManager. A groso modo podríamos decir que VideoDriver trabaja a bajo nivel y SceneManager nos proporciona la capa más alta de abstracción de todo el motor gráfico.
Ésto mismo lo comprobamos con la siguiente instrucción en la que añadimos una cámara a nuestra escena mediante "addCameraSceneNode". Sus parámetros son la posición de la cámara y el punto al que mira, y para encapsular estos valores se utiliza la clase irr::core::vector3d.

El siguiente paso es entrar de lleno en el bucle de ejecución de nuestra aplicación gráfica. La condición del bucle puede ser tan sencilla como "device->run()". En este caso el cerrar la ventana o la aplicación detendrá el dispositivo irrlicht y provocará que finalice el bucle.
El bucle estará gobernado por dos instrucciones al más puro estilo OpenGL ó máquina de estados: "beginScene" y "endScene". El objetivo de "beginScene" es básicamente limpiar la pantalla, y para ello le pasamos el color con el que queremos que deje el fondo utilizando la clase irr::video::SColor( alpha, r,g,b ). Entre ambos métodos colocaremos todas aquellas instrucciones que tengan como objetivo pintar cosas en pantalla. Por ese motivo encontramos en este primer tutorial una llamada al drawAll del SceneManager, el cual se encarga el solito de pintar todo aquello que le hayamos añadido, en nuestro caso tan solo ha sido la cámara así que la "colocará" en escena.
A continuación probamos hacer una pintada nosotros mismos echando mano directamente del driver de video para pintar un cubo. Si curioseamos las llamadas drawXXX del videoDriver observaremos como son primitivas: triángulos, lineas, cajas/cubos, vértices, etc. El objeto utilizado para definir el cubo no es más que una clase que almacena 2 puntos (x,y,z, X,Y,Z). La nomenclatura "aabbox" viene del término utilizado habitualmente para referirse a los boundingbox: "aabb".
Finalmente tenemos la llamada a endScene que es la encargada realmente de hacer el render por pantalla, ya que antes estábamos pintando sobre un buffer distinto.

Por último, solo queda destruir el device en el momento de cerrar la aplicación, y en el API de Irrlicht todos los objetos siempre de liberan/destruyen mediante un método común llamado "drop".

Y hasta aquí el tutorial.
En el próximo nos acercaremos un poco más al HelloWorld "oficial" de Irrlicht para entender cual es la filosofía a seguir del motor para pintar las cosas, ya que veremos que la idea es no utilizar el driver directamente, que para algo existe la abstracción del SceneManager.

No hay comentarios: