Chargement d'un modèle 3D et de textures 2D

Alys Beauvarlet, Thierry Bonnefoy, Bertrand Rix, Guillaume Saby

Explication du chargement d'un modèle 3D et de textures 2D avec OpenSceneGraph.

Commentez

- Titre : Chargement d'un modèle 3D et de textures 2D

- Auteur : Alys Beauvarlet, Thierry Bonnefoy, Bertrand Rix, Guillaume Saby

- Parution : 25 juin 2012

- Public visé :

- Licence : Copyright ® 2013 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 2. Mise en pratique

- 2-A. Chargement d'un modèle 3D avec OSG

- 2-B. Mise en place d'une texture 2D

- 3. Aller plus loin

- 4. Remerciements

1 - Introduction

Dans ce tutoriel nous allons apprendre à intégrer des modèles 3D .obj à notre scène et mettre en place des textures 2D sur des objets.

Le chargement d'objet 3D sous OpenSceneGraph se fait de manière très simple. En effet, la fonction osgDB::readNodeFile va nous charger le modèle et nous renvoyer un pointeur sur un nœud que l'on pourra ensuite intégrer à notre scène.

2 - Mise en pratique

Dans ce tutoriel nous auront besoin des include suivants :

#include <osgViewer/Viewer>
#include <osg/Geometry>
#include <osgDB/ReadFile>

<osgDB/ReadFile> va nous servir à charger les modèles 3D et les images utilisées pour

les textures 2D.

Nous créons notre main qui appellera la fonction creerLaScene afin de mettre en place notre scène 3D. Je ne reviens pas sur les fonctions utilisées dans le main et leur utilité, celles-ci ont été détaillées dans le tutoriel « Premier Programme ».

int main(int argc, char* argv[])
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setUpViewInWindow(50, 50, 512, 512);
viewer->setSceneData(creerLaScene());
return viewer->run();
}

2-A - Chargement d'un modèle 3D avec OSG

Charger un modèle 3D avec OpenSceneGraph est très simple. En effet, OSG supporte plusieurs format d'objet 3D et la fonction osgDB::readNodeFile(string nomObjet) nous retourne un pointeur sur nœud que nous ajouterons tout simplement à notre scène.

Il nous faut donc créer un Node* afin de récupérer le chargement du modèle.

Nous commençons donc par créer un Group* qui contiendra toute notre scène 3D.

// Nous créons un Group* qui comprendra toute notre scène.
osg::Group* scene = new osg::Group;

Ensuite nous créons un Node* dans lequel nous allons charger notre modèle 3D.

// Nous créons un Node* modele3D qui va récupérer notre objet3D.
osg::Node* modele3D = osgDB::readNodeFile("monObjet3D.obj");

Enfin nous rattachons notre Node « modele3D » à notre Group et nous retournons le Group « scene ».

// Nous ajoutons notre modele3D à notre scène.
scene->addChild(modele3D);
return scene;

Voila, nous avons réussi à charger notre modèle dans notre scène 3D.

2-B - Mise en place d'une texture 2D

Dans cette partie du tutoriel nous allons créer une primitive OSG et lui appliquer une texture 2D qui sera chargée à partir d'une image.

Nous allons donc commencer par créer un Quad. La technique est la même que celle vue dans le tutoriel « Premier Programme » lors de la création du triangle.

// Création d'un Quad.
osg::Geometry* geoQuad = new osg::Geometry;

osg::Vec3Array* tabSommet = new osg::Vec3Array;
tabSommet->push_back(osg::Vec3(-100, 0, 0));
tabSommet->push_back(osg::Vec3(100, 0, 0));
tabSommet->push_back(osg::Vec3(100, 0, 100));
tabSommet->push_back(osg::Vec3(-100, 0, 100));
geoQuad->setVertexArray(tabSommet);

osg::DrawElementsUInt* primitive = new
osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
primitive->push_back(0);
primitive->push_back(1);
primitive->push_back(2);
primitive->push_back(3);
geoQuad->addPrimitiveSet(primitive);

Le plus dans la création de notre Quad est que nous allons rajouter un tableau de coordonnées de texture à notre nœud Geometry.

// Nous créons ensuite une tableau qui contiendra nos coordonnées de texture.
osg::Vec2Array* coordonneeTexture = new osg::Vec2Array(4);
(*coordonneeTexture)[0].set(0.0f, 0.0f);
(*coordonneeTexture)[0].set(1.0f, 0.0f);
(*coordonneeTexture)[0].set(1.0f, 1.0f);
(*coordonneeTexture)[0].set(0.0f, 1.0f);
geoQuad->setTexCoordArray(0, coordonneeTexture);

Enfin nous ajoutons notre objet Geometry à un Geode comme dans le tutoriel précédent.

