lunes, 26 de mayo de 2008

La clase interfaz de ventana IApplication

Seguimos hoy con el bloque que trata de la estructura de ventanas con Irrlicht. Ahora nos toca ver la interfaz que nos permitirá implementar las distintas secciones de la aplicación.


Veamos la declaración de la clase.
#include <irrlicht/irrlicht.h>
#include "EventReceiver.h"
#include "GeneraID.h"
#include "FPSManager.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

class IApplication
{
public:
IApplication(IrrlichtDevice *device);
virtual ~IApplication();
virtual bool mRun();
virtual bool mInit() = 0;
virtual bool mRemove() = 0;
virtual bool mLoop();
bool getRunLoop();
void setRunLoop(bool value);

protected:
virtual bool mPreDraw();

protected:
IrrlichtDevice *f_device;
EventReceiver *f_eventReceiver;
GeneraID *f_genIDs;
IVideoDriver *f_driver;
IGUIEnvironment *f_env;
ISceneManager *f_smgr;

bool f_runLoop;
IFPSManager *f_FPSManager;
};


He de decir que me equivoqué con el nombre, no tenía que haberse llamado IApplication, puesto que no es una interfaz pura, tiene propiedades y metodos implementados (lo siento por los puristas del diseño ;)

En su parte privada vemos que tiene punteros a objetos que ya comentamos, como el IrrlichtDevice, EventReceiver, IFPSManager; otros de Irrlicht como IVideoDriver, IGUIEnvironment e ISceneManager y uno de cosecha propia, el GeneraID.
Los objetos de irrlicht vereis que son extraidos del IrrlichtDevice para usarlos con mayor comodidad.

En cuanto a GeneraID, vereis que es necesario para, como su nombre indica, generar identificadores únicos para los controles del GUI. Recordais el ejemplo típico del EventReceiver de los tutoriales? También hay que destacar que para que los id's sean verdaderamente únicos, solo puede haber una instancia de la clase. Esto se consigue con el patron de diseño singleton. He aqui su declaración:
#include <iostream>
#include <irrlicht/irrlicht.h>

using namespace irr;

class GeneraID
{
public:
static GeneraID* getInstance();
s32 getNewID();
s32 getLastID();
virtual ~GeneraID();

protected:
GeneraID(s32 initial = 0);
s32 f_id;
static GeneraID* s_instancia;
};

Y su implementación.
#include"GeneraID.h"
GeneraID* GeneraID::s_instancia = 0;

using namespace irr;

GeneraID*
GeneraID::getInstance()
{
if(!s_instancia) s_instancia = new GeneraID();
return s_instancia;
}

GeneraID::GeneraID(s32 initial)
{
f_id = initial >= 0 ? initial : 0;
}

GeneraID::~GeneraID()
{
}

s32
GeneraID::getNewID()
{
return ++f_id;
}

s32
GeneraID::getLastID()
{
return f_id;
}


volviendo a IApplication, en la parte pública, tenemos métodos implementados como mRun, mLoop, y los métodos get y set del atributo f_runLoop. mRun y mLoop nos permiten ejecutar el bucle de ejecución de la aplicación y mediante f_runLoop, podemos parar la ejecución del mismo.

Veamos ahora la implemetación de IApplication.
#include "IApplication.h"

IApplication::IApplication(IrrlichtDevice *device)
{
f_device = device;
f_driver = f_device->getVideoDriver();
f_env = f_device->getGUIEnvironment();
f_smgr = f_device->getSceneManager();
f_genIDs = GeneraID::getInstance();
f_eventReceiver = NULL;
f_runLoop = true;
f_FPSManager = new FPSManager(f_device);
}

IApplication::~IApplication()
{
f_device = NULL;
f_driver = NULL;
f_env = NULL;
f_smgr = NULL;
f_genIDs = NULL;
f_runLoop = false;
if(f_FPSManager != NULL)
delete f_FPSManager;
f_FPSManager = NULL;
}

bool
IApplication::mRun()
{
if(!mInit()) return false;
if(!mLoop()) return false;
if(!mRemove()) return false;
return true;
}

bool
IApplication::mLoop()
{
while(f_runLoop && f_device->run() && f_driver)
{
f_FPSManager->mUpdateFPS();
if (f_device->isWindowActive())
{
f_FPSManager->mDrawFPS();
mPreDraw();

f_driver->beginScene(true, true, SColor(0,122,65,171));
f_smgr->drawAll();
f_env->drawAll();
f_driver->endScene();
}
else
f_device->yield();
}

//f_device->drop();
return true;
}

bool
IApplication::getRunLoop()
{
return f_runLoop;
}

void
IApplication::setRunLoop(bool value)
{
f_runLoop = value;
}

bool
IApplication::mPreDraw()
{
return true;
}


Podemos observar que el contructor no tiene nada especial, tan solo que recibe el IrrlichtDevice y recupera de él, el videoDriver, GUIEnvironment y SceneManager.

El metodo mLoop es el interesante. Pese a que es un bucle típico de los tutoriales de Irrlicht, le hemos agregado un par de cosillas, tales como la gestion de los FPS y en método mPreDraw(), que lo implementarán las clases sucesoras si hiciese falta. mPreDraw() surgió como necesidad en el simulador, que necesita procesar instucciones ODE antes de dibujar en ventana.

En la próxima entrega veremos ejemplos de uso de IApplication con el menú principal, el editor y el simulador.

No hay comentarios: