DirectX 11 - Tutoriel 8 : Chargement de modèles Maya 2011

RasterTek

Traducteur

Jérôme Marsaguet

Ce tutoriel va vous expliquer comment importer des modèles 3D statiques depuis Maya 2011. Notez qu'il s'axe sur le logiciel Maya, mais qu'il peut facilement s'appliquer à n'importe quel autre logiciel de modélisation 3D, moyennant de légers changements.

Commentez

- Titre : DirectX 11 - Tutoriel 8 : Chargement de modèles Maya 2011

- Auteur : RasterTek

- Traducteur : Jérôme Marsaguet

- Parution : 4 septembre 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 RasterTek. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Navigation

- I. Introduction

- II. Cube.obj

- III. Conversion de système de coordonnées direct à indirect

- IV. Main.cpp

- V. Résumé

- VI. Exercices à faire

- VII. Code source

- VIII. Remerciements

- Navigation

Navigation

Tutoriel précédent : rendu de modèles 3D

Sommaire

I - Introduction

Dans les tutoriels précédents, nous avons créé notre propre format de modèle et affiché des modèles 3D en l'utilisant. L'objectif est maintenant de convertir des modèles Maya 2011 vers notre format et d'effectuer leur rendu. Je ne vais pas traiter la façon de modéliser des objets 3D avec Maya, il y a des centaines de tutoriels sur le Net et déjà consacrés à cela, nous allons commencer avec un modèle 3D texturé et triangulé prêt à l'exportation.
Pour Maya, nous utilisons le format d'exportation .OBJ car il est facilement lisible et agréable pour ceux qui débutent.

Pour exporter votre modèle vers le format .OBJ, vous devez d'abord activer l'exportateur .OBJ dans Maya. Cliquez sur « Window », puis « Settings/Preferences » et « Plug-in Manager ». Faites défiler jusqu'à objExport.mll et sélectionnez à la fois « Loaded » et « Auto Load ». Maintenant, pour exporter votre modèle dans ce format, cliquez sur « File », puis « Export All ». Puis sélectionnez vers le bas « Files of type: » et faites défiler pour sélectionner « OBJexport », donnez-lui un nom et cliquez sur « Export All » pour qu'il exporte votre modèle vers un fichier texte d'extension .obj. Pour consulter le fichier, vous pouvez faire un clic droit et sélectionner « Ouvrir avec… » puis choisir WordPad. Vous verrez alors quelque chose qui ressemble à ceci :

II - Cube.obj

# This file uses centimeters as units for non-parametric coordinates.

mtllib cube.mtl
g default
v -0.500000 -0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v 0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
v 0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.001992 0.001992
vt 0.998008 0.001992
vt 0.001992 0.998008
vt 0.998008 0.998008
vt 0.998008 0.998008
vt 0.001992 0.998008
vt 0.998008 0.001992
vt 0.001992 0.001992
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 0.000000 1.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 1.000000 0.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 0.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 0.000000 -1.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn 1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
vn -1.000000 0.000000 0.000000
s 1
g pCube1
usemtl file1SG
f 1/1/1 2/2/2 3/3/3
f 3/3/3 2/2/2 4/4/4
s 2
f 3/13/5 4/14/6 5/15/7
f 5/15/7 4/14/6 6/16/8
s 3
f 5/21/9 6/22/10 7/23/11
f 7/23/11 6/22/10 8/24/12
s 4
f 7/17/13 8/18/14 1/19/15
f 1/19/15 8/18/14 2/20/16
s 5
f 2/5/17 8/6/18 4/7/19
f 4/7/19 8/6/18 6/8/20
s 6
f 7/9/21 1/10/22 5/11/23
f 5/11/23 1/10/22 3/12/24

Ce fichier de modèle .OBJ représente un cube 3D. Il est composé de 8 sommets, 24 coordonnées de texture et normales et 6 côtés composés de 12 faces au total. Lorsque vous lisez le fichier, vous pouvez ignorer toutes les lignes ne commençant pas par « V », « VT », « VN », ou « F ». Les informations supplémentaires ne seront pas nécessaires pour la conversion. Regardons ce que chacune des lignes importantes signifie :

1. Les lignes « V » sont les sommets. Le cube est constitué de 8 sommets pour ses 8 coins. Chacun est représenté par le format flottant X,Y,Z ;

2. Les lignes « VT » sont les coordonnées de texture. Le cube en possède 24, pour la plupart en double, car chaque sommet de chaque triangle du modèle est reporté. Ils sont représentés par le format flottant TU, TV ;

