1. Introduction▲
Ce mini-tutoriel se concentrera sur l'explication de ce qu'est un graphe de scène, et sur le mécanisme du pointeur intelligent, utilisé au travers de la classe osg::ref_ptr<T> dans OpenSceneGraph.
2. Graphe de scène▲
Comme son nom l'indique, le graphe de scène est une structure de données de type arbre. Petit rappelle sur les arbres :
Ils possèdent une racine, des nœuds et des feuilles. Ici la racine est représentée par la pastille « Scène », les nœuds sont « Salle », « Perso » et « Tête » et les feuilles sont « Chaise », « Table » et « Oreille ».
L'avantage d'un tel partitionnement est la rapidité d'une part, puisque si l'arbre est découpé de manière logique, aller rechercher un élément sera beaucoup moins long que dans une liste (si on sait où aller à chaque embranchement) et la simplification d'autre part, car quand une scène commence à être grande, avoir tous les objets similaires sous un même nœud peut être très pratique.
Le graphe de scène est un type de données repris dans énormément de moteurs 3D pour ces raisons-là.
Une chose importante dans l'implémentation d'un graphe de scène découle de cette structure en père-fils : des classes spéciales servent de nœuds ayant des effets divers sur tous leurs fils. Exemple dans OpenSceneGraph : la classe MatrixTransform. C'est un « Group », soit un nœud, et il va appliquer la transformation matricielle que l'on lui a spécifié à tous ses fils.
Bien sur un nœud peut être fils d'un autre nœud, ce qui permet de combiner les effets entre eux.
Les feuilles, quant à elles, ne peuvent pas prendre de fils. Dans l'implémentation logicielle, ce sont les objets sur lesquels vont s'appliquer tous les effets des nœuds.
Enfin, il est à noter que la racine n'est rien d'autre qu'un simple nœud, d'où va partir la ramification de toute la scène.
Cette caractéristique, la transmission de données des nœuds vers leurs fils, n'est pas à perdre de vue quand on construit son graphe de scène pour son application.
3. Pointeurs intelligents▲
L'un des « problèmes » du C/C++ par rapport à des langages plus récents et haut niveau, comme le java par exemple, sont les « fuites de mémoire » (memory leaks) facilement faites.
Dans Java, la notion de pointeurs est cachée et une « garbage manager » se charge de supprimer ce qui n'est plus utilisé. En C/C++, les allocations dynamiques peuvent vite être un problème si on perd la trace de ce qu'on avait alloué, remplissant donc petit à petit la mémoire. Les pointeurs intelligents sont un moyen d'éviter ça.
Le principe du pointeur intelligent est simple : quand un objet est créé, il possède une donnée membre qui « compte » le nombre de pointeurs pointant vers lui. Si on rajoute un pointeur vers lui, il incrémente cette donnée. Si un pointeur est supprimé, la donnée est décrémentée. Quand cette donnée arrive à 0, l'objet s'autodétruit.
Sous OSG, les pointeurs intelligents sont les osg::ref_ptr<T>. Quand on écrit par exemple :
osg::
ref_ptr<
osg::
Group>
p =
new
osg::
Group;
on crée un nouvel objet Group référencé par le “ref_ptr p;”.
Si l'on fait ensuite :
osg::
ref_ptr<
osg::
Group>
q =
p.get();
q référence maintenant également le Group. Si p est détruit, Group ne sera pas supprimé de la mémoire car q pointera toujours sur lui. Maintenant si q est également supprimé, le Group n'a plus aucun pointeur qui le désigne. Il est donc impossible de le retrouver dans la mémoire pour le programme. Le Group se détruit automatiquement.
4. Remerciements▲
Merci jacques_jean pour les corrections.