D3D11 骨骼动画(基于MD5格式)

来源:互联网 发布:mac文件放在桌面 编辑:程序博客网 时间:2024/06/04 17:46

MD5格式利用了骨骼系统(也称为关节系统)来做动画效果,因此在本章节会介绍如何遍历存储在md5anim文件中的动画以及如何将动画应用在模型上。骨骼系统(也称为关节系统)优点在于比直接存储关键帧动画要占用更少的内存,因为关键帧动画相当于给每个动画帧赋予一个全新的模型,而关节系统仅仅在每帧存储骨头的方向以及位置信息。

简介:

在上一章节介绍了加载md5模型,并使用骨骼系统计算顶点位置和法线。在本章节会介绍如何导入md5动画文件.md5anim并使用存储在内的数据让模型动起来。整体的思路如下:

1.导入动画文件.md5anim,导入方法会在后面讲到

2.计算当前时间动画中的骨骼,也就是当前时间下在两帧间(也就是已过去的帧与将到来的帧之间)插值两幅骨骼

3.在创建完插值骨骼后,需要通过权重来重新计算顶点位置和法线

4.最后,在得到顶点位置和法线(得到切线和副切线的方法和法线一样)后,需要更新顶点缓冲

跳过第1步导入动画以及第2步,直接介绍第3步,创建插值骨骼。


创建插值骨骼

在导入动画时,就为每个动画帧创建了骨骼。随后就在这些帧(比如帧1和帧2之间)之间插入这些骨骼以得到动画的当前帧。当然可以仅仅使用动画帧本身而不用为每帧创建插值骨骼来得到动画,但是这会让动画看起来断断续续的。

动画文件会指定动画每秒需要跑的帧数,本章导入的模型是每秒30帧的。可以将当前动画时间(自动画开始以来的时间)乘以帧率得到当前所在的帧数,该帧数可能会是一个浮点型,比如5.3667,也就是当前处在第5帧上,可使用函数floorf()四舍五入来得到最近的整数值,也就是5。这也就是要存储在frame0中的数,它也是要插值的两帧骨骼中前一帧,可通过直接在frame0上加1来得到frame1也就是第6帧。这样就得到了用来更新模型的要插值的两帧骨骼,但是当插值这两个骨骼时,需要一个介于0和1之间的值(0表示frame0,0.5表示一半frame0一半frame1,1表示frame1)以便了解插值哪一个多一点。这个值也就是类似上面例子的0.3667,也就是5.3667减去frame0得到的值。

得到实际插值骨骼后,遍历模型的每个关节(因为动画使用的是同样的关节,不同的只是位置和方向)并对它们的位置和方向进行插值。可使用下面的方程式来更新它们的位置:

interpolatedJoint.position = joint0.pos + (interpolation * (joint1.pos - joint0.pos))

可使用球形插值技术(球面线性插值技术)来得到两个关节帧的插值方向,在xna数学库中有一个函数可得到该值,如下所示:

interpolatedJoint.orientation = XMQuaternionSlerp(joint0.orientation, joint1.orientation, interpolation))

针对每个关节都做上面的操作,最终得到已插值好的骨骼。

更新顶点位置和法线

这里通过插值骨骼来更新顶点和法线,与上一章节方式一样来得到顶点位置。遍历每个顶点的权重位置。首先根据关节方向来绕着关节旋转权重。方程式如下:

weightPos = XMQuaternionMultiply(XMQuaternionMultiply(jointOrientation, weightPos), jointOrientationConjugate))

这会得到关节空间的权重位置:这里需要将权重移到模型空间内的关节位置上,可通过将关节位置加上权重位置来完成这个任务。最后,将权重位置与权重偏差因子相乘,再加上顶点最终的位置。

这里也会计算法线:会使用权重法线(从更新函数也就是加载md5mesh文件的地方得到这个法线),可根据关节方向像旋转权重位置一样旋转权重法线,由于法线没有位置信息,因此可以不用将它和关节位置相加。然后将权重法线与权重偏差因子相乘并将其结果与顶点最终法线相加。


更新D3D的缓冲

最后就是更新顶点缓冲。有三种方法可做这件事(每种缓冲类型都对应一个缓冲,它们是D3D11_USAGE_STAGING,D3D11_USAGE_DYNAMIC,D3D11_USAGE_DEFAULT。D3D11_USAGE_IMMUTABLE类型不可更新)。

第一种方式使用D3D11_USAGE_DEFAULT缓冲,也就是一个D3D11_USAGE_STAGING缓冲。分段缓冲用来从缓冲(传送到GPU的缓冲)获得信息,因为默认缓冲其实也就是一个分段缓冲的拷贝。要更新缓冲,首先得使用ID3D11DeviceContext::Map和ID3D11DeviceContext::Unmap来更新分段缓冲,然后再使用ID3D11DeviceContext::CopyResource来将分段缓冲的内容拷贝到默认缓冲。Map方法返回D3D11_MAPPED_SUBRESOURCE对象,它的pData成员是指向缓冲中数据的起始位置的指针。随后使用函数memcpy()来存储顶点(或任何所想要的数据)。可用下面方式来做:

D3D11_MAPPED_SUBRESOURCE mappedVertBuff;ID3D11DeviceContext->Map(stagingVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedVertBuff);memcpy(mappedVertBuff.pData, &vertices[0], (sizeof(Vertex) * vertices.size()));ID3D11DeviceContext->Unmap(stagingVertexBuffer, 0);ID3D11DeviceContext->UpdateSubresource( defaultVertexBuffer, 0, NULL, &stagingVertexBuffer, 0, 0 );


第二种方式在不使用分段缓冲的情况下仅更新默认缓冲。只使用一个ID3D11DeviceContext::CopyResource方法来实现,可使用下面方法来更新默认缓冲:

ID3D11DeviceContext->UpdateSubresource( defaultVertexBuffer, 0, NULL, &vertices[0], 0, 0 );

最后一种方式是给用D3D11_USAGE_DYNAMIC来创建的缓冲用的。由于它会快速更新,所以这里会用这种方式来更新缓冲,会像在分段缓冲中使用Map和Unmap函数一样来更新这个缓冲。以下为例子:

D3D11_MAPPED_SUBRESOURCE mappedVertBuff;ID3D11DeviceContext->Map(dynamicVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedVertBuff);memcpy(mappedVertBuff.pData, &vertices[0], (sizeof(Vertex) * vertices.size()));ID3D11DeviceContext->Unmap(dynamicVertexBuffer, 0);


第一种和第二种的分段缓冲和默认缓冲会比动态缓冲更新的更慢,如果缓冲每帧少一次更新就需要使用前两个方法。由GPU读取的默认缓冲会更快但是更新更慢。GPU读取动态缓冲会更慢,但是更新更快。


.md5anim格式:

.md5anim格式分为五部分:头部header,层级hierarchy,界限bounds,基础帧baseframes,和帧frames。头部信息是关于文件和动画的,比如每秒的帧数,文件版本,总的关节和帧数等。层级是在模型中使用到的关节列表,在层级段落中关节数量和父子关系要与绑定的.md5mesh文件匹配。在层级段落后为界限段落,该段落定义了每个蒙皮帧的AABB。该AABB可用来快速计算比如碰撞测试或拾取操作。在界限之后就是基础帧,它再它的默认位置内存储每个关节的位置和方向。所有的帧都是基于该基础帧(或者说是基础骨骼)构建的。最后得到想要的帧。每帧都有一个段落且每个段落包含有一个浮点值列表用于解释关节是如何移动和旋转的。

"MD5Version"

随后的字符数字是文件版本号,由于这里是会用的是版本10导入,所以设为10.

MD5Version 10

“commandline”

该行包含不需要关心的内容

commandline ""

“numFrames”

动画中的帧数,这里使用46帧动画:

numFrames 46


“numJoints”

模型中的关节数,且在层级段落内。该数字应该与.md5mesh文件中匹配

numJoints 31

“frameRate”

帧率表示每秒动画跑的帧数。在程序中在同样的速度上可以确保动画稳定流畅

frameRate 30

“numAnimatedComponents”

在每个帧段落中的组件数量或者浮点数量。每个组件会替代基础帧关节位置或方向的一个组件。

numAnimatedComponents 186

“hierarchy”

这里是关节描述的起始。关节描述在下一行的(“joints{”)开始直到“}”为止结束。

关节数量,父节点索引以及关节名字必须与.md5mesh文件中相匹配。若不匹配,动画会出现很多意想不到的结果。

以"joints{"开始的每行都是新节点,这其中的每行以双引号开头,也就是关节的名字。关节名后面的为父节点的索引,父索引为-1的表示该关节在顶层也就是说无父节点。下一个数用于描述在随后的帧段落中关节的哪一部分(位置和方向的x,y和z)会更新。最后一个值为起始索引。起始索引为帧数据中的值用于开始更新这个特定关节。从下面的代码片段可知,Bip01的起始索引为0,也就意味着在帧数据的开始它就会更新。在帧数据中(1和6之间,3个给位置和3个给方向)标志段落会定义如何更新关节且在起始索引中有多少值可用。

joints {    "Bip01"    -1 63 0    ...}

“bounds”

每个帧的界限或者轴对齐环绕盒。在bounds后的行就是帧AABB,它由一个最小和最大点(也就是每个x,y和z轴上的最大和最小值)构成。括号中的前面三个浮点值表示最小点,后面的三个值表示最大点:

( -30.4137325286 -23.4689254760 -42.4965248107 ) ( 35.7306137084 6.3094730377 48.7827911376 )


“baseframe”

这是动画中的每个关节的默认位置。这不一定要与.md5mesh中描述的绑定姿态一样。因为不是每帧都会描述所有关节的位置和旋转,所以每帧都会基于这个基础帧来构建它的帧骨骼。鉴于此,所以基础帧会用一些甚至所有的0来填充,这种情况下在每帧中一些货所有关节位置或方向都要更新。

在basframe之后的每行描述了对应的关节位置和方向,前面三个值为位置,后面三个值为方向。

( 0.5196304321 1.7362534999 4.6482505798 ) ( 0.0000000000 0.0000000000 0.7071063041 )


“frame”

最后一个为帧段落是关于,md5anim文件的。动画的每帧都有自己的段落且在每个段落内都是一个浮点值列表。浮点值的数量在头部的numAnimatedComponents中定义了。每个浮点值会替换基础帧关节上的位置或方向的x,y和z值。关节标志和起始索引决定了关节的值以及组件(位置和方向的x,y,z值):

0.5196304321 1.7362534999 4.6482505798 0.0000000000 0.0000000000 0.7071063041


使法线动起来

为每个顶点计算法线有时会非常缓慢,当让模型动起来时,这里有一种方式来解决这个问题。也就是有时不必要去为每帧偶读计算法线值。也就是当从文件.md5mesh中导入原始模型时计算关节空间(为每个权重都计算)的法线。在得到关节空间的法线后,就可根据关节方向来为每帧旋转法线,方法是与根据关节方向来旋转权重位置一样。

当计算关节空间的法线时,首先为每个顶点计算法线。然后通过对关节旋转求逆使用这些顶点法线和位置。会遍历该顶点的每个权重值以及对每个绑定权重值的关节做计算。

右手/左手坐标系

在导入.md5mesh和.md5anim时交换了z和y的值。这是因为该文件是从右手坐标系导出的,但是directx是使用的左手坐标系。若模型正好是在左手坐标系中,当导入数据时只需要交换一下y和z值即可。


新的结构体

这里添加了许多新的结构体。首先是包围盒,也就是从md5anim文件中导入的界限段落。下一个结构体为镇数据结构体。该结构体包含了标识帧的索引号,以及一个帧数据的浮点数组。这些浮点值随后会用来为每帧更新基础帧骨骼。

下一个为关节的新结构体。该结构体包含与其他关节结构体不同的数据,由于对每个动画关节(因为md5mesh文件可包含一个或更多md5anim文件)而言是随着动画的不同而不同的。所以该结构体包含动画特定数据,比如标志(每帧哪一个关节帧应该被更新)以及起始索引(帧数据数组内第一个开始更新的值)。

下一个为ModelAnimation结构体,下面是一个向量成员变量。这是因为每个模型包含有一个或更多动画。该结构体包含关于动画以及动画自身(每帧的骨骼姿态)的信息。关于最后一个frameSkeleton成员,每个骨骼由关节向量定义,在动画中每帧有独立的骨骼。所以可得到第一个每个帧的向量,且它是骨骼的关节向量。随后从该成员比如frameSkeleton[frame][joint]或仅仅是一整个帧骨骼比如frameSkeleton[frame]中调用一个关节。

struct BoundingBox{    XMFLOAT3 min;    XMFLOAT3 max;};struct FrameData{    int frameID;    std::vector<float> frameData;};struct AnimJointInfo{    std::wstring name;    int parentID;    int flags;    int startIndex;};struct ModelAnimation{    int numFrames;    int numJoints;    int frameRate;    int numAnimatedComponents;    float frameTime;    float totalAnimTime;    float currAnimTime;    std::vector<AnimJointInfo> jointInfo;    std::vector<BoundingBox> frameBounds;    std::vector<Joint>    baseFrameJoints;    std::vector<FrameData>    frameData;    std::vector<std::vector<Joint>> frameSkeleton;};


更新权重结构体

更新权重结构体以包含法线。这是因为若在关节空间内定义法线,可非常容易的将它改到动画流程中,而不用为每帧重新计算法线:

struct Weight{    int jointID;    float bias;    XMFLOAT3 pos;    ///////////////**************new**************////////////////////    XMFLOAT3 normal;    ///////////////**************new**************////////////////////};

更新Model3D结构体

更新Model3D结构体来保存动画数组

struct Model3D{    int numSubsets;    int numJoints;    std::vector<Joint> joints;    std::vector<ModelSubset> subsets;    ///////////////**************new**************////////////////////    std::vector<ModelAnimation> animations;    ///////////////**************new**************////////////////////};


两个新的函数

第一个用来从文件.md5anim中导入动画,第二个来基于动画中经历的时间以及使用哪一个动画来更新顶点缓冲。

bool LoadMD5Anim(std::wstring filename,    Model3D& MD5Model);void UpdateMD5Model(Model3D& MD5Model, float deltaTime, int animation);


DetectInput()函数

这里添加新的按键检测,用字母R。当按下该键时,动画会更新。添加了一个浮点值timeFactor用来加速或减速在动画空间内的时间。若想要在整个游戏中加速或减速时间,需要在更大范围内放入该时间因子。

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 = 10.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;    }    ///////////////**************new**************////////////////////    if(keyboardState[DIK_R] & 0X80)    {        float timeFactor = 1.0f;    // You can speed up or slow down time by changing this        UpdateMD5Model(NewMD5Model, time*timeFactor, 0);    }    ///////////////**************new**************////////////////////    if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY))    {        camYaw += mouseLastState.lX * 0.001f;        camPitch += mouseCurrState.lY * 0.001f;        mouseLastState = mouseCurrState;    }    UpdateCamera();    return;}

LoadMD5Anim()函数

这里导入动画,该文件也会为动画的每个帧创建骨骼。

bool LoadMD5Anim(std::wstring filename,    Model3D& MD5Model){        ModelAnimation tempAnim;                        // Temp animation to later store in our model's animation array    std::wifstream fileIn (filename.c_str());        // Open file    std::wstring checkString;                        // Stores the next string from our file    if(fileIn)                                        // Check if the file was opened    {        while(fileIn)                                // Loop until the end of the file is reached        {                fileIn >> checkString;                    // Get next string from file            if ( checkString == L"MD5Version" )        // Get MD5 version (this function supports version 10)            {                fileIn >> checkString;                /*MessageBox(0, checkString.c_str(),    //display message                L"MD5Version", MB_OK);*/            }            else if ( checkString == L"commandline" )            {                std::getline(fileIn, checkString);    // Ignore the rest of this line            }            else if ( checkString == L"numFrames" )            {                fileIn >> tempAnim.numFrames;                // Store number of frames in this animation            }            else if ( checkString == L"numJoints" )            {                fileIn >> tempAnim.numJoints;                // Store number of joints (must match .md5mesh)            }            else if ( checkString == L"frameRate" )            {                fileIn >> tempAnim.frameRate;                // Store animation's frame rate (frames per second)            }            else if ( checkString == L"numAnimatedComponents" )            {                fileIn >> tempAnim.numAnimatedComponents;    // Number of components in each frame section            }            else if ( checkString == L"hierarchy" )            {                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)    // Load in each joint                {                    AnimJointInfo tempJoint;                    fileIn >> tempJoint.name;        // Get joints name                    // Sometimes the names might contain spaces. If that is the case, we need to continue                    // to read the name until we get to the closing " (quotation marks)                    if(tempJoint.name[tempJoint.name.size()-1] != '"')                    {                        wchar_t checkChar;                        bool jointNameFound = false;                        while(!jointNameFound)                        {                            checkChar = fileIn.get();                            if(checkChar == '"')                                jointNameFound = true;                                    tempJoint.name += checkChar;                                                                                    }                    }                    // Remove the quotation marks from joints name                    tempJoint.name.erase(0, 1);                    tempJoint.name.erase(tempJoint.name.size()-1, 1);                    fileIn >> tempJoint.parentID;            // Get joints parent ID                    fileIn >> tempJoint.flags;                // Get flags                    fileIn >> tempJoint.startIndex;            // Get joints start index                    // Make sure the joint exists in the model, and the parent ID's match up                    // because the bind pose (md5mesh) joint hierarchy and the animations (md5anim)                    // joint hierarchy must match up                    bool jointMatchFound = false;                    for(int k = 0; k < MD5Model.numJoints; k++)                    {                        if(MD5Model.joints[k].name == tempJoint.name)                        {                            if(MD5Model.joints[k].parentID == tempJoint.parentID)                            {                                jointMatchFound = true;                                tempAnim.jointInfo.push_back(tempJoint);                            }                        }                    }                    if(!jointMatchFound)                    // If the skeleton system does not match up, return false                        return false;                        // You might want to add an error message here                    std::getline(fileIn, checkString);        // Skip rest of this line                }            }            else if ( checkString == L"bounds" )            // Load in the AABB for each animation            {                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numFrames; i++)                {                    BoundingBox tempBB;                    fileIn >> checkString;                    // Skip "("                    fileIn >> tempBB.min.x >> tempBB.min.z >> tempBB.min.y;                    fileIn >> checkString >> checkString;    // Skip ") ("                    fileIn >> tempBB.max.x >> tempBB.max.z >> tempBB.max.y;                    fileIn >> checkString;                    // Skip ")"                    tempAnim.frameBounds.push_back(tempBB);                }            }                        else if ( checkString == L"baseframe" )            // This is the default position for the animation            {                                                // All frames will build their skeletons off this                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)                {                    Joint tempBFJ;                    fileIn >> checkString;                        // Skip "("                    fileIn >> tempBFJ.pos.x >> tempBFJ.pos.z >> tempBFJ.pos.y;                    fileIn >> checkString >> checkString;        // Skip ") ("                    fileIn >> tempBFJ.orientation.x >> tempBFJ.orientation.z >> tempBFJ.orientation.y;                    fileIn >> checkString;                        // Skip ")"                    tempAnim.baseFrameJoints.push_back(tempBFJ);                }            }            else if ( checkString == L"frame" )        // Load in each frames skeleton (the parts of each joint that changed from the base frame)            {                FrameData tempFrame;                fileIn >> tempFrame.frameID;        // Get the frame ID                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numAnimatedComponents; i++)                {                    float tempData;                    fileIn >> tempData;                // Get the data                    tempFrame.frameData.push_back(tempData);                }                tempAnim.frameData.push_back(tempFrame);                ///*** build the frame skeleton ***///                std::vector tempSkeleton;                for(int i = 0; i < tempAnim.jointInfo.size(); i++)                {                    int k = 0;                        // Keep track of position in frameData array                    // Start the frames joint with the base frame's joint                    Joint tempFrameJoint = tempAnim.baseFrameJoints[i];                    tempFrameJoint.parentID = tempAnim.jointInfo[i].parentID;                    // Notice how I have been flipping y and z. this is because some modeling programs such as                    // 3ds max (which is what I use) use a right handed coordinate system. Because of this, we                    // need to flip the y and z axes. If your having problems loading some models, it's possible                    // the model was created in a left hand coordinate system. in that case, just reflip all the                    // y and z axes in our md5 mesh and anim loader.                    if(tempAnim.jointInfo[i].flags & 1)        // pos.x    ( 000001 )                        tempFrameJoint.pos.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 2)        // pos.y    ( 000010 )                        tempFrameJoint.pos.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 4)        // pos.z    ( 000100 )                        tempFrameJoint.pos.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 8)        // orientation.x    ( 001000 )                        tempFrameJoint.orientation.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 16)    // orientation.y    ( 010000 )                        tempFrameJoint.orientation.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 32)    // orientation.z    ( 100000 )                        tempFrameJoint.orientation.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    // Compute the quaternions w                    float t = 1.0f - ( tempFrameJoint.orientation.x * tempFrameJoint.orientation.x )                        - ( tempFrameJoint.orientation.y * tempFrameJoint.orientation.y )                        - ( tempFrameJoint.orientation.z * tempFrameJoint.orientation.z );                    if ( t < 0.0f )                    {                        tempFrameJoint.orientation.w = 0.0f;                    }                    else                    {                        tempFrameJoint.orientation.w = -sqrtf(t);                    }                    // Now, if the upper arm of your skeleton moves, you need to also move the lower part of your arm, and then the hands, and then finally the fingers (possibly weapon or tool too)                    // This is where joint hierarchy comes in. We start at the top of the hierarchy, and move down to each joints child, rotating and translating them based on their parents rotation                    // and translation. We can assume that by the time we get to the child, the parent has already been rotated and transformed based of it's parent. We can assume this because                    // the child should never come before the parent in the files we loaded in.                    if(tempFrameJoint.parentID >= 0)                    {                        Joint parentJoint = tempSkeleton[tempFrameJoint.parentID];                        // Turn the XMFLOAT3 and 4's into vectors for easier computation                        XMVECTOR parentJointOrientation = XMVectorSet(parentJoint.orientation.x, parentJoint.orientation.y, parentJoint.orientation.z, parentJoint.orientation.w);                        XMVECTOR tempJointPos = XMVectorSet(tempFrameJoint.pos.x, tempFrameJoint.pos.y, tempFrameJoint.pos.z, 0.0f);                        XMVECTOR parentOrientationConjugate = XMVectorSet(-parentJoint.orientation.x, -parentJoint.orientation.y, -parentJoint.orientation.z, parentJoint.orientation.w);                        // Calculate current joints position relative to its parents position                        XMFLOAT3 rotatedPos;                        XMStoreFloat3(&rotatedPos, XMQuaternionMultiply(XMQuaternionMultiply(parentJointOrientation, tempJointPos), parentOrientationConjugate));                        // Translate the joint to model space by adding the parent joint's pos to it                        tempFrameJoint.pos.x = rotatedPos.x + parentJoint.pos.x;                        tempFrameJoint.pos.y = rotatedPos.y + parentJoint.pos.y;                        tempFrameJoint.pos.z = rotatedPos.z + parentJoint.pos.z;                        // Currently the joint is oriented in its parent joints space, we now need to orient it in                        // model space by multiplying the two orientations together (parentOrientation * childOrientation) <- In that order                        XMVECTOR tempJointOrient = XMVectorSet(tempFrameJoint.orientation.x, tempFrameJoint.orientation.y, tempFrameJoint.orientation.z, tempFrameJoint.orientation.w);                        tempJointOrient = XMQuaternionMultiply(parentJointOrientation, tempJointOrient);                        // Normalize the orienation quaternion                        tempJointOrient = XMQuaternionNormalize(tempJointOrient);                        XMStoreFloat4(&tempFrameJoint.orientation, tempJointOrient);                    }                    // Store the joint into our temporary frame skeleton                    tempSkeleton.push_back(tempFrameJoint);                }                // Push back our newly created frame skeleton into the animation's frameSkeleton array                tempAnim.frameSkeleton.push_back(tempSkeleton);                fileIn >> checkString;                // Skip closing bracket "}"            }        }        // Calculate and store some usefull animation data        tempAnim.frameTime = 1.0f / tempAnim.frameRate;                        // Set the time per frame        tempAnim.totalAnimTime = tempAnim.numFrames * tempAnim.frameTime;    // Set the total time the animation takes        tempAnim.currAnimTime = 0.0f;                                        // Set the current time to zero        MD5Model.animations.push_back(tempAnim);                            // Push back the animation into our model object    }    else    // If the file was not loaded    {        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;    }    return true;}

分别解释上面代码的意思。

打开文件和读取头部信息

bool LoadMD5Anim(std::wstring filename,    Model3D& MD5Model){        ModelAnimation tempAnim;                        // Temp animation to later store in our model's animation array    std::wifstream fileIn (filename.c_str());        // Open file    std::wstring checkString;                        // Stores the next string from our file    if(fileIn)                                        // Check if the file was opened    {        while(fileIn)                                // Loop until the end of the file is reached        {                fileIn >> checkString;                    // Get next string from file            if ( checkString == L"MD5Version" )        // Get MD5 version (this function supports version 10)            {                fileIn >> checkString;                /*MessageBox(0, checkString.c_str(),    //display message                L"MD5Version", MB_OK);*/            }            else if ( checkString == L"commandline" )            {                std::getline(fileIn, checkString);    // Ignore the rest of this line            }            else if ( checkString == L"numFrames" )            {                fileIn >> tempAnim.numFrames;                // Store number of frames in this animation            }            else if ( checkString == L"numJoints" )            {                fileIn >> tempAnim.numJoints;                // Store number of joints (must match .md5mesh)            }            else if ( checkString == L"frameRate" )            {                fileIn >> tempAnim.frameRate;                // Store animation's frame rate (frames per second)            }            else if ( checkString == L"numAnimatedComponents" )            {                fileIn >> tempAnim.numAnimatedComponents;    // Number of components in each frame section            }                        ...    }    else    // If the file was not loaded    {        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;    }    return true;}

导入关节层级

下面导入关节层级,检测确保该层级与.md5mesh文件中相匹配。若不匹配,则返回false。

            else if ( checkString == L"hierarchy" )            {                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)    // Load in each joint                {                    AnimJointInfo tempJoint;                    fileIn >> tempJoint.name;        // Get joints name                    // Sometimes the names might contain spaces. If that is the case, we need to continue                    // to read the name until we get to the closing " (quotation marks)                    if(tempJoint.name[tempJoint.name.size()-1] != '"')                    {                        wchar_t checkChar;                        bool jointNameFound = false;                        while(!jointNameFound)                        {                            checkChar = fileIn.get();                            if(checkChar == '"')                                jointNameFound = true;                                    tempJoint.name += checkChar;                                                                                    }                    }                    // Remove the quotation marks from joints name                    tempJoint.name.erase(0, 1);                    tempJoint.name.erase(tempJoint.name.size()-1, 1);                    fileIn >> tempJoint.parentID;            // Get joints parent ID                    fileIn >> tempJoint.flags;                // Get flags                    fileIn >> tempJoint.startIndex;            // Get joints start index                    // Make sure the joint exists in the model, and the parent ID's match up                    // because the bind pose (md5mesh) joint hierarchy and the animations (md5anim)                    // joint hierarchy must match up                    bool jointMatchFound = false;                    for(int k = 0; k < MD5Model.numJoints; k++)                    {                        if(MD5Model.joints[k].name == tempJoint.name)                        {                            if(MD5Model.joints[k].parentID == tempJoint.parentID)                            {                                jointMatchFound = true;                                tempAnim.jointInfo.push_back(tempJoint);                            }                        }                    }                    if(!jointMatchFound)                    // If the skeleton system does not match up, return false                        return false;                        // You might want to add an error message here                    std::getline(fileIn, checkString);        // Skip rest of this line                }            }



导入每个帧包围盒(AABB)

现在导入每个帧包围盒或是最小或最大点。

            else if ( checkString == L"bounds" )            // Load in the AABB for each animation            {                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numFrames; i++)                {                    BoundingBox tempBB;                    fileIn >> checkString;                    // Skip "("                    fileIn >> tempBB.min.x >> tempBB.min.z >> tempBB.min.y;                    fileIn >> checkString >> checkString;    // Skip ") ("                    fileIn >> tempBB.max.x >> tempBB.max.z >> tempBB.max.y;                    fileIn >> checkString;                    // Skip ")"                    tempAnim.frameBounds.push_back(tempBB);                }            }            



导入基础帧

骨骼以及所有的帧骨骼都是基于此来构建的。

            else if ( checkString == L"baseframe" )            // This is the default position for the animation            {                                                // All frames will build their skeletons off this                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)                {                    Joint tempBFJ;                    fileIn >> checkString;                        // Skip "("                    fileIn >> tempBFJ.pos.x >> tempBFJ.pos.z >> tempBFJ.pos.y;                    fileIn >> checkString >> checkString;        // Skip ") ("                    fileIn >> tempBFJ.orientation.x >> tempBFJ.orientation.z >> tempBFJ.orientation.y;                    fileIn >> checkString;                        // Skip ")"                    tempAnim.baseFrameJoints.push_back(tempBFJ);                }            }


导入帧

帧段落包含一个浮点值的数组(由头文件中numAnimatedComponents中描述的数)

            else if ( checkString == L"frame" )        // Load in each frames skeleton (the parts of each joint that changed from the base frame)            {                FrameData tempFrame;                fileIn >> tempFrame.frameID;        // Get the frame ID                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numAnimatedComponents; i++)                {                    float tempData;                    fileIn >> tempData;                // Get the data                    tempFrame.frameData.push_back(tempData);                }                tempAnim.frameData.push_back(tempFrame);



创建帧骨骼

在导入完包含有一批浮点数据的帧之后,就可以为该帧创建帧骨骼了。这也是关节flag以及起始索引导入的地方。

flag是一个64位的数字(0和1表示的),标志着关节的哪一个部位要更新。它是顺序的从关节位置x值的第一个要更新的位到最后一个要更新的方向z值。比如,1或000001表示关节位置x应该由帧数据数组的浮点值替换。7或者000111表示关节位置的x,y和z值要由帧数据中接下来的三个值来更新。63或111111表示位置和方向的x,y和z值都要更新。

关节的起始索引表示在帧数据数组中的起始位置,或者是用来更新关节的起始值。检测关节的六个标志位,若发现有一个设置了,则替换位移或方向的x,y和z值,然后增加用来跟踪帧数据数组中位置的k值。

在更新完基础帧骨骼关节之后,会在更新帧关节前基于它们的父关节位移和方向计算方向四元素的w值。

                ///*** build the frame skeleton ***///                std::vector<Joint> tempSkeleton;                for(int i = 0; i < tempAnim.jointInfo.size(); i++)                {                    int k = 0;                        // Keep track of position in frameData array                    // Start the frames joint with the base frame's joint                    Joint tempFrameJoint = tempAnim.baseFrameJoints[i];                    tempFrameJoint.parentID = tempAnim.jointInfo[i].parentID;                    // Notice how I have been flipping y and z. this is because some modeling programs such as                    // 3ds max (which is what I use) use a right handed coordinate system. Because of this, we                    // need to flip the y and z axes. If your having problems loading some models, it's possible                    // the model was created in a left hand coordinate system. in that case, just reflip all the                    // y and z axes in our md5 mesh and anim loader.                    if(tempAnim.jointInfo[i].flags & 1)        // pos.x    ( 000001 )                        tempFrameJoint.pos.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 2)        // pos.y    ( 000010 )                        tempFrameJoint.pos.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 4)        // pos.z    ( 000100 )                        tempFrameJoint.pos.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 8)        // orientation.x    ( 001000 )                        tempFrameJoint.orientation.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 16)    // orientation.y    ( 010000 )                        tempFrameJoint.orientation.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 32)    // orientation.z    ( 100000 )                        tempFrameJoint.orientation.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    // Compute the quaternions w                    float t = 1.0f - ( tempFrameJoint.orientation.x * tempFrameJoint.orientation.x )                        - ( tempFrameJoint.orientation.y * tempFrameJoint.orientation.y )                        - ( tempFrameJoint.orientation.z * tempFrameJoint.orientation.z );                    if ( t < 0.0f )                    {                        tempFrameJoint.orientation.w = 0.0f;                    }                    else                    {                        tempFrameJoint.orientation.w = -sqrtf(t);                    }



基于父关节的位移和方向更新帧骨骼关节

遍历每个关节,并找到它的父关节(只要有父关节的都要找,同时根关节没有父关节)。然后在父关节的方向基础上旋转关节的位移。然后将父关节位移添加到子关节位移上并把它放到模型空间中去。随后再将父关节和子关节相乘(parent*child)重新定位子关节。

                    // Now, if the upper arm of your skeleton moves, you need to also move the lower part of your arm, and then the hands, and then finally the fingers (possibly weapon or tool too)                    // This is where joint hierarchy comes in. We start at the top of the hierarchy, and move down to each joints child, rotating and translating them based on their parents rotation                    // and translation. We can assume that by the time we get to the child, the parent has already been rotated and transformed based of it's parent. We can assume this because                    // the child should never come before the parent in the files we loaded in.                    if(tempFrameJoint.parentID >= 0)                    {                        Joint parentJoint = tempSkeleton[tempFrameJoint.parentID];                        // Turn the XMFLOAT3 and 4's into vectors for easier computation                        XMVECTOR parentJointOrientation = XMVectorSet(parentJoint.orientation.x, parentJoint.orientation.y, parentJoint.orientation.z, parentJoint.orientation.w);                        XMVECTOR tempJointPos = XMVectorSet(tempFrameJoint.pos.x, tempFrameJoint.pos.y, tempFrameJoint.pos.z, 0.0f);                        XMVECTOR parentOrientationConjugate = XMVectorSet(-parentJoint.orientation.x, -parentJoint.orientation.y, -parentJoint.orientation.z, parentJoint.orientation.w);                        // Calculate current joints position relative to its parents position                        XMFLOAT3 rotatedPos;                        XMStoreFloat3(&rotatedPos, XMQuaternionMultiply(XMQuaternionMultiply(parentJointOrientation, tempJointPos), parentOrientationConjugate));                        // Translate the joint to model space by adding the parent joint's pos to it                        tempFrameJoint.pos.x = rotatedPos.x + parentJoint.pos.x;                        tempFrameJoint.pos.y = rotatedPos.y + parentJoint.pos.y;                        tempFrameJoint.pos.z = rotatedPos.z + parentJoint.pos.z;                        // Currently the joint is oriented in its parent joints space, we now need to orient it in                        // model space by multiplying the two orientations together (parentOrientation * childOrientation) <- In that order                        XMVECTOR tempJointOrient = XMVectorSet(tempFrameJoint.orientation.x, tempFrameJoint.orientation.y, tempFrameJoint.orientation.z, tempFrameJoint.orientation.w);                        tempJointOrient = XMQuaternionMultiply(parentJointOrientation, tempJointOrient);                        // Normalize the orienation quaternion                        tempJointOrient = XMQuaternionNormalize(tempJointOrient);                        XMStoreFloat4(&tempFrameJoint.orientation, tempJointOrient);                    }                    // Store the joint into our temporary frame skeleton                    tempSkeleton.push_back(tempFrameJoint);                }                // Push back our newly created frame skeleton into the animation's frameSkeleton array                tempAnim.frameSkeleton.push_back(tempSkeleton);                fileIn >> checkString;                // Skip closing bracket "}"            }        }



计算一些帧相关的东西

需要知道每帧的时长以及整个动画时长,所以要很快速的计算这些,还要确保当前动画时间设为0。这之后,将临时动画push会模型动画向量中并退出函数。

        // Calculate and store some usefull animation data        tempAnim.frameTime = 1.0f / tempAnim.frameRate;                        // Set the time per frame        tempAnim.totalAnimTime = tempAnim.numFrames * tempAnim.frameTime;    // Set the total time the animation takes        tempAnim.currAnimTime = 0.0f;                                        // Set the current time to zero        MD5Model.animations.push_back(tempAnim);                            // Push back the animation into our model object


UpdateMD5Model()函数

只要用动画来更新模型就要调用该函数。

void UpdateMD5Model(Model3D& MD5Model, float deltaTime, int animation){    MD5Model.animations[animation].currAnimTime += deltaTime;            // Update the current animation time    if(MD5Model.animations[animation].currAnimTime > MD5Model.animations[animation].totalAnimTime)        MD5Model.animations[animation].currAnimTime = 0.0f;    // Which frame are we on    float currentFrame = MD5Model.animations[animation].currAnimTime * MD5Model.animations[animation].frameRate;        int frame0 = floorf( currentFrame );    int frame1 = frame0 + 1;    // Make sure we don't go over the number of frames        if(frame0 == MD5Model.animations[animation].numFrames-1)        frame1 = 0;    float interpolation = currentFrame - frame0;    // Get the remainder (in time) between frame0 and frame1 to use as interpolation factor    std::vector<Joint> interpolatedSkeleton;        // Create a frame skeleton to store the interpolated skeletons in    // Compute the interpolated skeleton    for( int i = 0; i < MD5Model.animations[animation].numJoints; i++)    {        Joint tempJoint;        Joint joint0 = MD5Model.animations[animation].frameSkeleton[frame0][i];        // Get the i'th joint of frame0's skeleton        Joint joint1 = MD5Model.animations[animation].frameSkeleton[frame1][i];        // Get the i'th joint of frame1's skeleton        tempJoint.parentID = joint0.parentID;                                            // Set the tempJoints parent id        // Turn the two quaternions into XMVECTORs for easy computations        XMVECTOR joint0Orient = XMVectorSet(joint0.orientation.x, joint0.orientation.y, joint0.orientation.z, joint0.orientation.w);        XMVECTOR joint1Orient = XMVectorSet(joint1.orientation.x, joint1.orientation.y, joint1.orientation.z, joint1.orientation.w);        // Interpolate positions        tempJoint.pos.x = joint0.pos.x + (interpolation * (joint1.pos.x - joint0.pos.x));        tempJoint.pos.y = joint0.pos.y + (interpolation * (joint1.pos.y - joint0.pos.y));        tempJoint.pos.z = joint0.pos.z + (interpolation * (joint1.pos.z - joint0.pos.z));        // Interpolate orientations using spherical interpolation (Slerp)        XMStoreFloat4(&tempJoint.orientation, XMQuaternionSlerp(joint0Orient, joint1Orient, interpolation));        interpolatedSkeleton.push_back(tempJoint);        // Push the joint back into our interpolated skeleton    }    for ( int k = 0; k < MD5Model.numSubsets; k++)    {        for ( int i = 0; i < MD5Model.subsets[k].vertices.size(); ++i )        {            Vertex tempVert = MD5Model.subsets[k].vertices[i];            tempVert.pos = XMFLOAT3(0, 0, 0);    // Make sure the vertex's pos is cleared first            tempVert.normal = XMFLOAT3(0,0,0);    // Clear vertices normal            // Sum up the joints and weights information to get vertex's position and normal            for ( int j = 0; j < tempVert.WeightCount; ++j )            {                Weight tempWeight = MD5Model.subsets[k].weights[tempVert.StartWeight + j];                Joint tempJoint = interpolatedSkeleton[tempWeight.jointID];                // Convert joint orientation and weight pos to vectors for easier computation                XMVECTOR tempJointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);                XMVECTOR tempWeightPos = XMVectorSet(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f);                // We will need to use the conjugate of the joint orientation quaternion                XMVECTOR tempJointOrientationConjugate = XMQuaternionInverse(tempJointOrientation);                // Calculate vertex position (in joint space, eg. rotate the point around (0,0,0)) for this weight using the joint orientation quaternion and its conjugate                // We can rotate a point using a quaternion with the equation "rotatedPoint = quaternion * point * quaternionConjugate"                XMFLOAT3 rotatedPoint;                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightPos), tempJointOrientationConjugate));                // Now move the verices position from joint space (0,0,0) to the joints position in world space, taking the weights bias into account                tempVert.pos.x += ( tempJoint.pos.x + rotatedPoint.x ) * tempWeight.bias;                tempVert.pos.y += ( tempJoint.pos.y + rotatedPoint.y ) * tempWeight.bias;                tempVert.pos.z += ( tempJoint.pos.z + rotatedPoint.z ) * tempWeight.bias;                // Compute the normals for this frames skeleton using the weight normals from before                // We can comput the normals the same way we compute the vertices position, only we don't have to translate them (just rotate)                XMVECTOR tempWeightNormal = XMVectorSet(tempWeight.normal.x, tempWeight.normal.y, tempWeight.normal.z, 0.0f);                // Rotate the normal                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightNormal), tempJointOrientationConjugate));                // Add to vertices normal and ake weight bias into account                tempVert.normal.x -= rotatedPoint.x * tempWeight.bias;                tempVert.normal.y -= rotatedPoint.y * tempWeight.bias;                tempVert.normal.z -= rotatedPoint.z * tempWeight.bias;            }            MD5Model.subsets[k].positions[i] = tempVert.pos;                // Store the vertices position in the position vector instead of straight into the vertex vector            MD5Model.subsets[k].vertices[i].normal = tempVert.normal;        // Store the vertices normal            XMStoreFloat3(&MD5Model.subsets[k].vertices[i].normal, XMVector3Normalize(XMLoadFloat3(&MD5Model.subsets[k].vertices[i].normal)));        }        // Put the positions into the vertices for this subset        for(int i = 0; i < MD5Model.subsets[k].vertices.size(); i++)        {            MD5Model.subsets[k].vertices[i].pos = MD5Model.subsets[k].positions[i];        }        // Update the subsets vertex buffer        // First lock the buffer        D3D11_MAPPED_SUBRESOURCE mappedVertBuff;        hr = d3d11DevCon->Map(MD5Model.subsets[k].vertBuff, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedVertBuff);        // Copy the data into the vertex buffer.        memcpy(mappedVertBuff.pData, &MD5Model.subsets[k].vertices[0], (sizeof(Vertex) * MD5Model.subsets[k].vertices.size()));        d3d11DevCon->Unmap(MD5Model.subsets[k].vertBuff, 0);        // The line below is another way to update a buffer. You will use this when you want to update a buffer less        // than once per frame, since the GPU reads will be faster (the buffer was created as a DEFAULT buffer instead        // of a DYNAMIC buffer), and the CPU writes will be slower. You can try both methods to find out which one is faster        // for you. if you want to use the line below, you will have to create the buffer with D3D11_USAGE_DEFAULT instead        // of D3D11_USAGE_DYNAMIC        //d3d11DevCon->UpdateSubresource( MD5Model.subsets[k].vertBuff, 0, NULL, &MD5Model.subsets[k].vertices[0], 0, 0 );    }}


哪个帧要使用

通过更新动画当前时间来开始该函数。若当前动画时间超出了整个动画时间,则复位当前动画时间为0并重启动画。随后就能知道正在跑的动画是哪一帧。可通过将当前动画时间乘以frameRate来得到一个结果,将结果四舍五入到最近的整数值得到当前的帧,再在该结果上加1则得到下个帧。为了保持一个流畅的动画,将会插值这两个帧来得到当前帧骨骼。当插值两个骨骼帧时,必须有一个插值因子,它能够通过将当前动画时间乘以framerate中得到的余数得到。(比如currentFrameTime = 5.367,currentFrame=5, interpolationFactor=0.3667)

void UpdateMD5Model(Model3D& MD5Model, float deltaTime, int animation){    MD5Model.animations[animation].currAnimTime += deltaTime;            // Update the current animation time    if(MD5Model.animations[animation].currAnimTime > MD5Model.animations[animation].totalAnimTime)        MD5Model.animations[animation].currAnimTime = 0.0f;    // Which frame are we on    float currentFrame = MD5Model.animations[animation].currAnimTime * MD5Model.animations[animation].frameRate;        int frame0 = floorf( currentFrame );    int frame1 = frame0 + 1;    // Make sure we don't go over the number of frames        if(frame0 == MD5Model.animations[animation].numFrames-1)        frame1 = 0;    float interpolation = currentFrame - frame0;    // Get the remainder (in time) between frame0 and frame1 to use as interpolation factor    std::vector<Joint> interpolatedSkeleton;        // Create a frame skeleton to store the interpolated skeletons in


计算插值骨骼

首先使用方程式找到骨骼的插值位置,然后使用球面线性插值技术来插值两个表示关节方向的四元素。可根据插值因子来使用函数XMQuaternionSlerp()去插值两个四元素。

    // Compute the interpolated skeleton    for( int i = 0; i < MD5Model.animations[animation].numJoints; i++)    {        Joint tempJoint;        Joint joint0 = MD5Model.animations[animation].frameSkeleton[frame0][i];        // Get the i'th joint of frame0's skeleton        Joint joint1 = MD5Model.animations[animation].frameSkeleton[frame1][i];        // Get the i'th joint of frame1's skeleton        tempJoint.parentID = joint0.parentID;                                            // Set the tempJoints parent id        // Turn the two quaternions into XMVECTORs for easy computations        XMVECTOR joint0Orient = XMVectorSet(joint0.orientation.x, joint0.orientation.y, joint0.orientation.z, joint0.orientation.w);        XMVECTOR joint1Orient = XMVectorSet(joint1.orientation.x, joint1.orientation.y, joint1.orientation.z, joint1.orientation.w);        // Interpolate positions        tempJoint.pos.x = joint0.pos.x + (interpolation * (joint1.pos.x - joint0.pos.x));        tempJoint.pos.y = joint0.pos.y + (interpolation * (joint1.pos.y - joint0.pos.y));        tempJoint.pos.z = joint0.pos.z + (interpolation * (joint1.pos.z - joint0.pos.z));        // Interpolate orientations using spherical interpolation (Slerp)        XMStoreFloat4(&tempJoint.orientation, XMQuaternionSlerp(joint0Orient, joint1Orient, interpolation));        interpolatedSkeleton.push_back(tempJoint);        // Push the joint back into our interpolated skeleton    }


计算顶点位置和法线

遍历模型中的每个子集,然后遍历每个顶点,最后遍历顶点的每个权重。可使用上一章节中创建绑定姿态模型的方法来计算顶点位置。计算完顶点位置后,在计算法线。首先找到权重法线来计算法线值。通过旋转基于关节方向的法线来得到权重法线。在得到权重法线后,将它乘以权重偏差因子,最后将它与顶点法线相加。

    for ( int k = 0; k < MD5Model.numSubsets; k++)    {        for ( int i = 0; i < MD5Model.subsets[k].vertices.size(); ++i )        {            Vertex tempVert = MD5Model.subsets[k].vertices[i];            tempVert.pos = XMFLOAT3(0, 0, 0);    // Make sure the vertex's pos is cleared first            tempVert.normal = XMFLOAT3(0,0,0);    // Clear vertices normal            // Sum up the joints and weights information to get vertex's position and normal            for ( int j = 0; j < tempVert.WeightCount; ++j )            {                Weight tempWeight = MD5Model.subsets[k].weights[tempVert.StartWeight + j];                Joint tempJoint = interpolatedSkeleton[tempWeight.jointID];                // Convert joint orientation and weight pos to vectors for easier computation                XMVECTOR tempJointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);                XMVECTOR tempWeightPos = XMVectorSet(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f);                // We will need to use the conjugate of the joint orientation quaternion                XMVECTOR tempJointOrientationConjugate = XMQuaternionInverse(tempJointOrientation);                // Calculate vertex position (in joint space, eg. rotate the point around (0,0,0)) for this weight using the joint orientation quaternion and its conjugate                // We can rotate a point using a quaternion with the equation "rotatedPoint = quaternion * point * quaternionConjugate"                XMFLOAT3 rotatedPoint;                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightPos), tempJointOrientationConjugate));                // Now move the verices position from joint space (0,0,0) to the joints position in world space, taking the weights bias into account                tempVert.pos.x += ( tempJoint.pos.x + rotatedPoint.x ) * tempWeight.bias;                tempVert.pos.y += ( tempJoint.pos.y + rotatedPoint.y ) * tempWeight.bias;                tempVert.pos.z += ( tempJoint.pos.z + rotatedPoint.z ) * tempWeight.bias;                // Compute the normals for this frames skeleton using the weight normals from before                // We can comput the normals the same way we compute the vertices position, only we don't have to translate them (just rotate)                XMVECTOR tempWeightNormal = XMVectorSet(tempWeight.normal.x, tempWeight.normal.y, tempWeight.normal.z, 0.0f);                // Rotate the normal                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightNormal), tempJointOrientationConjugate));                // Add to vertices normal and ake weight bias into account                tempVert.normal.x -= rotatedPoint.x * tempWeight.bias;                tempVert.normal.y -= rotatedPoint.y * tempWeight.bias;                tempVert.normal.z -= rotatedPoint.z * tempWeight.bias;            }            MD5Model.subsets[k].positions[i] = tempVert.pos;                // Store the vertices position in the position vector instead of straight into the vertex vector            MD5Model.subsets[k].vertices[i].normal = tempVert.normal;        // Store the vertices normal            XMStoreFloat3(&MD5Model.subsets[k].vertices[i].normal, XMVector3Normalize(XMLoadFloat3(&MD5Model.subsets[k].vertices[i].normal)));        }


更新缓冲

最后就是更新缓冲了。在最后一个章节可知已经创建了一个动态顶点缓冲。要更新directx中的动态缓冲,首先需要锁住它们,然后将内容拷贝到想要的缓冲中去。map函数返回一个填充的D3D11_MAPPED_SUBRESOURCE对象,其中的pData成员为一个指向缓冲的起始数据的指针。可使用该指针来拷贝顶点数组到缓冲,然后在unmapping这个缓冲。当拷贝向量到缓冲中时,必须使用它的指针,且该指针必须是指向想要拷贝的缓冲的第一个元素[0]。这是因为指针直接指向向量的开始也将可通过它指向其他大量额外的用于描述向量的数据。

若使用默认缓冲,则可使用函数UpdateSubresource来更新它,虽然用这种缓冲不是很频繁,因为它比使用动态缓冲要慢的多。

        // Put the positions into the vertices for this subset        for(int i = 0; i < MD5Model.subsets[k].vertices.size(); i++)        {            MD5Model.subsets[k].vertices[i].pos = MD5Model.subsets[k].positions[i];        }        // Update the subsets vertex buffer        // First lock the buffer        D3D11_MAPPED_SUBRESOURCE mappedVertBuff;        hr = d3d11DevCon->Map(MD5Model.subsets[k].vertBuff, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedVertBuff);        // Copy the data into the vertex buffer.        memcpy(mappedVertBuff.pData, &MD5Model.subsets[k].vertices[0], (sizeof(Vertex) * MD5Model.subsets[k].vertices.size()));        d3d11DevCon->Unmap(MD5Model.subsets[k].vertBuff, 0);        // The line below is another way to update a buffer. You will use this when you want to update a buffer less        // than once per frame, since the GPU reads will be faster (the buffer was created as a DEFAULT buffer instead        // of a DYNAMIC buffer), and the CPU writes will be slower. You can try both methods to find out which one is faster        // for you. if you want to use the line below, you will have to create the buffer with D3D11_USAGE_DEFAULT instead        // of D3D11_USAGE_DYNAMIC        //d3d11DevCon->UpdateSubresource( MD5Model.subsets[k].vertBuff, 0, NULL, &MD5Model.subsets[k].vertices[0], 0, 0 );    }}

调用函数LoadMD5Model()

剩下的就是调用函数导入模型动画了。

    if(!LoadMD5Anim(L"boy.md5anim", NewMD5Model))        return false;



实例:

#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 <windows.h>#include "Resource.h"#include <d3d11.h>#include <d3dx11.h>#include <d3dx10.h>#include <xnamath.h>#include <D3D10_1.h>#include <DXGI.h>#include <D2D1.h>#include <sstream>#include <dwrite.h>///////////////**************new**************////////////////////#include <dinput.h>///////////////**************new**************///////////////////////////////////**************new**************////////////////////#include <vector>#include <fstream>#include <istream>///////////////**************new**************//////////////////////全局描述符IDXGISwapChain* SwapChain;ID3D11Device* d3d11Device;ID3D11DeviceContext* d3d11DevCon;ID3D11RenderTargetView* renderTargetView;//索引缓冲//ID3D11Buffer* squareIndexBuffer;//深度值-20170927ID3D11DepthStencilView* depthStencilView;ID3D11Texture2D* depthStencilBuffer;//着色器//ID3D11Buffer* squareVertBuffer;ID3D11VertexShader* VS;ID3D11PixelShader* PS;ID3D11PixelShader* D2D_PS;ID3D10Blob* D2D_PS_Buffer;ID3D10Blob* VS_Buffer;ID3D10Blob* 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<int> meshSubsetIndexStart;std::vector<int> meshSubsetTexture;//纹理和材质变量,用于所有的网格的加载std::vector<ID3D11ShaderResourceView*> meshSRV;std::vector<std::wstring> 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**************//////////////////////Because of HLSL structure packing, we will use windows BOOL//instead of bool because HLSL packs things into 4 bytes, and//bool is only one byte, where BOOL is 4 bytesBOOL hasNormMap;///////////////**************new**************////////////////////};cbPerObject cbPerObj;///////////////**************new**************//////////////////////创建材质结构体struct SurfaceMaterial{std::wstring matName;XMFLOAT4 difColor;int texArrayIndex;///////////////**************new**************////////////////////int normMapTexArrayIndex;bool hasNormMap;///////////////**************new**************////////////////////bool hasTexture;bool transparent;};std::vector<SurfaceMaterial> material;//自创建surfaceMaterial结构体后,定义函数LoadObjModelbool LoadObjModel(std::wstring filename,//.obj filenameID3D11Buffer** vertBuff,//mesh vertex bufferID3D11Buffer** indexBuff,//mesh index bufferstd::vector<int>& subsetIndexStart,//start index of each subsetstd::vector<int>& subsetMaterialArray,//index value of material for each subsetstd::vector<SurfaceMaterial>& material,//vector of material structuresint& subsetCount,//Number of subsets in meshbool 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,float tx, float ty, float tz): pos(x,y,z), texCoord(u, v), normal(nx, ny, nz),tangent(tx, ty, tz){}XMFLOAT3 pos;XMFLOAT2 texCoord;XMFLOAT3 normal;///////////////**************new**************////////////////////XMFLOAT3 tangent;XMFLOAT3 biTangent;///////////////**************new**************////////////////////    // 不会传入着色器中    int StartWeight;    int WeightCount;///////////////**************new**************////////////////////};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},///////////////**************new**************////////////////////{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT,    0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0}///////////////**************new**************////////////////////};UINT numElements = ARRAYSIZE(layout);///////////////**************new**************////////////////////struct Joint{    std::wstring name;    int parentID;    XMFLOAT3 pos;    XMFLOAT4 orientation;};///////////////**************new**************////////////////////struct BoundingBox{    XMFLOAT3 min;    XMFLOAT3 max;};struct FrameData{    int frameID;    std::vector<float> frameData;};struct AnimJointInfo{    std::wstring name;    int parentID;    int flags;    int startIndex;};struct ModelAnimation{    int numFrames;    int numJoints;    int frameRate;    int numAnimatedComponents;    float frameTime;    float totalAnimTime;    float currAnimTime;    std::vector<AnimJointInfo> jointInfo;    std::vector<BoundingBox> frameBounds;    std::vector<Joint>    baseFrameJoints;    std::vector<FrameData>    frameData;    std::vector<std::vector<Joint>> frameSkeleton;};///////////////**************new**************////////////////////struct Weight{    int jointID;    float bias;    XMFLOAT3 pos;    ///////////////**************new**************////////////////////    XMFLOAT3 normal;    ///////////////**************new**************////////////////////};struct ModelSubset{    int texArrayIndex;    int numTriangles;    std::vector<Vertex> vertices;    std::vector<XMFLOAT3> jointSpaceNormals;    std::vector<DWORD> indices;    std::vector<Weight> weights;    std::vector<XMFLOAT3> positions;    ID3D11Buffer* vertBuff;     ID3D11Buffer* indexBuff;};struct Model3D{    int numSubsets;    int numJoints;    std::vector<Joint> joints;    std::vector<ModelSubset> subsets;    ///////////////**************new**************////////////////////    std::vector<ModelAnimation> animations;    ///////////////**************new**************////////////////////};XMMATRIX smilesWorld;Model3D NewMD5Model;//LoadMD5Model() function prototypebool LoadMD5Model(std::wstring filename,    Model3D& MD5Model,    std::vector<ID3D11ShaderResourceView*>& shaderResourceViewArray,    std::vector<std::wstring> texFileNameArray);///////////////**************new**************////////////////////bool LoadMD5Anim(std::wstring filename,    Model3D& MD5Model);void UpdateMD5Model(Model3D& MD5Model, float deltaTime, int animation);//主函数,传入应用程序句柄hInstance,前一个应用程序句柄hPrevInstance,传给函数处理的命令行lpCmdLine以及窗口显示方式的nShowCmdint 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;}/////newif (!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是一个指向处理窗口消息函数的指针,设置窗口处理函数的函数名WndProcwc.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 + 1);//附加到窗口的菜单名字,不需要的话设置为NULLwc.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"model md5anim",WS_OVERLAPPEDWINDOW,         CW_USEDEFAULT, CW_USEDEFAULT,        width, height,NULL,NULL,hInstance,NULL);if (!hwnd)    {        MessageBox(NULL, L"Error creating window",            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和DirectWriteInitD2D_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; //绑定到OMdepthStencilDesc.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 factoryID2D1Factory *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);//DirectWritehr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown **>(&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);    // 自由相机    camRight = XMVector3TransformCoord(DefaultRight, camRotationMatrix);    camForward = XMVector3TransformCoord(DefaultForward, camRotationMatrix);    camUp = XMVector3Cross(camForward, camRight);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 = 10.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;}    ///////////////**************new**************////////////////////    if(keyboardState[DIK_R] & 0X80)    {        float timeFactor = 1.0f;    // 可通过改变该值来加速或减速时间        UpdateMD5Model(NewMD5Model, time*timeFactor, 0);    }    ///////////////**************new**************////////////////////if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)){camYaw += mouseLastState.lX * 0.001f;camPitch += mouseCurrState.lY * 0.001f;mouseLastState = mouseCurrState;}UpdateCamera();    return;}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 1Transparency->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();/// newcbPerFrameBuffer->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**************////////////////////    for(int i = 0; i < NewMD5Model.numSubsets; i++)    {        NewMD5Model.subsets[i].indexBuff->Release();        NewMD5Model.subsets[i].vertBuff->Release();    }}///////////////**************new**************////////////////////bool LoadMD5Anim(std::wstring filename,    Model3D& MD5Model){        ModelAnimation tempAnim;                        // Temp animation to later store in our model's animation array    std::wifstream fileIn (filename.c_str());        // Open file    std::wstring checkString;                        // Stores the next string from our file    if(fileIn)                                        // Check if the file was opened    {        while(fileIn)                                // Loop until the end of the file is reached        {                fileIn >> checkString;                    // Get next string from file            if ( checkString == L"MD5Version" )        // Get MD5 version (this function supports version 10)            {                fileIn >> checkString;                /*MessageBox(0, checkString.c_str(),    //display message                L"MD5Version", MB_OK);*/            }            else if ( checkString == L"commandline" )            {                std::getline(fileIn, checkString);    // Ignore the rest of this line            }            else if ( checkString == L"numFrames" )            {                fileIn >> tempAnim.numFrames;                // Store number of frames in this animation            }            else if ( checkString == L"numJoints" )            {                fileIn >> tempAnim.numJoints;                // Store number of joints (must match .md5mesh)            }            else if ( checkString == L"frameRate" )            {                fileIn >> tempAnim.frameRate;                // Store animation's frame rate (frames per second)            }            else if ( checkString == L"numAnimatedComponents" )            {                fileIn >> tempAnim.numAnimatedComponents;    // Number of components in each frame section            }            else if ( checkString == L"hierarchy" )            {                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)    // Load in each joint                {                    AnimJointInfo tempJoint;                    fileIn >> tempJoint.name;        // Get joints name                    // Sometimes the names might contain spaces. If that is the case, we need to continue                    // to read the name until we get to the closing " (quotation marks)                    if(tempJoint.name[tempJoint.name.size()-1] != '"')                    {                        wchar_t checkChar;                        bool jointNameFound = false;                        while(!jointNameFound)                        {                            checkChar = fileIn.get();                            if(checkChar == '"')                                jointNameFound = true;                                    tempJoint.name += checkChar;                                                                                    }                    }                    // Remove the quotation marks from joints name                    tempJoint.name.erase(0, 1);                    tempJoint.name.erase(tempJoint.name.size()-1, 1);                    fileIn >> tempJoint.parentID;            // Get joints parent ID                    fileIn >> tempJoint.flags;                // Get flags                    fileIn >> tempJoint.startIndex;            // Get joints start index                    // Make sure the joint exists in the model, and the parent ID's match up                    // because the bind pose (md5mesh) joint hierarchy and the animations (md5anim)                    // joint hierarchy must match up                    bool jointMatchFound = false;                    for(int k = 0; k < MD5Model.numJoints; k++)                    {                        if(MD5Model.joints[k].name == tempJoint.name)                        {                            if(MD5Model.joints[k].parentID == tempJoint.parentID)                            {                                jointMatchFound = true;                                tempAnim.jointInfo.push_back(tempJoint);                            }                        }                    }                    if(!jointMatchFound)                    // If the skeleton system does not match up, return false                        return false;                        // You might want to add an error message here                    std::getline(fileIn, checkString);        // Skip rest of this line                }            }            else if ( checkString == L"bounds" )            // Load in the AABB for each animation            {                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numFrames; i++)                {                    BoundingBox tempBB;                    fileIn >> checkString;                    // Skip "("                    fileIn >> tempBB.min.x >> tempBB.min.z >> tempBB.min.y;                    fileIn >> checkString >> checkString;    // Skip ") ("                    fileIn >> tempBB.max.x >> tempBB.max.z >> tempBB.max.y;                    fileIn >> checkString;                    // Skip ")"                    tempAnim.frameBounds.push_back(tempBB);                }            }                        else if ( checkString == L"baseframe" )            // This is the default position for the animation            {                                                // All frames will build their skeletons off this                fileIn >> checkString;                        // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numJoints; i++)                {                    Joint tempBFJ;                    fileIn >> checkString;                        // Skip "("                    fileIn >> tempBFJ.pos.x >> tempBFJ.pos.z >> tempBFJ.pos.y;                    fileIn >> checkString >> checkString;        // Skip ") ("                    fileIn >> tempBFJ.orientation.x >> tempBFJ.orientation.z >> tempBFJ.orientation.y;                    fileIn >> checkString;                        // Skip ")"                    tempAnim.baseFrameJoints.push_back(tempBFJ);                }            }            else if ( checkString == L"frame" )        // Load in each frames skeleton (the parts of each joint that changed from the base frame)            {                FrameData tempFrame;                fileIn >> tempFrame.frameID;        // Get the frame ID                fileIn >> checkString;                // Skip opening bracket "{"                for(int i = 0; i < tempAnim.numAnimatedComponents; i++)                {                    float tempData;                    fileIn >> tempData;                // Get the data                    tempFrame.frameData.push_back(tempData);                }                tempAnim.frameData.push_back(tempFrame);                ///*** build the frame skeleton ***///                std::vector<Joint> tempSkeleton;                for(int i = 0; i < tempAnim.jointInfo.size(); i++)                {                    int k = 0;                        // Keep track of position in frameData array                    // Start the frames joint with the base frame's joint                    Joint tempFrameJoint = tempAnim.baseFrameJoints[i];                    tempFrameJoint.parentID = tempAnim.jointInfo[i].parentID;                    // Notice how I have been flipping y and z. this is because some modeling programs such as                    // 3ds max (which is what I use) use a right handed coordinate system. Because of this, we                    // need to flip the y and z axes. If your having problems loading some models, it's possible                    // the model was created in a left hand coordinate system. in that case, just reflip all the                    // y and z axes in our md5 mesh and anim loader.                    if(tempAnim.jointInfo[i].flags & 1)        // pos.x    ( 000001 )                        tempFrameJoint.pos.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 2)        // pos.y    ( 000010 )                        tempFrameJoint.pos.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 4)        // pos.z    ( 000100 )                        tempFrameJoint.pos.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 8)        // orientation.x    ( 001000 )                        tempFrameJoint.orientation.x = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 16)    // orientation.y    ( 010000 )                        tempFrameJoint.orientation.z = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    if(tempAnim.jointInfo[i].flags & 32)    // orientation.z    ( 100000 )                        tempFrameJoint.orientation.y = tempFrame.frameData[tempAnim.jointInfo[i].startIndex + k++];                    // Compute the quaternions w                    float t = 1.0f - ( tempFrameJoint.orientation.x * tempFrameJoint.orientation.x )                        - ( tempFrameJoint.orientation.y * tempFrameJoint.orientation.y )                        - ( tempFrameJoint.orientation.z * tempFrameJoint.orientation.z );                    if ( t < 0.0f )                    {                        tempFrameJoint.orientation.w = 0.0f;                    }                    else                    {                        tempFrameJoint.orientation.w = -sqrtf(t);                    }                    // Now, if the upper arm of your skeleton moves, you need to also move the lower part of your arm, and then the hands, and then finally the fingers (possibly weapon or tool too)                    // This is where joint hierarchy comes in. We start at the top of the hierarchy, and move down to each joints child, rotating and translating them based on their parents rotation                    // and translation. We can assume that by the time we get to the child, the parent has already been rotated and transformed based of it's parent. We can assume this because                    // the child should never come before the parent in the files we loaded in.                    if(tempFrameJoint.parentID >= 0)                    {                        Joint parentJoint = tempSkeleton[tempFrameJoint.parentID];                        // Turn the XMFLOAT3 and 4's into vectors for easier computation                        XMVECTOR parentJointOrientation = XMVectorSet(parentJoint.orientation.x, parentJoint.orientation.y, parentJoint.orientation.z, parentJoint.orientation.w);                        XMVECTOR tempJointPos = XMVectorSet(tempFrameJoint.pos.x, tempFrameJoint.pos.y, tempFrameJoint.pos.z, 0.0f);                        XMVECTOR parentOrientationConjugate = XMVectorSet(-parentJoint.orientation.x, -parentJoint.orientation.y, -parentJoint.orientation.z, parentJoint.orientation.w);                        // Calculate current joints position relative to its parents position                        XMFLOAT3 rotatedPos;                        XMStoreFloat3(&rotatedPos, XMQuaternionMultiply(XMQuaternionMultiply(parentJointOrientation, tempJointPos), parentOrientationConjugate));                        // Translate the joint to model space by adding the parent joint's pos to it                        tempFrameJoint.pos.x = rotatedPos.x + parentJoint.pos.x;                        tempFrameJoint.pos.y = rotatedPos.y + parentJoint.pos.y;                        tempFrameJoint.pos.z = rotatedPos.z + parentJoint.pos.z;                        // Currently the joint is oriented in its parent joints space, we now need to orient it in                        // model space by multiplying the two orientations together (parentOrientation * childOrientation) <- In that order                        XMVECTOR tempJointOrient = XMVectorSet(tempFrameJoint.orientation.x, tempFrameJoint.orientation.y, tempFrameJoint.orientation.z, tempFrameJoint.orientation.w);                        tempJointOrient = XMQuaternionMultiply(parentJointOrientation, tempJointOrient);                        // Normalize the orienation quaternion                        tempJointOrient = XMQuaternionNormalize(tempJointOrient);                        XMStoreFloat4(&tempFrameJoint.orientation, tempJointOrient);                    }                    // Store the joint into our temporary frame skeleton                    tempSkeleton.push_back(tempFrameJoint);                }                // Push back our newly created frame skeleton into the animation's frameSkeleton array                tempAnim.frameSkeleton.push_back(tempSkeleton);                fileIn >> checkString;                // Skip closing bracket "}"            }        }        // Calculate and store some usefull animation data        tempAnim.frameTime = 1.0f / tempAnim.frameRate;                        // Set the time per frame        tempAnim.totalAnimTime = tempAnim.numFrames * tempAnim.frameTime;    // Set the total time the animation takes        tempAnim.currAnimTime = 0.0f;                                        // Set the current time to zero        MD5Model.animations.push_back(tempAnim);                            // Push back the animation into our model object    }    else    // If the file was not loaded    {        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;    }    return true;}void UpdateMD5Model(Model3D& MD5Model, float deltaTime, int animation){    MD5Model.animations[animation].currAnimTime += deltaTime;            // 更新当前动画时间    if(MD5Model.animations[animation].currAnimTime > MD5Model.animations[animation].totalAnimTime)        MD5Model.animations[animation].currAnimTime = 0.0f;    // 当前所在帧    float currentFrame = MD5Model.animations[animation].currAnimTime * MD5Model.animations[animation].frameRate;        int frame0 = floorf( currentFrame );    int frame1 = frame0 + 1;    // 确保不会超过帧的数量    if(frame0 == MD5Model.animations[animation].numFrames-1)        frame1 = 0;    float interpolation = currentFrame - frame0;    // 获取frame0与frame1之间的余数作为插值因子    std::vector<Joint> interpolatedSkeleton;        // 创建一个帧骨骼来存储插值的骨骼    // 计算插值骨骼    for( int i = 0; i < MD5Model.animations[animation].numJoints; i++)    {        Joint tempJoint;        Joint joint0 = MD5Model.animations[animation].frameSkeleton[frame0][i];        // 获取frame0的骨骼的第i个关节        Joint joint1 = MD5Model.animations[animation].frameSkeleton[frame1][i];        // 获取frame1的骨骼的第i个关节        tempJoint.parentID = joint0.parentID;                                            // 设置tempJoint的父节点id        // 将两个四元素转换进XMVECTOR        XMVECTOR joint0Orient = XMVectorSet(joint0.orientation.x, joint0.orientation.y, joint0.orientation.z, joint0.orientation.w);        XMVECTOR joint1Orient = XMVectorSet(joint1.orientation.x, joint1.orientation.y, joint1.orientation.z, joint1.orientation.w);        // 插值位置        tempJoint.pos.x = joint0.pos.x + (interpolation * (joint1.pos.x - joint0.pos.x));        tempJoint.pos.y = joint0.pos.y + (interpolation * (joint1.pos.y - joint0.pos.y));        tempJoint.pos.z = joint0.pos.z + (interpolation * (joint1.pos.z - joint0.pos.z));        // 使用球面线性插值(Slerp)插值方向        XMStoreFloat4(&tempJoint.orientation, XMQuaternionSlerp(joint0Orient, joint1Orient, interpolation));        interpolatedSkeleton.push_back(tempJoint);        // 将关节push进插值的骨骼    }    for ( int k = 0; k < MD5Model.numSubsets; k++)    {        for ( int i = 0; i < MD5Model.subsets[k].vertices.size(); ++i )        {            Vertex tempVert = MD5Model.subsets[k].vertices[i];            tempVert.pos = XMFLOAT3(0, 0, 0);    // 首先确保顶点的位置被清除            tempVert.normal = XMFLOAT3(0,0,0);    // 清除顶点法线            // 总结关节和权重信息以获得顶点的位置和法线            for ( int j = 0; j < tempVert.WeightCount; ++j )            {                Weight tempWeight = MD5Model.subsets[k].weights[tempVert.StartWeight + j];                Joint tempJoint = interpolatedSkeleton[tempWeight.jointID];                // 将关节方向和权重位置转换到向量                XMVECTOR tempJointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);                XMVECTOR tempWeightPos = XMVectorSet(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f);                // 对关节方向四元数求反                XMVECTOR tempJointOrientationConjugate = XMQuaternionInverse(tempJointOrientation);                // 使用关节方向四元数和它的四元素的反数为该权重计算顶点位置(在关节空间,比如,绕着原点旋转)                // 可使用方程rotatedPoint = quaternion * point * quaternionConjugate来旋转一个点                XMFLOAT3 rotatedPoint;                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightPos), tempJointOrientationConjugate));                //将顶点位置从关节空间(0,0,0)位置移动到世界空间的关节位置,要考虑权重偏差                tempVert.pos.x += ( tempJoint.pos.x + rotatedPoint.x ) * tempWeight.bias;                tempVert.pos.y += ( tempJoint.pos.y + rotatedPoint.y ) * tempWeight.bias;                tempVert.pos.z += ( tempJoint.pos.z + rotatedPoint.z ) * tempWeight.bias;                // 根据前面的权重法线为该帧骨骼计算法线                // 可使用计算顶点位置一样的方法来计算法线,只是不必转换它们(仅仅旋转即可)                XMVECTOR tempWeightNormal = XMVectorSet(tempWeight.normal.x, tempWeight.normal.y, tempWeight.normal.z, 0.0f);                // 旋转法线                XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightNormal), tempJointOrientationConjugate));                // 添加到顶点法线并考虑权重偏差                tempVert.normal.x -= rotatedPoint.x * tempWeight.bias;                tempVert.normal.y -= rotatedPoint.y * tempWeight.bias;                tempVert.normal.z -= rotatedPoint.z * tempWeight.bias;            }            MD5Model.subsets[k].positions[i] = tempVert.pos;                // 将顶点位置存储进位置向量而不是直接存储到顶点向量            MD5Model.subsets[k].vertices[i].normal = tempVert.normal;        // 存储顶点法线            XMStoreFloat3(&MD5Model.subsets[k].vertices[i].normal, XMVector3Normalize(XMLoadFloat3(&MD5Model.subsets[k].vertices[i].normal)));        }        // 将位置放入该子集的顶点中去        for(int i = 0; i < MD5Model.subsets[k].vertices.size(); i++)        {            MD5Model.subsets[k].vertices[i].pos = MD5Model.subsets[k].positions[i];        }        // 更新子集顶点缓冲        // 先锁定缓冲        D3D11_MAPPED_SUBRESOURCE mappedVertBuff;        hr = d3d11DevCon->Map(MD5Model.subsets[k].vertBuff, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedVertBuff);        // 将数据拷贝到顶点缓冲中去        memcpy(mappedVertBuff.pData, &MD5Model.subsets[k].vertices[0], (sizeof(Vertex) * MD5Model.subsets[k].vertices.size()));        d3d11DevCon->Unmap(MD5Model.subsets[k].vertBuff, 0);        // 下面这行时另外一种更新缓冲的方式.当想要更新一个每帧少一次的缓冲时会使用该方法,        // 由于GPU读取会更快(由DEFAULT创建的缓冲而不是DYNAMIC创建的        // ),但是CPU写入会更慢,可尝试两种都使用找出哪一个更快        // 若要使用下面这行,则必须创建一个带D3D11_USAGE_DEFAULT的缓冲而不是D3D11_USAGE_DYNAMIC的缓冲        //d3d11DevCon->UpdateSubresource( MD5Model.subsets[k].vertBuff, 0, NULL, &MD5Model.subsets[k].vertices[0], 0, 0 );    }}///////////////**************new**************////////////////////bool LoadMD5Model(std::wstring filename,    Model3D& MD5Model,    std::vector<ID3D11ShaderResourceView*>& shaderResourceViewArray,    std::vector<std::wstring> texFileNameArray){    std::wifstream fileIn (filename.c_str());        // 打开文件    std::wstring checkString;                        // 保存来自文件的字符    if(fileIn)                                        // 检测文件是否打开    {        while(fileIn)                                // 循环到文件结尾        {                fileIn >> checkString;                    // 获取文件下一个字符            if(checkString == L"MD5Version")        // 获得MD5版本,这里为版本10            {                /*fileIn >> checkString;                MessageBox(0, checkString.c_str(),    //显示信息                L"MD5Version", MB_OK);*/            }            else if ( checkString == L"commandline" )            {                std::getline(fileIn, checkString);    // 忽略剩下的行            }            else if ( checkString == L"numJoints" )            {                fileIn >> MD5Model.numJoints;        // 保存关节数            }            else if ( checkString == L"numMeshes" )            {                fileIn >> MD5Model.numSubsets;        // 保存要调用的蒙皮数            }            else if ( checkString == L"joints" )            {                Joint tempJoint;                fileIn >> checkString;                // 跳过 "{"                for(int i = 0; i < MD5Model.numJoints; i++)                {                    fileIn >> tempJoint.name;        // 保存关节名                    // 有时名字可能包含空格.若存在这种情况,则继续读取直到                    // 闭引号"为止                    if(tempJoint.name[tempJoint.name.size()-1] != '"')                    {                        wchar_t checkChar;                        bool jointNameFound = false;                        while(!jointNameFound)                        {                            checkChar = fileIn.get();                            if(checkChar == '"')                                jointNameFound = true;                                    tempJoint.name += checkChar;                                                                                    }                    }                    fileIn >> tempJoint.parentID;    // 保存父关节                    fileIn >> checkString;            // 跳过 "("                    // 保存关节位置(若模型在右手坐标系中,则调换y和z轴)                    fileIn >> tempJoint.pos.x >> tempJoint.pos.z >> tempJoint.pos.y;                    fileIn >> checkString >> checkString;    // 跳过 ")" 和 "("                    // 保存关节的方位                    fileIn >> tempJoint.orientation.x >> tempJoint.orientation.z >> tempJoint.orientation.y;                    // 删除关节名的双引号                    tempJoint.name.erase(0, 1);                    tempJoint.name.erase(tempJoint.name.size()-1, 1);                    // 计算四元素的w轴(MD5模型使用3D向量来表示骨头面对的方向。但是需要将它转为四元素,同时                    // 四元素工作的方式为xyz值表示旋转轴,w值表示旋转角度,介于0和1之间                    float t = 1.0f - ( tempJoint.orientation.x * tempJoint.orientation.x )                        - ( tempJoint.orientation.y * tempJoint.orientation.y )                        - ( tempJoint.orientation.z * tempJoint.orientation.z );                    if ( t < 0.0f )                    {                        tempJoint.orientation.w = 0.0f;                    }                    else                    {                        tempJoint.orientation.w = -sqrtf(t);                    }                    std::getline(fileIn, checkString);        // 跳过该行剩余部分                    MD5Model.joints.push_back(tempJoint);    // 将关节保存进模型的关节向量中                }                fileIn >> checkString;                    // 跳过"}"            }            else if ( checkString == L"mesh")            {                ModelSubset subset;                int numVerts, numTris, numWeights;                fileIn >> checkString;                    // 跳过"{"                fileIn >> checkString;                while ( checkString != L"}" )            // 一直读取到 '}' 为止                {                    // 本章中为简单起见,假设这里已经给定纹理名.                    // 尽管通常给定的是材质名 (保存在材质库中,在加载.obj文件时可知,材质库是保存在.mtl文件中的)                    if(checkString == L"shader")        // 加载纹理或材质                    {                                                std::wstring fileNamePath;                        fileIn >> fileNamePath;            // 获取纹理名                        // 若文件名或材质名含有空格时需要做的                        if(fileNamePath[fileNamePath.size()-1] != '"')                        {                            wchar_t checkChar;                            bool fileNameFound = false;                            while(!fileNameFound)                            {                                checkChar = fileIn.get();                                if(checkChar == '"')                                    fileNameFound = true;                                fileNamePath += checkChar;                                                                                                }                        }                        // 删除纹理路径的引号                        fileNamePath.erase(0, 1);                        fileNamePath.erase(fileNamePath.size()-1, 1);                        //检测纹理是否已经加载                        bool alreadyLoaded = false;                        for(int i = 0; i < texFileNameArray.size(); ++i)                        {                            if(fileNamePath == texFileNameArray[i])                            {                                alreadyLoaded = true;                                subset.texArrayIndex = i;                            }                        }                        //纹理还没有加载,现在加载                        if(!alreadyLoaded)                        {                            ID3D11ShaderResourceView* tempMeshSRV;                            hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(),                                NULL, NULL, &tempMeshSRV, NULL );                            if(SUCCEEDED(hr))                            {                                texFileNameArray.push_back(fileNamePath.c_str());                                subset.texArrayIndex = shaderResourceViewArray.size();                                shaderResourceViewArray.push_back(tempMeshSRV);                            }                            else                            {                                MessageBox(0, fileNamePath.c_str(),        //显示消息                                    L"Could Not Open:", MB_OK);                                return false;                            }                        }                            std::getline(fileIn, checkString);                // 跳过本行剩余部分                    }                    else if ( checkString == L"numverts")                    {                        fileIn >> numVerts;                                // 保存顶点数                        std::getline(fileIn, checkString);                // 跳过本行剩余部分                        for(int i = 0; i < numVerts; i++)                        {                            Vertex tempVert;                            fileIn >> checkString                        // 跳过 "vert # ("                                >> checkString                                >> checkString;                            fileIn >> tempVert.texCoord.x                // 保存tex坐标                                >> tempVert.texCoord.y;                                fileIn >> checkString;                        // 跳过 ")"                            fileIn >> tempVert.StartWeight;                // 本顶点要加权的权重索引                            fileIn >> tempVert.WeightCount;                // 本顶点的权重数                            std::getline(fileIn, checkString);            // 跳过本行剩余部分                            subset.vertices.push_back(tempVert);        // 将顶点push回蒙皮子集顶点向量中                        }                    }                    else if ( checkString == L"numtris")                    {                        fileIn >> numTris;                        subset.numTriangles = numTris;                        std::getline(fileIn, checkString);                // 跳过本行剩余部分                        for(int i = 0; i < numTris; i++)                // 轮询每个三角形                        {                            DWORD tempIndex;                            fileIn >> checkString;                        // 跳过 "tri"                            fileIn >> checkString;                        // 跳过 tri计数                            for(int k = 0; k < 3; k++)                    // 保存3个索引                            {                                fileIn >> tempIndex;                                subset.indices.push_back(tempIndex);                            }                            std::getline(fileIn, checkString);            // 跳过本行剩余部分                        }                    }                    else if ( checkString == L"numweights")                    {                        fileIn >> numWeights;                        std::getline(fileIn, checkString);                // 跳过本行剩余部分                        for(int i = 0; i < numWeights; i++)                        {                            Weight tempWeight;                            fileIn >> checkString >> checkString;        // 跳过 "weight #"                            fileIn >> tempWeight.jointID;                // 保存 weight的关节ID                            fileIn >> tempWeight.bias;                    // 保存施加在顶点上的weight的影响                            fileIn >> checkString;                        // 跳过 "("                            fileIn >> tempWeight.pos.x                    // 保存关节的模型空间的权重的位置                                >> tempWeight.pos.z                                >> tempWeight.pos.y;                            std::getline(fileIn, checkString);            // 跳过本行剩余部分                            subset.weights.push_back(tempWeight);        // 将临时关节push回蒙皮子集权重数组中                        }                    }                    else                        std::getline(fileIn, checkString);                // 跳过本行剩余部分                    fileIn >> checkString;                                // 跳过 "}"                }                //*** 使用关节和权重得到每个顶点位置信息 ***//                for ( int i = 0; i < subset.vertices.size(); ++i )                {                    Vertex tempVert = subset.vertices[i];                    tempVert.pos = XMFLOAT3(0, 0, 0);    // 确保顶点位置首先被清除                    // 将关节和权重信息相加得到顶点的位置信息                    for ( int j = 0; j < tempVert.WeightCount; ++j )                    {                        Weight tempWeight = subset.weights[tempVert.StartWeight + j];                        Joint tempJoint = MD5Model.joints[tempWeight.jointID];                        // 为更容易计算将关节方向和权重位置转换到向量                        // 当将3d向量转换到四元数时,要将w设为0,并且                        // 当将四元素转为3d向量时,只需要忽略w即可                        XMVECTOR tempJointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);                        XMVECTOR tempWeightPos = XMVectorSet(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f);                        // 需要使用关节方位四元素的共轭                        // 对x,y,z求逆即可得到四元素的共轭                        XMVECTOR tempJointOrientationConjugate = XMVectorSet(-tempJoint.orientation.x, -tempJoint.orientation.y, -tempJoint.orientation.z, tempJoint.orientation.w);                        // 计算顶点位置(在关节空间,比如,针对该权重,通过关节方位四元素以及它的共轭来绕着原点(0,0,0)旋转该点)                        // 通过方程式rotatedPoint = quaternion * point * quaternionConjugate以及它的四元素来旋转点                        XMFLOAT3 rotatedPoint;                        XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightPos), tempJointOrientationConjugate));                        // 现在将关节空间的顶点位置转换到世界空间的关节位置,考虑权重偏差                        // 考虑权重偏差是因为多权重可能会对顶点的最终位置有影响。每个权重关联到一个关节。                        tempVert.pos.x += ( tempJoint.pos.x + rotatedPoint.x ) * tempWeight.bias;                        tempVert.pos.y += ( tempJoint.pos.y + rotatedPoint.y ) * tempWeight.bias;                        tempVert.pos.z += ( tempJoint.pos.z + rotatedPoint.z ) * tempWeight.bias;                        // 上面的意思是,让权重位置和关节位置联系起来                        // 随后通过使用表示关节旋转的四元素来旋转权重位置(以便权重能够在世界空间绕着(0,0,0)旋转)                        // 在rotatedPoint变量中保存该旋转点,随后添加到世界空间的关节位置上(由于在世界空间中绕着(0,0,0)旋转权重的位置,//现在要对它进行转换,以便能够看起来像是绕着关节位置旋转)                        // 最后将结果和权重偏差或最终顶点位置受权重影响的大小量相乘。所有影响单个顶点位置的权重偏差必须加到1                                            }                    subset.positions.push_back(tempVert.pos);            // 将顶点位置保存进位置向量而不是直接保存进顶点向量                    // 由于可能还会需要在碰撞或拾取中使用到位置向量                    // 而避免使用到整个顶点结构                }                // 将位置保存进蒙皮的向量中                for(int i = 0; i < subset.vertices.size(); i++)                {                    subset.vertices[i].pos = subset.positions[i];                }                //*** 使用法线平均法计算顶点法线 ***///                std::vector<XMFLOAT3> tempNormal;                //规范和不规范的法线                XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f);                //用来从verts位置获取向量                float vecX, vecY, vecZ;                //三角形的两个边                XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);                XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);                //计算面法线                for(int i = 0; i < subset.numTriangles; ++i)                {                    //获取表示一个三角形的边的向量(edge 0,1)                    vecX = subset.vertices[subset.indices[(i*3)]].pos.x - subset.vertices[subset.indices[(i*3)+2]].pos.x;                    vecY = subset.vertices[subset.indices[(i*3)]].pos.y - subset.vertices[subset.indices[(i*3)+2]].pos.y;                    vecZ = subset.vertices[subset.indices[(i*3)]].pos.z - subset.vertices[subset.indices[(i*3)+2]].pos.z;                            edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f);    //创建第一个边                    //获取表示另一个三角形的边的向量(edge 2,1)                    vecX = subset.vertices[subset.indices[(i*3)+2]].pos.x - subset.vertices[subset.indices[(i*3)+1]].pos.x;                    vecY = subset.vertices[subset.indices[(i*3)+2]].pos.y - subset.vertices[subset.indices[(i*3)+1]].pos.y;                    vecZ = subset.vertices[subset.indices[(i*3)+2]].pos.z - subset.vertices[subset.indices[(i*3)+1]].pos.z;                            edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f);    //创建第二个边                    //叉乘两个边向量以获取非规范化面法线                    XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2));                    tempNormal.push_back(unnormalized);                }                //计算顶点法线(法线平均化)                XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);                int facesUsing = 0;                float tX, tY, tZ;    //临时轴变量                //遍历每个顶点                for(int i = 0; i < subset.vertices.size(); ++i)                {                    //检测哪个三角形使用该顶点                    for(int j = 0; j < subset.numTriangles; ++j)                    {                        if(subset.indices[j*3] == i ||                            subset.indices[(j*3)+1] == i ||                            subset.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);    //若一个面使用顶点,将非规范化面法线添加到normalSum                            facesUsing++;                        }                    }                    //通过用共享同一个顶点的面数除以normalSum得到真实法线                    normalSum = normalSum / facesUsing;                    //规范化normalSum向量                    normalSum = XMVector3Normalize(normalSum);                    //将法线和切线保存进当前顶点                    subset.vertices[i].normal.x = -XMVectorGetX(normalSum);                    subset.vertices[i].normal.y = -XMVectorGetY(normalSum);                    subset.vertices[i].normal.z = -XMVectorGetZ(normalSum); ///////////////**************new**************////////////////////                    // 为了更容易计算法线在动画中创建关节空间法线                    Vertex tempVert = subset.vertices[i];                        // 获取当前顶点                    subset.jointSpaceNormals.push_back(XMFLOAT3(0,0,0));        // push回一个空白法线                    XMVECTOR normal = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);        // 清除法线                    for ( int k = 0; k < tempVert.WeightCount; k++)                // 遍历每一个顶点权重                    {                        Joint tempJoint = MD5Model.joints[subset.weights[tempVert.StartWeight + k].jointID];    // 获取关节方向                        XMVECTOR jointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w);                        // 基于关节方向计算法线(转换进关节空间)                        normal = XMQuaternionMultiply(XMQuaternionMultiply(XMQuaternionInverse(jointOrientation), normalSum), jointOrientation);                                XMStoreFloat3(&subset.weights[tempVert.StartWeight + k].normal, XMVector3Normalize(normal));            // 将规范四元素存储进权重法线                    }                                    ///////////////**************new**************////////////////////                    //为下一个向量清除normalSum,faceUsing                    normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);                    facesUsing = 0;                }                // 创建索引缓冲                D3D11_BUFFER_DESC indexBufferDesc;                ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );                indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;                indexBufferDesc.ByteWidth = sizeof(DWORD) * subset.numTriangles * 3;                indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;                indexBufferDesc.CPUAccessFlags = 0;                indexBufferDesc.MiscFlags = 0;                D3D11_SUBRESOURCE_DATA iinitData;                iinitData.pSysMem = &subset.indices[0];                d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &subset.indexBuff);                //创建顶点缓冲                D3D11_BUFFER_DESC vertexBufferDesc;                ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) );                vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;                            // 将会持续更新该缓冲,因此要设为动态的                vertexBufferDesc.ByteWidth = sizeof( Vertex ) * subset.vertices.size();                vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;                vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;                // 赋给cpu写到缓冲的权限                vertexBufferDesc.MiscFlags = 0;                D3D11_SUBRESOURCE_DATA vertexBufferData;                 ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );                vertexBufferData.pSysMem = &subset.vertices[0];                hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &subset.vertBuff);                // 临时蒙皮push会模型的蒙皮向量中                MD5Model.subsets.push_back(subset);            }        }    }    else    {        SwapChain->SetFullscreenState(false, NULL);    // 确保退出全屏        // 创建消息        std::wstring message = L"Could not open: ";        message += filename;        MessageBox(0, message.c_str(),    // 显示消息            L"Error", MB_OK);        return false;    }    return true;}///////////////**************new**************////////////////////bool LoadObjModel(std::wstring filename, ID3D11Buffer** vertBuff, ID3D11Buffer** indexBuff,std::vector<int>& subsetIndexStart,std::vector<int>& subsetMaterialArray,std::vector<SurfaceMaterial>& material, int& subsetCount,bool isRHCoordSys,    bool computeNormals){HRESULT hr = 0;std::wifstream fileIn (filename.c_str());//打开文件std::wstring meshMatLib;//保存obj材质库文件名的字符串//存储我们模型的信息的数组std::vector<DWORD> indices;std::vector<XMFLOAT3> vertPos;std::vector<XMFLOAT3> vertNorm;std::vector<XMFLOAT2> vertTexCoord;std::vector<std::wstring> meshMaterials;//顶点定义索引std::vector<int> vertPosIndex;std::vector<int> vertNormIndex;std::vector<int> vertTCIndex;//如果没有定义纹理坐标或发现,确保有一个默认的值bool hasTexCoord = false;bool hasNorm = false;//用于存储向量的临时变量std::wstring meshMaterialsTemp;int vertPosIndexTemp;int vertNormIndexTemp;int vertTCIndexTemp;wchar_t checkChar;//每次从文件读出一个字符要存储的地方std::wstring face;//保存包含面顶点的字符串int vIndex = 0;//跟踪顶点索引数int triangleCount = 0;//所有三角形int totalVerts = 0;int meshTriangles = 0;//检测文件是否被打开if (fileIn){while(fileIn){checkChar = fileIn.get();//获取下一个字符switch (checkChar){case '#':checkChar = fileIn.get();while(checkChar != '\n')checkChar = fileIn.get();break;case 'v'://获取向量描述符checkChar = fileIn.get();if(checkChar == ' ')//v - 向量位置{float vz, vy, vx;fileIn >> vx >> vy >> vz;//存储接下来三个类型if(isRHCoordSys)//若模型来自右手坐标系vertPos.push_back(XMFLOAT3( vx, vy, vz * -1.0f));//转换Z轴elsevertPos.push_back(XMFLOAT3( vx, vy, vz));}if(checkChar == 't')//vt - 顶点纹理坐标{float vtcu, vtcv;fileIn >> vtcu >> vtcv;//保存接下来的两个类型if(isRHCoordSys)//若模型来自右手坐标系vertTexCoord.push_back(XMFLOAT2(vtcu, 1.0f-vtcv));//将v轴翻转elsevertTexCoord.push_back(XMFLOAT2(vtcu, vtcv));hasTexCoord = true;//模型使用纹理坐标系}//由于我们在后来计算法线,我们不必在此检测法线//In the file, but i'll do it here anywayif(checkChar == 'n')//vn - 顶点法线{float vnx, vny, vnz;fileIn >> vnx >> vny >> vnz;//存储接下来三个类型if(isRHCoordSys)//若模型来自右手坐标系vertNorm.push_back(XMFLOAT3( vnx, vny, vnz * -1.0f ));//将z轴翻转elsevertNorm.push_back(XMFLOAT3( vnx, vny, vnz ));hasNorm = true;//模型定义法线}break;//新组(子集)case 'g'://g - 定义一个组checkChar = fileIn.get();if(checkChar == ' '){subsetIndexStart.push_back(vIndex);//子集起始索引subsetCount++;}break;//获取面索引case 'f'://f - 定义面checkChar = fileIn.get();if(checkChar == ' '){face = L"";std::wstring VertDef;//一个时刻存储一个顶点定义triangleCount = 0;checkChar = fileIn.get();while(checkChar != '\n'){face += checkChar;//将字符添加到面字符串checkChar = fileIn.get();//获取下一个字符if(checkChar == ' ')//若为空格triangleCount++;//增加三角形计数}//在面字符串结尾检测空格if(face[face.length()-1] == ' ')triangleCount--;//每个空格添加到三角形计数triangleCount -= 1;//Ever vertex in the face AFTER the first two are new facesstd::wstringstream ss(face);if(face.length() > 0){int firstVIndex, lastVIndex;//Holds the first and last vertice's indexfor(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 stringfor(int j = 0; j < VertDef.length(); ++j){if(VertDef[j] != '/')//If there is no divider "/", add a char to our vertPartvertPart += VertDef[j];//If the current char is a divider "/", or its the last character in the stringif(VertDef[j] == '/' || j ==  VertDef.length()-1){std::wistringstream wstringToInt(vertPart);//Used to convert wstring to intif(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 specifiedif(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 defaultvertTCIndexTemp = 0;//If the cur. char is the second to last in the string, then//there must be no normal, so set a default normalif(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 partwhichPart++;//Move on to next vertex part}}//Check to make sure there is at least one subsetif(subsetCount == 0){subsetIndexStart.push_back(vIndex);//Start index for this subsetsubsetCount++;}//Avoid duplicate verticesbool vertAlreadyExists = false;if(totalVerts >= 3)//Make sure we at least have one triangle to check{//Loop through all the verticesfor(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 verticesif(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists){if(vertTCIndexTemp == vertTCIndex[iCheck]){indices.push_back(iCheck);//Set index for this vertexvertAlreadyExists = true;//If we've made it here, the vertex already exists}}}}//If this vertex is not already in our vertex arrays, put it thereif(!vertAlreadyExists){vertPosIndex.push_back(vertPosIndexTemp);vertTCIndex.push_back(vertTCIndexTemp);vertNormIndex.push_back(vertNormIndexTemp);totalVerts++;//We created a new vertexindices.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 vertexif(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 trianglefor(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 vertexvIndex++;//Second Vertex of this triangle (the last vertex used in the tri before this one)indices.push_back(lastVIndex);//Set index for this vertexvIndex++;//Get the third vertex for this triangless >> 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 specifiedif(j == VertDef.length()-1){vertTCIndexTemp = 0;vertNormIndexTemp = 0;}}else if(whichPart == 1){if(vertPart != L""){wstringToInt >> vertTCIndexTemp;vertTCIndexTemp -= 1;}elsevertTCIndexTemp = 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 verticesbool 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 vertexvertAlreadyExists = 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 vertsindices.push_back(totalVerts-1);//Set index for this vertex}//Set the second vertex for the next triangle to the last vertex we gotlastVIndex = indices[vIndex];//The last vertex index of this TRIANGLEmeshTriangles++;//New triangle definedvIndex++;}}}break;case 'm'://mtllib - material library filenamecheckChar = 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 namefileIn >> meshMatLib;}}}}}}break;case 'u'://usemtl - which material to usecheckChar = 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 clearedfileIn >> 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 messagestd::wstring message = L"Could not open: ";message += filename;MessageBox(0, message.c_str(),//display messageL"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 specifiedif(!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 filefileIn.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 ambientbool kdset = false;if (fileIn){while(fileIn){checkChar = fileIn.get();//Get next charswitch (checkChar){            //Check for commentcase '#':checkChar = fileIn.get();while(checkChar != '\n')checkChar = fileIn.get();break;//Set diffuse colorcase 'K':checkChar = fileIn.get();if(checkChar == 'd')//Diffuse Color{checkChar = fileIn.get();//remove spacefileIn >> 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 spaceif(!kdset){fileIn >> material[matCount-1].difColor.x;fileIn >> material[matCount-1].difColor.y;fileIn >> material[matCount-1].difColor.z;}}break;//Check for transparencycase 'T':checkChar = fileIn.get();if(checkChar == 'r'){checkChar = fileIn.get();//remove spacefloat 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 transparencycase 'd':checkChar = fileIn.get();if(checkChar == ' '){float Transparency;fileIn >> Transparency;//'d' - 0 being most transparent, and 1 being opaque, opposite of TrTransparency = 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 mapcheckChar = 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 extensionbool 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 loadedbool 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 nowif(!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 mapelse 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 mapmaterial[matCount-1].transparent = true;}///////////////**************new**************//////////////////////map_bump - bump map (we're usinga normal map though)else if(checkChar == 'b'){checkChar = fileIn.get();if(checkChar == 'u'){checkChar = fileIn.get();if(checkChar == 'm'){checkChar = fileIn.get();if(checkChar == 'p'){std::wstring fileNamePath;fileIn.get();//Remove whitespace between map_bump 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 extensionbool 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 loadedbool alreadyLoaded = false;for(int i = 0; i < textureNameArray.size(); ++i){if(fileNamePath == textureNameArray[i]){alreadyLoaded = true;material[matCount-1].normMapTexArrayIndex = i;material[matCount-1].hasNormMap = true;}}//if the texture is not already loaded, load it nowif(!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].normMapTexArrayIndex = meshSRV.size();meshSRV.push_back(tempMeshSRV);material[matCount-1].hasNormMap = true;}}}}}}///////////////**************new**************////////////////////}}}break;case 'n'://newmtl - Declare new materialcheckChar = 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 defaultsSurfaceMaterial tempMat;material.push_back(tempMat);fileIn >> material[matCount].matName;material[matCount].transparent = false;material[matCount].hasTexture = false;///////////////**************new**************////////////////////material[matCount].hasNormMap = false;material[matCount].normMapTexArrayIndex = 0;///////////////**************new**************////////////////////material[matCount].texArrayIndex = 0;matCount++;kdset = false;}}}}}}break;default:break;}}}else{SwapChain->SetFullscreenState(false, NULL);//Make sure we are out of fullscreenstd::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 arrayfor(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<Vertex> vertices;Vertex tempVert;//Create our vertices using the information we got //from the file and store them in a vectorfor(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);///////////////**************new**************////////////////////}    //////////////////////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<XMFLOAT3> tempNormal;//normalized and unnormalized normalsXMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f);///////////////**************new**************//////////////////////tangent stuffstd::vector<XMFLOAT3> tempTangent;XMFLOAT3 tangent = XMFLOAT3(0.0f, 0.0f, 0.0f);float tcU1, tcV1, tcU2, tcV2;///////////////**************new**************//////////////////////Used to get vectors (sides) from the position of the vertsfloat vecX, vecY, vecZ;//Two edges of our triangleXMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);//Compute face normalsfor(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 normalXMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2));tempNormal.push_back(unnormalized);///////////////**************new**************//////////////////////Find first texture coordinate edge 2d vectortcU1 = vertices[indices[(i*3)]].texCoord.x - vertices[indices[(i*3)+2]].texCoord.x;tcV1 = vertices[indices[(i*3)]].texCoord.y - vertices[indices[(i*3)+2]].texCoord.y;//Find second texture coordinate edge 2d vectortcU2 = vertices[indices[(i*3)+2]].texCoord.x - vertices[indices[(i*3)+1]].texCoord.x;tcV2 = vertices[indices[(i*3)+2]].texCoord.y - vertices[indices[(i*3)+1]].texCoord.y;//Find tangent using both tex coord edges and position edgestangent.x = (tcV1 * XMVectorGetX(edge1) - tcV2 * XMVectorGetX(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1));tangent.y = (tcV1 * XMVectorGetY(edge1) - tcV2 * XMVectorGetY(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1));tangent.z = (tcV1 * XMVectorGetZ(edge1) - tcV2 * XMVectorGetZ(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1));tempTangent.push_back(tangent);///////////////**************new**************////////////////////}//Compute vertex normals (normal Averaging)XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);XMVECTOR tangentSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);int facesUsing = 0;float tX, tY, tZ;//temp axis variables//Go through each vertexfor(int i = 0; i < totalVerts; ++i){//Check which triangles use this vertexfor(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///////////////**************new**************//////////////////////We can reuse tX, tY, tZ to sum up tangentstX = XMVectorGetX(tangentSum) + tempTangent[j].x;tY = XMVectorGetY(tangentSum) + tempTangent[j].y;tZ = XMVectorGetZ(tangentSum) + tempTangent[j].z;tangentSum = XMVectorSet(tX, tY, tZ, 0.0f); //sum up face tangents using this vertex///////////////**************new**************////////////////////facesUsing++;}}//Get the actual normal by dividing the normalSum by the number of faces sharing the vertexnormalSum = normalSum / facesUsing;///////////////**************new**************////////////////////tangentSum = tangentSum / facesUsing;///////////////**************new**************//////////////////////Normalize the normalSum vectornormalSum = XMVector3Normalize(normalSum);///////////////**************new**************////////////////////tangentSum =  XMVector3Normalize(tangentSum);///////////////**************new**************//////////////////////Store the normal in our current vertexvertices[i].normal.x = XMVectorGetX(normalSum);vertices[i].normal.y = XMVectorGetY(normalSum);vertices[i].normal.z = XMVectorGetZ(normalSum);///////////////**************new**************////////////////////vertices[i].tangent.x = XMVectorGetX(tangentSum);vertices[i].tangent.y = XMVectorGetY(tangentSum);vertices[i].tangent.z = XMVectorGetZ(tangentSum);///////////////**************new**************//////////////////////Clear normalSum and facesUsing for next vertexnormalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);///////////////**************new**************////////////////////tangentSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);///////////////**************new**************////////////////////facesUsing = 0;}}//Create index bufferD3D11_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 BufferD3D11_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<Vertex> 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<DWORD> 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[] ={// Front FaceVertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f),Vertex(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f,-1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f),Vertex( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f, 1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 0.0f),Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.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, &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"ground.obj", &meshVertBuff, &meshIndexBuff, meshSubsetIndexStart, meshSubsetTexture, material, meshSubsets, true, true))        return false;    ///////////////**************new**************////////////////////    if(!LoadMD5Model(L"boy.md5mesh", NewMD5Model, meshSRV, textureNameArray))return false;///////////////**************new**************////////////////////    ///////////////**************new**************////////////////////        if(!LoadMD5Anim(L"boy.md5anim", NewMD5Model))        return false;    // 获取包围体信息   // CreateBoundingVolumes(bottleVertPosArray, bottleBoundingBoxVertPosArray, bottleBoundingBoxVertIndexArray, bottleBoundingSphere, bottleCenterOffset);///////////////**************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);/// newhr = 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);///newhr = 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, 7.0f, 0.0f);light.dir = XMFLOAT3(0.5f, 0.75f, -0.5f);light.range = 1000.0f;light.cone = 12.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);//Create the Input Layout//创建输入布局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);//创建缓冲用来发送到效果文件的cbufferD3D11_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.5f, 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);///////////////**************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**************//////////////////////配置采样状态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);///////////////**************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);    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 matrixScale = XMMatrixScaling( 5.0f, 5.0f, 5.0f );//Make sure the sphere is always centered around cameraTranslation = XMMatrixTranslation( XMVectorGetX(camPosition), XMVectorGetY(camPosition), XMVectorGetZ(camPosition) );//Set sphereWorld's world space using the transformationssphereWorld = Scale * Translation;///////////////**************new**************////////////////////meshWorld = XMMatrixIdentity();//Define cube1's world space matrixRotation = XMMatrixRotationY(3.14f);Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f );Translation = XMMatrixTranslation( 0.0f, 0.0f, 0.0f );meshWorld = Rotation * Scale * Translation;    ///////////////**************new**************////////////////////    Scale = XMMatrixScaling( 0.04f, 0.04f, 0.04f );            // 将模型尺寸缩小    Translation = XMMatrixTranslation( 0.0f, 3.0f, 0.0f );    smilesWorld = Scale * Translation;    ///////////////**************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;    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 };    float bgColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);//刷新深度模板视图d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);//newconstbuffPerFrame.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);    ///////////////**************new**************////////////////////    ///***绘制MD5模型***///    for(int i = 0; i < NewMD5Model.numSubsets; i ++)    {        //设置地面索引缓冲        d3d11DevCon->IASetIndexBuffer( NewMD5Model.subsets[i].indexBuff, DXGI_FORMAT_R32_UINT, 0);        //设置地面顶点缓冲        d3d11DevCon->IASetVertexBuffers( 0, 1, &NewMD5Model.subsets[i].vertBuff, &stride, &offset );        //设置WVP矩阵并将它传入到效果文件的常量缓冲        WVP = smilesWorld * camView * camProjection;        cbPerObj.WVP = XMMatrixTranspose(WVP);            cbPerObj.World = XMMatrixTranspose(smilesWorld);            cbPerObj.hasTexture = true;        //假设所有的md5蒙皮子集都有纹理        cbPerObj.hasNormMap = false;    // 假设md5模型没有法线贴图(随后若要修改也会很容易)        d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );        d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );        d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer );        d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[NewMD5Model.subsets[i].texArrayIndex] );        d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState );        d3d11DevCon->RSSetState(RSCullNone);        d3d11DevCon->DrawIndexed( NewMD5Model.subsets[i].indices.size(), 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;///////////////**************new**************////////////////////cbPerObj.hasNormMap = material[meshSubsetTexture[i]].hasNormMap;///////////////**************new**************////////////////////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] );///////////////**************new**************////////////////////if(material[meshSubsetTexture[i]].hasNormMap)d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[meshSubsetTexture[i]].normMapTexArrayIndex] );///////////////**************new**************////////////////////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 );}    /////Draw the Sky's Sphere//////    //Set the spheres index buffer/////绘制天空的球面////////设置球面的索引缓冲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;///////////////**************new**************////////////////////cbPerObj.hasNormMap = material[meshSubsetTexture[i]].hasNormMap;///////////////**************new**************////////////////////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] );///////////////**************new**************////////////////////if(material[meshSubsetTexture[i]].hasNormMap)d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[meshSubsetTexture[i]].normMapTexArrayIndex] );///////////////**************new**************////////////////////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 screenSwapChain->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,则在读取之后会被删除*/BOOL PeekMessageL(             LPMSG lpMsg,            HWND hWnd,            UINT wMsgFilterMin,            UINT wMsgFilterMax,            UINT wRemoveMsg            );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;///////////////**************new**************//////////////////////case WM_SIZE://ClientWidth  = LOWORD(lParam);//ClientHeight = HIWORD(lParam);//return 0;//break;default:break;}//调用默认窗口过程函数return DefWindowProc(hwnd,msg,wParam,lParam);}


shader文件与上一章节一样无变化。

效果:




参考网址

anim文件github网址











原创粉丝点击