3. Les lignes « VN » sont les normales. Le cube en a 24, également en double. Ils sont représentés par le format flottant NX, NY, NZ ;

4. Les lignes « F » sont les faces de chaque triangle du cube. Les valeurs listées correspondent aux indices des sommets, coordonnées de texture et normales. Chaque face est représentée par :

f Vertex1/Texture1/Normal1 Vertex2/Texture2/Normal2 Vertex3/Texture3/Normal3

Ainsi une ligne f 3/13/5 4/14/6 5/15/7 se traduit par Sommet3/Texture13/Normale5 Sommet4/Texture14/Normale6 Sommet5/Texture15/Normale7.

L'ordre dans lequel les données sont écrites est très important. Par exemple, le premier sommet correspond au Sommet1 dans la liste des faces. Il en va de même pour les coordonnées de texture et les normales.

Remarquez que dans les lignes des faces, chaque triplet d'indices de chaque ligne, correspond à un triangle. Dans notre cas, le total des 12 faces constituent les 6 côtés du cube avec deux triangles de chaque côté.

III - Conversion de système de coordonnées direct à indirect

Par défaut, Maya 2011 travaille avec un système de coordonnées direct et exporte son fichier en le conservant. Afin de convertir les données vers un système indirect qu'utilise DirectX 11 par défaut, vous devez effectuer ce qui suit :

1. Inverser la coordonnée Z des sommets. Vous verrez ceci vertices[vertexIndex].z =vertices[vertexIndex].z * -1.0f dans le code ;

2. Inverser la coordonnée TV de texture. Vous verrez dans le code que je fais texcoords[texcoordIndex].y = 1.0f - texcoords[texcoordIndex].y ;

3. Inverser la composante Z de la normale. Vous pourrez voir ce code normals[normalIndex].z = normals[normalIndex].z * -1.0f le faire ;

4. Passer le dessin de l'ordre direct à l'ordre indirect. Dans le code, je lis simplement les indices dans l'ordre inverse plutôt que de les réorganiser après lecture.

fin >> faces[faceIndex].vIndex3 >> input2 >> faces[faceIndex].tIndex3 >> input2 >> faces[faceIndex].nIndex3;
fin >> faces[faceIndex].vIndex2 >> input2 >> faces[faceIndex].tIndex2 >> input2 >> faces[faceIndex].nIndex2;
fin >> faces[faceIndex].vIndex1 >> input2 >> faces[faceIndex].tIndex1 >> input2 >> faces[faceIndex].nIndex1;

Ces quatre étapes effectuées, les données du modèle sont prêtes à être rendues correctement sous DirectX 11.

IV - Main.cpp

Ce programme permettant le passage de fichiers Maya 2011 d'extension .obj vers notre format DirectX 11 est plutôt simple et est contenu au sein d'un seul fichier nommé main.cpp. Il ouvre une fenêtre de commande et demande le chemin du fichier du fichier .obj à convertir. Une fois le nom tapé, il va tenter d'ouvrir le fichier et, si le fichier s'ouvre, de lire la valeur du nombre de données, et mettre en place les structures requises servant à les stocker. Après quoi, il les charge et les convertit vers un système de coordonnées indirect. Ceci fait, il écrit les données dans un fichier de modèle .txt et ce dernier peut être renommé et utilisé pour le rendu dans DirectX 11 en appliquant le projet de rendu de modèles 3D du tutoriel précédent.

////////////////////////////////////////////////////////////////////////////////
// Nom du fichier : main.cpp
////////////////////////////////////////////////////////////////////////////////

////////////////
// INCLUSIONS //
////////////////
#include <iostream>
#include <fstream>
using namespace std;

//////////////////////////
// DÉFINITION DES TYPES //
//////////////////////////
typedef struct
{
float x, y, z;
}VertexType;

typedef struct
{
int vIndex1, vIndex2, vIndex3;
int tIndex1, tIndex2, tIndex3;
int nIndex1, nIndex2, nIndex3;
}FaceType;

//////////////////////////////
// PROTOTYPES DES FONCTIONS //
//////////////////////////////
void GetModelFilename(char*);
bool ReadFileCounts(char*, int&, int&, int&, int&);
bool LoadDataStructures(char*, int, int, int, int);

