本章节将学习如何从.obj文件加载静态3D模型。通常在游戏内不会想要使用.obj文件,因为它们不包含动画,且它们是ascii格式的,所以它会比其他格式的文件比如.3ds的要大一些,但是这是一个用于学习如何加载模型的好起点。
本章基于聚光灯构建。
和.obj文件一起的是.mtl文件,它是一个材质库。还会学习使用.mtl文件来为模型加载材质。.obj文件很大并且没有包含动画,所以通常会想要给游戏使用一个不同的模型格式。然而,.obj格式是一个学习如何加载模型的很好的文件。
.obj文件格式
.obj格式文件已经存在很长一段时间。它是三大最老的模型格式之一,现在仍然广泛使用。
.obj文件不包含类似其他文件格式的头信息。头信息位于文件的开头,会给人一点关于文件的信息。比如,在一个3D模型文件内的头可能告诉你在文件内有多少顶点,纹理坐标,法线,材质,子集以及面。由于.obj文件不包含这个信息,我们能够使用后面的两种方式中的一种。第一件事就是可以读该文件两次。第一次读会收集信息,这些信息包括多少个顶点,面,法线等。还有,所以我们能够为每条信息初始化矩阵。第二次读取会获取实际模型信息,比如顶点位置,并将它们存储在一个初始化的数组中,这是一种很低效的方式来获得向量。第二种方式就是我们能够加载一个obj文件并使用向量来实现。一个向量是一个动态数组,不是使用一个设置大小来初始化它,而是“push_back”元素以增加它的大小。另外一个关于向量的事情就是清理它们,所以我们不必像标准数组一样来删除它们。使用该方法,我们只需要读一次文件即可,并将模型信息存到向量中。
obj文件中的每个信息片段都是放在它自己的那行的,在那儿有一个以上的字符串(比如."v")用于解释那行剩余信息的作用,且以“\n”作为每行的结束字符使用。
下面是一个.obj文件的例子。
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 26.07.2011 13:47:43
mtllib spaceCompound.mtl
#
# object Window
#
v -8.6007 1.3993 10.0000
v -8.6007 8.6007 10.0000
v 8.6007 8.6007 10.0000
v 8.6007 1.3993 10.0000
# 4 vertices
vn 0.0000 0.0000 -1.0000
# 1 vertex normals
vt 0.0000 0.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 1.0000 0.0000 0.0000
# 4 texture coords
g Window
usemtl Window
f 1/1/1 2/2/1 3/3/1
f 3/3/1 4/4/1 1/1/1
# 2 faces
# - 注释
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
mtllib - 材质库文件
mtllib spaceCompound.mtl
mtllib为一行的起始时,用于包含一个材质库文件名。一个材质库是一个伴随.obj文件的独立文件,它为每个组定义了材质。.obj文件常常会包含一个库文件,有时它还会包含多个库文件。
v - 顶点位置
v -8.6007 1.3993 10.0000
vn - 法线
vn 0.0000 0.0000 -1.0000
vt - 纹理坐标
vt 0.0000 1.0000 0.0000
o-Objects对象
o spaceCompound
g-Groups(子集)
g Window
usemtl - 组材质
usemtl Window
s-光滑组
s 1
...
s 2
...
s off
f-面
一个面包含有位置,纹理坐标和法线。
f 1/1/1 2/2/1 3/3/1
f 1//1 2//1 3//1
f 1/1 2/1 3/1
f 1 2 3
文件的开头以1开始表示一个索引值。强调起始1是因为C++数组是以0起始的,所以你在存储它们之前必须在这些数字上面减去1。比如行f 1 2 3表示该面的每个顶点只包含一个位置信息。所以在文件的开始处,第一个顶点位置v -8.6007 1.3993 10.000会被读取到。面1 2 3中的1表示顶点位置。然而,当我们存储该顶点位置时在C++中,我们将它存储在向量的[0]元素中。所以当我们存储这个面时,我们实际上需要以0 1 2方式存储它而不是1 2 3。且当有多个组和对象时,这些数字仍然是基于集体指数值的,这些指数值是每个定义起始于文件开头的。
另外一件关于面的事情就是,它们并不总是以三角形的方式存储在obj文件中的。有时它们是以包含更多的边的矩形或其他图形存储的。
f 1 2 3 4 5
tri1 = "1 2 3"
tri2 = "1 3 4"
tri3 = "1 4 5"
可通过计算两条边之间的夹角以找到哪里凹陷来解决这个问题,且面的重叠三角形是依赖于凹度的。
.mtl文件格式
mtl文件是对象文件的材质库。该文件会包含每个组的面属性。不是所有的obj模型都会包含一个材质库文件的,但是我们会假设有包含。向上面所说的,本节的模型使用两个材质,一个是窗户,一个是墙。
本章中加载模型不是唯一的新事情,我们已经创建了一个SurfaceMaterial结构体,它会保存每个子集的各种面属性,比如漫反射颜色,透明度,以及纹理。我们很快会讨论该结构体,但是现在让我们看一下mtl文件的内容。
下面是mtl文件:
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 26.07.2011 13:47:43
newmtl Window
Ns 32.0000
Ni 1.5000
d 0.8000
Tr 0.2000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0100
Kd 0.0000 0.0000 0.0100
Ks 0.3500 0.3500 0.3500
Ke 0.0000 0.0000 0.0000
newmtl MetalPanels
Ns 10.0000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka metalpanel.jpg
map_Kd metalpanel.jpg
map_bump metalpanelnormals.jpg
# -注释
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
Ns - 镜面光焦度
Ns 10.0000
Ni - 光密度
Ni 1.5000
d - 透明度
d 0.8000
Tr - 透明度
Tr也表示表面透明度。一些obj模型定义材质透明度时使用d或者使用Tr,或者两个都用。范围是从0到1, 但是这里 1是彻底透明的,或者说是不可见的。
Tf - 传输过滤
Tf 1.0000 1.0000 1.0000
illum - 光照模型
illum 2
Ka - 环境光
Ka 0.0000 0.0000 0.0100
Kd-漫反射颜色
Kd 0.0000 0.0000 0.0100
Ks - 镜面颜色
Ks 0.3500 0.3500 0.3500
Ke-自发光颜色
Ke 0.0000 0.0000 0.0000
map_Ka - 环境光贴图
map_Ka metalpanel.jpg
map-Kd - 漫反射颜色贴图
map_Kd metalpanel.jpg
map_Ks - 镜面颜色贴图
map_Ks metalpanel.jpg
map_bump或bump - 凹凸贴图
map_bump metalpanel.jpg
bump metalpanel.jpg
右手或左手坐标系系统
DirectX使用左手坐标系。它的意思是z轴的正方向是面向远离相机的方向,然而右手坐标系的正z轴是面向靠近相机的方向的。正y是朝向的,正x是朝右的。许多模型软件比如maya和3ds max使用的是右手坐标系。因为directx使用左手坐标系,我们需要将这些模型转换到左手坐标系。为了从右手转到左手坐标系,我们需要做许多事情。首先要将顶点位置的z轴乘以-1.0f颠倒过来。我们好需要将纹理坐标的v轴减去1.0f让它颠倒过来。最终我们需要将顶点法线的z轴乘以-1.0f转换一下。我们已经在加载模型函数的参数中定义了一个布尔变量,当它设为true时,我们会做该转换,到设为false时,不会做转换。
透明子集
在本章节中当加载模型时,我们已经将透明度考虑进来了。记得在混合章节,通过blending使得混合技术让已经在渲染目标上的带有透明度的物体生效。因此,我们必须确保透明子集是最后绘制的。
我们已经在混合章节说了关于多个透明物体,但是这里还会快速的过一下该方案。我们不会实现该技术,但是一个更为先进的绘制透明物体的顺序的技术将会用于找到物体离相机的距离,并将它们从远到近的绘制。
删除网格接口(比如,ID3DX10Mesh)
Direct3D 11已经删除了网格接口。因此,我们必须处理手动所有的3D模型,比如顶点和索引缓冲,子集数量,网格交叉(它对用鼠标挑选3D对象来说非常有用)。无论怎么说,它不是一个大问题,但是我们不是从文件直接将3D模型加载到网格对象,而是将顶点和索引列表存储进缓冲区中,并跟踪子集和材质。
在前面的章节可知,它们从来不使用类,是为了看起来跟直观。但是由于类使得代码更有效,且当开发一个游戏时,大家都想使用类,在本章中,会使用网格类包含了用于从文件加载模型的方法,以及绘制模型子集的方法,和存储该模型信息来替换删除网格接口的成员。
新包含文件
第一行使能向量的使用,是动态数组。另外两个用于读取文件和使用stringstreams。
#include
#include
#include
全局用于一个特定的模型,且为所有对象的纹理所使用。我们创建一个新的透明度状态,所以我们能够将我们的透明度对象和它们的α值混合。随后,我们有一个顶点索引缓冲。每个加载的模型都需要它们自己的缓冲。随后,我们有模型世界空间矩阵。随后我们有一个变量以跟踪该模型的子集数量。我们做这些所以我们能够将每个子集应用到它们子集的纹理上。下面就是利用向量了。第一个向量用于让我们知道索引缓冲中的子集在那儿开始。我们会以它的meshSubsetIndexStart的索引值为起始来绘制子集,并在下一个子集的meshSubsetIndexStart结束。随后,我们有一个向量,它包含了子集纹理的索引值。该子集纹理索引会用于查看来自下一个全局的nesgSRV的材质,它表示网格着色器资源视图。最后的全局用于确保我们没有两次加载同一个材质。
ID3D11BlendState* Transparency;
ID3D11Buffer* meshVertBuff;
ID3D11Buffer* meshIndexBuff;
XMMATRIX meshWorld;
int meshSubsets = 0;
std::vector meshSubsetIndexStart;
std::vector meshSubsetTexture;
std::vector meshSRV;
std::vector textureNameArray;
我们已经更新我们的常量缓冲结构体。这两个我们创建的成员用于在像素着色器,然而另外两个矩阵用于顶点着色器。将可能想要为每个着色器创建一个独立的常量缓冲区。
struct cbPerObject
{
XMMATRIX WVP;
XMMATRIX World;
///////////////**************new**************////////////////////
//These will be used for the pixel shader
XMFLOAT4 difColor;
bool hasTexture;
///////////////**************new**************////////////////////
};
材质结构体
(SurfaceMaterial())
我们已经创建了一个叫做SurfaceMaterial的结构体。该结构体会保存自己的表面的属性。我们将从.mtl文件获取我们的表面材质,.mtl文件是和.obj文件在一起的。
第一个成员是款字符串,叫做matName,它包含了材质的名字。名字来自newmtl行。在.mtl文件中,每次当到达一个新的newmtl时,我们就会创建一个材质,并将它推入我们叫做简单材质的表面材质向量中(值把它叫做material,是因为游戏中的每个材质都可能存储在这里)。
下一行是一个XMFLOAT4变量,叫做difColor,并且包含表面的RGBA(red,green,blue,alpha)数据。RGB数据来自于行Kd或Ka,它会是表面的纯色。alpha值来自于行Tr或d,它会告诉表面的透明度和不透明度。
随后,我们有一个整型,texArrayIndex,包含存储在我们的meshSRV向量中的纹理的索引值,该材质表面会使用到。
随后是hasTexture,一个布尔变量用于表示该材质是否会使用一个纹理。若材质使用纹理,则设为ture,若设为false,则表示材质不使用纹理。若材质使用纹理,则纹理的颜色和alpha值会覆盖掉包含在difColor变量中的纯漫反射颜色和alpha值。我们会在像素着色器中看到这个覆盖重写的完成。若在.mtl文件中包含了一行带有map_Kd或map_Ka的行的材质,我们将会知道材质是否使用了一个纹理。
最后一个成员就是transparent。这是另外一个布尔变量,它表示纹理是否透明。我们会用该变量决定我们的透明和非透明子集的绘制顺序。若一个子集的材质有一个transparent = false的值,那么该子集将会在子集的该模型的第一组中被绘制(每个模型被分为两组,透明组和非透明组,透明组子集绘制于非透明组之后)。若子集的材质transparent为true,则子集会在子集的最后一组中被绘制。这就确保了非透明对象首先被绘制到渲染目标中,以便透明物体和非透明物体混合。
当然,该结构体可能会更为复杂,但是现在已经够用了。我们将会在之后添加成员。
在创建完材质结构体后,我们声明一个SurfaceMaterials的向量,叫做material。
struct SurfaceMaterial
{
std::wstring matName;
XMFLOAT4 difColor;
int texArrayIndex;
bool hasTexture;
bool transparent;
};
std::vector material;
模型加载函数原型
LoadObjModel()
这是LoadObjModel函数原型。该函数的参数如下:
filename
一个宽字符串,包含要加载的obj模型的文件名(带或者不带文件路径)
vertBuff
一个ID3D11Buffer对象的指针,该对象用来存储模型的顶点。每个加载的模型需要它们自己的vertBuff对象。
indexBuff
一个ID3D11Buffer对象的指针,该对象用来存储模型的索引列表。每个加载的模型需要它们自己的indexBuffer对象。
subsetIndexStart
这是一个整数向量会存储offset,或者为每个子集启动索引列表的索引值。例如,第一个子集起始于索引值0。当我们绘制第一个子集时,我们会开始从索引缓冲中的索引值0开始绘制,直到下一个子集的索引值为止。在所有的子集从文件中加载完毕后,我们会推回最后一个subsetIndexStart,它是索引缓冲区中的索引的总数量。每个加载的模型需要它们子集的subsetIndexStart向量。
subsetMaterialArray
这是一个整型向量用于存储每个子集要用到的索引值。这个索引值会用于从下一个参数的材质向量获取材质,material。每个加载的模型需要它们子集的subsetMaterialArray向量。
material
这是一个SurfaceMaterial结构体向量。该向量能够用于场景中所有的加载的模型,同时该函数会检查在推回一个新的材质之前以确保不会已经存在材质。我们会使用该向量以查找每个子集所使用的材质。
subsetCount
模型中的子集数量。我们会使用这个参数以轮询每个子集,并当我们到达subsetCount时停止轮询。每个加载的模型需要它们子集的subsetCount变量。
isRHCoordSys
该值为true或false。若模型基于3ds max和maya在右手坐标系中被创建,我们会设置该值为true。以便函数会做合适的转换。若该模型在一个左手坐标系中被创建则指定false。若不清楚,可以两个值都试一下,看哪一个更为准确。
computeNormals
这是一个true或false值。若该函数应该创建顶点法线,则指定为ture。若该函数应该使用拥有顶点法线的模型则指定为false。并不是所有的模型都会定义它们的法线,所以该函数默认会给每个顶点法线值(0,0,0),它是完全无用的,并且这就是为什么没有定义它自己法线的模型应该被指定为true。
//Define LoadObjModel function after we create surfaceMaterial structure
bool LoadObjModel(std::wstring filename, //.obj filename
ID3D11Buffer** vertBuff, //mesh vertex buffer
ID3D11Buffer** indexBuff, //mesh index buffer
std::vector& subsetIndexStart, //start index of each subset
std::vector& subsetMaterialArray, //index value of material for each subset
std::vector& material, //vector of material structures
int& subsetCount, //Number of subsets in mesh
bool isRHCoordSys, //true if model was created in right hand coord system
bool computeNormals); //true to compute the normals, false to use the files normals
销毁
void CleanUp()
{
...
///////////////**************new**************////////////////////
meshVertBuff->Release();
meshIndexBuff->Release();
///////////////**************new**************////////////////////
}
该函数不支持光滑界面阴影,所以有很大的优化空间。
bool LoadObjModel(std::wstring filename,
ID3D11Buffer** vertBuff,
ID3D11Buffer** indexBuff,
std::vector& subsetIndexStart,
std::vector& subsetMaterialArray,
std::vector& material,
int& subsetCount,
bool isRHCoordSys,
bool computeNormals)
{
HRESULT hr = 0;
std::wifstream fileIn (filename.c_str()); //Open file
std::wstring meshMatLib; //String to hold our obj material library filename
//Arrays to store our model's information
std::vector indices;
std::vector vertPos;
std::vector vertNorm;
std::vector vertTexCoord;
std::vector meshMaterials;
//Vertex definition indices
std::vector vertPosIndex;
std::vector vertNormIndex;
std::vector vertTCIndex;
//Make sure we have a default if no tex coords or normals are defined
bool hasTexCoord = false;
bool hasNorm = false;
//Temp variables to store into vectors
std::wstring meshMaterialsTemp;
int vertPosIndexTemp;
int vertNormIndexTemp;
int vertTCIndexTemp;
wchar_t checkChar; //The variable we will use to store one char from file at a time
std::wstring face; //Holds the string containing our face vertices
int vIndex = 0; //Keep track of our vertex index count
int triangleCount = 0; //Total Triangles
int totalVerts = 0;
int meshTriangles = 0;
//Check to see if the file was opened
if (fileIn)
{
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
case 'v': //Get Vertex Descriptions
checkChar = fileIn.get();
if(checkChar == ' ') //v - vert position
{
float vz, vy, vx;
fileIn >> vx >> vy >> vz; //Store the next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertPos.push_back(XMFLOAT3( vx, vy, vz * -1.0f)); //Invert the Z axis
else
vertPos.push_back(XMFLOAT3( vx, vy, vz));
}
if(checkChar == 't') //vt - vert tex coords
{
float vtcu, vtcv;
fileIn >> vtcu >> vtcv; //Store next two types
if(isRHCoordSys) //If model is from an RH Coord System
vertTexCoord.push_back(XMFLOAT2(vtcu, 1.0f-vtcv)); //Reverse the "v" axis
else
vertTexCoord.push_back(XMFLOAT2(vtcu, vtcv));
hasTexCoord = true; //We know the model uses texture coords
}
//Since we compute the normals later, we don't need to check for normals
//In the file, but i'll do it here anyway
if(checkChar == 'n') //vn - vert normal
{
float vnx, vny, vnz;
fileIn >> vnx >> vny >> vnz; //Store next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz * -1.0f )); //Invert the Z axis
else
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz ));
hasNorm = true; //We know the model defines normals
}
break;
//New group (Subset)
case 'g': //g - defines a group
checkChar = fileIn.get();
if(checkChar == ' ')
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
break;
//Get Face Index
case 'f': //f - defines the faces
checkChar = fileIn.get();
if(checkChar == ' ')
{
face = L"";
std::wstring VertDef; //Holds one vertex definition at a time
triangleCount = 0;
checkChar = fileIn.get();
while(checkChar != '\n')
{
face += checkChar; //Add the char to our face string
checkChar = fileIn.get(); //Get the next Character
if(checkChar == ' ') //If its a space...
triangleCount++; //Increase our triangle count
}
//Check for space at the end of our face string
if(face[face.length()-1] == ' ')
triangleCount--; //Each space adds to our triangle count
triangleCount -= 1; //Ever vertex in the face AFTER the first two are new faces
std::wstringstream ss(face);
if(face.length() > 0)
{
int firstVIndex, lastVIndex; //Holds the first and last vertice's index
for(int i = 0; i < 3; ++i) //First three vertices (first triangle)
{
ss >> VertDef; //Get vertex definition (vPos/vTexCoord/vNorm)
std::wstring vertPart;
int whichPart = 0; //(vPos, vTexCoord, or vNorm)
//Parse this string
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/') //If there is no divider "/", add a char to our vertPart
vertPart += VertDef[j];
//If the current char is a divider "/", or its the last character in the string
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart); //Used to convert wstring to int
if(whichPart == 0) //If vPos
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertNormIndexTemp = 0;
vertTCIndexTemp = 0;
}
}
else if(whichPart == 1) //If vTexCoord
{
if(vertPart != L"") //Check to see if there even is a tex coord
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
else //If there is no tex coord, make a default
vertTCIndexTemp = 0;
//If the cur. char is the second to last in the string, then
//there must be no normal, so set a default normal
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2) //If vNorm
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
vertPart = L""; //Get ready for next vertex part
whichPart++; //Move on to next vertex part
}
}
//Check to make sure there is at least one subset
if(subsetCount == 0)
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
//Avoid duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
//Loop through all the vertices
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
//If the vertex position and texture coordinate in memory are the same
//As the vertex position and texture coordinate we just now got out
//of the obj file, we will set this faces vertex index to the vertex's
//index value in memory. This makes sure we don't create duplicate vertices
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
//If this vertex is not already in our vertex arrays, put it there
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //We created a new vertex
indices.push_back(totalVerts-1); //Set index for this vertex
}
//If this is the very first vertex in the face, we need to
//make sure the rest of the triangles use this vertex
if(i == 0)
{
firstVIndex = indices[vIndex]; //The first vertex index of this FACE
}
//If this was the last vertex in the first triangle, we will make sure
//the next triangle uses this one (eg. tri1(1,2,3) tri2(1,3,4) tri3(1,4,5))
if(i == 2)
{
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
}
vIndex++; //Increment index count
}
meshTriangles++; //One triangle down
//If there are more than three vertices in the face definition, we need to make sure
//we convert the face to triangles. We created our first triangle above, now we will
//create a new triangle for every new vertex in the face, using the very first vertex
//of the face, and the last vertex from the triangle before the current triangle
for(int l = 0; l < triangleCount-1; ++l) //Loop through the next vertices to create new triangles
{
//First vertex of this triangle (the very first vertex of the face too)
indices.push_back(firstVIndex); //Set index for this vertex
vIndex++;
//Second Vertex of this triangle (the last vertex used in the tri before this one)
indices.push_back(lastVIndex); //Set index for this vertex
vIndex++;
//Get the third vertex for this triangle
ss >> VertDef;
std::wstring vertPart;
int whichPart = 0;
//Parse this string (same as above)
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/')
vertPart += VertDef[j];
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart);
if(whichPart == 0)
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1;
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertTCIndexTemp = 0;
vertNormIndexTemp = 0;
}
}
else if(whichPart == 1)
{
if(vertPart != L"")
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1;
}
else
vertTCIndexTemp = 0;
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2)
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1;
}
vertPart = L"";
whichPart++;
}
}
//Check for duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //New vertex created, add to total verts
indices.push_back(totalVerts-1); //Set index for this vertex
}
//Set the second vertex for the next triangle to the last vertex we got
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
meshTriangles++; //New triangle defined
vIndex++;
}
}
}
break;
case 'm': //mtllib - material library filename
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'i')
{
checkChar = fileIn.get();
if(checkChar == 'b')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//Store the material libraries file name
fileIn >> meshMatLib;
}
}
}
}
}
}
break;
case 'u': //usemtl - which material to use
checkChar = fileIn.get();
if(checkChar == 's')
{
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
meshMaterialsTemp = L""; //Make sure this is cleared
fileIn >> meshMaterialsTemp; //Get next type (string)
meshMaterials.push_back(meshMaterialsTemp);
}
}
}
}
}
}
break;
default:
break;
}
}
}
else //If we could not open the file
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
//create message
std::wstring message = L"Could not open: ";
message += filename;
MessageBox(0, message.c_str(), //display message
L"Error", MB_OK);
return false;
}
subsetIndexStart.push_back(vIndex); //There won't be another index start after our last subset, so set it here
//sometimes "g" is defined at the very top of the file, then again before the first group of faces.
//This makes sure the first subset does not conatain "0" indices.
if(subsetIndexStart[1] == 0)
{
subsetIndexStart.erase(subsetIndexStart.begin()+1);
meshSubsets--;
}
//Make sure we have a default for the tex coord and normal
//if one or both are not specified
if(!hasNorm)
vertNorm.push_back(XMFLOAT3(0.0f, 0.0f, 0.0f));
if(!hasTexCoord)
vertTexCoord.push_back(XMFLOAT2(0.0f, 0.0f));
//Close the obj file, and open the mtl file
fileIn.close();
fileIn.open(meshMatLib.c_str());
std::wstring lastStringRead;
int matCount = material.size(); //total materials
//kdset - If our diffuse color was not set, we can use the ambient color (which is usually the same)
//If the diffuse color WAS set, then we don't need to set our diffuse color to ambient
bool kdset = false;
if (fileIn)
{
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
//Check for comment
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
//Set diffuse color
case 'K':
checkChar = fileIn.get();
if(checkChar == 'd') //Diffuse Color
{
checkChar = fileIn.get(); //remove space
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
kdset = true;
}
//Ambient Color (We'll store it in diffuse if there isn't a diffuse already)
if(checkChar == 'a')
{
checkChar = fileIn.get(); //remove space
if(!kdset)
{
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
}
}
break;
//Check for transparency
case 'T':
checkChar = fileIn.get();
if(checkChar == 'r')
{
checkChar = fileIn.get(); //remove space
float Transparency;
fileIn >> Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Some obj files specify d for transparency
case 'd':
checkChar = fileIn.get();
if(checkChar == ' ')
{
float Transparency;
fileIn >> Transparency;
//'d' - 0 being most transparent, and 1 being opaque, opposite of Tr
Transparency = 1.0f - Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Get the diffuse map (texture)
case 'm':
checkChar = fileIn.get();
if(checkChar == 'a')
{
checkChar = fileIn.get();
if(checkChar == 'p')
{
checkChar = fileIn.get();
if(checkChar == '_')
{
//map_Kd - Diffuse map
checkChar = fileIn.get();
if(checkChar == 'K')
{
checkChar = fileIn.get();
if(checkChar == 'd')
{
std::wstring fileNamePath;
fileIn.get(); //Remove whitespace between map_Kd and file
//Get the file path - We read the pathname char by char since
//pathnames can sometimes contain spaces, so we will read until
//we find the file extension
bool texFilePathEnd = false;
while(!texFilePathEnd)
{
checkChar = fileIn.get();
fileNamePath += checkChar;
if(checkChar == '.')
{
for(int i = 0; i < 3; ++i)
fileNamePath += fileIn.get();
texFilePathEnd = true;
}
}
//check if this texture has already been loaded
bool alreadyLoaded = false;
for(int i = 0; i < textureNameArray.size(); ++i)
{
if(fileNamePath == textureNameArray[i])
{
alreadyLoaded = true;
material[matCount-1].texArrayIndex = i;
material[matCount-1].hasTexture = true;
}
}
//if the texture is not already loaded, load it now
if(!alreadyLoaded)
{
ID3D11ShaderResourceView* tempMeshSRV;
hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(),
NULL, NULL, &tempMeshSRV, NULL );
if(SUCCEEDED(hr))
{
textureNameArray.push_back(fileNamePath.c_str());
material[matCount-1].texArrayIndex = meshSRV.size();
meshSRV.push_back(tempMeshSRV);
material[matCount-1].hasTexture = true;
}
}
}
}
//map_d - alpha map
else if(checkChar == 'd')
{
//Alpha maps are usually the same as the diffuse map
//So we will assume that for now by only enabling
//transparency for this material, as we will already
//be using the alpha channel in the diffuse map
material[matCount-1].transparent = true;
}
}
}
}
break;
case 'n': //newmtl - Declare new material
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'w')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//New material, set its defaults
SurfaceMaterial tempMat;
material.push_back(tempMat);
fileIn >> material[matCount].matName;
material[matCount].transparent = false;
material[matCount].hasTexture = false;
material[matCount].texArrayIndex = 0;
matCount++;
kdset = false;
}
}
}
}
}
}
break;
default:
break;
}
}
}
else
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
std::wstring message = L"Could not open: ";
message += meshMatLib;
MessageBox(0, message.c_str(),
L"Error", MB_OK);
return false;
}
//Set the subsets material to the index value
//of the its material in our material array
for(int i = 0; i < meshSubsets; ++i)
{
bool hasMat = false;
for(int j = 0; j < material.size(); ++j)
{
if(meshMaterials[i] == material[j].matName)
{
subsetMaterialArray.push_back(j);
hasMat = true;
}
}
if(!hasMat)
subsetMaterialArray.push_back(0); //Use first material in array
}
std::vector vertices;
Vertex tempVert;
//Create our vertices using the information we got
//from the file and store them in a vector
for(int j = 0 ; j < totalVerts; ++j)
{
tempVert.pos = vertPos[vertPosIndex[j]];
tempVert.normal = vertNorm[vertNormIndex[j]];
tempVert.texCoord = vertTexCoord[vertTCIndex[j]];
vertices.push_back(tempVert);
}
//////////////////////Compute Normals///////////////////////////
//If computeNormals was set to true then we will create our own
//normals, if it was set to false we will use the obj files normals
if(computeNormals)
{
std::vector tempNormal;
//normalized and unnormalized normals
XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f);
//Used to get vectors (sides) from the position of the verts
float vecX, vecY, vecZ;
//Two edges of our triangle
XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
//Compute face normals
for(int i = 0; i < meshTriangles; ++i)
{
//Get the vector describing one edge of our triangle (edge 0,2)
vecX = vertices[indices[(i*3)]].pos.x - vertices[indices[(i*3)+2]].pos.x;
vecY = vertices[indices[(i*3)]].pos.y - vertices[indices[(i*3)+2]].pos.y;
vecZ = vertices[indices[(i*3)]].pos.z - vertices[indices[(i*3)+2]].pos.z;
edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge
//Get the vector describing another edge of our triangle (edge 2,1)
vecX = vertices[indices[(i*3)+2]].pos.x - vertices[indices[(i*3)+1]].pos.x;
vecY = vertices[indices[(i*3)+2]].pos.y - vertices[indices[(i*3)+1]].pos.y;
vecZ = vertices[indices[(i*3)+2]].pos.z - vertices[indices[(i*3)+1]].pos.z;
edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge
//Cross multiply the two edge vectors to get the un-normalized face normal
XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2));
tempNormal.push_back(unnormalized); //Save unormalized normal (for normal averaging)
}
//Compute vertex normals (normal Averaging)
XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
int facesUsing = 0;
float tX;
float tY;
float tZ;
//Go through each vertex
for(int i = 0; i < totalVerts; ++i)
{
//Check which triangles use this vertex
for(int j = 0; j < meshTriangles; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
tX = XMVectorGetX(normalSum) + tempNormal[j].x;
tY = XMVectorGetY(normalSum) + tempNormal[j].y;
tZ = XMVectorGetZ(normalSum) + tempNormal[j].z;
normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
normalSum = normalSum / facesUsing;
//Normalize the normalSum vector
normalSum = XMVector3Normalize(normalSum);
//Store the normal in our current vertex
vertices[i].normal.x = XMVectorGetX(normalSum);
vertices[i].normal.y = XMVectorGetY(normalSum);
vertices[i].normal.z = XMVectorGetZ(normalSum);
//Clear normalSum and facesUsing for next vertex
normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
facesUsing = 0;
}
}
//Create index buffer
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * meshTriangles*3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = &indices[0];
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, indexBuff);
//Create Vertex Buffer
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * totalVerts;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
vertexBufferData.pSysMem = &vertices[0];
hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, vertBuff);
return true;
}
函数变量
以下是该函数使用的变量。一个stringstream会让我们操作并获取来自于一个字符串的数据,就像它是一个输入、输入流一样。可使用>>来从流中获取下一个类型。
HRESULT hr = 0;
std::wifstream fileIn (filename.c_str()); //Open file
std::wstring meshMatLib; //String to hold our obj material library filename
//Arrays to store our model's information
std::vector indices;
std::vector vertPos;
std::vector vertNorm;
std::vector vertTexCoord;
std::vector meshMaterials;
//Vertex definition indices
std::vector vertPosIndex;
std::vector vertNormIndex;
std::vector vertTCIndex;
//Make sure we have a default if no tex coords or normals are defined
bool hasTexCoord = false;
bool hasNorm = false;
//Temp variables to store into vectors
std::wstring meshMaterialsTemp;
int vertPosIndexTemp;
int vertNormIndexTemp;
int vertTCIndexTemp;
wchar_t checkChar; //The variable we will use to store one char from file at a time
std::wstring face; //Holds the string containing our face vertices
int vIndex = 0; //Keep track of our vertex index count
int triangleCount = 0; //Total Triangles
int totalVerts = 0;
int meshTriangles = 0;
首先我们想要检查文件是否能够打开。若不能打开,我们要确保退出全屏,随后呈现一条信息告诉我们不能够打开特定的文件。
//Check to see if the file was opened
if (fileIn)
{
...
}
else //If we could not open the file
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
//create message
std::wstring message = L"Could not open: ";
message += filename;
MessageBox(0, message.c_str(), //display message
L"Error", MB_OK);
return false;
}
在我们的文件打开后,我们就要读取它了。首先调到一个while循环,我们会一直保持循环直到到达文件尾部。我们会逐个字符地读取文件。我们实际上能够使用>>而不是get()方法来逐个字符串的读取它,但是现在我们会获取字符。。
可使用方法get()从stringstream中来获取下一个单字符,下一个类型可使用>>来获取,或使用getline()来获取下一行。
在读取字符之后,我们就跳到一个switch条件。在该条件下,我们会首先检测看该行是否为一个注释(记住obj文件是由行分段的)。若该行是一个注释,我们会通过继续get()字符而跳过该行,直到我们到达一个新的行字符“\n”。
下一步我们会通过检测该字符是否为v来检测该行是否为顶点位置。若它是我们获取到的下一个字符或者顶点位置。顶点位置是由空格分开的float数值,所以我们能够使用>>来获取到来自于字符串流的值,并将它们存储到三个float变量中,vx,vy和vz。在我们获取到值后,我们检测该模型是否被指定为RH坐标系。若是,我们将位置的z值乘以-1.0f来转换并将该值推回到vertPos向量。如果它不是,随后我们就在毫无修改的情况下将该值推回。
随后我们对纹理坐标做同样的事情,Obj模型有时指定一个第三方值,来表示(u,v,w)纹理坐标系中的w值。就像之前所说的,w用于立方体纹理,且我们不会将该立方体纹理考虑进本章节中。当然,若正好需要w值,改起来也是很容易的。我们还需要检测一下模型是否为RH坐标系统或LH坐标系统。若我们需要将它从RH转换过来,我们只需要通过将v轴的v减去1.0f即可。在存储纹理坐标之后,我们设置hasTexCoord为true。我们做了这些所以我们知道我们在后面就不必生成一个默认的纹理坐标了,由于若没有纹理坐标,且我们尝试设置第0个纹理坐标(若在面内没有指定,则它为默认纹理坐标),我们会得到一个错误。
随后我们检查法线。和顶点位置类似,除了在我们推回法线到vertNorm向量之后,我们需要设置hasNorm为true,它和hasTexCoord是一样的使用方式。
我们不会考虑objects(o),但是我们会关心groups。groups也就是subsets子集。组或子集就是一系列三角形,它们共享同样的属性,所以无论何时我们到达一行以g起始的行时,我们会加一下子集的数量,并压入一个新的subsetIndexStart。
下一步我们会获取到面的信息,但是我们在后面来说它。
我们会检测材质库mtllib。并不是所有的obj文件都会包含一个材质库,且一些包含多个库,然而,我们会假设它总是包含了一个库。我们会将材质库文件名保存在宽字符meshMatLib。
最后,我们获取到组材质usemtl并将它压入到meshMaterials向量,在这儿之后我们会使用这个来查找材质向量中的材质索引值。
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
case 'v': //Get Vertex Descriptions
checkChar = fileIn.get();
if(checkChar == ' ') //v - vert position
{
float vz, vy, vx;
fileIn >> vx >> vy >> vz; //Store the next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertPos.push_back(XMFLOAT3( vx, vy, vz * -1.0f)); //Invert the Z axis
else
vertPos.push_back(XMFLOAT3( vx, vy, vz));
}
if(checkChar == 't') //vt - vert tex coords
{
float vtcu, vtcv;
fileIn >> vtcu >> vtcv; //Store next two types
if(isRHCoordSys) //If model is from an RH Coord System
vertTexCoord.push_back(XMFLOAT2(vtcu, 1.0f-vtcv)); //Reverse the "v" axis
else
vertTexCoord.push_back(XMFLOAT2(vtcu, vtcv));
hasTexCoord = true; //We know the model uses texture coords
}
//Since we compute the normals later, we don't need to check for normals
//In the file, but i'll do it here anyway
if(checkChar == 'n') //vn - vert normal
{
float vnx, vny, vnz;
fileIn >> vnx >> vny >> vnz; //Store next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz * -1.0f )); //Invert the Z axis
else
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz ));
hasNorm = true; //We know the model defines normals
}
break;
//New group (Subset)
case 'g': //g - defines a group
checkChar = fileIn.get();
if(checkChar == ' ')
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
break;
//Get Face Index
...
case 'm': //mtllib - material library filename
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'i')
{
checkChar = fileIn.get();
if(checkChar == 'b')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//Store the material libraries file name
fileIn >> meshMatLib;
}
}
}
}
}
}
break;
case 'u': //usemtl - which material to use
checkChar = fileIn.get();
if(checkChar == 's')
{
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
meshMaterialsTemp = L""; //Make sure this is cleared
fileIn >> meshMaterialsTemp; //Get next type (string)
meshMaterials.push_back(meshMaterialsTemp);
}
}
}
}
}
}
break;
default:
break;
}
}
加载面f
加载面有点复杂,所以还记得是如何说obj模型中的面吗?有时模型程序能够创建一个大于3个边的面,且不仅那些,有时面还是凹的。还记得direct3d使用三角形的事。然而在这儿我们可以解决第一个问题,通过用大于3个顶点将面转化为三角形,但是不能解决第二个问题,也就是凹面。当使用函数加载一个凹面会发生的事情就是,三角形会相互交叠在一起,引起闪烁以及模型的不准确表达。要解决这个问题不是很难,要做的就是为每个顶点计算两个边的角度,且根据大于180度的角度来重新调整 面。调整面的方式就是在函数中为所有的三角形使用单个顶点开始,且前一个三角形的最后一个顶点作为当前三角形的第二个顶点。在随后解释。
首先在检测面是,获取整行。当我们获取整行时,我们检测空格。检测空格的原因是每个顶点由空格分开的,且我们必须确保当面有超过3个顶点时,面被重新调整。在获取整行后,我们检测以确保行中的最后一个字符不是空格,由于每个空格会添加一个额外的三角形。随后我们对三角形数量减1,由于面中前面三个顶点是由两个空格构成的,它会给我们的triangleCount两个三角形而不是一个。
我们将包含面的行的字符串转化为叫做ss的stringstream。我们能够每次使用字符串流来取出一个顶点定义,并解析它以找到顶点位置,纹理坐标和法线的索引值。随后我们检测确保该行大于0.我们创建两个整型,一个为面中的第一个顶点的索引值,另一个为最后一个创建的三角形的最后一个顶点的索引值。这些用于调整面。
随后我们进入一个循环,它会为面中的第一个三角形创建索引。该循环在面中的前面三个顶点已经被转化到三角形中并被存储在索引列表中之后退出。我们可以每次通过为字符串流ss使用>>来获取一个顶点定义并把它存储到VertDef。我们创建一个字符串,用来跟踪索引值,并且另外一个整型值用于跟踪我们所在的(位置/纹理坐标/法线)顶点定义的某个部分。若该值为0,说明我们是在顶点位置上,若为1,说明我们是在纹理坐标上,若为2说明我们是在法线上。
随后解析字符串VertDef,它保存当前顶点的定义。该定义是索引值,将用于找到位置,纹理坐标,和法线值,这些值都是从我们之前放进向量中的被加载的位置,纹理坐标和法线值。
我们轮询VertDef字符串。首先我们检测确保当前字符不是/。若不是,我们就移动到下一个字符。当我们遇到一个/时或者如果当前字符是字符串中的最后一个字符,我们可以设置该值到合适的临时变量,该值是刚刚从字符串中获取到的。由于whichPart变量让我们知道是哪一个变量。如果whichPart为0,怎我们知道我们在位置上。首先我们必须从vertPart字符串创建另外一个字符串流。我们能够从字符串流中解析类型,所以我们能够从这解析数字并将它存储在一个int变量中。该整型是索引值用于查找该顶点要使用的在位置,纹理左边或发现向量中的哪一个元素。
若whichPart为0,则我们会将它减1由于obj文件起始于索引数量为1,然而c==数组起始于0。随后,我们会检测是否位于向量字符串的最后一个字符串上(比如,f 1 2 3)。若是的,则我们知道只有位置被指定,且设置一个默认索引值0给到vertNormIndexTemp和vertTCIndexTemp。之后,我们要复位vertPart并增加whichPart
若whichPart为1,我们知道我们在纹理坐标应该在的地方。若vertPart不是空的(比如f 1/1/1 2/2/2 3/3/3),我们会为纹理坐标设置临时索引值并减1.若vertPart是空的(比如 f 1//1 2//2 3//3),则我们会设置一默认的索引值0给到临时纹理坐标索引。随后我们检测当前字符是否为vertPart字符串的最后一个字符(比如f 1/1 2/2 3/3)。若它是,我们知道没有法线并设置vertNormIndexTemp为0。
最终,若我们还没有达到字符串的尾部,whichPart就为2了,则我们正在查看顶点法线。若我们已经做到这一点,我们只要将法线值减1,并将它存储在vertNormIndexTemp中。
case 'f': //f - defines the faces
checkChar = fileIn.get();
if(checkChar == ' ')
{
face = L"";
std::wstring VertDef; //Holds one vertex definition at a time
triangleCount = 0;
checkChar = fileIn.get();
while(checkChar != '\n')
{
face += checkChar; //Add the char to our face string
checkChar = fileIn.get(); //Get the next Character
if(checkChar == ' ') //If its a space...
triangleCount++; //Increase our triangle count
}
//Check for space at the end of our face string
if(face[face.length()-1] == ' ')
triangleCount--; //Each space adds to our triangle count
triangleCount -= 1; //Ever vertex in the face AFTER the first two are new faces
std::wstringstream ss(face);
if(face.length() > 0)
{
int firstVIndex, lastVIndex; //Holds the first and last vertice's index
for(int i = 0; i < 3; ++i) //First three vertices (first triangle)
{
ss >> VertDef; //Get vertex definition (vPos/vTexCoord/vNorm)
std::wstring vertPart;
int whichPart = 0; //(vPos, vTexCoord, or vNorm)
//Parse this string
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/') //If there is no divider "/", add a char to our vertPart
vertPart += VertDef[j];
//If the current char is a divider "/", or its the last character in the string
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart); //Used to convert wstring to int
if(whichPart == 0) //If vPos
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertNormIndexTemp = 0;
vertTCIndexTemp = 0;
}
}
else if(whichPart == 1) //If vTexCoord
{
if(vertPart != L"") //Check to see if there even is a tex coord
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
else //If there is no tex coord, make a default
vertTCIndexTemp = 0;
//If the cur. char is the second to last in the string, then
//there must be no normal, so set a default normal
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2) //If vNorm
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
vertPart = L""; //Get ready for next vertex part
whichPart++; //Move on to next vertex part
}
}
存储面
在加载完第一个三角形顶点索引信息后,我们需要存储该信息,但是只是在我们检测重复顶点之后。
首先我们要确保至少有一个子集,这是因为有时obj模型不会指定g,并且我们是根据g来告诉我们有多少子集的,且如何将面分割成子集。
下一步我们会检测重复顶点。通过查询包含有顶点位置和顶点纹理坐标的每个向量来实现,并检查当前纹理定义是否与已经存储的顶点定义一样,若有同样的顶点被存储,则我们压入已经存储到索引列表中的顶点的索引数据。
若顶点定义还没有被存储,则我们将顶点定义存储到三个向量中,vertPoseIndex,vertTCIndex和vertNormIndex。我们会使用来自这三个向量的索引值,以参考存储在vertPos中的实际位置,在vertTexCoord中的纹理坐标和在vertNorm中的法线来创建顶点,这些顶点将会在该模型的顶点缓冲中被存储。
下一步我们将来自该三角形的第一个和最后一个顶点索引存储在变量firstVIndex和lastVIndex中,若有该面中有超过三个顶点将会用来重新调整面。
在我们在面中加载并存储第一个三角形之后,我们增加总的三角形数量。
//Check to make sure there is at least one subset
if(subsetCount == 0)
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
//Avoid duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
//Loop through all the vertices
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
//If the vertex position and texture coordinate in memory are the same
//As the vertex position and texture coordinate we just now got out
//of the obj file, we will set this faces vertex index to the vertex's
//index value in memory. This makes sure we don't create duplicate vertices
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
//If this vertex is not already in our vertex arrays, put it there
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //We created a new vertex
indices.push_back(totalVerts-1); //Set index for this vertex
}
//If this is the very first vertex in the face, we need to
//make sure the rest of the triangles use this vertex
if(i == 0)
{
firstVIndex = indices[vIndex]; //The first vertex index of this FACE
}
//If this was the last vertex in the first triangle, we will make sure
//the next triangle uses this one (eg. tri1(1,2,3) tri2(1,3,4) tri3(1,4,5))
if(i == 2)
{
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
}
vIndex++; //Increment index count
}
meshTriangles++; //One triangle down
重新调整面
现在,像之前所说的,有时obj模型包含超过三个顶点的面。若这种事情发生的话,我们需要重新调整面,并将一个面转化为多个三角形。首先检测该面中是否有超过一个三角形(这检查基本上适合循环结合的),若有,之后进入到调整面。
我们进入循环,这里会查找面中的每一个顶点,并创建额外的三角形。
我们压入firstVIndex值到索引列表,并加上一个vIndex值。记住firstVIndex是面的第一个顶点的索引值。随后,我们将lastVindex压入到索引列表。lastVIndex是最后一个存储的三角形的最后一个顶点。
现在我们有三角形前面的两个顶点了,第三个将来自文件。我们像在上面那种方式一样获取该顶点。
在获得下一个顶点后,完成该三角形的生成,我们想要存储它,但是在存储它之前,我们再次想要检测以确保该顶点定义还没有存在。
最终,我们设置lastVIndex为我们刚获取的该最后顶点的索引值,所以我们能够使用这作为下一个三角形的第二个顶点。然后我们增加meshTriangles和vIndex的值,完成该面。
下面是一个面所要做的所有事情
//If there are more than three vertices in the face definition, we need to make sure
//we convert the face to triangles. We created our first triangle above, now we will
//create a new triangle for every new vertex in the face, using the very first vertex
//of the face, and the last vertex from the triangle before the current triangle
for(int l = 0; l < triangleCount-1; ++l) //Loop through the next vertices to create new triangles
{
//First vertex of this triangle (the very first vertex of the face too)
indices.push_back(firstVIndex); //Set index for this vertex
vIndex++;
//Second Vertex of this triangle (the last vertex used in the tri before this one)
indices.push_back(lastVIndex); //Set index for this vertex
vIndex++;
//Get the third vertex for this triangle
ss >> VertDef;
std::wstring vertPart;
int whichPart = 0;
//Parse this string (same as above)
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/')
vertPart += VertDef[j];
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart);
if(whichPart == 0)
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1;
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertTCIndexTemp = 0;
vertNormIndexTemp = 0;
}
}
else if(whichPart == 1)
{
if(vertPart != L"")
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1;
}
else
vertTCIndexTemp = 0;
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2)
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1;
}
vertPart = L"";
whichPart++;
}
}
//Check for duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //New vertex created, add to total verts
indices.push_back(totalVerts-1); //Set index for this vertex
}
//Set the second vertex for the next triangle to the last vertex we got
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
meshTriangles++; //New triangle defined
vIndex++;
}
}
}
break;
加载之后的准备
在加载我们的模型几何体之后我们必须要做的事情。首先,我们必须压入一个最终的subsetIndexStart,由于每个模型将会在它们所对应的subsetIndexStart值的索引开始绘制,并且在下一个子集对应的subsetIndexStart值处停止绘制。当我们到达该点时,每个子集会有它们自己的,但是最后一个子集不会有一个stop绘制点,所以我们会在这儿设置一下。我们压入vIndex,它表示我们已经为模型存储的索引的总量。
下一步,我们检测以确认第一个子集没有包含要绘制的0索引。由于一旦在某一时段一个组的g在文件头部被定义且在实际面被定义之前,这都可能会发生。这将创建一个要被绘制的带0索引的子集。若这发生了,我们会从总数中减去1个子集,且擦除subsetIndexStart向量中的第二个元素,它包含致命的0值。
下一步我们确保我们有一个默认的顶点纹理坐标和法线,由于有时obj文件不包含任何信息,且我们程序的顶点结果期望某些参数传入。
下一步我们关闭obj文件,并打开mtl文件。
现在我们必须开始准备加载和存储我们的材质了。首先我们创建一个新的宽字符叫做lastStringRead,且一个新的整型叫做matCount来跟踪被加载的材质的总量。随后我们创建一个布尔变量,叫做kdset。该布尔变量所要做的事情是,知道一个漫反射颜色是否已经加载,因为我们将假设环境光颜色和漫反射颜色是一样的。如果我们加载一个漫反射颜色,我们会设置该布尔值为true,所以当我们加载环境光颜色时,我们不会存储它,由于我们已经有一个漫反射颜色了。但是如果我们首先遇到的是环境光,或者漫反射根本就没有指定,那么kdset为false,且环境光将会成为材质的漫反射颜色。
subsetIndexStart.push_back(vIndex); //There won't be another index start after our last subset, so set it here
//sometimes "g" is defined at the very top of the file, then again before the first group of faces.
//This makes sure the first subset does not conatain "0" indices.
if(subsetIndexStart[1] == 0)
{
subsetIndexStart.erase(subsetIndexStart.begin()+1);
meshSubsets--;
}
//Make sure we have a default for the tex coord and normal
//if one or both are not specified
if(!hasNorm)
vertNorm.push_back(XMFLOAT3(0.0f, 0.0f, 0.0f));
if(!hasTexCoord)
vertTexCoord.push_back(XMFLOAT2(0.0f, 0.0f));
//Close the obj file, and open the mtl file
fileIn.close();
fileIn.open(meshMatLib.c_str());
std::wstring lastStringRead;
int matCount = material.size(); //total materials
//kdset - If our diffuse color was not set, we can use the ambient color (which is usually the same)
//If the diffuse color WAS set, then we don't need to set our diffuse color to ambient
bool kdset = false;
打开材质库(mtl文件)
和打开obj文件类似,我们首先检测确保它能够被打开,且若不能打开,则退出全屏,并显示一个消息,然后返回失败,最终结束程序。
if (fileIn)
{
...
}
else
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
std::wstring message = L"Could not open: ";
message += meshMatLib;
MessageBox(0, message.c_str(),
L"Error", MB_OK);
return false;
}
加载材质
现在来加载材质。这和之前加载模型几何体非常相似。首先进入到一个循环,该循环在到文件底部时会退出。首先使用方法get()来获得字符。然后进入switch条件语句,并检测字符值。首先检测注释符。随后检测K。
检测K值是针对漫反射光和环境光颜色的。然而,也可检测镜面反射和自发光,Ks和Ke。
下一步检测透明度,Tr或d。Tr是Transparency的缩写,d是diffuse的缩写。记住Tr是为0到1的值,1是完全透明而0是不透明的。d则相反。它的值也是位于0到1,但是0是完全透明,1位不透明。
下一步检测纹理贴图,map_,首先检测的是漫反射贴图map_Kd。我们通过检查一行中的每个字符直到到达一段.来加载文件名或文件路径。在到达一段.之后,我们假设扩展名是三个字符长度,并取得下面三个字符。下一步我们检测确保该纹理还没有被加载进来,通过将它与每个储存在textureNameArray向量中的字符串做对比来实现。若它已经加载,则我们在当前材质texArrayIndex成员中存储它的索引值,用于查找meshSRV向量中的实际纹理。
若纹理还没有加载进来,则下一步加载它。若成功被加载,则我们压入它的文件名到textureNameArray中,将当前的材质texArrayIndex成员设置为meshSRV的大小(最后一个存储在meshSRV中的纹理的索引值)。我们压入临时加载的着色器资源视图到meshSRV,最后将材质hasTexture成员设为true,以便我们知道要使用一个纹理来着色子集而不是从文件加载的纯漫反射颜色。
我们还会检测一个alpha贴图map_d。几乎这个是和漫反射贴图同样的纹理,所以我们会假设它是一样的,且仅将材质的透明度成员设为true,以便我们知道在使用该材质的子集上要使用混合,并最后绘制它们。
我们还要检查由newmtl开头的行的材质,因此我们知道创建一个新的材质。当我们遇到这行时,我们压入一个新的材质并设置它的默认值。
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
//Check for comment
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
//Set diffuse color
case 'K':
checkChar = fileIn.get();
if(checkChar == 'd') //Diffuse Color
{
checkChar = fileIn.get(); //remove space
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
kdset = true;
}
//Ambient Color (We'll store it in diffuse if there isn't a diffuse already)
if(checkChar == 'a')
{
checkChar = fileIn.get(); //remove space
if(!kdset)
{
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
}
}
break;
//Check for transparency
case 'T':
checkChar = fileIn.get();
if(checkChar == 'r')
{
checkChar = fileIn.get(); //remove space
float Transparency;
fileIn >> Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Some obj files specify d for transparency
case 'd':
checkChar = fileIn.get();
if(checkChar == ' ')
{
float Transparency;
fileIn >> Transparency;
//'d' - 0 being most transparent, and 1 being opaque, opposite of Tr
Transparency = 1.0f - Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Get the diffuse map (texture)
case 'm':
checkChar = fileIn.get();
if(checkChar == 'a')
{
checkChar = fileIn.get();
if(checkChar == 'p')
{
checkChar = fileIn.get();
if(checkChar == '_')
{
//map_Kd - Diffuse map
checkChar = fileIn.get();
if(checkChar == 'K')
{
checkChar = fileIn.get();
if(checkChar == 'd')
{
std::wstring fileNamePath;
fileIn.get(); //Remove whitespace between map_Kd and file
//Get the file path - We read the pathname char by char since
//pathnames can sometimes contain spaces, so we will read until
//we find the file extension
bool texFilePathEnd = false;
while(!texFilePathEnd)
{
checkChar = fileIn.get();
fileNamePath += checkChar;
if(checkChar == '.')
{
for(int i = 0; i < 3; ++i)
fileNamePath += fileIn.get();
texFilePathEnd = true;
}
}
//check if this texture has already been loaded
bool alreadyLoaded = false;
for(int i = 0; i < textureNameArray.size(); ++i)
{
if(fileNamePath == textureNameArray[i])
{
alreadyLoaded = true;
material[matCount-1].texArrayIndex = i;
material[matCount-1].hasTexture = true;
}
}
//if the texture is not already loaded, load it now
if(!alreadyLoaded)
{
ID3D11ShaderResourceView* tempMeshSRV;
hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(),
NULL, NULL, &tempMeshSRV, NULL );
if(SUCCEEDED(hr))
{
textureNameArray.push_back(fileNamePath.c_str());
material[matCount-1].texArrayIndex = meshSRV.size();
meshSRV.push_back(tempMeshSRV);
material[matCount-1].hasTexture = true;
}
}
}
}
//map_d - alpha map
else if(checkChar == 'd')
{
//Alpha maps are usually the same as the diffuse map
//So we will assume that for now by only enabling
//transparency for this material, as we will already
//be using the alpha channel in the diffuse map
material[matCount-1].transparent = true;
}
}
}
}
break;
case 'n': //newmtl - Declare new material
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'w')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//New material, set its defaults
SurfaceMaterial tempMat;
material.push_back(tempMat);
fileIn >> material[matCount].matName;
material[matCount].transparent = false;
material[matCount].hasTexture = false;
material[matCount].texArrayIndex = 0;
matCount++;
kdset = false;
}
}
}
}
}
}
break;
default:
break;
}
}
设置子集的材质索引值
在加载完所有的材质后,我们一般必须绑定每个子集到它的材质。我们通过轮询每个子集来实现,且将它的存储在meshMaterials中的材质名字与存储在材质向量的matName中的材质名做比较。当我们遇到材质向量中的正确材质时,我们压入subsetMaterialArray到材质向量的索引元素。然后我们设置hasMat为true退出循环。若材质向量中的所有的材质被检测完了,且子集材质没有再那儿,我们会给它一个默认材质,它会成为材质向量中的第一个材质。
//Set the subsets material to the index value
//of the its material in our material array
for(int i = 0; i < meshSubsets; ++i)
{
bool hasMat = false;
for(int j = 0; j < material.size(); ++j)
{
if(meshMaterials[i] == material[j].matName)
{
subsetMaterialArray.push_back(j);
hasMat = true;
}
}
if(!hasMat)
subsetMaterialArray.push_back(0); //Use first material in array
}
现在创建要存储在模型顶点缓冲中的顶点。首先初始化一个新的Vertex向量以存储创建的向量。然后一个临时的向量来存储当前向量。
我们进入一个循环,并在我们创建完totalVerts个顶点后退出。每个循环会创建单个顶点并将它压入到顶点向量中。首先我们查找当前顶点位置。可通过查找vertPosIndex向量中的第j个元素来实现,它是存储在vertPose中的位置的索引值。随后,我们对法线和纹理坐标做同样的事情。
最终我们压入临时顶点tempVert到顶点向量中。
在创建完所有的顶点后如果我们将该函数的computeNormals参数设为true,则法线会被覆盖重写。
std::vector vertices;
Vertex tempVert;
//Create our vertices using the information we got
//from the file and store them in a vector
for(int j = 0 ; j < totalVerts; ++j)
{
tempVert.pos = vertPos[vertPosIndex[j]];
tempVert.normal = vertNorm[vertNormIndex[j]];
tempVert.texCoord = vertTexCoord[vertTCIndex[j]];
vertices.push_back(tempVert);
}
使用法线平均法来计算顶点法线。首先会查找模型中的每个三角形并找到它的非规范化的法线。然后我们会查找每个顶点并找到所有的使用哪个特定顶点的面。我们会平均化这些共享该顶点的面的非规范化的法线,并使它规范化,然后将它存储在当前顶点的法线成员中。
首先是初始化一些事情。tempNormal向量会为我们的网格中的每个三角形存储非规范化法线。我们没有规范化它,所以我们能够获取到共享单个顶点的面的正确的法线平均值。我们有一个XMFLOAT3变量叫做unnormalized,它会保存当前面非规范化法线的x,y和z的值,所以它能够被压入到tempNormal向量。然后还有三个浮点变量,x,y和z每个都对应一个,我们会使用它们来找到一个三角形的两条边。然后我们有两个XMVECTOR来将边存储进去。
若我们能够在标准向量数组中存储XMVECTOR,这将会更简单。然而,在搜索之后,发现已经发现一个最好的解决方案就是创建一个XMFLOAT3的向量而不是一个XMVECOTR向量。若尝试创建一个XMVector向量,可能会访问异常。这必须处理XMVECTOR是如何存储到内存的,且不能够以一个动态分配的类或者结构体就像一个向量一样来存储。
现在来计算面法线。我们进入循环,它会在我们一旦轮询完模型中的每个三角形后就退出。为了获得一个面法线,我们必须获得面的两条边(edge1X = v1X - v3X, edge2X = v3X - v2X,对每个x,y和z轴使用该方程式)。为了从两条边获得面法线,我们要对他们进行叉乘。我们能够通过使用xna数学库函数XMStoreFloat3()将一个XMVECTOR存储到一个XMFLOAT3变量中,这儿第一个参数是一个指向XMFLOAT3变量的指针,并是希望能够从第二个参数中接收XMVECTOR的x y z值。在我们找到非规范化法线后,我们将它压入到tempNormal向量中。
//////////////////////Compute Normals///////////////////////////
//If computeNormals was set to true then we will create our own
//normals, if it was set to false we will use the obj files normals
if(computeNormals)
{
std::vector tempNormal;
//normalized and unnormalized normals
XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f);
//Used to get vectors (sides) from the position of the verts
float vecX, vecY, vecZ;
//Two edges of our triangle
XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
//Compute face normals
for(int i = 0; i < meshTriangles; ++i)
{
//Get the vector describing one edge of our triangle (edge 0,2)
vecX = vertices[indices[(i*3)]].pos.x - vertices[indices[(i*3)+2]].pos.x;
vecY = vertices[indices[(i*3)]].pos.y - vertices[indices[(i*3)+2]].pos.y;
vecZ = vertices[indices[(i*3)]].pos.z - vertices[indices[(i*3)+2]].pos.z;
edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge
//Get the vector describing another edge of our triangle (edge 2,1)
vecX = vertices[indices[(i*3)+2]].pos.x - vertices[indices[(i*3)+1]].pos.x;
vecY = vertices[indices[(i*3)+2]].pos.y - vertices[indices[(i*3)+1]].pos.y;
vecZ = vertices[indices[(i*3)+2]].pos.z - vertices[indices[(i*3)+1]].pos.z;
edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge
//Cross multiply the two edge vectors to get the un-normalized face normal
XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2));
tempNormal.push_back(unnormalized); //Save unormalized normal (for normal averaging)
}
计算法线(顶点法线-法线平均化)
在将模型中每个三角形的面法线得到后,我们必须查找顶点法线,由于这些是要发送到着色器的。
首先初始化一些变量,包括一个XMVECTOR的normalSum变量,一个整型facesUsing变量,以及三个float型的tX,tY,tZ变量。
然后我们进入循环,在我们创建完每个顶点法线之后会退出。
在循环中做的第一件事就是查找每个使用当前顶点的三角形。可通过使用顶点的索引i来实现,并轮询网格中的每个三角形查看它的顶点中是否有任意一个索引为i。若有一个为i,则我们知道它正在使用当前顶点。若正在使用当前顶点,则我们将它的非规范化法线添加到normalSum XMVECTOR,并增加facesUsing值。
在使用该顶点查找完所有的面之后,我们通过将normalSum除以facesUsing获得真实的法线,normalSum为所有共享该顶点的法线之和,并将它存储回到normalSum中。在那之后,我们规范化该存储在normalSum中的非规范化法线。
我们将该规范化法线设置为当前顶点的法线,清除nomalSum和facesUsing并移动到下一个顶点。
//Compute vertex normals (normal Averaging)
XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
int facesUsing = 0;
float tX;
float tY;
float tZ;
//Go through each vertex
for(int i = 0; i < totalVerts; ++i)
{
//Check which triangles use this vertex
for(int j = 0; j < meshTriangles; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
tX = XMVectorGetX(normalSum) + tempNormal[j].x;
tY = XMVectorGetY(normalSum) + tempNormal[j].y;
tZ = XMVectorGetZ(normalSum) + tempNormal[j].z;
normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
normalSum = normalSum / facesUsing;
//Normalize the normalSum vector
normalSum = XMVector3Normalize(normalSum);
//Store the normal in our current vertex
vertices[i].normal.x = XMVectorGetX(normalSum);
vertices[i].normal.y = XMVectorGetY(normalSum);
vertices[i].normal.z = XMVectorGetZ(normalSum);
//Clear normalSum and facesUsing for next vertex
normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
facesUsing = 0;
}
}
现在我们成功加载模型和它的材质了,创建了所有的顶点和索引列表,且计算了顶点的法线。所有剩下的要做的就是创建索引和顶点缓冲了。我们应该已经知道如何做了,但是要提及一些关于对顶点和索引列表使用向量的事情。当设置顶点或索引向量为缓冲的pSysMen或data时,必须使用它们的指针,并确保明确在向量中的哪个位置启动,比如,[0]为向量中的第一个元素。
//Create index buffer
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * meshTriangles*3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = &indices[0];
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, indexBuff);
//Create Vertex Buffer
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * totalVerts;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
vertexBufferData.pSysMem = &vertices[0];
hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, vertBuff);
return true;
}
现在来到了InitScene()函数,这儿我们会调用我们的模型加载函数。我们可以在if条件判断中加载它,以便若加载模型失败,能够返回false。结束程序。
if(!LoadObjModel(L"spaceCompound.obj", &meshVertBuff, &meshIndexBuff, meshSubsetIndexStart, meshSubsetTexture, material, meshSubsets, true, false))
return false;
我们必须为透明子集创建一个混合状态。该混合状态会根据漫反射颜色的alpha值混合对象,或者来自于文件的纯alpha值(Tr或d),或者来自于文件map_Kd的漫反射贴图。我们会在Transparency中保存该混合状态。
ZeroMemory( &rtbd, sizeof(rtbd) );
rtbd.BlendEnable = true;
rtbd.SrcBlend = D3D11_BLEND_INV_SRC_ALPHA;
rtbd.DestBlend = D3D11_BLEND_SRC_ALPHA;
rtbd.BlendOp = D3D11_BLEND_OP_ADD;
rtbd.SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
rtbd.DestBlendAlpha = D3D11_BLEND_SRC_ALPHA;
rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD;
rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;
blendDesc.AlphaToCoverageEnable = false;
blendDesc.RenderTarget[0] = rtbd;
d3d11Device->CreateBlendState(&blendDesc, &Transparency);
UpdateScene()函数
在我们更新场景函数中,我们需要确保我们定义了我们模型的世界空间。
meshWorld = XMMatrixIdentity();
//Define cube1's world space matrix
Rotation = XMMatrixRotationY(3.14f);
Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f );
Translation = XMMatrixTranslation( 0.0f, 0.0f, 0.0f );
meshWorld = Rotation * Scale * Translation;
绘制非透明子集
这儿我们绘制我们模型的非透明子集。我们进入一个循环遍历网格中每个子集。我们设置正确的顶点和索引缓冲,以及世界空间矩阵,以及在每个对象更新我们的常量缓冲。注意在我们的常量缓冲中如何更新我们的两个新成员,一个是材质的漫反射颜色,另外一个是布尔值表示材质是否使用了纹理。然后我们设置常量缓冲为顶点着色器以及像素着色器(而在之前我们只对它设置到顶点着色器)。然后我们检测确保材质使用了一个纹理。若使用了,则我们将正确的纹理绑定到像素着色器。然后我们设置采样状态。在那之后,我们设置渲染状态(这儿我们不使用背面剪裁,但实际上我们应该使用(因为它更有效))。
现在我们创建两个整型,一个给当前子集的起始索引,一个给要绘制的索引的数量。最终我们检测一下看该子集是否透明。若不是,我们使用后面设置的两个变量绘制子集。
//Draw our model's NON-transparent subsets
for(int i = 0; i < meshSubsets; ++i)
{
//Set the grounds index buffer
d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0);
//Set the grounds vertex buffer
d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset );
//Set the WVP matrix and send it to the constant buffer in effect file
WVP = meshWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(meshWorld);
cbPerObj.difColor = material[meshSubsetTexture[i]].difColor;
cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture;
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer );
if(material[meshSubsetTexture[i]].hasTexture)
d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
d3d11DevCon->RSSetState(RSCullNone);
int indexStart = meshSubsetIndexStart[i];
int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i];
if(!material[meshSubsetTexture[i]].transparent)
d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 );
}
绘制透明子集
确保在天空盒子后你绘制了这些,以便他们能够和天空盒子混合。和上面绘制子集要做的事情大部分都是相同的,只有一些改变。首先,我们必须设置混合状态为Transparency,因为这些是透明的,以便我们的子集会使用混合。随后查看这个,其他的每件事情都是一样的除了当我们要去绘制子集时。此时我们检测确保子集时透明的,而不是像上面一样非透明。
//Draw our model's TRANSPARENT subsets now
//Set our blend state
d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff);
for(int i = 0; i < meshSubsets; ++i)
{
//Set the grounds index buffer
d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0);
//Set the grounds vertex buffer
d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset );
//Set the WVP matrix and send it to the constant buffer in effect file
WVP = meshWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(meshWorld);
cbPerObj.difColor = material[meshSubsetTexture[i]].difColor;
cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture;
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer );
if(material[meshSubsetTexture[i]].hasTexture)
d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
d3d11DevCon->RSSetState(RSCullNone);
int indexStart = meshSubsetIndexStart[i];
int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i];
if(material[meshSubsetTexture[i]].transparent)
d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 );
}
效果文件(cbPerObject)
需要修改效果文件的cbPerObject常量缓冲来包含我们材质的漫反射颜色,以及一个布尔值以判断我们的材质是否使用了纹理。
cbuffer cbPerObject
{
float4x4 WVP;
float4x4 World;
float4 difColor;
bool hasTexture;
};
最后要修改PS了,首先可看到在cbPerObject缓冲中将我们的漫反射颜色值设为纯漫反射颜色了。
在这之后,我们检测材质是否使用了纹理。若使用了纹理,则使用来自纹理的颜色覆盖重写漫反射颜色。
float4 PS(VS_OUTPUT input) : SV_TARGET
{
input.normal = normalize(input.normal);
//Set diffuse color of material
float4 diffuse = difColor;
//If material has a diffuse texture map, set it now
if(hasTexture == true)
diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord );
float3 finalColor = float3(0.0f, 0.0f, 0.0f);
...
}
spaceCompound.obj:
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 26.07.2011 13:47:43
mtllib spaceCompound.mtl
#
# object Window
#
v -8.6007 1.3993 10.0000
v -8.6007 8.6007 10.0000
v 8.6007 8.6007 10.0000
v 8.6007 1.3993 10.0000
# 4 vertices
vn 0.0000 0.0000 -1.0000
# 1 vertex normals
vt 0.0000 0.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 1.0000 0.0000 0.0000
# 4 texture coords
g Window
usemtl Window
f 1/1/1 2/2/1 3/3/1
f 3/3/1 4/4/1 1/1/1
# 2 faces
#
# object DoorWay1
#
v 15.0000 7.6721 -3.5444
v 15.0000 7.6721 3.6507
v 10.0000 7.6721 3.5444
v 10.0000 7.6721 -3.6507
v 15.0000 0.0000 3.6507
v 10.0000 0.0000 3.5444
v 15.0000 0.0000 -3.5444
v 10.0000 0.0000 -3.6507
# 8 vertices
vn 0.0000 -1.0000 -0.0000
vn 0.0213 0.0000 -0.9998
vn 0.0213 -0.0000 -0.9998
vn 0.0000 1.0000 -0.0000
vn -0.0213 -0.0000 0.9998
vn -0.0213 0.0000 0.9998
# 6 vertex normals
vt 0.5000 0.2911 0.8836
vt 0.5000 1.7301 0.8836
vt 1.5000 1.7089 0.8836
vt 1.5000 0.2699 0.8836
vt 1.5000 1.7672 0.8651
vt 1.5000 0.2328 0.8651
vt 0.5000 0.2328 0.8544
vt 0.5000 1.7672 0.8544
vt 0.5000 1.7089 0.1164
vt 1.5000 1.7301 0.1164
vt 1.5000 0.2911 0.1164
vt 0.5000 0.2699 0.1164
vt 0.5000 0.2328 0.1456
vt 0.5000 1.7672 0.1456
vt 1.5000 1.7672 0.1349
vt 1.5000 0.2328 0.1349
# 16 texture coords
g DoorWay1
usemtl MetalPanels
f 5/5/2 6/6/2 7/7/2
f 7/7/2 8/8/2 5/5/2
f 6/9/3 9/10/3 10/11/3
f 10/11/4 7/12/4 6/9/4
f 10/13/5 9/14/5 11/15/5
f 11/15/5 12/16/5 10/13/5
f 11/17/6 5/18/6 8/19/6
f 8/19/7 12/20/7 11/17/7
# 8 faces
#
# object DoorWay3
#
v -15.0000 7.6721 3.5444
v -15.0000 7.6721 -3.6507
v -10.0000 7.7028 -3.6317
v -10.0000 7.7028 3.5386
v -10.0000 0.0000 3.5386
v -15.0000 0.0000 3.5444
v -15.0000 0.0000 -3.6507
v -10.0000 0.0000 -3.6317
# 8 vertices
vn 0.0061 -1.0000 -0.0000
vn -0.0012 0.0000 -1.0000
vn -0.0012 -0.0000 -1.0000
vn 0.0000 1.0000 -0.0000
vn -0.0038 -0.0000 1.0000
vn -0.0038 0.0000 1.0000
# 6 vertex normals
vt 1.5000 1.7195 0.8821
vt 1.5000 0.2805 0.8821
vt 0.5000 0.2843 0.8851
vt 0.5000 1.7184 0.8851
vt 1.5000 1.7703 0.8592
vt 1.5000 0.2297 0.8592
vt 0.5000 0.2297 0.8598
vt 0.5000 1.7641 0.8598
vt 0.5000 0.2805 0.1149
vt 0.5000 1.7195 0.1149
vt 1.5000 1.7184 0.1149
vt 1.5000 0.2843 0.1149
vt 0.5000 0.2297 0.1421
vt 0.5000 1.7703 0.1421
vt 1.5000 1.7641 0.1402
vt 1.5000 0.2297 0.1402
# 16 texture coords
g DoorWay3
usemtl MetalPanels
f 13/21/8 14/22/8 15/23/8
f 15/23/8 16/24/8 13/21/8
f 16/25/9 17/26/9 18/27/9
f 18/27/10 13/28/10 16/25/10
f 19/29/11 18/30/11 17/31/11
f 17/31/11 20/32/11 19/29/11
f 20/33/12 15/34/12 14/35/12
f 14/35/13 19/36/13 20/33/13
# 8 faces
#
# object MainRoom
#
v 10.0000 10.0000 -10.0000
v 10.0000 10.0000 10.0000
v -10.0000 10.0000 10.0000
v -10.0000 10.0000 -10.0000
v 10.0000 0.0000 3.5444
v 10.0000 0.0000 10.0000
v 10.0000 7.6721 3.5444
v 3.6270 0.0000 -10.0000
v 10.0000 0.0000 -10.0000
v 3.6270 7.6842 -10.0000
v -10.0000 0.0000 -3.6317
v -10.0000 0.0000 -10.0000
v -10.0000 7.7028 -3.6317
v 10.0000 0.0000 -3.6507
v 10.0000 7.6721 -3.6507
v -3.5401 0.0000 -10.0000
v -3.5401 7.6842 -10.0000
v -10.0000 0.0000 10.0000
v -10.0000 0.0000 3.5386
v -10.0000 7.7028 3.5386
v -8.6007 1.3993 10.0000
v 8.6007 1.3993 10.0000
v 8.6007 8.6007 10.0000
v -8.6007 8.6007 10.0000
# 24 vertices
vn 0.0000 -1.0000 -0.0000
vn -1.0000 -0.0000 0.0000
vn -1.0000 0.0000 0.0000
vn -1.0000 0.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn -0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn 0.0000 -0.0000 -1.0000
vn 0.0000 0.0000 -1.0000
vn -0.0000 0.0000 -1.0000
vn 0.0000 1.0000 -0.0000
# 13 vertex normals
vt -1.0000 -1.0000 1.0000
vt -1.0000 3.0000 1.0000
vt 3.0000 3.0000 1.0000
vt 3.0000 -1.0000 1.0000
vt 0.2911 0.0000 -0.5000
vt -1.0000 0.0000 -0.5000
vt -1.0000 2.0000 -0.5000
vt 0.2911 1.5344 -0.5000
vt 0.2746 0.0000 -0.5000
vt 0.2746 1.5368 -0.5000
vt 0.2737 0.0000 1.5000
vt -1.0000 0.0000 1.5000
vt -1.0000 2.0000 1.5000
vt 0.2737 1.5406 1.5000
vt 3.0000 0.0000 -0.5000
vt 1.7301 0.0000 -0.5000
vt 1.7301 1.5344 -0.5000
vt 3.0000 2.0000 -0.5000
vt 1.7080 0.0000 -0.5000
vt 1.7080 1.5368 -0.5000
vt 3.0000 0.0000 1.5000
vt 1.7077 0.0000 1.5000
vt 1.7077 1.5406 1.5000
vt 3.0000 2.0000 1.5000
vt -0.7201 0.2799 1.5000
vt 2.7201 0.2799 1.5000
vt 2.7201 1.7201 1.5000
vt -0.7201 1.7201 1.5000
vt -1.0000 3.0000 0.0000
vt 3.0000 3.0000 0.0000
vt 3.0000 1.7089 0.0000
vt 3.0000 0.2699 0.0000
vt 3.0000 -1.0000 0.0000
vt 1.7254 -1.0000 0.0000
vt 0.2920 -1.0000 0.0000
vt -1.0000 -1.0000 0.0000
vt -1.0000 0.2737 0.0000
vt -1.0000 1.7077 0.0000
# 38 texture coords
g MainRoom
usemtl MetalPanels
f 21/37/14 22/38/14 23/39/14
f 23/39/14 24/40/14 21/37/14
f 25/41/15 26/42/16 22/43/17
f 22/43/17 27/44/17 25/41/15
f 28/45/18 29/42/18 21/43/19
f 21/43/19 30/46/19 28/45/18
f 31/47/20 32/48/20 24/49/20
f 24/49/20 33/50/20 31/47/20
f 29/51/15 34/52/15 35/53/21
f 35/53/21 21/54/21 29/51/15
f 21/54/21 35/53/21 27/44/17
f 27/44/17 22/43/17 21/54/21
f 32/51/18 36/55/18 37/56/22
f 37/56/22 24/54/22 32/51/18
f 24/54/22 37/56/22 30/46/19
f 30/46/19 21/43/19 24/54/22
f 38/57/20 39/58/20 40/59/20
f 40/59/20 23/60/20 38/57/20
f 23/60/20 40/59/20 33/50/20
f 33/50/20 24/49/20 23/60/20
f 38/48/23 41/61/23 42/62/23
f 42/62/24 26/57/24 38/48/24
f 26/57/25 42/62/25 43/63/25
f 43/63/25 22/60/25 26/57/25
f 22/60/23 43/63/23 44/64/23
f 44/64/24 23/49/24 22/60/24
f 23/49/25 44/64/25 41/61/25
f 41/61/25 38/48/25 23/49/25
f 38/65/26 26/66/26 25/67/26
f 38/65/26 25/67/26 34/68/26
f 38/65/26 34/68/26 29/69/26
f 38/65/26 29/69/26 28/70/26
f 38/65/26 28/70/26 36/71/26
f 36/71/26 32/72/26 31/73/26
f 36/71/26 31/73/26 39/74/26
f 38/65/26 36/71/26 39/74/26
# 36 faces
#
# object Room1
#
v -10.0000 0.0000 -35.0000
v -10.0000 0.0000 -15.0000
v -3.5444 0.0000 -15.0000
v 3.6507 0.0000 -15.0000
v 10.0000 0.0000 -15.0000
v 10.0000 0.0000 -35.0000
v 10.0000 10.0000 -15.0000
v -10.0000 10.0000 -15.0000
v -10.0000 10.0000 -35.0000
v 10.0000 10.0000 -35.0000
v -3.5444 7.7103 -15.0080
v 3.6507 7.7103 -15.0080
# 12 vertices
vn 0.0000 1.0000 -0.0000
vn 0.0000 -1.0000 -0.0000
vn 1.0000 0.0000 0.0000
vn -1.0000 0.0000 -0.0000
vn -0.0000 0.0000 1.0000
vn -0.0007 0.0003 -1.0000
vn -0.0007 0.0017 -1.0000
vn -0.0006 -0.0004 -1.0000
vn -0.0000 0.0000 -1.0000
vn 0.0003 0.0019 -1.0000
vn 0.0010 0.0008 -1.0000
vn 0.0006 -0.0006 -1.0000
vn -0.0000 -0.0010 -1.0000
# 13 vertex normals
vt -6.0000 -6.0000 0.5000
vt -6.0000 -2.0000 0.5000
vt -4.7089 -2.0000 0.5000
vt -3.2699 -2.0000 0.5000
vt -2.0000 -2.0000 0.5000
vt -2.0000 -6.0000 0.5000
vt 4.0000 -2.0000 1.5000
vt 8.0000 -2.0000 1.5000
vt 8.0000 -6.0000 1.5000
vt 4.0000 -6.0000 1.5000
vt -2.0000 3.0000 4.0000
vt -2.0000 1.0000 4.0000
vt -6.0000 1.0000 4.0000
vt -6.0000 3.0000 4.0000
vt 8.0000 3.0000 2.0000
vt 8.0000 1.0000 2.0000
vt 4.0000 1.0000 2.0000
vt 4.0000 3.0000 2.0000
vt 8.0000 3.0000 -3.0000
vt 8.0000 1.0000 -3.0000
vt 4.0000 1.0000 -3.0000
vt 4.0000 3.0000 -3.0000
vt -6.0000 3.0000 -1.0000
vt -4.7089 2.5421 -1.0008
vt -4.7089 1.0000 -1.0000
vt -6.0000 1.0000 -1.0000
vt -3.2699 2.5421 -1.0008
vt -2.0000 3.0000 -1.0000
vt -2.0000 1.0000 -1.0000
vt -3.2699 1.0000 -1.0000
# 30 texture coords
g Room1
usemtl MetalPanels
f 45/75/27 46/76/27 47/77/27
f 45/75/27 47/77/27 48/78/27
f 45/75/27 48/78/27 49/79/27
f 45/75/27 49/79/27 50/80/27
f 51/81/28 52/82/28 53/83/28
f 53/83/28 54/84/28 51/81/28
f 52/85/29 46/86/29 45/87/29
f 45/87/29 53/88/29 52/85/29
f 54/89/30 50/90/30 49/91/30
f 49/91/30 51/92/30 54/89/30
f 53/93/31 45/94/31 50/95/31
f 50/95/31 54/96/31 53/93/31
f 52/97/32 55/98/33 47/99/34
f 47/99/34 46/100/35 52/97/32
f 56/101/36 51/102/37 49/103/38
f 49/103/38 48/104/39 56/101/36
f 55/98/33 52/97/32 51/102/37
f 51/102/37 56/101/36 55/98/33
# 18 faces
#
# object Room2
#
v -35.0000 0.0000 10.0000
v -15.0000 0.0000 10.0000
v -15.0000 0.0000 3.5444
v -15.0000 0.0000 -3.6507
v -15.0000 0.0000 -10.0000
v -35.0000 0.0000 -10.0000
v -35.0000 10.0000 10.0000
v -35.0000 10.0000 -10.0000
v -15.0000 10.0000 -10.0000
v -15.0000 10.0000 10.0000
v -15.0000 7.6721 3.5444
v -15.0000 7.6721 -3.6507
# 12 vertices
vn 0.0000 1.0000 -0.0000
vn 0.0000 -1.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 0.0000
vn -0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
# 6 vertex normals
vt -1.0000 3.0000 0.0000
vt 3.0000 3.0000 0.0000
vt 3.0000 1.7089 0.0000
vt 3.0000 0.2699 0.0000
vt 3.0000 -1.0000 0.0000
vt -1.0000 -1.0000 0.0000
vt 3.0000 3.0000 1.0000
vt 3.0000 -1.0000 1.0000
vt -1.0000 -1.0000 1.0000
vt -1.0000 3.0000 1.0000
vt -1.0000 0.0000 1.5000
vt -1.0000 2.0000 1.5000
vt 3.0000 2.0000 1.5000
vt 3.0000 0.0000 1.5000
vt 0.2911 0.0000 -0.5000
vt -1.0000 0.0000 -0.5000
vt -1.0000 2.0000 -0.5000
vt 0.2911 1.5344 -0.5000
vt 3.0000 2.0000 -0.5000
vt 3.0000 0.0000 -0.5000
vt 1.7301 0.0000 -0.5000
vt 1.7301 1.5344 -0.5000
# 22 texture coords
g Room2
usemtl MetalPanels
f 57/105/40 58/106/40 59/107/40
f 57/105/40 59/107/40 60/108/40
f 57/105/40 60/108/40 61/109/40
f 57/105/40 61/109/40 62/110/40
f 63/111/41 64/112/41 65/113/41
f 65/113/41 66/114/41 63/111/41
f 57/115/42 63/116/42 66/117/42
f 66/117/42 58/118/42 57/115/42
f 59/119/43 58/120/43 66/121/43
f 66/121/43 67/122/43 59/119/43
f 61/120/44 65/121/44 64/123/44
f 64/123/44 62/124/44 61/120/44
f 62/115/45 64/116/45 63/117/45
f 63/117/45 57/118/45 62/115/45
f 61/124/43 60/125/43 68/126/43
f 68/126/43 65/123/43 61/124/43
f 65/123/43 68/126/43 67/122/43
f 67/122/43 66/121/43 65/123/43
# 18 faces
#
# object Room3
#
v 35.0000 0.0000 -10.0000
v 15.0000 0.0000 -10.0000
v 15.0000 0.0000 -3.5444
v 15.0000 0.0000 3.6507
v 15.0000 0.0000 10.0000
v 35.0000 0.0000 10.0000
v 35.0000 10.0000 -10.0000
v 35.0000 10.0000 10.0000
v 15.0000 10.0000 10.0000
v 15.0000 10.0000 -10.0000
v 15.0000 7.6721 -3.5444
v 15.0000 7.6721 3.6507
# 12 vertices
vn 0.0000 1.0000 -0.0000
vn 0.0000 -1.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn -0.0000 0.0000 -1.0000
vn -1.0000 0.0000 0.0000
# 6 vertex normals
vt 3.0000 -1.0000 0.0000
vt -1.0000 -1.0000 0.0000
vt -1.0000 0.2911 0.0000
vt -1.0000 1.7301 0.0000
vt -1.0000 3.0000 0.0000
vt 3.0000 3.0000 0.0000
vt -1.0000 -1.0000 1.0000
vt -1.0000 3.0000 1.0000
vt 3.0000 3.0000 1.0000
vt 3.0000 -1.0000 1.0000
vt -1.0000 0.0000 -0.5000
vt -1.0000 2.0000 -0.5000
vt 3.0000 2.0000 -0.5000
vt 3.0000 0.0000 -0.5000
vt 0.2911 0.0000 1.5000
vt -1.0000 0.0000 1.5000
vt -1.0000 2.0000 1.5000
vt 0.2911 1.5344 1.5000
vt 3.0000 2.0000 1.5000
vt 3.0000 0.0000 1.5000
vt 1.7301 0.0000 1.5000
vt 1.7301 1.5344 1.5000
# 22 texture coords
g Room3
usemtl MetalPanels
f 69/127/46 70/128/46 71/129/46
f 69/127/46 71/129/46 72/130/46
f 69/127/46 72/130/46 73/131/46
f 69/127/46 73/131/46 74/132/46
f 75/133/47 76/134/47 77/135/47
f 77/135/47 78/136/47 75/133/47
f 69/137/48 75/138/48 78/139/48
f 78/139/48 70/140/48 69/137/48
f 71/141/49 70/142/49 78/143/49
f 78/143/49 79/144/49 71/141/49
f 73/142/50 77/143/50 76/145/50
f 76/145/50 74/146/50 73/142/50
f 74/137/51 76/138/51 75/139/51
f 75/139/51 69/140/51 74/137/51
f 73/146/49 72/147/49 80/148/49
f 80/148/49 77/145/49 73/146/49
f 77/145/49 80/148/49 79/144/49
f 79/144/49 78/143/49 77/145/49
# 18 faces
#
# object DoorWay2
#
v 3.6270 7.6842 -10.0000
v -3.5401 7.6842 -10.0000
v -3.5444 7.7103 -15.0080
v 3.6507 7.7103 -15.0080
v -3.5401 0.0000 -10.0000
v -3.5444 0.0000 -15.0000
v 3.6507 0.0000 -15.0000
v 3.6270 0.0000 -10.0000
# 8 vertices
vn 0.0000 -1.0000 -0.0052
vn 1.0000 0.0000 -0.0009
vn 1.0000 -0.0000 -0.0009
vn 0.0000 1.0000 -0.0000
vn -1.0000 0.0000 -0.0047
vn -1.0000 -0.0000 -0.0047
# 6 vertex normals
vt 0.2852 1.5008 0.8829
vt 1.7187 1.5008 0.8829
vt 1.7195 0.4992 0.8855
vt 0.2805 0.4992 0.8855
vt 1.5008 1.7658 0.8593
vt 1.5008 0.2290 0.8593
vt 0.5008 0.2290 0.8598
vt 0.4992 1.7710 0.8598
vt 1.7195 0.5008 0.1145
vt 0.2805 0.5008 0.1145
vt 0.2813 1.5008 0.1145
vt 1.7148 1.5008 0.1145
vt 0.4992 0.2290 0.1426
vt 0.4992 1.7658 0.1426
vt 1.5008 1.7710 0.1402
vt 1.4992 0.2290 0.1402
# 16 texture coords
g DoorWay2
usemtl MetalPanels
f 81/149/52 82/150/52 83/151/52
f 83/151/52 84/152/52 81/149/52
f 82/153/53 85/154/53 86/155/53
f 86/155/54 83/156/54 82/153/54
f 87/157/55 86/158/55 85/159/55
f 85/159/55 88/160/55 87/157/55
f 88/161/56 81/162/56 84/163/56
f 84/163/57 87/164/57 88/161/57
# 8 faces
spaceCompound.mtl:
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 26.07.2011 13:47:43
newmtl Window
Ns 32
Ni 1.5000
d 0.8000
Tr 0.2000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.0000 0.0000 0.0100
Kd 0.0000 0.0000 0.0100
Ks 0.3500 0.3500 0.3500
Ke 0.0000 0.0000 0.0000
newmtl MetalPanels
Ns 10.0000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
map_Ka metalpanel.jpg
map_Kd metalpanel.jpg
map_bump metalpanelnormals.jpg
效果文件:
struct Light
{
float3 pos;
float range;
float3 dir;
float cone;
float3 att;
float4 ambient;
float4 diffuse;
};
cbuffer cbPerFrame
{
Light light;
};
cbuffer cbPerObject
{
float4x4 WVP;
float4x4 World;
float4 difColor;
bool hasTexture;
};
Texture2D ObjTexture;
SamplerState ObjSamplerState;
TextureCube SkyMap;
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float4 worldPos : POSITION;
float2 TexCoord : TEXCOORD;
float3 normal : NORMAL;
};
struct SKYMAP_VS_OUTPUT //output structure for skymap vertex shader
{
float4 Pos : SV_POSITION;
float3 texCoord : TEXCOORD;
};
VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL)
{
VS_OUTPUT output;
output.Pos = mul(inPos, WVP);
output.worldPos = mul(inPos, World);
output.normal = mul(normal, World);
output.TexCoord = inTexCoord;
return output;
}
SKYMAP_VS_OUTPUT SKYMAP_VS(float3 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL)
{
SKYMAP_VS_OUTPUT output = (SKYMAP_VS_OUTPUT)0;
//Set Pos to xyww instead of xyzw, so that z will always be 1 (furthest from camera)
output.Pos = mul(float4(inPos, 1.0f), WVP).xyww;
output.texCoord = inPos;
return output;
}
float4 PS(VS_OUTPUT input) : SV_TARGET
{
input.normal = normalize(input.normal);
//Set diffuse color of material
float4 diffuse = difColor;
//If material has a diffuse texture map, set it now
if(hasTexture == true)
diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord );
float3 finalColor = float3(0.0f, 0.0f, 0.0f);
//Create the vector between light position and pixels position
float3 lightToPixelVec = light.pos - input.worldPos;
//Find the distance between the light pos and pixel pos
float d = length(lightToPixelVec);
//Add the ambient light
float3 finalAmbient = diffuse * light.ambient;
//If pixel is too far, return pixel color with ambient light
if( d > light.range )
return float4(finalAmbient, diffuse.a);
//Turn lightToPixelVec into a unit length vector describing
//the pixels direction from the lights position
lightToPixelVec /= d;
//Calculate how much light the pixel gets by the angle
//in which the light strikes the pixels surface
float howMuchLight = dot(lightToPixelVec, input.normal);
//If light is striking the front side of the pixel
if( howMuchLight > 0.0f )
{
//Add light to the finalColor of the pixel
finalColor += diffuse * light.diffuse;
//Calculate Light's Distance Falloff factor
finalColor /= (light.att[0] + (light.att[1] * d)) + (light.att[2] * (d*d));
//Calculate falloff from center to edge of pointlight cone
finalColor *= pow(max(dot(-lightToPixelVec, light.dir), 0.0f), light.cone);
}
//make sure the values are between 1 and 0, and add the ambient
finalColor = saturate(finalColor + finalAmbient);
//Return Final Color
return float4(finalColor, diffuse.a);
}
float4 SKYMAP_PS(SKYMAP_VS_OUTPUT input) : SV_Target
{
return SkyMap.Sample(ObjSamplerState, input.texCoord);
}
float4 D2D_PS(VS_OUTPUT input) : SV_TARGET
{
float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord );
return diffuse;
}
代码实例:
#include "stdafx.h"
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dx11.lib")
#pragma comment(lib, "d3dx10.lib")
#pragma comment(lib, "D3D10_1.lib")
#pragma comment(lib, "DXGI.lib")
#pragma comment(lib, "D2D1.lib")
#pragma comment(lib, "dwrite.lib")
///////////////**************new**************////////////////////
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")
///////////////**************new**************////////////////////
#include
#include "Resource.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
///////////////**************new**************////////////////////
#include
///////////////**************new**************////////////////////
///////////////**************new**************////////////////////
#include
#include
#include
///////////////**************new**************////////////////////
//全局描述符
IDXGISwapChain* SwapChain;
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11RenderTargetView* renderTargetView;
//索引缓冲
ID3D11Buffer* squareIndexBuffer;
//深度值-20170927
ID3D11DepthStencilView* depthStencilView;
ID3D11Texture2D* depthStencilBuffer;
//着色器
ID3D11Buffer* squareVertBuffer;
ID3D11VertexShader* VS;
ID3D11PixelShader* PS;
ID3D11PixelShader* D2D_PS;
ID3D10Blob* VS_Buffer;
ID3D10Blob* PS_Buffer;
ID3D10Blob* D2D_PS_Buffer;
ID3D11InputLayout* vertLayout;
///
ID3D11Buffer* cbPerObjectBuffer;
ID3D11BlendState* d2dTransparency;
ID3D11RasterizerState* CCWcullMode;
ID3D11RasterizerState* CWcullMode;
ID3D11ShaderResourceView* CubesTexture;
ID3D11SamplerState* CubesTexSamplerState;
ID3D11Buffer* cbPerFrameBuffer;
ID3D10Device1 *d3d101Device;
IDXGIKeyedMutex *keyedMutex11;
IDXGIKeyedMutex *keyedMutex10;
ID2D1RenderTarget *D2DRenderTarget;
ID2D1SolidColorBrush *Brush;
ID3D11Texture2D *BackBuffer11;
ID3D11Texture2D *sharedTex11;
ID3D11Buffer *d2dVertBuffer;
ID3D11Buffer *d2dIndexBuffer;
ID3D11ShaderResourceView *d2dTexture;
IDWriteFactory *DWriteFactory;
IDWriteTextFormat *TextFormat;
///////////////**************new**************////////////////////
IDirectInputDevice8* DIKeyboard;
IDirectInputDevice8* DIMouse;
///////////////**************new**************////////////////////
ID3D11Buffer* sphereIndexBuffer;
ID3D11Buffer* sphereVertBuffer;
ID3D11VertexShader* SKYMAP_VS;
ID3D11PixelShader* SKYMAP_PS;
ID3D10Blob* SKYMAP_VS_Buffer;
ID3D10Blob* SKYMAP_PS_Buffer;
ID3D11ShaderResourceView* smrv;
ID3D11DepthStencilState* DSLessEqual;
ID3D11RasterizerState* RSCullNone;
///////////////**************new**************////////////////////
ID3D11BlendState* Transparency;
//网格变量,每个被加载的网格需要它自己的集
ID3D11Buffer* meshVertBuff;
ID3D11Buffer* meshIndexBuff;
XMMATRIX meshWorld;
int meshSubsets = 0;
std::vector meshSubsetIndexStart;
std::vector meshSubsetTexture;
//纹理和材质变量,用于所有的网格的加载
std::vector meshSRV;
std::vector textureNameArray;
///////////////**************new**************////////////////////
std::wstring printText;
/////
LPCTSTR WndClassName = L"firstwindow";
HWND hwnd = NULL;
HRESULT hr;
const int Width = 1920; //设置宽
const int Height = 1200; // 设置高
///////////////**************new**************////////////////////
DIMOUSESTATE mouseLastState;
LPDIRECTINPUT8 DirectInput;
float rotx = 0;
float rotz = 0;
float scaleX = 1.0f;
float scaleY = 1.0f;
XMMATRIX Rotationx;
//XMMATRIX Rotationy;
XMMATRIX Rotationz;
XMMATRIX Rotationy;
///////////////**************new**************////////////////////
///四个空间以及相机属性
XMMATRIX WVP;
//立方体
XMMATRIX cube1World;
XMMATRIX cube2World;
//
//XMMATRIX World;
XMMATRIX camView;
XMMATRIX camProjection;
XMMATRIX d2dWorld;
XMVECTOR camPosition;
XMVECTOR camTarget;
XMVECTOR camUp;
///////////////**************new**************////////////////////
XMVECTOR DefaultForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f);
XMVECTOR DefaultRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f);
XMVECTOR camForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f);
XMVECTOR camRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f);
XMMATRIX camRotationMatrix;
XMMATRIX groundWorld;
float moveLeftRight = 0.0f;
float moveBackForward = 0.0f;
float camYaw = 0.0f;
float camPitch = 0.0f;
///////////////**************new**************////////////////////
int NumSphereVertices;
int NumSphereFaces;
XMMATRIX sphereWorld;
///////////////**************new**************////////////////////
XMMATRIX Rotation;
XMMATRIX Scale;
XMMATRIX Translation;
float rot = 0.01f;
///////////////**************new**************////////////////////
double countsPerSecond = 0.0;
__int64 CounterStart = 0;
int frameCount = 0;
int fps = 0;
__int64 frameTimeOld = 0;
double frameTime;
///////////////**************new**************////////////////////
//Function Prototypes//
bool InitializeDirect3d11App(HINSTANCE hInstance);
void CleanUp();
bool InitScene();
void DrawScene();
bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter);
void InitD2DScreenTexture();
///////////////**************new**************////////////////////
void UpdateScene(double time);
///////////////**************new**************////////////////////
void UpdateCamera();
void RenderText(std::wstring text, int inInt);
//void RenderText(std::wstring text);
void StartTimer();
double GetTime();
double GetFrameTime();
// 初始化窗口
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed);
//初始化消息循环函数
int messageloop();
//初始化窗口回调过程。Windows API是事件驱动型的编程模型。在该函数中捕获Windows消息,比如一个按键按下(也叫事件)以及程序操作流程。
///////////////**************new**************////////////////////
bool InitDirectInput(HINSTANCE hInstance);
void DetectInput(double time);
///////////////**************new**************////////////////////
void CreateSphere(int LatLines, int LongLines);
LRESULT CALLBACK WndProc(HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
///new
//创建效果常量缓冲的结构体
struct cbPerObject
{
XMMATRIX WVP;
XMMATRIX World;
///////////////**************new**************////////////////////
//用于像素着色器
XMFLOAT4 difColor;
bool hasTexture;
///////////////**************new**************////////////////////
};
cbPerObject cbPerObj;
///////////////**************new**************////////////////////
//创建材质结构体
struct SurfaceMaterial
{
std::wstring matName;
XMFLOAT4 difColor;
int texArrayIndex;
bool hasTexture;
bool transparent;
};
std::vector material;
//自创建surfaceMaterial结构体后,定义函数LoadObjModel
bool LoadObjModel(std::wstring filename, //.obj filename
ID3D11Buffer** vertBuff, //mesh vertex buffer
ID3D11Buffer** indexBuff, //mesh index buffer
std::vector& subsetIndexStart, //start index of each subset
std::vector& subsetMaterialArray, //index value of material for each subset
std::vector& material, //vector of material structures
int& subsetCount, //Number of subsets in mesh
bool isRHCoordSys, //true if model was created in right hand coord system
bool computeNormals); //true to compute the normals, false to use the files normals
///////////////**************new**************////////////////////
struct Light
{
Light()
{
ZeroMemory(this, sizeof(Light));
}
XMFLOAT3 pos;
float range;
XMFLOAT3 dir;
float cone;
XMFLOAT3 att;
float pad2;
XMFLOAT4 ambient;
XMFLOAT4 diffuse;
};
Light light;
struct cbPerFrame
{
Light light;
};
cbPerFrame constbuffPerFrame;
//顶点结构体以及顶点布局(输入布局)
struct Vertex
{
Vertex(){}
Vertex(float x, float y, float z,
float u, float v,
float nx, float ny, float nz)
:pos(x, y, z), texCoord(u, v), normal(nx, ny, nz){}
XMFLOAT3 pos;
XMFLOAT2 texCoord;
XMFLOAT3 normal;
};
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
UINT numElements = ARRAYSIZE(layout);
//主函数,传入应用程序句柄hInstance,前一个应用程序句柄hPrevInstance,传给函数处理的命令行lpCmdLine以及窗口显示方式的nShowCmd
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
//创建并注册窗口
if (!InitializeWindow(hInstance, nShowCmd, Width, Height, true))
{
MessageBox(0, L"Window Initialization - Failed",
L"Error", MB_OK);
return 0;
}
/////new
if (!InitializeDirect3d11App(hInstance)) // 初始化D3D
{
MessageBox(0, L"Direct3D Initialization - Failed",
L"Error", MB_OK);
return 0;
}
if(!InitScene()) //Initialize our scene
{
MessageBox(0, L"Scene Initialization - Failed",
L"Error", MB_OK);
return 0;
}
///////////////**************new**************////////////////////
if(!InitDirectInput(hInstance))
{
MessageBox(0, L"Direct Input Initialization - Failed",
L"Error", MB_OK);
return 0;
}
///////////////**************new**************////////////////////
messageloop();
CleanUp();
//ReleaseObjects();
return 0;
}
// windowed 若为true则为窗口模式显示,若为false则为全屏模式显示
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
int width, int height,
bool windowed)
{
/*typedef struct _WNDCLASS{
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
}WNDCLASS;
*/
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX); //window类的大小
/********windows类风格
*CS_CLASSDC 一个使用该类创建的在所有窗口间共享的设备上下文
*CS_DBLCLKS 在窗口上使能双击功能
*CS_HREDRAW 若窗口的宽度有改变或者窗口水平地移动,窗口将会刷新
*CS_NOCLOSE 窗口菜单上禁止关闭选项
*CS_OWNDC 为每个窗口创建自己的设备上下文。正好与CS_CLASSDC相反
*CS_PARENTDC 这会设置创建的子窗口的剪裁四边形到父窗口,这允许子窗口能够在父窗口上绘画
*CS_VERDRAW 若在窗口的高度或窗口在垂直方向有移动窗口会重绘
**/
wc.style = CS_HREDRAW | CS_VREDRAW;
//lpfnWndProc是一个指向处理窗口消息函数的指针,设置窗口处理函数的函数名WndProc
wc.lpfnWndProc = WndProc;
//cbClsExtra是WNDCLASSEX之后额外申请的字节数
wc.cbClsExtra = NULL;
//cbWndExtra指定窗口实例之后所申请的字节数
wc.cbWndExtra = NULL;
//当前窗口应用程序的句柄,通过给函数GetModuleHandle()函数第一个参数传入NULL可获取当前窗口应用程序。
wc.hInstance = hInstance;
//hIcon用来指定窗口标题栏左上角的图标。以下是一些标准图标:
/*
*IDI_APPLICATION 默认应用程序图标
*IDI_HAND 手形状的图标
*IDI_EXCLAMATION 感叹号图标
*IDI_INFORMATION 星号图标
*IDI_QUESTION 问号图标
*IDI_WINLOGO 若使用的是XP则是默认应用程序图标,否则是窗口logo
*/
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
/*定义光标图标
*IDC_APPSTARTING 标准箭头以及小型沙漏光标
*IDC_ARROW 标准箭头光标
*IDC_CROSS 十字线光标
*IDC_HAND 手型光标
*IDC_NO 斜线圈光标
*IDC_WAIT 沙漏光标
*/
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
//hbrBackground是一个刷子的句柄,可使得背景黑色。
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2);
//附加到窗口的菜单名字,不需要的话设置为NULL
wc.lpszMenuName = NULL;
//对类进行命名
wc.lpszClassName = WndClassName;
//指定任务栏的图标,使用上面的IDI_图标
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
//注册类。若失败则会获得一个错误,若成功,则继续创建窗口
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Error registering class",
L"Error", MB_OK | MB_ICONERROR);
return 1;
}
//创建窗口
hwnd = CreateWindowEx(
NULL,
WndClassName,
L"skybox",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
width,
height,
NULL,
NULL,
hInstance,
NULL
);
if (!hwnd)
{
MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR);
return 1;
}
//BOOL ShowWindow(HWND hWnd, int nCmdShow);
//BOOL UpdateWindow(HWND hWnd);
ShowWindow(hwnd, ShowWnd);
UpdateWindow(hwnd);// 发送WM_PAINT消息到窗口过程,若窗口客户区没有任何东西要显示,则不发送消息。返回true,继续运行到mainloop中去。
return true;
}
bool InitializeDirect3d11App(HINSTANCE hInstance)
{
//声明缓冲
DXGI_MODE_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
//声明交换链
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
///////////////**************new**************////////////////////
swapChainDesc.Windowed = true;
///////////////**************new**************////////////////////
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
//创建DXGI factory来枚举显卡
IDXGIFactory1 *DXGIFactory;
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)&DXGIFactory);
//使用第一个显卡
IDXGIAdapter1 *Adapter;
hr = DXGIFactory->EnumAdapters1(0, &Adapter);
DXGIFactory->Release();
//创建D3D11设备和交换链
//hr = D3D11C
//创建交换链
D3D11CreateDeviceAndSwapChain(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
//初始化D2D D3D10.1和DirectWrite
InitD2D_D3D101_DWrite(Adapter);
//释放Adapter接口
Adapter->Release();
//创建后缓冲
ID3D11Texture2D* BackBuffer;
SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&BackBuffer);
//创建渲染目标
d3d11Device->CreateRenderTargetView(BackBuffer, NULL, &renderTargetView);
BackBuffer->Release();
//创建深度模板缓冲
D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width = Width;
depthStencilDesc.Height = Height;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; //绑定到OM
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags = 0;
//创建深度模板视图
d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer);
d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView);
return true;
}
bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter)
{
//创建D3D101设备
hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, D3D10_CREATE_DEVICE_BGRA_SUPPORT,
D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device);
//创建共享纹理,D3D101将会渲染它
D3D11_TEXTURE2D_DESC sharedTexDesc;
ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc));
sharedTexDesc.Width = Width;
sharedTexDesc.Height = Height;
sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;// DXGI_FORMAT_R8G8B8A8_UNORM;// DXGI_FORMAT_B8G8R8A8_UNORM;
sharedTexDesc.MipLevels = 1;
sharedTexDesc.ArraySize = 1;
sharedTexDesc.SampleDesc.Count = 1;
sharedTexDesc.Usage = D3D11_USAGE_DEFAULT;
sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11);
//为共享纹理获取key互斥量(为D3D11)
hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **)&keyedMutex11);
//获取共享句柄需要在D3D10.1中打开共享纹理
IDXGIResource *sharedResource10;
HANDLE sharedHandle10;
hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void **)&sharedResource10);
hr = sharedResource10->GetSharedHandle(&sharedHandle10);
sharedResource10->Release();
//在D3D10.1中为共享纹理打开界面
IDXGISurface1 *sharedSurface10;
hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void **)(&sharedSurface10));
hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **)&keyedMutex10);
//创建D2D factory
ID2D1Factory *D2DFactory;
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void **)&D2DFactory);
D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;
ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties));
renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE;
renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED);
hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget);
sharedSurface10->Release();
D2DFactory->Release();
//创建立体彩色画笔绘制一些东西
hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), &Brush);
//DirectWrite
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
reinterpret_cast(&DWriteFactory));
hr = DWriteFactory->CreateTextFormat(
L"Script",
NULL,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
24.0f,
L"en-us",
&TextFormat
);
hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);
return true;
}
///////////////**************new**************////////////////////
bool InitDirectInput(HINSTANCE hInstance)
{
hr = DirectInput8Create(hInstance,
DIRECTINPUT_VERSION,
IID_IDirectInput8,
(void**)&DirectInput,
NULL);
hr = DirectInput->CreateDevice(GUID_SysKeyboard,
&DIKeyboard,
NULL);
hr = DirectInput->CreateDevice(GUID_SysMouse,
&DIMouse,
NULL);
hr = DIKeyboard->SetDataFormat(&c_dfDIKeyboard);
hr = DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
hr = DIMouse->SetDataFormat(&c_dfDIMouse);
hr = DIMouse->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND);
return true;
}
void UpdateCamera()
{
camRotationMatrix = XMMatrixRotationRollPitchYaw(camPitch, camYaw, 0);
camTarget = XMVector3TransformCoord(DefaultForward, camRotationMatrix );
camTarget = XMVector3Normalize(camTarget);
XMMATRIX RotateYTempMatrix;
RotateYTempMatrix = XMMatrixRotationY(camYaw);
camRight = XMVector3TransformCoord(DefaultRight, RotateYTempMatrix);
camUp = XMVector3TransformCoord(camUp, RotateYTempMatrix);
camForward = XMVector3TransformCoord(DefaultForward, RotateYTempMatrix);
camPosition += moveLeftRight*camRight;
camPosition += moveBackForward*camForward;
moveLeftRight = 0.0f;
moveBackForward = 0.0f;
camTarget = camPosition + camTarget;
camView = XMMatrixLookAtLH( camPosition, camTarget, camUp );
}
void DetectInput(double time)
{
DIMOUSESTATE mouseCurrState;
BYTE keyboardState[256];
DIKeyboard->Acquire();
DIMouse->Acquire();
DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState);
DIKeyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState);
if(keyboardState[DIK_ESCAPE] & 0x80)
PostMessage(hwnd, WM_DESTROY, 0, 0);
float speed = 15.0f * time;
if(keyboardState[DIK_A] & 0x80)
{
moveLeftRight -= speed;
}
if(keyboardState[DIK_D] & 0x80)
{
moveLeftRight += speed;
}
if(keyboardState[DIK_W] & 0x80)
{
moveBackForward += speed;
}
if(keyboardState[DIK_S] & 0x80)
{
moveBackForward -= speed;
}
if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY))
{
camYaw += mouseLastState.lX * 0.001f;
camPitch += mouseCurrState.lY * 0.001f;
mouseLastState = mouseCurrState;
}
UpdateCamera();
return;
}
///////////////**************new**************////////////////////
void CleanUp()
{
///////////////**************new**************////////////////////
SwapChain->SetFullscreenState(false, NULL);
PostMessage(hwnd, WM_DESTROY, 0, 0);
///////////////**************new**************////////////////////
SwapChain->Release();
d3d11Device->Release();
d3d11DevCon->Release();
renderTargetView->Release();
squareVertBuffer->Release();
squareIndexBuffer->Release();
//triangleVertBuffer->Release();
VS->Release();
PS->Release();
VS_Buffer->Release();
PS_Buffer->Release();
vertLayout->Release();
depthStencilView->Release();
depthStencilBuffer->Release();
//
cbPerObjectBuffer->Release();
//释放不裁剪对象
// noCull->Release();
//释放混合对象
#if 1
Transparency->Release();
CCWcullMode->Release();
CWcullMode->Release();
#endif
//释放线框
//WireFrame->Release();
d3d101Device->Release();
keyedMutex11->Release();
keyedMutex10->Release();
D2DRenderTarget->Release();
Brush->Release();
// BackBuffer11->Release();
sharedTex11->Release();
DWriteFactory->Release();
TextFormat->Release();
d2dTexture->Release();
/// new
cbPerFrameBuffer->Release();
///////////////**************new**************////////////////////
DIKeyboard->Unacquire();
DIMouse->Unacquire();
DirectInput->Release();
sphereIndexBuffer->Release();
sphereVertBuffer->Release();
SKYMAP_VS->Release();
SKYMAP_PS->Release();
SKYMAP_VS_Buffer->Release();
SKYMAP_PS_Buffer->Release();
smrv->Release();
DSLessEqual->Release();
RSCullNone->Release();
///////////////**************new**************////////////////////
meshVertBuff->Release();
meshIndexBuff->Release();
///////////////**************new**************////////////////////
}
///////////////**************new**************////////////////////
bool LoadObjModel(std::wstring filename,
ID3D11Buffer** vertBuff,
ID3D11Buffer** indexBuff,
std::vector& subsetIndexStart,
std::vector& subsetMaterialArray,
std::vector& material,
int& subsetCount,
bool isRHCoordSys,
bool computeNormals)
{
HRESULT hr = 0;
std::wifstream fileIn (filename.c_str()); //Open file
std::wstring meshMatLib; //String to hold our obj material library filename
//存储我们模型的信息的数组
std::vector indices;
std::vector vertPos;
std::vector vertNorm;
std::vector vertTexCoord;
std::vector meshMaterials;
//顶点定义索引
std::vector vertPosIndex;
std::vector vertNormIndex;
std::vector vertTCIndex;
//如果没有定义纹理坐标或发现,确保有一个默认的值
bool hasTexCoord = false;
bool hasNorm = false;
//用于存储向量的临时变量
std::wstring meshMaterialsTemp;
int vertPosIndexTemp;
int vertNormIndexTemp;
int vertTCIndexTemp;
wchar_t checkChar; //The variable we will use to store one char from file at a time
std::wstring face; //Holds the string containing our face vertices
int vIndex = 0; //Keep track of our vertex index count
int triangleCount = 0; //Total Triangles
int totalVerts = 0;
int meshTriangles = 0;
//检测文件是否被打开
if (fileIn)
{
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
case 'v': //获取向量描述符
checkChar = fileIn.get();
if(checkChar == ' ') //v - vert position
{
float vz, vy, vx;
fileIn >> vx >> vy >> vz; //Store the next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertPos.push_back(XMFLOAT3( vx, vy, vz * -1.0f)); //Invert the Z axis
else
vertPos.push_back(XMFLOAT3( vx, vy, vz));
}
if(checkChar == 't') //vt - vert tex coords
{
float vtcu, vtcv;
fileIn >> vtcu >> vtcv; //Store next two types
if(isRHCoordSys) //If model is from an RH Coord System
vertTexCoord.push_back(XMFLOAT2(vtcu, 1.0f-vtcv)); //Reverse the "v" axis
else
vertTexCoord.push_back(XMFLOAT2(vtcu, vtcv));
hasTexCoord = true; //We know the model uses texture coords
}
//由于我们在后来计算法线,我们不必在此检测法线
//In the file, but i'll do it here anyway
if(checkChar == 'n') //vn - vert normal
{
float vnx, vny, vnz;
fileIn >> vnx >> vny >> vnz; //Store next three types
if(isRHCoordSys) //If model is from an RH Coord System
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz * -1.0f )); //Invert the Z axis
else
vertNorm.push_back(XMFLOAT3( vnx, vny, vnz ));
hasNorm = true; //We know the model defines normals
}
break;
//新组(子集)
case 'g': //g - defines a group
checkChar = fileIn.get();
if(checkChar == ' ')
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
break;
//获取面索引
case 'f': //f - defines the faces
checkChar = fileIn.get();
if(checkChar == ' ')
{
face = L"";
std::wstring VertDef; //Holds one vertex definition at a time
triangleCount = 0;
checkChar = fileIn.get();
while(checkChar != '\n')
{
face += checkChar; //Add the char to our face string
checkChar = fileIn.get(); //Get the next Character
if(checkChar == ' ') //If its a space...
triangleCount++; //Increase our triangle count
}
//Check for space at the end of our face string
if(face[face.length()-1] == ' ')
triangleCount--; //Each space adds to our triangle count
triangleCount -= 1; //Ever vertex in the face AFTER the first two are new faces
std::wstringstream ss(face);
if(face.length() > 0)
{
int firstVIndex, lastVIndex; //Holds the first and last vertice's index
for(int i = 0; i < 3; ++i) //First three vertices (first triangle)
{
ss >> VertDef; //Get vertex definition (vPos/vTexCoord/vNorm)
std::wstring vertPart;
int whichPart = 0; //(vPos, vTexCoord, or vNorm)
//Parse this string
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/') //If there is no divider "/", add a char to our vertPart
vertPart += VertDef[j];
//If the current char is a divider "/", or its the last character in the string
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart); //Used to convert wstring to int
if(whichPart == 0) //If vPos
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertNormIndexTemp = 0;
vertTCIndexTemp = 0;
}
}
else if(whichPart == 1) //If vTexCoord
{
if(vertPart != L"") //Check to see if there even is a tex coord
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
else //If there is no tex coord, make a default
vertTCIndexTemp = 0;
//If the cur. char is the second to last in the string, then
//there must be no normal, so set a default normal
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2) //If vNorm
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1
}
vertPart = L""; //Get ready for next vertex part
whichPart++; //Move on to next vertex part
}
}
//Check to make sure there is at least one subset
if(subsetCount == 0)
{
subsetIndexStart.push_back(vIndex); //Start index for this subset
subsetCount++;
}
//Avoid duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
//Loop through all the vertices
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
//If the vertex position and texture coordinate in memory are the same
//As the vertex position and texture coordinate we just now got out
//of the obj file, we will set this faces vertex index to the vertex's
//index value in memory. This makes sure we don't create duplicate vertices
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
//If this vertex is not already in our vertex arrays, put it there
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //We created a new vertex
indices.push_back(totalVerts-1); //Set index for this vertex
}
//If this is the very first vertex in the face, we need to
//make sure the rest of the triangles use this vertex
if(i == 0)
{
firstVIndex = indices[vIndex]; //The first vertex index of this FACE
}
//If this was the last vertex in the first triangle, we will make sure
//the next triangle uses this one (eg. tri1(1,2,3) tri2(1,3,4) tri3(1,4,5))
if(i == 2)
{
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
}
vIndex++; //Increment index count
}
meshTriangles++; //One triangle down
//If there are more than three vertices in the face definition, we need to make sure
//we convert the face to triangles. We created our first triangle above, now we will
//create a new triangle for every new vertex in the face, using the very first vertex
//of the face, and the last vertex from the triangle before the current triangle
for(int l = 0; l < triangleCount-1; ++l) //Loop through the next vertices to create new triangles
{
//First vertex of this triangle (the very first vertex of the face too)
indices.push_back(firstVIndex); //Set index for this vertex
vIndex++;
//Second Vertex of this triangle (the last vertex used in the tri before this one)
indices.push_back(lastVIndex); //Set index for this vertex
vIndex++;
//Get the third vertex for this triangle
ss >> VertDef;
std::wstring vertPart;
int whichPart = 0;
//Parse this string (same as above)
for(int j = 0; j < VertDef.length(); ++j)
{
if(VertDef[j] != '/')
vertPart += VertDef[j];
if(VertDef[j] == '/' || j == VertDef.length()-1)
{
std::wistringstream wstringToInt(vertPart);
if(whichPart == 0)
{
wstringToInt >> vertPosIndexTemp;
vertPosIndexTemp -= 1;
//Check to see if the vert pos was the only thing specified
if(j == VertDef.length()-1)
{
vertTCIndexTemp = 0;
vertNormIndexTemp = 0;
}
}
else if(whichPart == 1)
{
if(vertPart != L"")
{
wstringToInt >> vertTCIndexTemp;
vertTCIndexTemp -= 1;
}
else
vertTCIndexTemp = 0;
if(j == VertDef.length()-1)
vertNormIndexTemp = 0;
}
else if(whichPart == 2)
{
std::wistringstream wstringToInt(vertPart);
wstringToInt >> vertNormIndexTemp;
vertNormIndexTemp -= 1;
}
vertPart = L"";
whichPart++;
}
}
//Check for duplicate vertices
bool vertAlreadyExists = false;
if(totalVerts >= 3) //Make sure we at least have one triangle to check
{
for(int iCheck = 0; iCheck < totalVerts; ++iCheck)
{
if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists)
{
if(vertTCIndexTemp == vertTCIndex[iCheck])
{
indices.push_back(iCheck); //Set index for this vertex
vertAlreadyExists = true; //If we've made it here, the vertex already exists
}
}
}
}
if(!vertAlreadyExists)
{
vertPosIndex.push_back(vertPosIndexTemp);
vertTCIndex.push_back(vertTCIndexTemp);
vertNormIndex.push_back(vertNormIndexTemp);
totalVerts++; //New vertex created, add to total verts
indices.push_back(totalVerts-1); //Set index for this vertex
}
//Set the second vertex for the next triangle to the last vertex we got
lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE
meshTriangles++; //New triangle defined
vIndex++;
}
}
}
break;
case 'm': //mtllib - material library filename
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == 'i')
{
checkChar = fileIn.get();
if(checkChar == 'b')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//Store the material libraries file name
fileIn >> meshMatLib;
}
}
}
}
}
}
break;
case 'u': //usemtl - which material to use
checkChar = fileIn.get();
if(checkChar == 's')
{
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
meshMaterialsTemp = L""; //Make sure this is cleared
fileIn >> meshMaterialsTemp; //Get next type (string)
meshMaterials.push_back(meshMaterialsTemp);
}
}
}
}
}
}
break;
default:
break;
}
}
}
else //If we could not open the file
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
//create message
std::wstring message = L"Could not open: ";
message += filename;
MessageBox(0, message.c_str(), //display message
L"Error", MB_OK);
return false;
}
subsetIndexStart.push_back(vIndex); //There won't be another index start after our last subset, so set it here
//sometimes "g" is defined at the very top of the file, then again before the first group of faces.
//This makes sure the first subset does not conatain "0" indices.
if(subsetIndexStart[1] == 0)
{
subsetIndexStart.erase(subsetIndexStart.begin()+1);
meshSubsets--;
}
//Make sure we have a default for the tex coord and normal
//if one or both are not specified
if(!hasNorm)
vertNorm.push_back(XMFLOAT3(0.0f, 0.0f, 0.0f));
if(!hasTexCoord)
vertTexCoord.push_back(XMFLOAT2(0.0f, 0.0f));
//Close the obj file, and open the mtl file
fileIn.close();
fileIn.open(meshMatLib.c_str());
std::wstring lastStringRead;
int matCount = material.size(); //total materials
//kdset - 若没有设置漫反射颜色,则使用环境光颜色(通常是一样的)
//If the diffuse color WAS set, then we don't need to set our diffuse color to ambient
bool kdset = false;
if (fileIn)
{
while(fileIn)
{
checkChar = fileIn.get(); //Get next char
switch (checkChar)
{
//Check for comment
case '#':
checkChar = fileIn.get();
while(checkChar != '\n')
checkChar = fileIn.get();
break;
//Set diffuse color
case 'K':
checkChar = fileIn.get();
if(checkChar == 'd') //Diffuse Color
{
checkChar = fileIn.get(); //remove space
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
kdset = true;
}
//Ambient Color (We'll store it in diffuse if there isn't a diffuse already)
if(checkChar == 'a')
{
checkChar = fileIn.get(); //remove space
if(!kdset)
{
fileIn >> material[matCount-1].difColor.x;
fileIn >> material[matCount-1].difColor.y;
fileIn >> material[matCount-1].difColor.z;
}
}
break;
//Check for transparency
case 'T':
checkChar = fileIn.get();
if(checkChar == 'r')
{
checkChar = fileIn.get(); //remove space
float Transparency;
fileIn >> Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Some obj files specify d for transparency
case 'd':
checkChar = fileIn.get();
if(checkChar == ' ')
{
float Transparency;
fileIn >> Transparency;
//'d' - 0 being most transparent, and 1 being opaque, opposite of Tr
Transparency = 1.0f - Transparency;
material[matCount-1].difColor.w = Transparency;
if(Transparency > 0.0f)
material[matCount-1].transparent = true;
}
break;
//Get the diffuse map (texture)
case 'm':
checkChar = fileIn.get();
if(checkChar == 'a')
{
checkChar = fileIn.get();
if(checkChar == 'p')
{
checkChar = fileIn.get();
if(checkChar == '_')
{
//map_Kd - Diffuse map
checkChar = fileIn.get();
if(checkChar == 'K')
{
checkChar = fileIn.get();
if(checkChar == 'd')
{
std::wstring fileNamePath;
fileIn.get(); //Remove whitespace between map_Kd and file
//Get the file path - We read the pathname char by char since
//pathnames can sometimes contain spaces, so we will read until
//we find the file extension
bool texFilePathEnd = false;
while(!texFilePathEnd)
{
checkChar = fileIn.get();
fileNamePath += checkChar;
if(checkChar == '.')
{
for(int i = 0; i < 3; ++i)
fileNamePath += fileIn.get();
texFilePathEnd = true;
}
}
//check if this texture has already been loaded
bool alreadyLoaded = false;
for(int i = 0; i < textureNameArray.size(); ++i)
{
if(fileNamePath == textureNameArray[i])
{
alreadyLoaded = true;
material[matCount-1].texArrayIndex = i;
material[matCount-1].hasTexture = true;
}
}
//if the texture is not already loaded, load it now
if(!alreadyLoaded)
{
ID3D11ShaderResourceView* tempMeshSRV;
hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(),
NULL, NULL, &tempMeshSRV, NULL );
if(SUCCEEDED(hr))
{
textureNameArray.push_back(fileNamePath.c_str());
material[matCount-1].texArrayIndex = meshSRV.size();
meshSRV.push_back(tempMeshSRV);
material[matCount-1].hasTexture = true;
}
}
}
}
//map_d - alpha map
else if(checkChar == 'd')
{
//Alpha maps are usually the same as the diffuse map
//So we will assume that for now by only enabling
//transparency for this material, as we will already
//be using the alpha channel in the diffuse map
material[matCount-1].transparent = true;
}
}
}
}
break;
case 'n': //newmtl - Declare new material
checkChar = fileIn.get();
if(checkChar == 'e')
{
checkChar = fileIn.get();
if(checkChar == 'w')
{
checkChar = fileIn.get();
if(checkChar == 'm')
{
checkChar = fileIn.get();
if(checkChar == 't')
{
checkChar = fileIn.get();
if(checkChar == 'l')
{
checkChar = fileIn.get();
if(checkChar == ' ')
{
//New material, set its defaults
SurfaceMaterial tempMat;
material.push_back(tempMat);
fileIn >> material[matCount].matName;
material[matCount].transparent = false;
material[matCount].hasTexture = false;
material[matCount].texArrayIndex = 0;
matCount++;
kdset = false;
}
}
}
}
}
}
break;
default:
break;
}
}
}
else
{
SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen
std::wstring message = L"Could not open: ";
message += meshMatLib;
MessageBox(0, message.c_str(),
L"Error", MB_OK);
return false;
}
//Set the subsets material to the index value
//of the its material in our material array
for(int i = 0; i < meshSubsets; ++i)
{
bool hasMat = false;
for(int j = 0; j < material.size(); ++j)
{
if(meshMaterials[i] == material[j].matName)
{
subsetMaterialArray.push_back(j);
hasMat = true;
}
}
if(!hasMat)
subsetMaterialArray.push_back(0); //Use first material in array
}
std::vector vertices;
Vertex tempVert;
//Create our vertices using the information we got
//from the file and store them in a vector
for(int j = 0 ; j < totalVerts; ++j)
{
tempVert.pos = vertPos[vertPosIndex[j]];
tempVert.normal = vertNorm[vertNormIndex[j]];
tempVert.texCoord = vertTexCoord[vertTCIndex[j]];
vertices.push_back(tempVert);
}
//////////////////////Compute Normals///////////////////////////
//If computeNormals was set to true then we will create our own
//normals, if it was set to false we will use the obj files normals
if(computeNormals)
{
std::vector tempNormal;
//normalized and unnormalized normals
XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f);
//Used to get vectors (sides) from the position of the verts
float vecX, vecY, vecZ;
//Two edges of our triangle
XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
//Compute face normals
for(int i = 0; i < meshTriangles; ++i)
{
//Get the vector describing one edge of our triangle (edge 0,2)
vecX = vertices[indices[(i*3)]].pos.x - vertices[indices[(i*3)+2]].pos.x;
vecY = vertices[indices[(i*3)]].pos.y - vertices[indices[(i*3)+2]].pos.y;
vecZ = vertices[indices[(i*3)]].pos.z - vertices[indices[(i*3)+2]].pos.z;
edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge
//Get the vector describing another edge of our triangle (edge 2,1)
vecX = vertices[indices[(i*3)+2]].pos.x - vertices[indices[(i*3)+1]].pos.x;
vecY = vertices[indices[(i*3)+2]].pos.y - vertices[indices[(i*3)+1]].pos.y;
vecZ = vertices[indices[(i*3)+2]].pos.z - vertices[indices[(i*3)+1]].pos.z;
edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge
//Cross multiply the two edge vectors to get the un-normalized face normal
XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2));
tempNormal.push_back(unnormalized); //Save unormalized normal (for normal averaging)
}
//Compute vertex normals (normal Averaging)
XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
int facesUsing = 0;
float tX;
float tY;
float tZ;
//Go through each vertex
for(int i = 0; i < totalVerts; ++i)
{
//Check which triangles use this vertex
for(int j = 0; j < meshTriangles; ++j)
{
if(indices[j*3] == i ||
indices[(j*3)+1] == i ||
indices[(j*3)+2] == i)
{
tX = XMVectorGetX(normalSum) + tempNormal[j].x;
tY = XMVectorGetY(normalSum) + tempNormal[j].y;
tZ = XMVectorGetZ(normalSum) + tempNormal[j].z;
normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum
facesUsing++;
}
}
//Get the actual normal by dividing the normalSum by the number of faces sharing the vertex
normalSum = normalSum / facesUsing;
//Normalize the normalSum vector
normalSum = XMVector3Normalize(normalSum);
//Store the normal in our current vertex
vertices[i].normal.x = XMVectorGetX(normalSum);
vertices[i].normal.y = XMVectorGetY(normalSum);
vertices[i].normal.z = XMVectorGetZ(normalSum);
//Clear normalSum and facesUsing for next vertex
normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
facesUsing = 0;
}
}
//Create index buffer
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * meshTriangles*3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = &indices[0];
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, indexBuff);
//Create Vertex Buffer
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * totalVerts;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
vertexBufferData.pSysMem = &vertices[0];
hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, vertBuff);
return true;
}
///////////////**************new**************////////////////////
void CreateSphere(int LatLines, int LongLines)
{
NumSphereVertices = ((LatLines-2) * LongLines) + 2;
NumSphereFaces = ((LatLines-3)*(LongLines)*2) + (LongLines*2);
float sphereYaw = 0.0f;
float spherePitch = 0.0f;
std::vector vertices(NumSphereVertices);
XMVECTOR currVertPos = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
vertices[0].pos.x = 0.0f;
vertices[0].pos.y = 0.0f;
vertices[0].pos.z = 1.0f;
for(DWORD i = 0; i < LatLines-2; ++i)
{
spherePitch = (i+1) * (3.14f/(LatLines-1));
Rotationx = XMMatrixRotationX(spherePitch);
for(DWORD j = 0; j < LongLines; ++j)
{
sphereYaw = j * (6.28f/(LongLines));
Rotationy = XMMatrixRotationZ(sphereYaw);
currVertPos = XMVector3TransformNormal( XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), (Rotationx * Rotationy) );
currVertPos = XMVector3Normalize( currVertPos );
vertices[i*LongLines+j+1].pos.x = XMVectorGetX(currVertPos);
vertices[i*LongLines+j+1].pos.y = XMVectorGetY(currVertPos);
vertices[i*LongLines+j+1].pos.z = XMVectorGetZ(currVertPos);
}
}
vertices[NumSphereVertices-1].pos.x = 0.0f;
vertices[NumSphereVertices-1].pos.y = 0.0f;
vertices[NumSphereVertices-1].pos.z = -1.0f;
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * NumSphereVertices;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
vertexBufferData.pSysMem = &vertices[0];
hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &sphereVertBuffer);
std::vector indices(NumSphereFaces * 3);
int k = 0;
for(DWORD l = 0; l < LongLines-1; ++l)
{
indices[k] = 0;
indices[k+1] = l+1;
indices[k+2] = l+2;
k += 3;
}
indices[k] = 0;
indices[k+1] = LongLines;
indices[k+2] = 1;
k += 3;
for(DWORD i = 0; i < LatLines-3; ++i)
{
for(DWORD j = 0; j < LongLines-1; ++j)
{
indices[k] = i*LongLines+j+1;
indices[k+1] = i*LongLines+j+2;
indices[k+2] = (i+1)*LongLines+j+1;
indices[k+3] = (i+1)*LongLines+j+1;
indices[k+4] = i*LongLines+j+2;
indices[k+5] = (i+1)*LongLines+j+2;
k += 6; // next quad
}
indices[k] = (i*LongLines)+LongLines;
indices[k+1] = (i*LongLines)+1;
indices[k+2] = ((i+1)*LongLines)+LongLines;
indices[k+3] = ((i+1)*LongLines)+LongLines;
indices[k+4] = (i*LongLines)+1;
indices[k+5] = ((i+1)*LongLines)+1;
k += 6;
}
for(DWORD l = 0; l < LongLines-1; ++l)
{
indices[k] = NumSphereVertices-1;
indices[k+1] = (NumSphereVertices-1)-(l+1);
indices[k+2] = (NumSphereVertices-1)-(l+2);
k += 3;
}
indices[k] = NumSphereVertices-1;
indices[k+1] = (NumSphereVertices-1)-LongLines;
indices[k+2] = NumSphereVertices-2;
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * NumSphereFaces * 3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = &indices[0];
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &sphereIndexBuffer);
}
void InitD2DScreenTexture()
{
//创建顶点缓冲
Vertex v[] =
{
//字体面
Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, -1.0f),
Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, -1.0f),
Vertex(1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f),
Vertex(1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f),
};
DWORD indices[] = {
//字体面
0, 1, 2,
0, 2, 3,
};
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc));
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = indices;
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer);
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc));
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(Vertex) * 4;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory(&vertexBufferData, sizeof(vertexBufferData));
vertexBufferData.pSysMem = v;
hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &d2dVertBuffer);
//从纹理D2D,创建一个着色器资源视图
//因此,能够使用它来创建一个矩形纹理,用于覆盖场景
d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture);
}
//void ReleaseObjects()
//{
//释放创建的COM对象
// SwapChain->Release();
// d3d11Device->Release();
// d3d11DevCon->Release();
//}
bool InitScene()
{
//
InitD2DScreenTexture();
//编译着色器
CreateSphere(10, 10);
///////////////**************new**************////////////////////
if(!LoadObjModel(L"spaceCompound.obj", &meshVertBuff, &meshIndexBuff, meshSubsetIndexStart, meshSubsetTexture, material, meshSubsets, true, false))
return false;
///////////////**************new**************////////////////////
///////////////**************new**************////////////////////
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0);
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0);
/// new
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "D2D_PS", "ps_4_0", 0, 0, 0, &D2D_PS_Buffer, 0, 0);
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "SKYMAP_VS", "vs_4_0", 0, 0, 0, &SKYMAP_VS_Buffer, 0, 0);
hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "SKYMAP_PS", "ps_4_0", 0, 0, 0, &SKYMAP_PS_Buffer, 0, 0);
///////////////**************new**************////////////////////
//创建着色器对象
hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS);
hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS);
///new
hr = d3d11Device->CreatePixelShader(D2D_PS_Buffer->GetBufferPointer(), D2D_PS_Buffer->GetBufferSize(), NULL, &D2D_PS);
hr = d3d11Device->CreateVertexShader(SKYMAP_VS_Buffer->GetBufferPointer(), SKYMAP_VS_Buffer->GetBufferSize(), NULL, &SKYMAP_VS);
hr = d3d11Device->CreatePixelShader(SKYMAP_PS_Buffer->GetBufferPointer(), SKYMAP_PS_Buffer->GetBufferSize(), NULL, &SKYMAP_PS);
///////////////**************new**************////////////////////
///////////////**************new**************////////////////////
//设置顶点和像素着色器
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);
///////////////**************new**************////////////////////
light.pos = XMFLOAT3(0.0f, 1.0f, 0.0f);
light.dir = XMFLOAT3(0.0f, 0.0f, 1.0f);
light.range = 1000.0f;
light.cone = 20.0f;
light.att = XMFLOAT3(0.4f, 0.02f, 0.000f);
light.ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
light.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
///////////////**************new**************////////////////////
//Create the vertex buffer
Vertex v[] =
{
// Bottom Face
Vertex(-1.0f, -1.0f, -1.0f, 100.0f, 100.0f, 0.0f, 1.0f, 0.0f),
Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 100.0f, 0.0f, 1.0f, 0.0f),
Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f),
Vertex(-1.0f, -1.0f, 1.0f, 100.0f, 0.0f, 0.0f, 1.0f, 0.0f),
};
DWORD indices[] = {
0, 1, 2,
0, 2, 3,
};
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc));
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA iinitData;
iinitData.pSysMem = indices;
d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer);
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc));
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
ZeroMemory(&vertexBufferData, sizeof(vertexBufferData));
vertexBufferData.pSysMem = v;
hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &squareVertBuffer);
//设置顶点缓冲
// UINT stride = sizeof(Vertex);
// UINT offset = 0;
// d3d11DevCon->IASetVertexBuffers(0, 1, &squareVertBuffer, &stride, &offset);
//创建输入布局
d3d11Device->CreateInputLayout(layout, numElements, VS_Buffer->GetBufferPointer(),
VS_Buffer->GetBufferSize(), &vertLayout);
//设置输入布局
d3d11DevCon->IASetInputLayout(vertLayout);
//设置图元拓扑
d3d11DevCon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//创建视口
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = Width;
viewport.Height = Height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
//设置视口
d3d11DevCon->RSSetViewports(1, &viewport);
//创建缓冲用来发送到效果文件的cbuffer
D3D11_BUFFER_DESC cbbd;
ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC));
cbbd.Usage = D3D11_USAGE_DEFAULT;
cbbd.ByteWidth = sizeof(cbPerObject);
cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbbd.CPUAccessFlags = 0;
cbbd.MiscFlags = 0;
hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer);
//创建缓冲用于每帧发送cbuffer到着色器文件
ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC));
cbbd.Usage = D3D11_USAGE_DEFAULT;
cbbd.ByteWidth = sizeof(cbPerFrame);
cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbbd.CPUAccessFlags = 0;
cbbd.MiscFlags = 0;
d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerFrameBuffer);
//相机信息
//相机信息
camPosition = XMVectorSet( 0.0f, 5.0f, -8.0f, 0.0f );
//camPosition = XMVectorSet(0.0f, 0.0f, -0.5f, 0.0f);
camTarget = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
camUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
//设置视图矩阵
camView = XMMatrixLookAtLH(camPosition, camTarget, camUp);
//设置投影矩阵
camProjection = XMMatrixPerspectiveFovLH(0.4f*3.14f, (float)Width / Height, 1.0f, 1000.0f);
D3D11_BLEND_DESC blendDesc;
ZeroMemory( &blendDesc, sizeof(blendDesc) );
D3D11_RENDER_TARGET_BLEND_DESC rtbd;
ZeroMemory( &rtbd, sizeof(rtbd) );
rtbd.BlendEnable = true;
rtbd.SrcBlend = D3D11_BLEND_SRC_COLOR;
///////////////**************new**************////////////////////
rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
///////////////**************new**************////////////////////
rtbd.BlendOp = D3D11_BLEND_OP_ADD;
rtbd.SrcBlendAlpha = D3D11_BLEND_ONE;
rtbd.DestBlendAlpha = D3D11_BLEND_ZERO;
rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD;
rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;
blendDesc.AlphaToCoverageEnable = false;
blendDesc.RenderTarget[0] = rtbd;
d3d11Device->CreateBlendState(&blendDesc, &d2dTransparency);
///////////////**************new**************////////////////////
ZeroMemory( &rtbd, sizeof(rtbd) );
rtbd.BlendEnable = true;
rtbd.SrcBlend = D3D11_BLEND_INV_SRC_ALPHA;
rtbd.DestBlend = D3D11_BLEND_SRC_ALPHA;
rtbd.BlendOp = D3D11_BLEND_OP_ADD;
rtbd.SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
rtbd.DestBlendAlpha = D3D11_BLEND_SRC_ALPHA;
rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD;
rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;
blendDesc.AlphaToCoverageEnable = false;
blendDesc.RenderTarget[0] = rtbd;
d3d11Device->CreateBlendState(&blendDesc, &Transparency);
///////////////**************new**************////////////////////
//加载图像纹理
//hr =
//#if 1
hr = D3DX11CreateShaderResourceViewFromFile(d3d11Device, L"grass.jpg",
NULL, NULL, &CubesTexture, NULL);
#if 1
///////////////**************new**************////////////////////
//告诉D3D我们正在加载一个立方体纹理
D3DX11_IMAGE_LOAD_INFO loadSMInfo;
loadSMInfo.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
//加载纹理
ID3D11Texture2D* SMTexture = 0;
hr = D3DX11CreateTextureFromFile(d3d11Device, L"skymap.dds",
&loadSMInfo, 0, (ID3D11Resource**)&SMTexture, 0);
//创建纹理描述符
D3D11_TEXTURE2D_DESC SMTextureDesc;
SMTexture->GetDesc(&SMTextureDesc);
//告诉D3D我们有一个立方体纹理,它是一个2D纹理的数组
D3D11_SHADER_RESOURCE_VIEW_DESC SMViewDesc;
SMViewDesc.Format = SMTextureDesc.Format;
SMViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
SMViewDesc.TextureCube.MipLevels = SMTextureDesc.MipLevels;
SMViewDesc.TextureCube.MostDetailedMip = 0;
//创建资源视图
hr = d3d11Device->CreateShaderResourceView(SMTexture, &SMViewDesc, &smrv);
///////////////**************new**************////////////////////
#endif
//配置采样状态
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
//创建采样状态
hr = d3d11Device->CreateSamplerState(&sampDesc, &CubesTexSamplerState);
//d3d11Device->CreateBlendState(&blendDesc, &Transparency);
//创建逆时针和顺时针状态
D3D11_RASTERIZER_DESC cmdesc;
ZeroMemory(&cmdesc, sizeof(D3D11_RASTERIZER_DESC));
cmdesc.FillMode = D3D11_FILL_SOLID;
cmdesc.CullMode = D3D11_CULL_BACK;
cmdesc.FrontCounterClockwise = true;
hr = d3d11Device->CreateRasterizerState(&cmdesc, &CCWcullMode);
cmdesc.FrontCounterClockwise = false;
hr = d3d11Device->CreateRasterizerState(&cmdesc, &CWcullMode);
#if 1
///////////////**************new**************////////////////////
cmdesc.CullMode = D3D11_CULL_NONE;
hr = d3d11Device->CreateRasterizerState(&cmdesc, &RSCullNone);
D3D11_DEPTH_STENCIL_DESC dssDesc;
ZeroMemory(&dssDesc, sizeof(D3D11_DEPTH_STENCIL_DESC));
dssDesc.DepthEnable = true;
dssDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dssDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
d3d11Device->CreateDepthStencilState(&dssDesc, &DSLessEqual);
///////////////**************new**************////////////////////
#endif
return true;
}
///////////////**************new**************////////////////////
void StartTimer()
{
LARGE_INTEGER frequencyCount;
QueryPerformanceFrequency(&frequencyCount);
countsPerSecond = double(frequencyCount.QuadPart);
QueryPerformanceCounter(&frequencyCount);
CounterStart = frequencyCount.QuadPart;
}
double GetTime()
{
LARGE_INTEGER currentTime;
QueryPerformanceCounter(¤tTime);
return double(currentTime.QuadPart-CounterStart)/countsPerSecond;
}
double GetFrameTime()
{
LARGE_INTEGER currentTime;
__int64 tickCount;
QueryPerformanceCounter(¤tTime);
tickCount = currentTime.QuadPart-frameTimeOld;
frameTimeOld = currentTime.QuadPart;
if(tickCount < 0.0f)
tickCount = 0.0f;
return float(tickCount)/countsPerSecond;
}
///////////////**************new**************////////////////////
///////////////**************new**************////////////////////
void UpdateScene(double time)
///////////////**************new**************////////////////////
//void UpdateScene()
{
//Reset cube1World
groundWorld = XMMatrixIdentity();
//Define cube1's world space matrix
///////////////**************new**************////////////////////
Scale = XMMatrixScaling( 500.0f, 10.0f, 500.0f );
Translation = XMMatrixTranslation( 0.0f, 10.0f, 0.0f );
//Set cube1's world space using the transformations
groundWorld = Scale * Translation;
//复位球面世界
sphereWorld = XMMatrixIdentity();
//Define sphereWorld's world space matrix
Scale = XMMatrixScaling( 5.0f, 5.0f, 5.0f );
//Make sure the sphere is always centered around camera
Translation = XMMatrixTranslation( XMVectorGetX(camPosition), XMVectorGetY(camPosition), XMVectorGetZ(camPosition) );
//Set sphereWorld's world space using the transformations
sphereWorld = Scale * Translation;
///////////////**************new**************////////////////////
meshWorld = XMMatrixIdentity();
//Define cube1's world space matrix
Rotation = XMMatrixRotationY(3.14f);
Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f );
Translation = XMMatrixTranslation( 0.0f, 0.0f, 0.0f );
meshWorld = Rotation * Scale * Translation;
///////////////**************new**************////////////////////
///////////////**************new**************////////////////////
light.pos.x = XMVectorGetX(camPosition);
light.pos.y = XMVectorGetY(camPosition);
light.pos.z = XMVectorGetZ(camPosition);
light.dir.x = XMVectorGetX(camTarget) - light.pos.x;
light.dir.y = XMVectorGetY(camTarget) - light.pos.y;
light.dir.z = XMVectorGetZ(camTarget) - light.pos.z;
///////////////**************new**************////////////////////
}
///////////////**************new**************////////////////////
void RenderText(std::wstring text, int inInt)
//void RenderText(std::wstring text)
{
//释放D3D11设备
d3d11DevCon->PSSetShader(D2D_PS, 0, 0);
keyedMutex11->ReleaseSync(0);
//使用D3D10.1设备
keyedMutex10->AcquireSync(0, 5);
//绘制D2D内容
D2DRenderTarget->BeginDraw();
//清空D2D背景色
D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
//创建字符串
std::wostringstream printString;
///////////////**************new**************////////////////////
printString << text << inInt;
// printString << text;
///////////////**************new**************////////////////////
printText = printString.str();
//设置字体颜色
D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f);
//设置D2D绘制要用到的画笔颜色
Brush->SetColor(FontColor);
//创建D2D渲染区域
D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height);
//绘制文本
D2DRenderTarget->DrawText(
printText.c_str(),
wcslen(printText.c_str()),
TextFormat,
layoutRect,
Brush
);
D2DRenderTarget->EndDraw();
//释放D3D10.1设备
keyedMutex10->ReleaseSync(1);
//使用D3D11设备
keyedMutex11->AcquireSync(1, 5);
//使用着色器资源表示d2d渲染目标来创建一个矩形纹理,该矩形是被渲染进屏幕空间的。使用α混合以便整个D2D
//渲染目标的背景为不可见的,且只有使用D2D绘制的东西才可见(文本)。
//为D2D渲染目标纹理对象设置混合状态
d3d11DevCon->OMSetBlendState(d2dTransparency, NULL, 0xffffffff);
//Set d2d's pixel shader so lighting calculations are not done
// d3d11DevCon->PSSetShader(D2D_PS, 0, 0);
//设置d2d索引缓冲
d3d11DevCon->IASetIndexBuffer(d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
//设置d2d顶点缓冲
UINT stride = sizeof(Vertex);
UINT offset = 0;
d3d11DevCon->IASetVertexBuffers(0, 1, &d2dVertBuffer, &stride, &offset);
WVP = XMMatrixIdentity();
///new
// cbPerObj.World = XMMatrixTranspose(WVP);
cbPerObj.WVP = XMMatrixTranspose(WVP);
d3d11DevCon->UpdateSubresource(cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0);
d3d11DevCon->VSSetConstantBuffers(0, 1, &cbPerObjectBuffer);
d3d11DevCon->PSSetShaderResources(0, 1, &d2dTexture);
d3d11DevCon->PSSetSamplers(0, 1, &CubesTexSamplerState);
d3d11DevCon->RSSetState(CWcullMode);
//画第二个立方体
d3d11DevCon->DrawIndexed(6, 0, 0);
}
void DrawScene()
{
//将更新的颜色填充后缓冲
// D3DXCOLOR bgColor(red, green, blue, 1.0f);
float bgColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);
//刷新深度模板视图
d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
//new
constbuffPerFrame.light = light;
d3d11DevCon->UpdateSubresource(cbPerFrameBuffer, 0, NULL, &constbuffPerFrame, 0, 0);
d3d11DevCon->PSSetConstantBuffers(0, 1, &cbPerFrameBuffer);
//复位顶点和像素着色器
// d3d11DevCon->VSSetShader(VS, 0, 0);
// d3d11DevCon->PSSetShader(PS, 0, 0);
//使能默认光栅化状态
// d3d11DevCon->RSSetState(NULL);
//绘制使用背面裁剪的对象
//关闭背面裁剪
// d3d11DevCon->RSSetState(noCull);
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView );
d3d11DevCon->OMSetBlendState(0, 0, 0xffffffff);
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);
//Set the cubes index buffer
//设置立方体的索引缓冲
d3d11DevCon->IASetIndexBuffer(squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
//设置立方体的顶点缓冲
UINT stride = sizeof(Vertex);
UINT offset = 0;
d3d11DevCon->IASetVertexBuffers(0, 1, &squareVertBuffer, &stride, &offset);
//设置WVP矩阵并将它送到效果文件中的常量缓冲中
WVP = groundWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(groundWorld);
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
d3d11DevCon->RSSetState(CCWcullMode);
// d3d11DevCon->DrawIndexed( 6, 0, 0 );
///////////////**************new**************////////////////////
//绘制我们模型的非透明子集
for(int i = 0; i < meshSubsets; ++i)
{
//设置地面索引缓冲
d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0);
//设置地面顶点缓冲
d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset );
//设置WVP矩阵并发送它到效果文件中的常量缓冲中
WVP = meshWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(meshWorld);
cbPerObj.difColor = material[meshSubsetTexture[i]].difColor;
cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture;
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer );
if(material[meshSubsetTexture[i]].hasTexture)
d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
d3d11DevCon->RSSetState(RSCullNone);
int indexStart = meshSubsetIndexStart[i];
int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i];
if(!material[meshSubsetTexture[i]].transparent)
d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 );
}
///////////////**************new**************////////////////////
/////绘制天空的球面//////
//设置球面的索引缓冲
d3d11DevCon->IASetIndexBuffer( sphereIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
//设置球面的顶点缓冲
d3d11DevCon->IASetVertexBuffers( 0, 1, &sphereVertBuffer, &stride, &offset );
//设置WVP矩阵并将它发送给效果文件中的常量缓冲
WVP = sphereWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(sphereWorld);
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
//发送我们的天空贴图资源视图到像素着色器
d3d11DevCon->PSSetShaderResources( 0, 1, &smrv );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
//设置新的VS和PS着色器
d3d11DevCon->VSSetShader(SKYMAP_VS, 0, 0);
d3d11DevCon->PSSetShader(SKYMAP_PS, 0, 0);
//设置新的深度模板和RS状态
d3d11DevCon->OMSetDepthStencilState(DSLessEqual, 0);
d3d11DevCon->RSSetState(RSCullNone);
d3d11DevCon->DrawIndexed( NumSphereFaces * 3, 0, 0 );
//设置默认的VS,PS着色器和深度模板状态
d3d11DevCon->VSSetShader(VS, 0, 0);
d3d11DevCon->PSSetShader(PS, 0, 0);
d3d11DevCon->OMSetDepthStencilState(NULL, 0);
///////////////**************new**************////////////////////
//绘制我们的模型的透明度子集
//设置我们的混合状态
d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff);
for(int i = 0; i < meshSubsets; ++i)
{
//设置地面索引缓冲
d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0);
//设置地面顶点缓冲
d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset );
//设置WVP矩阵并将它发送给效果文件中的常量缓冲中
WVP = meshWorld * camView * camProjection;
cbPerObj.WVP = XMMatrixTranspose(WVP);
cbPerObj.World = XMMatrixTranspose(meshWorld);
cbPerObj.difColor = material[meshSubsetTexture[i]].difColor;
cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture;
d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer );
if(material[meshSubsetTexture[i]].hasTexture)
d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] );
d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );
d3d11DevCon->RSSetState(RSCullNone);
int indexStart = meshSubsetIndexStart[i];
int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i];
if(material[meshSubsetTexture[i]].transparent)
d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 );
}
///////////////**************new**************////////////////////
RenderText(L"FPS: ", fps);
//Present the backbuffer to the screen
SwapChain->Present(0, 0);
}
int messageloop(){
MSG msg;
ZeroMemory(&msg, sizeof(MSG));//清除结构体被设为NULL。
while (true)
{
//使用PeekMessage()检查是否有消息传进来
/*LPMSG lpMsg 消息结构体的指针
*HWND hWnd 发送消息的窗口句柄。若设为NULL,那么它会从当前程序中接收来自任何一个窗口的消息
*UINT wMsgFilterMin 指定消息范围内第一个要检查的消息的值。若wMsgFilterMin和wMsgFilterMax都设为0,那么PeekMessage将会检查素有的消息
*UINT wMsgFilterMax 指定消息范围内最后一个要检测的消息的值
*UINT wRemoveMsg 指定消息的处理方式。若设置为PM_REMOVE,则在读取之后会被删除
*/
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
//若消息为窗口消息,则解析并分发它。TranslateMessage()将会让窗口做一些解析,类似键盘的虚拟键值转换到字符形式。
//而DispatchMessage()则发送消息到窗口过程WndProc。
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else //若没有窗口消息,则运行游戏
///////////////**************new**************////////////////////
{
frameCount++;
if(GetTime() > 1.0f)
{
fps = frameCount;
frameCount = 0;
StartTimer();
}
frameTime = GetFrameTime();
///////////////**************new**************////////////////////
DetectInput(frameTime);
///////////////**************new**************////////////////////
UpdateScene(frameTime);
DrawScene();
}
}
return msg.wParam;
}
//窗口消息处理函数
//HWND hwnd 获取消息的窗口句柄
//UINT msg 消息的内容
/*
*WM_ACTIVE 当窗口激活时发送的消息
*WM_CLOSE 当窗口关闭时发送的消息
*WM_CREATE 当窗口创建时发送的消息
*WM_DESTROY 当窗口销毁时发送的消息
*/
//wParam和lParam时消息的额外信息。使用wParam来检测键盘输入消息
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
// 这是事件检测消息的地方,若escape键被按下,会显示一个消息框,询问是否真的退出。若点击yes,则程序关闭。若不点击,则消息框关闭。若消息包含WM_DESTROY
// 则意味着窗口正在被销毁,返回0并且程序关闭
switch (msg)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
if (MessageBox(0, L"Are you sure you want to exit?",
L"Really?", MB_YESNO | MB_ICONASTERISK) == IDYES)
{
DestroyWindow(hwnd);
}
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
break;
}
//调用默认窗口过程函数
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
}
效果图:
参考网址