I. Prérequis

Image non disponible
 
Sélectionnez
#include <irrlicht.h>

#if defined ( _IRR_WINDOWS_ )
    #include <windows.h>
#endif

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

#pragma comment(lib, "Irrlicht.lib")

class EventReceiver_basic : public IEventReceiver
{
private:
    IrrlichtDevice *Device;
public:
    EventReceiver_basic ( IrrlichtDevice *device ): Device ( device ) {}

    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_GUI_EVENT)
        {
            s32 id = event.GUIEvent.Caller->getID();

            switch(event.GUIEvent.EventType)
            {
                case EGET_BUTTON_CLICKED:
                if (id == 2)
                {
                    Device->closeDevice();
                    return true;
                } break;
            }
        }

        return false;
    }
};

class CSampleSceneNode : public ISceneNode
{
    aabbox3d<f32> Box;
    S3DVertex Vertices[4];
    SMaterial Material;
public:

    CSampleSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id)
        : ISceneNode(parent, mgr, id)
    {
        Material.Wireframe = false;
        Material.Lighting = false;

        Vertices[0] = S3DVertex(0,0,10, 1,1,0, SColor(255,0,255,255), 0, 1);
        Vertices[1] = S3DVertex(10,0,-10, 1,0,0, SColor(255,255,0,255), 1, 1);
        Vertices[2] = S3DVertex(0,20,0, 0,1,1, SColor(255,255,255,0), 1, 0);
        Vertices[3] = S3DVertex(-10,0,-10, 0,0,1, SColor(255,0,255,0), 0, 0);
        Box.reset(Vertices[0].Pos);
        for (s32 i=1; i<4; ++i)
            Box.addInternalPoint(Vertices[i].Pos);
    }
    virtual void OnRegisterSceneNode()
    {
        if (IsVisible)
            SceneManager->registerNodeForRendering(this);

        ISceneNode::OnRegisterSceneNode();
    }

    virtual void render()
    {
        u16 indices[] = {   0,2,3, 2,1,3, 1,0,3, 2,0,1  };
        IVideoDriver* driver = SceneManager->getVideoDriver();

        driver->setMaterial(Material);
        driver->setTransform(ETS_WORLD, AbsoluteTransformation);
        driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
    }

    virtual const aabbox3d<f32>& getBoundingBox() const
    {
        return Box;
    }

    virtual u32 getMaterialCount()
    {
        return 1;
    }

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

II. Création du moteur Windows Mobile

Démarre un moteur Windows Mobile.

 
Sélectionnez
IrrlichtDevice *startup()
{
    // EDT_SOFTWARE et EDT_BURNINGSVIDEO peuvent tout deux être utilisés
    E_DRIVER_TYPE driverType = EDT_SOFTWARE; // EDT_BURNINGSVIDEO;

    // Crée le moteur.
    IrrlichtDevice *device = 0;

#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
    // On se met en mode plein écran standard pour les mobiles : 240x320.
    device = createDevice(driverType, dimension2d<u32>(240, 320), 16, true );
#else
    // Sur un PC, utilise le mode fenêtré.
    device = createDevice(driverType, dimension2d<u32>(240, 320), 16, false );
#endif      
    if ( 0 == device )
        return 0;

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

    // Définit un système de fichier relatif reposant sur  l'exécutable.
#if defined (_IRR_WINDOWS_)
    {
        wchar_t buf[255];
        GetModuleFileNameW ( 0, buf, 255 );

        io::path base = buf;
        base = base.subString ( 0, base.findLast ( '\\' ) + 1 );
        device->getFileSystem()->addFileArchive ( base );
    }
#endif

    IGUIStaticText *text = guienv->addStaticText(L"FPS: 25",
        rect<s32>(140,15,200,30), false, false, 0, 100 );

    guienv->addButton(core::rect<int>(200,10,238,30), 0, 2, L"Quit");

    // Ajoute le logo Irrlicht.
    guienv->addImage(driver->getTexture("../../media/irrlichtlogo3.png"),
                    core::position2d<s32>(0,-2));
    return device;
}

