1. Introduction

Nous avons installez OpenSceneGraph grâce au tutoriel précédent, nous allons maintenant créer notre premier programme OSG et apprendre les bases de la programmation avec OpenSceneGraph.

OSG fonctionne grâce à un graphe de scène. Le graphe de scène est composé de nœuds décrivant la scène 3D et a une forme arborescente. Les nœuds inférieurs subissent toutes les transformations des nœuds supérieurs.

Par exemple, tous les nœuds en dessous d'un nœud effectuant une translation subiront cette transformation.

Dans ce tutoriel, nous allons créer une primitive et ajouter une forme OSG simple.

2. Fonctions utilisées

Fonctions du Viewer :

  • setUpViewInWindow(int positionX, int PositionY, int largeur, int hauteur) : permet de créer une fenêtre de dimension largeur*hauteur positionnée selon positionX et positionY dans l'écran ;
  • setSceneData(Node *node) : permet de mettre en place la scène exploitée par le Viewer ;
  • run().

Fonctions de l'objet Geometry utilisées :

  • Vec3(int posX, int posY, int posZ) ;
  • setVertexArray(Array *array) : associe un tableau de sommet à un objet Geometry ;
  • addPrimitiveSet(PrimitiveSet *primitiveSet) : ajoute une primitive à un objet Geometry ;
  • setColorBinding(AttributeBinding ab) : permet de gérer l'affichage des couleurs de l'objet Geometry ;
  • getOrCreateStateSet() : retourne le nœud StateSet pour un nœud donné ou le crée s'il n'existe pas.

Autres fonctions utilisées :

  • setMode(StateAttribute::GLMode mode, StateAttribute::GLModeValue value) : permet de modifier certains attributs du noeud ;
  • addDrawable(Drawable *drawable) : ajoute un objet de type Drawable au nœud concerné ;
  • Box(osg::Vec3(int posX, int posY, int posZ), int taille))); : crée une boite de taille ‘taille' à la position définie par le Vec3.

3. Mise en Pratique

Créez un nouveau projet sous Visual Studio. Le nom du projet importe peu. Ajoutez un fichier main.cpp.

Les includes nécessaires afin de créer l'application sont les suivants :

 
Sélectionnez
#include <osgViewer/Viewer> 
#include <osg/Geometry> 
#include <osg/ShapeDrawable> 

Pour démarrer notre application, il nous faut créer une fenêtre dans laquelle nous allons mettre en place notre scène 3D.

Nous construisons donc un Viewer dans le main.

 
Sélectionnez
// Construction du Viewer. 
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; 

Nous allons maintenant créer une fenêtre dans laquelle nous pourrons mettre en place notre scène 3D.

 
Sélectionnez
// Nous demandons au viewer de créer une fenêtre de 512x512 pixels que nous plaçons à 50 pixels du bord haut gauche de notre écran. 
viewer->setUpViewInWindow(50, 50, 512, 512); 

Notre fenêtre est prête, mais celle-ci attend qu'on lui associe une scène 3D afin de pouvoir effectuer un rendu. Nous lui associons une scène que nous créerons dans une fonction à part.

 
Sélectionnez
// Nous associons au viewer la scène 3D dont nous voulons effectuer le rendu. 
viewer->setSceneData(creerLaScene()); 

Ici, la fonction où nous mettrons en place notre scène 3D s'appelle creerLaScene. Nous la lions au viewer grâce à la méthode setSceneData() du viewer.

Il ne nous reste plus qu'à exécuter le viewer afin de mettre en place la scène 3D.

 
Sélectionnez
// Nous exécutons la fonction run du viewer. 
return viewer->run(); 

Notre main est désormais complet :

 
Sélectionnez
int tmain(int argc, char* argv[]) 
{ 
     // Construction du Viewer. 
     osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; 

     // Nous demandons au viewer de créer une fenêtre de 512x512 pixels que nous plaçons à 50 pixels du bord haut gauche de notre écran. 
     viewer->setUpViewInWindow(50, 50, 512, 512); 

     // Nous associons au viewer la scène 3D dont nous voulons effectuer le rendu. 
     viewer->setSceneData(creerLaScene()); 

     // Nous exécutons la fonction run du viewer. 
     return viewer->run(); 
} 