/////////////////////////
// PROGRAMME PRINCIPAL //
/////////////////////////
int main()
{
bool result;
char filename[256];
int vertexCount, textureCount, normalCount, faceCount;
char garbage;

// Lit le nom du modèle.
GetModelFilename(filename);

// Lit le nombre de sommets, de coordonnées de texture, de normales et de
// faces afin d'initialiser correctement la structure de données à la taille requise.
result = ReadFileCounts(filename, vertexCount, textureCount, normalCount, faceCount);
if(!result)
{
return -1;
}

// Affiche les quantités à l'écran à titre d'information.
cout << endl;
cout << "Vertices: " << vertexCount << endl;
cout << "UVs:      " << textureCount << endl;
cout << "Normals:  " << normalCount << endl;
cout << "Faces:    " << faceCount << endl;

// Charge les données dans la structure et les retourne vers notre format de modèle.
result = LoadDataStructures(filename, vertexCount, textureCount, normalCount, faceCount);
if(!result)
{
return -1;
}

// Avertit l'utilisateur que le modèle a été converti.
cout << "\nFile has been converted." << endl;
cout << "\nDo you wish to exit (y/n)? ";
cin >> garbage;

return 0;
}

GetModelFilename

void GetModelFilename(char* filename)
{
bool done;
ifstream fin;

// Boucle jusqu'à avoir le nom d'un fichier.
done = false;
while(!done)
{
// Demande le nom du fichier à l'utilisateur.
cout << "Enter model filename: ";

// Lit le nom du fichier.
cin >> filename;

// Essaie d'ouvrir le fichier.
fin.open(filename);

if(fin.good())
{
// Si le fichier existe et si aucun problème ne survient,
// on quitte la fonction puisque l'on a obtenu le nom du fichier.
done = true;
}
else
{
// Si le fichier n'existe pas ou qu'un problème est survenu à son ouverture, on avertit l'utilisateur et on recommence.
fin.clear();
cout << endl;
cout << "File " << filename << " could not be opened." << endl << endl;
}
}

return;
}

ReadFileCounts

bool ReadFileCounts(char* filename, int& vertexCount, int& textureCount, int& normalCount, int& faceCount)
{
ifstream fin;
char input;

// Initialise les compteurs.
vertexCount = 0;
textureCount = 0;
normalCount = 0;
faceCount = 0;

// Ouvre le fichier.
fin.open(filename);

// Vérifie si le fichier a bien été ouvert.
if(fin.fail() == true)
{
return false;
}

// Effectue la lecture du fichier jusqu'à atteindre la fin.
fin.get(input);
while(!fin.eof())
{
// Si la ligne commence par 'v', compte soit les sommets,
// soit les coordonnées de texture, soit les normales.
if(input == 'v')
{
fin.get(input);
if(input == ' ') { vertexCount++; }
if(input == 't') { textureCount++; }
if(input == 'n') { normalCount++; }
}

// Si la ligne commence par 'f', incrémente le nombre de faces.
if(input == 'f')
{
fin.get(input);
if(input == ' ') { faceCount++; }
}

// Sinon, lit le reste de la ligne.
while(input != '\n')
{
fin.get(input);
}

// Commence à lire le début de la ligne suivante.
fin.get(input);
}

// Close the file.
fin.close();

return true;
}

LoadDataStructures

