I. Présentation

Image non disponible

Un nœud de scène personnalisé est nécessaire si vous voulez implémenter un rendu technique actuellement non supporté par le moteur Irrlicht. Par exemple vous pouvez écrire tout aussi bien un moteur de rendu intérieur basé sur les portails qu'un nœud de scène spécialisé dans les paysages. En créant des nœuds de scène personnalisés, vous pouvez facilement étendre le moteur Irrlicht et l'adapter à vos propres besoins.

Je vais garder le tutoriel assez simple. Il sera donc court, le tout dans un fichier .cpp et j'utiliserai le moteur Irrlicht de la même manière que dans tous les autres tutoriels.

Pour commencer, j'inclus les fichiers d'en-têtes, j'utilise le namespace « irr » et je dis à l'éditeur de lien de lier le fichier .lib

 
Sélectionnez
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

Nous y voilà, la partie la plus sophistiquée de ce tutoriel : la classe de notre propre nœud de scène. Pour rester simple, notre nœud de scène ne sera ni un moteur de rendu intérieur ni un nœud de scène pour les paysages, mais un simple tétraèdre, un objet 3D composé de quatre vertex connectés, qui ne peut que se dessiner lui-même et qui ne fait rien de plus. Notez que ce cas ne nécessite pas de nœud de scène personnalisé. Au lieu de cela, on préférera créer un mesh à partir de la géométrie et le passer à un irr::scene::IMeshSceneNode. Cet exemple illustre juste la création d'un nœud de scène personnalisé dans un cadre très simple.

Pour commencer notre nœud de scène sera capable d'être inséré dans une scène du moteur Irrlicht, la classe que nous créons doit hériter de Image non disponibleirr::scene::ISceneNode et redéfinir quelques méthodes.

 
Sélectionnez
class CSampleSceneNode : public scene::ISceneNode
{

II. Définition du nœud

Tout d'abord, nous déclarons quelques attributs : la boîte (« bounding box ») délimitant notre tétraèdre, quatre vertex et le matériel du tétraèdre.

 
Sélectionnez
    core::aabbox3d<f32> Box;
    video::S3DVertex Vertices[4];
    video::SMaterial Material;

Les paramètres du constructeur seront le parent du nœud de scène, un pointeur sur le gestionnaire de scène et un identifiant pour le nœud de scène. Le constructeur appellera le constructeur de la classe parente, fixera quelques propriétés du matériel et créera les quatre vertex du tétraèdre que nous dessinerons plus tard.

 
Sélectionnez
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);

Le moteur Irrlicht a besoin de connaître la boîte délimitant le tétraèdre. Il l'utilisera pour les sélections automatiques et d'autres choses. Nous devons donc créer une boîte délimitant le tétraèdre à partir des quatre vertex que nous utilisons. Si vous ne souhaitez pas que le moteur utilise la boîte pour une sélection automatique, et/ou que vous ne souhaitez pas créer cette boîte, vous pouvez appeler irr::scene::ISceneNode::setAutomaticCulling() avec irr::scene::EAC_OFF.

 
Sélectionnez
        Box.reset(Vertices[0].Pos);
        for (s32 i=1; i<4; ++i)
            Box.addInternalPoint(Vertices[i].Pos);
    }

III. Rendu

Avant qu'il ne soit dessiné, la méthode Image non disponibleirr::scene::ISceneNode::OnRegisterSceneNode() de chaque nœud de scène est appelée par le gestionnaire de scène. Si le nœud de scène souhaite se dessiner lui-même, il devrait s'enregistrer lui-même dans le gestionnaire de scène pour être dessiné. Ceci est nécessaire pour dire au gestionnaire de scène quand il doit appeler Image non disponibleirr::scene::ISceneNode::render(). Par exemple, des nœuds de scène ordinaires dessineront leur contenu l'un après l'autre, alors que le stencil buffer sera dessiné après tous les autres nœuds de scène. Et la caméra ou le nœud de scène de lumière a besoin d'être dessiné avant tous les autres nœuds de scène (si possible). Nous allons donc simplement enregistrer le nœud de scène pour être dessiné normalement. Si nous voulons le laisser être dessiné comme une caméra ou une lumière, nous devrons appeler SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA); Après ceci, nous appelons la méthode irr::scene::ISceneNode::OnRegisterSceneNode() de la classe de base qui laissera simplement tous les nœuds de scène enfants s'enregistrer eux-mêmes.

 
Sélectionnez
    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);

        ISceneNode::OnRegisterSceneNode();
    }

Dans la méthode render() se trouvent la plupart des choses intéressantes  : le nœud de scène se dessine lui-même. Nous redéfinissons cette méthode et dessinons le tétraèdre.

 
Sélectionnez
 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->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
    }