Il nous faut maintenant créer notre scène 3D. Nous allons commencer par afficher des primitives géométriques, puis nous rajouterons certaines formes de base d'OSG.

3-A. Mise en place d'une primitive OSG : le Triangle

Voici le prototype de notre fonction creerLaScene. On remarquera que cette fonction retourne un Node* car la méthode setSceneData de notre viewer attend tout simplement un Node*.

 
Sélectionnez
Osg::Node* creerLaScene() 

Nous pouvons maintenant créer notre triangle :

 
Sélectionnez
// Nous créons un objet Geometry dans lequel nous allons construire notre triangle. 
     osg::Geometry* geoTriangle = new osg::Geometry; 

     // Nous créons un tableau de trois sommets. 
     osg::Vec3Array* tabSommet = new osg::Vec3Array; 
     tabSommet->push_back(osg::Vec3(-1, 0, -1)); 
     tabSommet->push_back(osg::Vec3(1, 0, -1)); 
     tabSommet->push_back(osg::Vec3(0, 0, 1)); 

     // Nous ajoutons le tableau de sommet a notre objet Geometry. 
     geoTriangle ->setVertexArray(tabSommet); 

     // Nous créons une primitive Triangle et nous ajoutons les sommets selon leur index dans le tableau tabSommet 
     osg::DrawElementsUInt* pPrimitiveSet = 
     new osg::DrawElementsUInt( osg::PrimitiveSet::TRIANGLES, 0 ); 
     pPrimitiveSet->push_back(0); 
     pPrimitiveSet->push_back(1); 
     pPrimitiveSet->push_back(2); 

     // Nous ajoutons notre primitive a notre objet Geometry. 
     geoTriangle->addPrimitiveSet(pPrimitiveSet); 

     // On met en place un tableau de couleurs. Dans notre exemple chaque sommet du triangle aura une couleur différente. 
     osg::Vec4Array* tabCouleur = new osg::Vec4Array; 
     tabCouleur->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); 
     tabCouleur->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); 
     tabCouleur->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); 
     geoTriangle->setColorArray(tabCouleur); 

     // Nous nous assurons que notre triangle utilisera bien une couleur par sommet. 
     geoTriangle->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 

Nous venons de créer une primitive Triangle composée d'une couleur par sommet. Il faut maintenant l'associer à un Geode, un nœud acceptant les objets de type drawable comme notre triangle.

 
Sélectionnez
// Nous créons un nœud géométrique afin de stocker notre triangle et nous désactivons sa lumière. 
    osg::Geode* noeudGeo = new osg::Geode; 
    osg::StateSet* status = noeudGeo->getOrCreateStateSet(); 
    status->setMode(GL_LIGHTING, osg::StateAttribute::OFF); 
    noeudGeo->addDrawable(geoTriangle); 

Enfin, afin de pouvoir visualiser notre triangle à l'écran, notre fonction creerLaScene doit retourner le Geode noeudGeo.

 
Sélectionnez
return noeudGeo; 

