DirectX 10 教程8:加载Maya 2011模型

原文地址:Tutorial 8: Loading Maya 2011 Models(http://www.rastertek.com/dx10tut08.html)。

源代码:dx10tut08.zip

本教程介绍如何导入Maya 2011制作的3D模型。虽然本教程讨论的是Maya,但是要稍加修改也可以处理其他3D建模工具导出的3D模型。

在前面的教程中我们创建了自己的模型格式并进行了绘制。本教程我们要把Maya 2011模型格式变换为自己的格式并进行绘制。这里不讨论如何在Maya创建3D模型,网上有无数的教程,我们直接从一个已经附有纹理的3D模型开始。

Maya导出的格式很多,我们使用的是.OBJ,因为它易读,所以适用于初学者。

要在Maya中将模型导出为.obj格式,你必须首先打开.OBJ exporter。点击“Window”,选择“Settings/Preferences”,然后是“Plug-in Manager”。下拉到objExport.mll并选择“Loaded”和“Auto load”。导出时只需点击“File”,选择“Export All”。在底部选择“Files of type:”找到“OBJexport”。指定一个文件名词并点击“Export All”,Maya就会把模型导出为一个.obj后缀的文本文件,用任意一个文本编辑器(例如记事本)打开这个文件,内容如下:

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

这个.OBJ模型文件表示一个3D立方体。它有6个顶点,24个纹理坐标和法线矢量,6个面共12个三角形。注意每行开头的“V”,“VT”,“VN”"或“F”。这些额外的信息在转换为我们自定义的格式时是无用的。下面我们讨论一下字母的含义:

1.“V”开头的行用于顶点。立方体有8个顶点,每个顶点由三个代表X,Y,Z的浮点数构成。

2.“VT”开头的行用于纹理坐标。立方体有24个纹理坐标,大部分坐标是重复的,由两个TU,TV浮点数构成。

3.“VN”开头的行用于法线矢量。立方体有24个法线,大部分是重复的,由三个NX,NY,NZ浮点数构成。

4.“F”开头的行用于每个三角形。由顶点索引,纹理坐标索引和法线索引构成,每个面的格式为:

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

因此“f 3/13/5 4/14/6 5/15/7”表示“Vertex3/Texture13/Normal5 Vertex4/Texture14/Normal6 Vertex5/Texture15/Normal7”。

数据的顺序非常重要。例如文件中的第一个顶点对应face列表中的Vertex1,纹理坐标和法线的顺序也是如此。在行face中每行的三个索引组代表一个三角形,一个立方体有12face组成6个面,每个面有2个三角形。

右手坐标系到左手坐标系的转换

Maya 2011默认使用右手坐标系。要将数据转换为DirectX 10使用的左手坐标系,你需要进行以下步骤:

1.反转顶点的Z坐标,代码为:vertices[vertexIndex].z = vertices[vertexIndex].z * -1.0f;

2.反转TV纹理坐标,代码为:texcoords[texcoordIndex].y = 1.0f - texcoords[texcoordIndex].y;

3.反转NZ法线,代码为:normals[normalIndex].z = normals[normalIndex].z * -1.0f;

4.将绘制顺序由逆时针方向转换为顺时针方向。在代码中只是简单地逆序读取索引组即可实现:

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; 

通过以上四个步骤,模型数据就能把DirectX 10正确绘制了。

Main.cpp

将Maya 2011的.obj文件转换为自定义的DirectX 10格式非常简单,只有一个程序文件叫做main.cpp。这个程序打开一个命令行窗口询问要转换的.obj文件名称。用户输入了名称之后,程序就会打开文件并读取到数据结构,然后将它们转换到左手坐标系,完成后就将这些数据写入到model.txt文件。这个文件就可以使用上一个教程的代码进行绘制了。

////////////////////////////////////////////////////////////////////////////////
// Filename: main.cpp
////////////////////////////////////////////////////////////////////////////////


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


//////////////
// TYPEDEFS //
//////////////
typedef struct
{
	float x, y, z;
}VertexType;

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


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


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


	// Read in the name of the model file.
	GetModelFilename(filename);

	// Read in the number of vertices, tex coords, normals, and faces so that the data structures can be initialized with the exact sizes needed.
	result = ReadFileCounts(filename, vertexCount, textureCount, normalCount, faceCount);
	if(!result)
	{
		return -1;
	}

	// Display the counts to the screen for information purposes.
	cout << endl;
	cout << "Vertices: " << vertexCount << endl;
	cout << "UVs:      " << textureCount << endl;
	cout << "Normals:  " << normalCount << endl;
	cout << "Faces:    " << faceCount << endl;

	// Now read the data from the file into the data structures and then output it in our model format.
	result = LoadDataStructures(filename, vertexCount, textureCount, normalCount, faceCount);
	if(!result)
	{
		return -1;
	}

	// Notify the user the model has been converted.
	cout << "\nFile has been converted." << endl;
	cout << "\nDo you wish to exit (y/n)? ";
	cin >> garbage;

	return 0;
}


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


	// Loop until we have a file name.
	done = false;
	while(!done)
	{
		// Ask the user for the filename.
		cout << "Enter model filename: ";

		// Read in the filename.
		cin >> filename;

		// Attempt to open the file.
		fin.open(filename);

		if(fin.good())
		{
			// If the file exists and there are no problems then exit since we have the file name.
			done = true;
		}
		else
		{
			// If the file does not exist or there was an issue opening it then notify the user and repeat the process.
			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;


	// Initialize the counts.
	vertexCount = 0;
	textureCount = 0;
	normalCount = 0;
	faceCount = 0;

	// Open the file.
	fin.open(filename);

	// Check if it was successful in opening the file.
	if(fin.fail() == true)
	{
		return false;
	}

	// Read from the file and continue to read until the end of the file is reached.
	fin.get(input);
	while(!fin.eof())
	{
		// If the line starts with 'v' then count either the vertex, the texture coordinates, or the normal vector.
		if(input == 'v')
		{
			fin.get(input);
			if(input == ' ') { vertexCount++; }
			if(input == 't') { textureCount++; }
			if(input == 'n') { normalCount++; }
		}

		// If the line starts with 'f' then increment the face count.
		if(input == 'f')
		{
			fin.get(input);
			if(input == ' ') { faceCount++; }
		}
		
		// Otherwise read in the remainder of the line.
		while(input != '\n')
		{
			fin.get(input);
		}

		// Start reading the beginning of the next line.
		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;


	// Initialize the four data structures.
	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;
	}

	// Initialize the indexes.
	vertexIndex = 0;
	texcoordIndex = 0;
	normalIndex = 0;
	faceIndex = 0;

	// Open the file.
	fin.open(filename);

	// Check if it was successful in opening the file.
	if(fin.fail() == true)
	{
		return false;
	}

	// Read in the vertices, texture coordinates, and normals into the data structures.
	// Important: Also convert to left hand coordinate system since Maya uses right hand coordinate system.
	fin.get(input);
	while(!fin.eof())
	{
		if(input == 'v')
		{
			fin.get(input);

			// Read in the vertices.
			if(input == ' ') 
			{ 
				fin >> vertices[vertexIndex].x >> vertices[vertexIndex].y >> vertices[vertexIndex].z;

				// Invert the Z vertex to change to left hand system.
				vertices[vertexIndex].z = vertices[vertexIndex].z * -1.0f;
				vertexIndex++; 
			}

			// Read in the texture uv coordinates.
			if(input == 't') 
			{ 
				fin >> texcoords[texcoordIndex].x >> texcoords[texcoordIndex].y;

				// Invert the V texture coordinates to left hand system.
				texcoords[texcoordIndex].y = 1.0f - texcoords[texcoordIndex].y;
				texcoordIndex++; 
			}

			// Read in the normals.
			if(input == 'n') 
			{ 
				fin >> normals[normalIndex].x >> normals[normalIndex].y >> normals[normalIndex].z;

				// Invert the Z normal to change to left hand system.
				normals[normalIndex].z = normals[normalIndex].z * -1.0f;
				normalIndex++; 
			}
		}

		// Read in the faces.
		if(input == 'f') 
		{
			fin.get(input);
			if(input == ' ')
			{
				// Read the face data in backwards to convert it to a left hand system from right hand system.
				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++;
			}
		}

		// Read in the remainder of the line.
		while(input != '\n')
		{
			fin.get(input);
		}

		// Start reading the beginning of the next line.
		fin.get(input);
	}

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

	// Open the output file.
	fout.open("model.txt");
	
	// Write out the file header that our model format uses.
	fout << "Vertex Count: " << (faceCount * 3) << endl;
	fout << endl;
	fout << "Data:" << endl;
	fout << endl;

	// Now loop through all the faces and output the three vertices for each face.
	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;
	}

	// Close the output file.
	fout.close();

	// Release the four data structures.
	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;
}

总结

现在我们就可以将Maya 2011导出的.obj文件转换为自己的简单模型格式了。

Maya中的模型

练习

1.编译程序处理提供的.obj模型文件。

2.创建(或获得)一个Maya 2011模型,将它导出为.obj格式,运行程序进行转换。

3.修改代码读取并导出一个不同的模型格式。

文件下载(已下载 1402 次)

发布时间:2012/7/17 下午12:06:35  阅读次数:9932

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号