Navigation▲
Tutoriel précédent : rendu de modèles 3D |
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 :
- 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 ;
- 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 ;
- 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 ;
- 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 :
- Inverser la coordonnée Z des sommets. Vous verrez ceci vertices[vertexIndex].z =vertices[vertexIndex].z * -1.0f dans le code ;
- Inverser la coordonnée TV de texture. Vous verrez dans le code que je fais texcoords[texcoordIndex].y = 1.0f - texcoords[texcoordIndex].y ;
- Inverser la composante Z de la normale. Vous pourrez voir ce code normals[normalIndex].z = normals[normalIndex].z * -1.0f le faire ;
- 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 <<
"
\n
File has been converted."
<<
endl;
cout <<
"
\n
Do you wish to exit (y/n)? "
;
cin >>
garbage;
return
0
;
}
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
;
}
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
;
}
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.0
f;
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.0
f -
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.0
f;
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▲
- Recompilez le programme et exécutez-le sur le fichier .obj fourni.
- 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.
- 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 |