Voilà, notre triangle est construit et prêt à être visualisé.

 
Sélectionnez
Osg::Node* creerLaScene() 
{ 
     // Nous créons un objet Geometry dans lequel nous allons construire notre triangle. 
     osg::Geometry* geoTriangle = new osg::Geometry; 

     // Nous créons un tableau de trois sommets. 
     osg::Vec3Array* tabSommet = new osg::Vec3Array; 
     tabSommet->push_back(osg::Vec3(-1, 0, -1)); 
     tabSommet->push_back(osg::Vec3(1, 0, -1)); 
     tabSommet->push_back(osg::Vec3(0, 0, 1)); 

     // Nous ajoutons le tableau de sommet a notre objet Geometry. 
     geoTriangle ->setVertexArray(tabSommet); 

     // Nous créons une primitive Triangle et nous ajoutons les sommets selon leur index dans le tableau tabSommet 
     osg::DrawElementsUInt* pPrimitiveSet = 
     new osg::DrawElementsUInt( osg::PrimitiveSet::TRIANGLES, 0 ); 
     pPrimitiveSet->push_back(0); 
     pPrimitiveSet->push_back(1); 
     pPrimitiveSet->push_back(2); 

     // Nous ajoutons notre primitive a notre objet Geometry. 
     geoTriangle->addPrimitiveSet(pPrimitiveSet); 

     // On met en place un tableau de couleurs. Dans notre exemple chaque sommet du triangle aura une couleur différente. 
     osg::Vec4Array* tabCouleur = new osg::Vec4Array; 
     tabCouleur->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); 
     tabCouleur->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); 
     tabCouleur->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); 
     geoTriangle->setColorArray(tabCouleur); 

     // Nous nous assurons que notre triangle utilisera bien une couleur par sommet. 
     geoTriangle->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 

     /*---------------------------------/!\----------------------------------*/ 
     // Nous créons un nœud géométrique afin de stocker notre triangle et nous désactivons sa lumière. 
     osg::Geode* noeudGeo = new osg::Geode; 
     osg::StateSet* status = noeudGeo->getOrCreateStateSet(); 
     status->setMode(GL_LIGHTING, osg::StateAttribute::OFF); 
     noeudGeo->addDrawable(geoTriangle); 
     /*----------------------------------------------------------------------*/ 
     
     return noeudGeo; 
} 

Il est important de ne pas oublier de créer le nœud géométrique dans lequel nous mettons notre triangle. En effet, l'objet Geometry dans lequel nous l'avons construit n'est pas un nœud.

Image non disponible

On retrouve dans OSG les primitives déjà présentes dans OpenGL, POINTS, LINES, LINE_STRIP, LINE_LOOP, POLYGON, QUADS, QUAD_STRIP, TRIANGLES, TRIANGLE_STRIP ou TRIANGLE_FAN. Essayez-les afin de pouvoir découvrir leurs différentes caractéristiques. Comment va se comporter un polygone auquel on applique seulement deux couleurs ou comment créer une pyramide en agençant quatre sommets de la bonne manière...

On peut aussi remarquer que notre Viewer, par défaut, nous permet de déplacer notre primitive, de zoomer ou dézoomer grâce à la souris.

Nous allons maintenant passer à la seconde partie de notre application : insérer des formes de base OSG.

3-B. Mise en place d'une forme OSG : le cube

Dans cette seconde partie, nous allons intégrer un cube OSG à notre scène 3D. Comme beaucoup d'API permettant la mise en place de scènes 3D, OSG dispose d'un certain nombre de formes de base comme le cube, le cône, la sphère... Dans cet exemple nous verrons comment insérer un cube en dessous de notre triangle.

Contrairement aux primitives, les formes de bases OSG ne sont pas des objets géométriques et ne peuvent donc pas être créés de la même manière que notre triangle. Chaque forme est un objet dérivant de osg::ShapeDrawable et doit être ajouté à un nœud géométrique.

Les formes de base OSG sont très simples d'utilisation et s'intègrent facilement.

 
Sélectionnez
noeudGeo->addDrawable(new osg::ShapeDrawable 
(new osg::Box(osg::Vec3(0.0f,0.0f,-2.0f),2.0f))); 

Ici, nous créons tout simplement une box que l'on ajoute à notre nœud “nœudGeo”. Le constructeur de notre cube nous demande un point à trois dimensions afin de placer l'objet dans la scène ainsi que la taille de notre cube.

Image non disponible

On peut remarquer que le Viewer fait très bien les choses puisqu'il met en place la caméra afin de pouvoir visualiser le maximum de notre scène sans que l'on ait à changer quoi que ce soit.

Vous pouvez essayer de mettre en place plusieurs formes afin de voir lesquelles sont mises à disposition et comment les insérer facilement.

4. Aller plus loin

D'autres tutoriels permettent de manipuler les primitives et les formes de base d'OSG. Ici, nous avons surtout appris à créer une application fonctionnelle permettant la mise en place de ces deux fonctionnalités.

Le tutoriel Working with Open Scene Graph Geometry permet d'apprendre à créer une pyramide en utilisant les primitives d'OSG et Working with Shapes, Changing State

permet de manipuler les formes de bases comme le cube ou la sphère.

5. Remerciements

Merci jacques_jean pour les corrections.