III. Boucle principale

 
Sélectionnez
int run ( IrrlichtDevice *device )
{
    while(device->run())
    if (device->isWindowActive())
    {
        device->getVideoDriver()->beginScene(true, true, SColor(0,100,100,100));
        device->getSceneManager()->drawAll();
        device->getGUIEnvironment()->drawAll();
        device->getVideoDriver()->endScene ();

        IGUIElement *stat = device->getGUIEnvironment()->
            getRootGUIElement()->getElementFromId ( 100 );
        if ( stat )
        {
            stringw str = L"FPS: ";
            str += (s32)device->getVideoDriver()->getFPS();

            stat->setText ( str.c_str() );
        }
    }

    device->drop();
    return 0;
}

IV. Exemple nœud de scène personnalisé

 
Sélectionnez
int example_customscenenode()
{
    // Crée le moteur.
    IrrlichtDevice *device = startup();
    if (device == 0)
        return 1; // Ne peut pas créer le pilote sélectionné.

    // Crée le moteur et la caméra.
    EventReceiver_basic receiver(device);
    device->setEventReceiver(&receiver);
    
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();


    smgr->addCameraSceneNode(0, vector3df(0,-40,0), vector3df(0,0,0));

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

    ISceneNodeAnimator* anim = 
        smgr->createRotationAnimator(vector3df(0.8f, 0, 0.8f));

    if(anim)
    {
        myNode->addAnimator(anim);
        anim->drop();
        anim = 0; // Comme je ne devrais plus l'utiliser à nouveau, je m'assure que je ne le pourrai pas.
    }

    myNode->drop();
    myNode = 0; // Comme je ne devrais plus l'utiliser à nouveau, je m'assure que je ne le pourrai pas.

    return run ( device );
}

class EventReceiver_terrain : public IEventReceiver
{
public:

    EventReceiver_terrain(IrrlichtDevice *device, scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
        Device ( device ), Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true)
    {
        Skybox->setVisible(true);
        Skydome->setVisible(false);
    }

    bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_GUI_EVENT)
        {
            s32 id = event.GUIEvent.Caller->getID();

            switch(event.GUIEvent.EventType)
            {
                case EGET_BUTTON_CLICKED:
                if (id == 2)
                {
                    Device->closeDevice();
                    return true;
                } break;
            }
        }

        // Vérifie si l'utilisateur presse les touches 'W' ou 'D'.
        if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
        {
            switch (event.KeyInput.Key)
            {
            case irr::KEY_KEY_W: // Passe en mode fil de fer.
                Terrain->setMaterialFlag(video::EMF_WIREFRAME,
                        !Terrain->getMaterial(0).Wireframe);
                Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
                return true;
            case irr::KEY_KEY_P: // Passe en mode fil de fer.
                Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
                        !Terrain->getMaterial(0).PointCloud);
                Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
                return true;
            case irr::KEY_KEY_D: // Change le détail de la carte.
                Terrain->setMaterialType(
                    Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
                    video::EMT_DETAIL_MAP : video::EMT_SOLID);
                return true;
            case irr::KEY_KEY_S: // Change le ciel.
                showBox=!showBox;
                Skybox->setVisible(showBox);
                Skydome->setVisible(!showBox);
                return true;
            default:
                break;
            }
        }

        return false;
    }

private:
    IrrlichtDevice *Device;
    scene::ISceneNode* Terrain;
    scene::ISceneNode* Skybox;
    scene::ISceneNode* Skydome;
    bool showBox;
};

V. Exemple rendu de terrain

