La "partie" rendu de la fonction commence tout bêtement par la déclaration d'une variable qui va servir à parcourir des tableaux. Rien de neuf sous le soleil :
Code : C++ | u32 i; // new ISO for scoping problem in some compilers
|
Là on attaque du lourd, le "véritable" rendu commence maintenant. Première constatation, ce bloc concerne les cameras :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | //render camera scenes
{
CurrentRendertime = ESNRP_CAMERA;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
if(LightManager)
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<CameraList.size(); ++i)
CameraList[i]->render();
CameraList.set_used(0);
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
La ligne 3 est intéressante, et va se retrouver au début de chaque "bloc de rendu".
CurrentRendertime est un attribut du scene manager de type
E_SCENE_NODE_RENDER_PASS. En fouillant un peu dans le fichier ISceneManager.h (à ce stade vous pouvez tout simplement oublier la doc...

) on peut trouver l'énumération de cette chose. Je vous mets la version longue qui, certes l'est, mais permet de tout comprendre d'un coup :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 | //! Enumeration for render passes.
/** A parameter passed to the registerNodeForRendering() method of the ISceneManager,
specifying when the node wants to be drawn in relation to the other nodes. */
enum E_SCENE_NODE_RENDER_PASS
{
//! No pass currently active
ESNRP_NONE =0,
//! Camera pass. The active view is set up here. The very first pass.
ESNRP_CAMERA =1,
//! In this pass, lights are transformed into camera space and added to the driver
ESNRP_LIGHT =2,
//! This is used for sky boxes.
ESNRP_SKY_BOX =4,
//! All normal objects can use this for registering themselves.
/** This value will never be returned by
ISceneManager::getSceneNodeRenderPass(). The scene manager
will determine by itself if an object is transparent or solid
and register the object as SNRT_TRANSPARENT or SNRT_SOLD
automatically if you call registerNodeForRendering with this
value (which is default). Note that it will register the node
only as ONE type. If your scene node has both solid and
transparent material types register it twice (one time as
SNRT_SOLID, the other time as SNRT_TRANSPARENT) and in the
render() method call getSceneNodeRenderPass() to find out the
current render pass and render only the corresponding parts of
the node. */
ESNRP_AUTOMATIC =24,
//! Solid scene nodes or special scene nodes without materials.
ESNRP_SOLID =8,
//! Transparent scene nodes, drawn after solid nodes. They are sorted from back to front and drawn in that order.
ESNRP_TRANSPARENT =16,
//! Transparent effect scene nodes, drawn after Transparent nodes. They are sorted from back to front and drawn in that order.
ESNRP_TRANSPARENT_EFFECT =32,
//! Drawn after the transparent nodes, the time for drawing shadow volumes
ESNRP_SHADOW =64
};
|
Et voilà donc que
CurrentRendertime est un nombre indiquant quel est le type de scene node actuellement en train d'être rendu. Son utilité est visible dès la ligne suivante où il sert à effectuer un ET logique. En gros cette ligne 3 teste que le type de node en train d'être rendu correspond avec ce que l'
OverrideMaterial doit "surpasser" (
souvenez vous). Si c'est le cas alors il fait son boulot, sinon le résultat du ET logique renverra 0 et rien ne se passera.
Ligne 9, la liste chainée des cameras est parcourue et la méthode
render de chaque élément appelée. Et enfin ligne 12, la liste chainée est redimensionnée à 0 éléments.
Le prochain bloc est un peu plus complexe (si si c'est possible, vous allez voir). Il concerne la lumière comme l'indique le commentaire et les 9 premières lignes ne devraient pas vous poser de problème. C'est après que ça se corse.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | //render lights scenes
{
CurrentRendertime = ESNRP_LIGHT;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
}
else
{
// Sort the lights by distance from the camera
core::vector3df camWorldPos(0, 0, 0);
if(ActiveCamera)
camWorldPos = ActiveCamera->getAbsolutePosition();
core::array<DistanceNodeEntry> SortedLights;
SortedLights.set_used(LightList.size());
for(s32 light = (s32)LightList.size() - 1; light >= 0; --light)
SortedLights[light].setNodeAndDistanceFromPosition(LightList[light], camWorldPos);
SortedLights.set_sorted(false);
SortedLights.sort();
for(s32 light = (s32)LightList.size() - 1; light >= 0; --light)
LightList[light] = static_cast<ILightSceneNode*>(SortedLights[light].Node);
}
Driver->deleteAllDynamicLights();
Driver->setAmbientLight(AmbientLight);
u32 maxLights = LightList.size();
if(!LightManager)
maxLights = core::min_ ( Driver->getMaximalDynamicLightAmount(), maxLights);
for (i=0; i< maxLights; ++i)
LightList[i]->render();
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
Lignes 13 à 15, un vecteur contenant la position de la camera active est créé et initialisé. Ligne 17, c'est une liste chainée qui est créée, elle va servir à stocker les nodes à rendre en fonction de leur distance avec la caméra. Notez au passage qu'elle est de type
DistanceNodeEntry, ce qui lui permet de stocker un pointeur vers un node
ILightSceneNode et une distance (un nombre) associé à celui-ci. Elle est d'ailleurs paramétrée ligne 18 pour atteindre la taille de la liste chainée du scene manager contenant tous les scene nodes "lumières".
Lignes 19 et 20, une boucle est faite permettant à la liste "sorted" de dupliquer la liste originale en stockant pour chaque noeud la distance par rapport à la camera. C'est alors un jeu d'enfant en ligne 23 de trier la liste, pour ensuite la re-basculer (l.25-26) en bonne et due forme dans la liste originale gérée par le scene manager. La seule subtilité réside dans le cast qui permet de revenir à un
ILightSceneNode classique, sachant que la liste chainée "sorted" était de type
DistanceNodeEntry.
La ligne 29 est on ne peut plus explicite, et la ligne 31 est tout à fait logique quand on se souvient que c'est le scene manager et non le driver qui gère la lumière ambiante (
comme nous l'avons déjà vu ici). Enfin ligne 33, une variable contenant la taille de la liste chainée est créée, et est utilisée ligne 38 pour passer tous les éléments en revue et ainsi pouvoir appeler leur fonction
render. Retenez au passage que cette fois-ci la liste chainée n'a pas été réduite à 0, ce sera pour plus tard.
Le bloc suivant est une promenade de santé en comparaison. A vrai dire il n'y a rien que nous n'ayons déjà vu, aussi allons nous directement passer à celui d'après.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | // render skyboxes
{
CurrentRendertime = ESNRP_SKY_BOX;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<SkyBoxList.size(); ++i)
{
ISceneNode* node = SkyBoxList[i];
LightManager->OnNodePreRender(node);
node->render();
LightManager->OnNodePostRender(node);
}
}
else
{
for (i=0; i<SkyBoxList.size(); ++i)
SkyBoxList[i]->render();
}
SkyBoxList.set_used(0);
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
Là encore pas de difficultés majeures mais quelques subtilités néanmoins sur lesquelles nous allons nous attarder. Ligne 6 nous pouvons voir que la liste contenant les nodes "solid" est triée. C'est le même principe qu'auparavant, le type de cette liste est hybride et ne contient pas uniquement des scene nodes.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | // render default objects
{
CurrentRendertime = ESNRP_SOLID;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
SolidNodeList.sort(); // sort by textures
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<SolidNodeList.size(); ++i)
{
ISceneNode* node = SolidNodeList[i].Node;
LightManager->OnNodePreRender(node);
node->render();
LightManager->OnNodePostRender(node);
}
}
else
{
for (i=0; i<SolidNodeList.size(); ++i)
SolidNodeList[i].Node->render();
}
Parameters.setAttribute ( "drawn_solid", (s32) SolidNodeList.size() );
SolidNodeList.set_used(0);
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
Ensuite c'est du grand classique, on appelle la fonction render de chaque noeud lignes 21 et 22. Petite subtilité ligne 25 où on stocke le nombre de noeuds rendus dans un "attribut" vu plus haut dans ce chapitre. Et enfin on remet la liste chainée à 0 ligne 26.
Seules les lignes 23 à 25 devraient vous poser problème dans le bloc suivant. En fait le nom de la fonction est assez explicite puisque celle-ci va se servir du stencil buffer pour dessiner les ombres du rendu. C'est trop complexe pour être abordé ici (le chapitre est déjà suffisamment long

), mais qui sait, peut être plus tard...
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | // render shadows
{
CurrentRendertime = ESNRP_SHADOW;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<ShadowNodeList.size(); ++i)
{
ISceneNode* node = ShadowNodeList[i];
LightManager->OnNodePreRender(node);
node->render();
LightManager->OnNodePostRender(node);
}
}
else
{
for (i=0; i<ShadowNodeList.size(); ++i)
ShadowNodeList[i]->render();
}
if (!ShadowNodeList.empty())
Driver->drawStencilShadow(true,ShadowColor, ShadowColor,
ShadowColor, ShadowColor);
ShadowNodeList.set_used(0);
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
Rien de très palpitant pour le bloc suivant :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | // render transparent objects.
{
CurrentRendertime = ESNRP_TRANSPARENT;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
TransparentNodeList.sort(); // sort by distance from camera
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<TransparentNodeList.size(); ++i)
{
ISceneNode* node = TransparentNodeList[i].Node;
LightManager->OnNodePreRender(node);
node->render();
LightManager->OnNodePostRender(node);
}
}
else
{
for (i=0; i<TransparentNodeList.size(); ++i)
TransparentNodeList[i].Node->render();
}
Parameters.setAttribute ( "drawn_transparent", (s32) TransparentNodeList.size() );
TransparentNodeList.set_used(0);
if(LightManager)
LightManager->OnRenderPassPostRender(CurrentRendertime);
}
|
Idem. Ca en devient presque trop facile.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | // render transparent effect objects.
{
CurrentRendertime = ESNRP_TRANSPARENT_EFFECT;
Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRendertime) != 0);
TransparentEffectNodeList.sort(); // sort by distance from camera
if(LightManager)
{
LightManager->OnRenderPassPreRender(CurrentRendertime);
for (i=0; i<TransparentEffectNodeList.size(); ++i)
{
ISceneNode* node = TransparentEffectNodeList[i].Node;
LightManager->OnNodePreRender(node);
node->render();
LightManager->OnNodePostRender(node);
}
}
else
{
for (i=0; i<TransparentEffectNodeList.size(); ++i)
TransparentEffectNodeList[i].Node->render();
}
Parameters.setAttribute ( "drawn_transparent_effect", (s32) TransparentEffectNodeList.size() );
TransparentEffectNodeList.set_used(0);
}
|
Et nous voici
déjà au bout de la fonction. On peut voir ligne 4 la mise à 0 de la chaine des scene nodes "lumières" qui manquait plus haut. Ligne 5, la
DeletionList est nettoyée. Il s'agit d'une liste chainée à laquelle on peut ajouter des noeuds qui seront donc supprimés à la fin de chaque rendu. Par défaut elle est vide. Et enfin ligne 7, on remet le
CurrentRendertime à sa valeur d'origine, indiquant qu'aucun rendu n'est en cours.
Code : C++ | if(LightManager)
LightManager->OnPostRender();
LightList.set_used(0);
clearDeletionList();
CurrentRendertime = ESNRP_NONE;
|