bool LoadDataStructures(char* filename, int vertexCount, int textureCount, int normalCount, int faceCount)
{
VertexType *vertices, *texcoords, *normals;
FaceType *faces;
ifstream fin;
int vertexIndex, texcoordIndex, normalIndex, faceIndex, vIndex, tIndex, nIndex;
char input, input2;
ofstream fout;

// Initialise la structure des quatre données.
vertices = new VertexType[vertexCount];
if(!vertices)
{
return false;
}

texcoords = new VertexType[textureCount];
if(!texcoords)
{
return false;
}

normals = new VertexType[normalCount];
if(!normals)
{
return false;
}

faces = new FaceType[faceCount];
if(!faces)
{
return false;
}

// Initialise les indices.
vertexIndex = 0;
texcoordIndex = 0;
normalIndex = 0;
faceIndex = 0;

// Ouvre le fichier.
fin.open(filename);

// Vérifie si le fichier a bien été ouvert.
if(fin.fail() == true)
{
return false;
}

// Charge les sommets, coordonnées de texture, normales et faces dans la structure.
// Important : effectue la conversion vers le système de coordonnées indirect puisque Maya utilise un système direct.
fin.get(input);
while(!fin.eof())
{
if(input == 'v')
{
fin.get(input);

// Lit les sommets.
if(input == ' ')
{
fin >> vertices[vertexIndex].x >> vertices[vertexIndex].y >> vertices[vertexIndex].z;

// Inverse la composante Z du sommet pour passer en système indirect.
vertices[vertexIndex].z = vertices[vertexIndex].z * -1.0f;
vertexIndex++;
}

// Lit les coordonnées UV de texture.
if(input == 't')
{
fin >> texcoords[texcoordIndex].x >> texcoords[texcoordIndex].y;

// Inverse la composante V de la coordonnées de texture pour passer en système indirect.
texcoords[texcoordIndex].y = 1.0f - texcoords[texcoordIndex].y;
texcoordIndex++;
}

// Lit les normales.
if(input == 'n')
{
fin >> normals[normalIndex].x >> normals[normalIndex].y >> normals[normalIndex].z;

// Inverse la composante Z pour passer en système indirect.
normals[normalIndex].z = normals[normalIndex].z * -1.0f;
normalIndex++;
}
}

// Lit les faces.
if(input == 'f')
{
fin.get(input);
if(input == ' ')
{
// Lit les données des faces à partir de la fin pour passer en système indirect.
fin >> faces[faceIndex].vIndex3 >> input2 >> faces[faceIndex].tIndex3 >> input2 >> faces[faceIndex].nIndex3
>> faces[faceIndex].vIndex2 >> input2 >> faces[faceIndex].tIndex2 >> input2 >> faces[faceIndex].nIndex2
>> faces[faceIndex].vIndex1 >> input2 >> faces[faceIndex].tIndex1 >> input2 >> faces[faceIndex].nIndex1;
faceIndex++;
}
}

// Lit le reste de la ligne.
while(input != '\n')
{
fin.get(input);
}

// Commence à lire la ligne suivante.
fin.get(input);
}

// Ferme le fichier.
fin.close();

// Ouvre le fichier de sortie.
fout.open("model.txt");

// Écrit l'en-tête de notre format de modèle.
fout << "Vertex Count: " << (faceCount * 3) << endl;
fout << endl;
fout << "Data:" << endl;
fout << endl;

// Parcourt toutes les faces et écrit leurs sommets.
for(int i=0; i<faceIndex; i++)
{
vIndex = faces[i].vIndex1 - 1;
tIndex = faces[i].tIndex1 - 1;
nIndex = faces[i].nIndex1 - 1;

fout << vertices[vIndex].x << ' ' << vertices[vIndex].y << ' ' << vertices[vIndex].z << ' '
<< texcoords[tIndex].x << ' ' << texcoords[tIndex].y << ' '
<< normals[nIndex].x << ' ' << normals[nIndex].y << ' ' << normals[nIndex].z << endl;

vIndex = faces[i].vIndex2 - 1;
tIndex = faces[i].tIndex2 - 1;
nIndex = faces[i].nIndex2 - 1;

fout << vertices[vIndex].x << ' ' << vertices[vIndex].y << ' ' << vertices[vIndex].z << ' '
<< texcoords[tIndex].x << ' ' << texcoords[tIndex].y << ' '
<< normals[nIndex].x << ' ' << normals[nIndex].y << ' ' << normals[nIndex].z << endl;

vIndex = faces[i].vIndex3 - 1;
tIndex = faces[i].tIndex3 - 1;
nIndex = faces[i].nIndex3 - 1;

fout << vertices[vIndex].x << ' ' << vertices[vIndex].y << ' ' << vertices[vIndex].z << ' '
<< texcoords[tIndex].x << ' ' << texcoords[tIndex].y << ' '
<< normals[nIndex].x << ' ' << normals[nIndex].y << ' ' << normals[nIndex].z << endl;
}

// Ferme le fichier de sortie.
fout.close();

// Libère la structure des quatre données.
if(vertices)
{
delete [] vertices;
vertices = 0;
}
if(texcoords)
{
delete [] texcoords;
texcoords = 0;
}
if(normals)
{
delete [] normals;
normals = 0;
}
if(faces)
{
delete [] faces;
faces = 0;
}

return true;
}

V - Résumé

Dorénavant, nous pouvons convertir les fichiers Maya 2011 d'extension .obj vers notre format simple de modèle.

VI - Exercices à faire

1. Recompilez le programme et exécutez-le sur le fichier .obj fourni.

2. Créez (ou demandez à quelqu'un) un modèle Maya 2011 et exportez-le au format .obj, puis exécutez ce programme pour le convertir.

3. Modifiez ce code pour lire et exporter le format de modèle différent que vous préférez.

VII - Code source

- Projet Visual Studio 2010 : dx11tut08.zip.

- Source seule : dx11src08.zip.

- Exécutable seul : dx11exe08.zip.

VIII - Remerciements

Cet article est une traduction autorisée de l'article paru sur RasterTek.

Merci à LittleWhite pour sa relecture lors de la traduction et jacques_jean pour sa relecture orthographique.

Navigation

Tutoriel précédent : rendu de modèles 3D

Sommaire