Le début de la fonction main commence comme dans la plupart des autres exemples. Nous demandons à l'utilisateur le moteur de rendu à utiliser et nous le démarrons. Cette fois-ci avec le gestionnaire de paramètres avancés.

 
Sélectionnez
int example_terrain()
{
    // Crée le moteur.
    IrrlichtDevice *device = startup();
    if (device == 0)
        return 1; // Ne peut pas créer le pilote sélectionné.

En premier lieu, nous ajoutons les choses habituelles à la scène : un joli logo du moteur Irrlicht, un petit texte d'aide, une caméra contrôlée par l'utilisateur et nous cachons le curseur de la souris.

 
Sélectionnez
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
    gui::IGUIEnvironment* env = device->getGUIEnvironment();


    // Définit une autre police d'écriture.
    //env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

    // Ajoute un texte d'aide.
    env->addStaticText(
        L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
        core::rect<s32>(5,250,235,320), true, true, 0, -1, true);

    // Ajoute la caméra.
    scene::ICameraSceneNode* camera =
        smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);

    camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
    camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
    camera->setFarValue(42000.0f);

    // Cache le curseur de la souris.
    device->getCursorControl()->setVisible(false);

Maintenant, voici venu le nœud de scène de rendu de terrain : nous l'ajoutons à la scène comme n'importe quel autre nœud de scène avec IsceneManager::addTerrainSceneNode().

Le seul paramètre que nous utilisons est le nom de fichier de la texture de relief que nous utilisons. Une texture de relief est une simple texture en nuances de gris. Le dessinateur de terrain charge et crée le terrain 3D à partir de ce fichier.

Pour rendre le terrain plus grand, nous changeons le facteur de redimensionnement à (40, 4.4, 40). Parce que nous n'avons aucune lumière dynamique dans la scène, nous désactivons l'éclairage et nous mettons le fichier terrain-texture.jpg comme texture pour le terrain et detailmap3.jpg comme seconde texture appelée carte de détails. Enfin, nous définissons les valeurs de redimensionnement pour les textures : la première texture sera répétée une seule fois dans tout le terrain et la seconde (carte de détails) vingt fois.

 
Sélectionnez
    // Ajoute le nœud de scène de terrain. 
    scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode( 
        "../../media/terrain-heightmap.bmp", 
        0,                  // nœud parent 
        -1,                 // identifiant du nœud 
        core::vector3df(0.f, 0.f, 0.f),     // position 
        core::vector3df(0.f, 0.f, 0.f),     // rotation 
        core::vector3df(40.f, 4.4f, 40.f),  // échelle 
        video::SColor ( 255, 255, 255, 255 ),   // couleur du vertex 
        5,                  // LOD maximum 
        scene::ETPS_17,             // taille de la pièce 
        4                   // facteur de lissage 
        ); 

    terrain->setMaterialFlag(video::EMF_LIGHTING, false); 

    terrain->setMaterialTexture(0, 
            driver->getTexture("../../media/terrain-texture.jpg")); 
    terrain->setMaterialTexture(1, 
            driver->getTexture("../../media/detailmap3.jpg")); 
    
    terrain->setMaterialType(video::EMT_DETAIL_MAP); 

    terrain->scaleTexture(1.0f, 20.0f);

Pour être capable de faire des collisions avec le terrain, nous créons un sélectionneur de triangles. Si vous voulez savoir ce que fait le sélectionneur de triangles, jetez un coup d'œil au tutoriel Collision. Le sélectionneur de triangles travaille avec le terrain. Pour expérimenter, nous créons un animateur répondant aux collisions et l'attachons à la caméra, ainsi la caméra ne sera pas capable de voler à travers le terrain.

 
Sélectionnez
    // Crée un sélectionneur de triangles pour le terrain. 
    scene::ITriangleSelector* selector 
        = smgr->createTerrainTriangleSelector(terrain, 0); 
    terrain->setTriangleSelector(selector); 

    // Crée l'animateur répondant aux collisions et l'attache à la caméra. 
    scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator( 
        selector, camera, core::vector3df(60,100,60), 
        core::vector3df(0,0,0), 
        core::vector3df(0,50,0)); 
    selector->drop(); 
    camera->addAnimator(anim); 
    anim->drop();