Finalement, nous créons trois petites méthodes additionnelles. Image non disponibleirr::scene::ISceneNode::getBoundingBox() retourne la boîte délimitant le tétraèdre de ce nœud de scène, Image non disponibleirr::scene::ISceneNode::getMaterialCount() retourne le nombre de matériels de ce nœud de scène (notre tétraèdre a seulement un matériel) et irr::scene::ISceneNode::getMaterial() retourne le matériel à l'index. Parce que nous n'avons qu'un matériel nous ne pouvons retourner qu'un seul matériel, en supposant que personne n'appellera jamais getMaterial() avec un index supérieur à 0.

 
Sélectionnez
    virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return Box;
    }

    virtual u32 getMaterialCount() const
    {
        return 1;
    }

    virtual video::SMaterial& getMaterial(u32 i)
    {
        return Material;
    }   
};

Voilà. Le nœud de scène est fini. Maintenant nous devons simplement démarrer le moteur, créer le nœud de scène et une caméra puis regarder le résultat.

 
Sélectionnez
int main()
{
    // demande a l'utilisateur le pilote
    video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (driverType==video::EDT_COUNT)
        return 1;

    // cree le moteur

    IrrlichtDevice *device = createDevice(driverType,
            core::dimension2d<u32>(640, 480), 16, false);
        
    if (device == 0)
        return 1; // ne peut pas cree le pilote selectionne

    // cree le moteur et la camera

    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));

IV. Instanciation du nœud de scène

Créons notre nœud de scène. Je ne vérifie pas le résultat de l'appel à new, comme il lance une exception au lieu de retourner zéro en cas d'échec. Parce que le nouveau nœud se créera lui-même avec un compteur de référence à 1 et qu'ensuite une nouvelle référence sera créée par le nœud parent quand il sera ajouté à la scène, j'ai besoin de lui laisser ma référence. La meilleure pratique est de lui laisser après que j'ai fini de l'utiliser, quel que soit le nombre de références de l'objet après sa création.

 
Sélectionnez
    CSampleSceneNode *myNode =
        new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

Pour animer quelque chose dans cette scène ennuyante composée uniquement d'un tétraèdre et pour vous montrer que vous pouvez maintenant utiliser votre nœud de scène comme tout autre nœud de scène dans le moteur, nous ajoutons un animateur au nœud de scène qui tournera un peu le nœud. irr::scene::ISceneManager::createRotationAnimator() peut retourner zéro, nous devons donc vérifier la valeur renvoyée.

 
Sélectionnez
    scene::ISceneNodeAnimator* anim =
        smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));

    if(anim)
    {
        myNode->addAnimator(anim);

J'ai fini de renseigner l'animation, donc je dois maintenant Image non disponibleirr::IReferenceCounted::drop() cette référence parce qu'elle a été fabriquée par une fonction createFoo(). Comme je ne devrais plus l'utiliser ensuite, je m'assure que je ne pourrais plus l'utiliser en le mettant à 0.

 
Sélectionnez
        anim->drop();
        anim = 0;
    }

J'ai fini avec mon objet CsampleSceneNode, je dois donc supprimer ma référence. Ceci ne supprimera pas mon objet maintenant, parce qu'il est encore attaché au graphe de scène qui empêche la suppression jusqu'à ce que le graphe soit supprimé ou que le nœud de scène personnalisé ne le supprime.

 
Sélectionnez
    myNode->drop();
    myNode = 0; // As I shouldn't refer to it again, ensure that I can't

Maintenant dessinons tout et terminons.

 
Sélectionnez
    u32 frames=0;
    while(device->run())
    {
        driver->beginScene(true, true, video::SColor(0,100,100,100));

        smgr->drawAll();

        driver->endScene();
        if (++frames==100)
        {
            core::stringw str = L"Irrlicht Engine [";
            str += driver->getName();
            str += L"] FPS: ";
            str += (s32)driver->getFPS();

            device->setWindowCaption(str.c_str());
            frames=0;
        }
    }

    device->drop();
    
    return 0;
}

Voilà, compilons et testons le programme.

V. Conclusion

Vous pouvez désormais créer votre propre nœud de scène personnalisé en utilisant Irrlicht.

Dans le prochain tutoriel Mouvement, nous verrons comment bouger et animer des nœuds de scène.

VI. Remerciements

Merci à Eclesia pour ses conseils.

Merci à Nikolaus Gebhardt de nous permettre de traduire ce tutoriel.

Merci à LittleWhite pour sa relecture technique ainsi qu'à f-leb pour sa relecture orthographique.