I. Présentation▲
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
#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 irr::scene::ISceneNode et redéfinir quelques méthodes.
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.
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.
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.
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 irr::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 irr::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.
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.
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. irr::scene::ISceneNode::getBoundingBox() retourne la boîte délimitant le tétraèdre de ce nœud de scène, irr::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.
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.
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.
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.
scene::
ISceneNodeAnimator*
anim =
smgr->
createRotationAnimator(core::
vector3df(0.8
f, 0
, 0.8
f));
if
(anim)
{
myNode->
addAnimator(anim);
J'ai fini de renseigner l'animation, donc je dois maintenant irr::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.
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.
myNode->
drop();
myNode =
0
; // As I shouldn't refer to it again, ensure that I can't
Maintenant dessinons tout et terminons.
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.