Si vous avez besoin d'accéder aux données du terrain, vous pouvez aussi le faire via le bout de code suivant :

 
Sélectionnez
    scene::CDynamicMeshBuffer* buffer = new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT); 
    terrain->getMeshBufferForLOD(*buffer, 0); 
    video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->getVertexBuffer().getData(); 
    // Travaille sur les donnée ou obtient un IndexBuffer avec un appel similaire. 
    buffer->drop(); // Quand on a fini, jette le buffer encore une fois

Pour permettre à l'utilisateur de pouvoir passer du mode fil de fer au mode normal et vice-versa, nous créons une instance du receveur d'événements ci-dessus et nous en avertissons Irrlicht. De plus, nous ajoutons la « skybox » que nous avons déjà utilisée dans un bon nombre d'exemples d'Irrlicht ainsi qu'un « skydome » qui est alterné avec la « skybox » en pressant 'S'.

 
Sélectionnez
    // Crée la « skybox » et le « skydome ».
    driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

    scene::ISceneNode* skybox=smgr->addSkyBoxSceneNode(
        driver->getTexture("../../media/irrlicht2_up.jpg"),
        driver->getTexture("../../media/irrlicht2_dn.jpg"),
        driver->getTexture("../../media/irrlicht2_lf.jpg"),
        driver->getTexture("../../media/irrlicht2_rt.jpg"),
        driver->getTexture("../../media/irrlicht2_ft.jpg"),
        driver->getTexture("../../media/irrlicht2_bk.jpg"));
    scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(driver->getTexture("../../media/skydome.jpg"),16,8,0.95f,2.0f);

    driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);

    // Crée le receveur d'événements.
    MyEventReceiver receiver(terrain, skybox, skydome);
    device->setEventReceiver(&receiver);

    return run ( device );
}
 
Sélectionnez
int example_helloworld()
{
    // Crée le moteur.
    IrrlichtDevice *device = startup();
    if (device == 0)
        return 1; // Ne peut pas créer le pilote sélectionné.

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

    IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
    if (!mesh)
    {
        device->drop();
        return 1;
    }
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );

Pour rendre le mesh un peu plus joli, nous changeons son matériel. Nous désactivons l'éclairage parce que nous n'avons pas de lumière dynamique à l'intérieur et le mesh serait totalement noir sinon. Puis nous choisissons de boucler pour que l'animation prédéfinie STAND soit utilisée. Et enfin, nous appliquons une texture au mesh. Sans cela, le mesh serait dessiné avec une seule couleur.

 
Sélectionnez
    if (node)
    {
        node->setMaterialFlag(EMF_LIGHTING, false);
        node->setMD2Animation(scene::EMAT_STAND);
        node->setMaterialTexture( 0, driver->getTexture("../../media/sydney.bmp") );
    }

Pour regarder vers le mesh, nous plaçons une caméra dans l'espace 3D à la position (0,30,-40). La caméra regarde vers le point (0,5,0) qui est approximativement la position de notre modèle md2.

 
Sélectionnez
    smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

    EventReceiver_basic receiver(device);
    device->setEventReceiver(&receiver);

    return run ( device );

}

#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
    #pragma comment(linker, "/subsystem:WINDOWSCE /ENTRY:main") 
#elif defined (_IRR_WINDOWS_)
    #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

VI. Fonction main

 
Sélectionnez
int main()
{
    example_helloworld ();
    example_customscenenode();
    //example_terrain();
}

VII. Conclusion

Vous pouvez désormais utiliser Irrlicht sur votre mobile.

Dans le prochain tutoriel Écran divisé, nous verrons comment diviser l'écran en 4 partie.

VIII. Remerciements

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

Merci à LittleWhite pour sa relecture technique ainsi qu'à ced pour sa relecture orthographique.