osg::Geode* noeudGeo = new osg::Geode;
osg::StateSet* statuts = noeudGeo->getOrCreateStateSet();
statuts->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
noeudGeo->addDrawable(geoQuad);

Une fois notre Quad créé il ne nous reste plus qu'à charger l'image qui servira de texture et l'associer à notre nœud géométrique. Remarquez qu'étant donné que la texture est appliquée à tout le Geode, tous les objets qu'il contiendra auront la même texture.

Nous commençons donc par créer un objet Texture2D.

// Nous créons une Texture2D.
osg::Texture2D* texture = new osg::Texture2D;

Ensuite, nous créons un objet Image dans lequel nous chargeons notre image qui servira de texture. Ici l'image s'appelle « monImage.jpg », elle doit être dans le répertoire du projet.

// Maintenant nous chargeons notre image grâce à la fonction readImageFile.
osg::Image* image = osgDB::readImageFile("monImage.jpg");

Après avoir chargé notre image, nous devons l'associer à notre objet Texture2D.

// Nous associons notre image à notre objet Texture2D.
texture->setImage(image);

Dans un dernier temps nous activons les textures sur notre objet Geometry à travers l'objet statuts qui nous a déjà servi à désactiver la lumière.

// Enfin nous activons les texture de notre objet Geometry à travers l'objet statuts.
statuts->setTextureAttributeAndModes (0, texture, osg::StateAttribute::ON);

Le chargement de la texture et son application à notre nœud Geometry ne sont donc pas compliqués, il ne faut pas oublier de bien spécifier les coordonnées de texture associées au nœud et activer les textures pour le nœud.

// Nous créons une Texture2D.
osg::Texture2D* texture = new osg::Texture2D;
// Maintenant nous chargeons notre image grâce à la fonction readImageFile.
osg::Image* image = osgDB::readImageFile("monImage.jpg");
// Nous associons notre image à notre objet Texture2D.
texture->setImage(image);
// Enfin nous activons les texture de notre objet Geometry à travers l'objet statuts.
statuts->setTextureAttributeAndModes(0, texture,
osg::StateAttribute::ON);
// On rattache enfin notre noeudGeo a notre Group scène.
scene->addChild(noeudGeo);

Ici il n'y a que la texture 2D qui a été chargée, notre modèle est en commentaire.

Si nous chargeons notre modèle et en même temps nous obtenons ceci :

Notre canard est bien trop gros par rapport à notre Quad qui est au milieu de son corps. Nous ne nous en étions pas rendu compte avant car, rappelez-vous, le viewer nous simplifie la vie en plaçant la caméra de telle manière que l'on observe l'objet dans son ensemble en occupant la plus grande partie de la fenêtre. Nous avions donc l'impression que notre canard avait à peu près les même dimensions que notre plan, mais il n'en est rien.

Pour résoudre ce problème et pour déplacer le canard nous pouvons utiliser la classe MatrixTransform afin d'affecter une mise à l'échelle et une translation à notre modèle 3D.

Avant de charger notre modèle 3D il nous faut donc créer une matrice qui contiendra les transformations et un objet de type MatrixTransform auquel nous associerons notre matrice.

// Nous créons un objet MatrixTransform et une matrice.
osg::MatrixTransform* transformation = new osg::MatrixTransform();
osg::Matrix matrice;

Ensuite nous créons nos transformation en utilisant les fonction « makeScale » et « setTrans ».

// Nous mettons en place deux transformations dans notre matrice, une remise à l'échelle et une translation selon les Y.
matrice.makeScale(0.2, 0.2, 0.2);
matrice.setTrans(0.0, -100.0, 0.0);

Pour finir, nous associons notre matrice à notre MatrixTransform et nous chargeons notre modèle 3D.

// Nous associons notre matrice à notre objet transformation.
transformation->setMatrix(matrice);

// Nous chargeons notre modèle comme vu plus haut.
osg::Node* modele3D = osgDB::readNodeFile("ducky.obj");
// Mais nous l'ajoutons à notre objet MatrixTransform
transformation->addChild(modele3D);

// Que nous ajoutons à notre scène.
scene->addChild(transformation);

Attention nous n'ajoutons pas directement notre Node modele3D à notre scène. Afin de lui appliquer les transformations, il faut l'ajouter à notre MatrixTransform. En effet, tous les éléments « sous » cet objet dans le graphe de scène subiront les transformations.

3 - Aller plus loin

Plus d'informations peuvent être trouvées sur les textures et le chargement de modèle sur le site d'OSG. On trouve notamment un tutoriel permettant la mise en place d'un affichage tête haute utilisant des Quads pouvant être utile pour des applications plus poussées.

4 - Remerciements

Merci à _Max_ pour les corrections.