Voy a mantener este tutorial simple dejando todo en un único archivo .cpp y usando el engine del mismo modo que en los otros tutoriales. Al final del tutorial, el resultado debería verse como muestra la imagen de abajo. Esto no se ve muy excitante, pero es un nodo de escena completamente personalizado y un buen punto de partida para crear sus propios nodos de escena.

| ¡Comencemos! |
Para comenzar, incluiré los archivos de encabezado, usaré el namespace irr y le diré al linker que enlace el archivo .lib.
#include <irrlicht.h> using namespace irr; #pragma comment(lib, "Irrlicht.lib") |
Y aquí viene la parte más sofisticada del tutorial: la clase de nuestro muy personalizado nodo de escena. Para mantenerlo simple, nuestro nodo no será un renderizador de interiores con portales, ni un nodo de terreno, sino un simple tetraedro: un objeto 3D compuesto por 4 vértices conectados que solamente se dibujará a sí mismo, nada más.
Para que nuestro nodo sea capaz de insertarse dentro de una escena del Irrlicht Engine, la clase que creamos sólo necesita ser derivada de ISceneNode y sobrescribir algunos métodos.
class CSampleSceneNode : public scene::ISceneNode { |
Primero, declaramos algunas variables miembro para guardar los datos de nuestro tetraedro: la bounding box (la "caja de colisiones" empleada por el árbol AABB para detectar rápidamente los potenciales choques y demás), los 4 vértices y el material del tetraedro.
core::aabbox3d<f32> Box; video::S3DVertex Vertices[4]; video::SMaterial Material; |
Los parámetros del constructor especifican el padre del nodo de escena, un puntero al manejador de escenas (scene manager) y el id asignado a nuestro nodo. En el constructor mismo llamamos al constructor de la clase padre, inicializamos algunas propiedades del material que usaremos para dibujar el nodo de escena y creamos los 4 vértices del tetraedro que pintaremos después.
public:
CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id) : scene::ISceneNode(parent, mgr, id)
{
Material.Wireframe = false;
Material.Lighting = false;
Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255),0,1);
Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255),1,1);
Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0),1,0);
Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0),0,0); |
El Irrlicht Engine necesita saber la bounding box de nuestro scene node. Ésta será usada para hacer el automatic culling y otras cosas. Así es que necesitamos crear una bounding box basados en los 4 vértices que usamos. Si no queremos que el motor use la caja para el automatic culling (descarte automático de cosas que no se ven) y/ó no queremos crear la caja, podemos escribir: AutomaticCullingEnabled? = false;.
Box.reset(Vertices[0].Pos);
for (s32 i=1; i<4; ++i)
Box.addInternalPoint(Vertices[i].Pos); //Ver Nota del Traductor (1)
} |
Antes de ser dibujado, el scene manager llama al método OnPreRender() de cada nodo de escena (scene node) en la escena. Si el scene node desea dibujarse a sí mismo, debe registrarse en el scene manager que lo va a dibujar. Esto puede ser necesario para decirle al scene manager cuando debe llamar al método ::render. Por ejemplo: los nodos de escena normales renderizan (dibujan) su contenido uno tras otro, mientras que el stencil buffer de sombras tendría que ser dibujado después de todos los nodos de escena; y un nodo de cámara o luz necesita ser renderizado antes que cualquier otro nodo de escena.
Así que nosotros simplemente registraremos el scene node para ser renderizado normalmente. Si quisieramos que sea renderizado como las cámaras ó luces, tendríamos que llamar al método SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);
Después de esto, llamamos al OnPreRender() de la clase base ISceneNode, el cual sencillamente hace que todos los nodos hijos de este nodo se registren por si mismos también.
virtual void OnPreRender()
{
if (IsVisible)
SceneManager->registerNodeForRendering(this);
ISceneNode::OnPreRender();
} |
En el método render() sucede lo más interesante: el nodo de escena se renderiza a sí mismo. Aquí nosotros sobrescribimos el método y dibujamos el tetraedro.
virtual void render()
{
u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(Material);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
} |
Al final, crearemos tres métodos adicionales: GetBoundingBox() devolverá la bounding box del scene node, GetMaterialCount() devolverá la cantidad de materiales que hay en el scene node (nuestro tetraedro sólo tiene un material) y getMaterial(s32 i) devuelve el material en la posición i. Debido a que tenemos sólo un material, podemos retornar el material directamente, asumiendo que nadie va a llamar a getMaterial() con un index mayor a 0.
virtual const core::aabbox3d<f32>& getBoundingBox() const
{
return Box;
}
virtual s32 getMaterialCount()
{
return 1;
}
virtual video::SMaterial& getMaterial(s32 i)
{
return Material;
}
}; //Fin de la clase |
Esto fue todo. El nodo de escena está hecho. Ahora simplemente arrancamos el motor, creamos el de escena y una cámara mirándolo.
int main()
{
IrrlichtDevice *device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480), 16, false);
device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));
|
Al crear nuestro nodo de escena note que es eliminado (->drop()) instantáneamente después de crearlo. Esto es posible porque el scene manager se encarga de él desde su creación (2). Sin embargo ésto no es necesario, también podría ser eliminado al final del programa.
CSampleSceneNode *myNode = new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666); myNode->drop(); |
Para animar un poco esta naciente escena que consiste de sólo un tetraedro, y mostrar lo que usted puede hacer con su nodo igual que lo haría con cualquier otro nodo nativo del motor, agregaremos un animator al nodo de escena, el que rotará un poco nuestro nodo.
Scene::ISceneNodeAnimator* anim = smgr-> createRotationAnimator(core::vector3df(0.8f, 0, 0.8f)); myNode->addAnimator(anim); anim->drop(); |
Ahora hay que dibujar todo y finalizar.
while(device->run())
{
driver->beginScene(true, true, video::SColor(0,100,100,100));
smgr->drawAll();
driver->endScene();
}
device->drop();
return 0;
} |
¡Esto es todo!. Compile y juegue con el programa.
Notas del Traductor:
(1) Si observa el código seguramente se preguntará donde están definidos los otros 4 vértices que faltan para hacer una caja. Irrlicht autogenera la caja en base a dos esquinas opuestas, cuyas componentes son los máximos y mínimos de cada vértice agregado por medio de addIntenalPoint() incluyendo al parámetro del reset().
(2) Esto se debe a que en algún lugar del constructor se llama al scene manager para que agregue el nodo, con lo cual el objeto ya queda referenciado en el scene manager y es posible destruirlo.
