间时紧张,先记一笔,后续优化与完善。
本系列文章由zhmxy555(毛星云)编写,载转请注明出处。
文章链接: http://blog.csdn.net/zhmxy555/article/details/8832812
作者:毛星云(浅墨) 邮箱: [email protected]
这是允许大家的解讲骨骼动画的文章的N部曲的第二篇。这篇文章里,我们对现行的三种模型动画术技行进了概述,然后对X文件成构行进了细详的析剖,最后放出了骨骼动画的第一个示例序程,载入了《诛仙》中陆雪琪非常优雅的”剑舞“动画。伊人在漫天雪花中之翩翩剑舞,非常有意境:)。
先上几张截图来一睹陆雪琪舞剑的采风吧:
示例序程源代码在文章尾末供提下载。
好吧,咱们开始文正。
我们平日说的模型动画,其实有三大类。对于大部份模型动画的现实道理基本上是殊途同归的,也就是供提一种机制,用于描述三维模型中各个顶点的位置随着间时的化变。
平日有三种模型动画的现实方法,他们分别是:
1.枢纽动画
2.变渐动画
3.骨骼蒙皮动画
接着我们分别来做一下简介。
枢纽动画的思惟是把角色分为若干个独立的部份,个每部份都对应了一个独立的网格模型,并且这些网格模型按照角色的特色成组一个次层构结。
网格模型中持保了最初态状的顶点坐标和他们的位置等等数据,还有一系列后续刻时所对应的动运矩阵(一般而言,为了省节存储的空间,不会去保存每时每刻的顶点数据)。
枢纽动画的长处是它的占用空间很小,并且利用关键帧的插值算运可以现实杂复的动画效果。但是,它的缺陷是角色成组部份之间的交接处易容生产显明的接缝,显得很假。
变渐动画的思惟是将角色通过一个完全的网格模型行进描述。而在模型动画的序列中,通过关键帧去录记网格模型中个每顶点的新位置(也就是相对原位置的变改量)。这类方法只须要在关键帧之间行进插值算运从而变改网格模型中各个顶点的位置,就可以现实模型的动画效果来。
而与上面我们分析到的枢纽动画比相,变渐动画用使了单一而天衣无缝的网格模型,使现实的角色更加实真,而且也不会生产像枢纽动画那样为难地要面临着接缝问题。同时,因为变渐动画没有用使次层模型,所以在获得网格模型中各顶点位置时的算计量比拟小。但是问题当然也是有的,那就是这类方法要保存一系列刻时网格模型中关相顶点的位置,所以占的存储空间是非常大的,而且也比拟板死,活灵性很差。
万人迷来了。:D
我们在游戏序程中平日会用采骨骼蒙皮动画来制造出动作效果。
我们来看看它到底有哪些人迷的特质。
上面我们刚讲到枢纽动画和变渐动画,可以这样懂得,他们是两个极端:
枢纽动画,占用空间小,表现力差。
变渐动画,占用空间大,表现力好。
而有具成功人士特质的现在用使最遍普的三维动画术技“骨骼蒙皮动画”,自然会取其精华,去其糟粕,取吸它们的长处,摒弃它们的缺陷,最后糅合中折而成的属性就是——占用空间小,表现力好。
好了,我们来看一下骨骼蒙皮动画的体具道理。
骨骼动画的现实道理是模仿人体的动运方法,其中将角色由一种称作“蒙皮(skin)”的单一网格模型和按照必定次层组织起来的“骨骼(Bone)”成组。骨骼次层模仿枢纽动画的组织构结将角色组织成一个次层构结。而相邻的骨骼之间通过枢纽相连,他们之间通过做相对动运来现实特定的动作效果,从而就现实了不同的模型动画效果。
而皮肤网格模型与骨骼关相联,用于供提制绘动画全部须要的何几模型(比如顶点、法向量等等),还有纹理和材质等一些信息。成组皮肤网格的个每顶点会都到受一个或者多个骨骼的影响,而个每顶点到受多个骨骼影响的水平通过权值(Weight)肯定。通过算计个每顶点到受不同骨骼对他们影响的加权和,就可以到得这个顶点在动运程过中所处的际实位置。
另外须要意注的是,骨骼蒙皮动画通过关键帧肯定骨骼的位置、朝向等等一些信息。通过在动画序列(Animation Set)中相邻的两个关键帧之间行进插值算运,就可以肯定某一刻时各骨骼所处的新位置和新朝向等一些额外信息。
以上的各项“首脑气质”,注定了“骨骼蒙皮动画术技”会引领潮水,力压有显明缺陷的“枢纽动画术技”和“变渐动画术技”,成为类各实时动画用应用中使最遍普最心核的动画术技。
想好好把握骨骼蒙皮动画术技的用使,首先还得从泉源处解了X模型文件的成构,看看这些模型究竟是徒有其表的模型,还是除了面表展示出来的模型效果以外,还有更深次层的西东——动画序列(Animation Set)。
我们先来对遍普的X文件来一个分析。
我们道知X文件式格是微软义定的3D模型文件式格,三维模建件软3ds Max、Maya制造出来的三维模型,可以很易容地转换为X式格。X式格在我们学习三维游戏编程的期初用起来是非常便方的。
我们如果用记事本打开X文件的话,会发明其用中大批的代码和数字,义定了括包网格的顶点、纹理、动画、材质以及其他的一些内容。X文件是以模板驱动的,也就是说,它存储数据的式格是基于模板的,这使得X文件有具构结由自、内容丰富、易用应和可移植性等高长处。- -怎么认为自己在裹空。
好了,不裹空了,我们来细仔看一下这些所谓的基于模板的X文件义定方法到底卖得什么药。要想在Direct3D序程中活灵自若地用使网格模型,应该深刻懂得.x文件式格。如果后以用使physX,bullet等做物理模拟碰撞检测的话,也因为解了X文件的式格而来擒到手的。
个每.x文件都是以一个首部(header)来头开的。对应于我们次这用使的《诛仙》中的陆雪琪的X文件,用记事本打开之后(更简略的方法是直接把X文件拖到Visual Studio中打开,因为有行号,看起来更加服舒),第一句就是如下所示:
xof 0303txt 0032
这是比拟见常的一种X文件的首部。其中,xof代表这是一个X文件。接下来,版本号由两部份成组,前两位为主版本号,后两位为次版本号,那么0303就代表X文件是用使3.3版本的模板。txt代表接下来的X文件是用文本文件(text)式格存储的,而不是二进制(bin)。最后的0032是浮点数的位数是32位。
另一种比拟见常的X文件的首部是这样的:
xof 0303bin 0064
通过上面的解讲,我们可以很易容地推算出,它表现3.3版本的二进制文件式格贮存的64位浮点数的X文件。
上面我们讲到了X文件存储数据的式格是基于模板的。为了大家印象更加深刻,首先,贴出次这用使的《诛仙》中陆雪琪人物模型的X文件的0~145行“代码“:
xof 0303txt 0032 template ColorRGBA { <35ff44e0-6c7c-11cf-8f52-0040333594a3> FLOAT red; FLOAT green; FLOAT blue; FLOAT alpha; } template ColorRGB { <d3e16e81-7835-11cf-8f52-0040333594a3> FLOAT red; FLOAT green; FLOAT blue; } template Material { <3d82ab4d-62da-11cf-ab39-0020af71e433> ColorRGBA faceColor; FLOAT power; ColorRGB specularColor; ColorRGB emissiveColor; [...] } template TextureFilename { <a42790e1-7810-11cf-8f52-0040333594a3> STRING filename; } template Frame { <3d82ab46-62da-11cf-ab39-0020af71e433> [...] } template Matrix4x4 { <f6f23f45-7686-11cf-8f52-0040333594a3> array FLOAT matrix[16]; } template FrameTransformMatrix { <f6f23f41-7686-11cf-8f52-0040333594a3> Matrix4x4 frameMatrix; } template Vector { <3d82ab5e-62da-11cf-ab39-0020af71e433> FLOAT x; FLOAT y; FLOAT z; } template MeshFace { <3d82ab5f-62da-11cf-ab39-0020af71e433> DWORD nFaceVertexIndices; array DWORDfaceVertexIndices[nFaceVertexIndices]; } template Mesh { <3d82ab44-62da-11cf-ab39-0020af71e433> DWORD nVertices; array Vector vertices[nVertices]; DWORD nFaces; array MeshFace faces[nFaces]; [...] } template MeshNormals { <f6f23f43-7686-11cf-8f52-0040333594a3> DWORD nNormals; array Vector normals[nNormals]; DWORD nFaceNormals; array MeshFace faceNormals[nFaceNormals]; } template MeshMaterialList { <f6f23f42-7686-11cf-8f52-0040333594a3> DWORD nMaterials; DWORD nFaceIndexes; array DWORD faceIndexes[nFaceIndexes]; [Material<3d82ab4d-62da-11cf-ab39-0020af71e433>] } template Coords2d { <f6f23f44-7686-11cf-8f52-0040333594a3> FLOAT u; FLOAT v; } template MeshTextureCoords { <f6f23f40-7686-11cf-8f52-0040333594a3> DWORD nTextureCoords; array Coords2d textureCoords[nTextureCoords]; } template XSkinMeshHeader { <3cf169ce-ff7c-44ab-93c0-f78f62d172e2> WORDnMaxSkinWeightsPerVertex; WORDnMaxSkinWeightsPerFace; WORDnBones; } template SkinWeights { <6f0d123b-bad2-4167-a0d0-80224f25fabb> STRING transformNodeName; DWORD nWeights; array DWORD vertexIndices[nWeights]; array FLOAT weights[nWeights]; Matrix4x4 matrixOffset; } template Animation { <3d82ab4f-62da-11cf-ab39-0020af71e433> [...] } template AnimationSet { <3d82ab50-62da-11cf-ab39-0020af71e433> [Animation<3d82ab4f-62da-11cf-ab39-0020af71e433>] } template AnimationOptions { <e2bf56c0-840f-11cf-8f52-0040333594a3> DWORD openclosed; DWORD positionquality; } template FloatKeys { <10dd46a9-775b-11cf-8f52-0040333594a3> DWORD nValues; array FLOAT values[nValues]; } template TimedFloatKeys { <f406b180-7b3b-11cf-8f52-0040333594a3> DWORD time; FloatKeys tfkeys; } template AnimationKey { <10dd46a8-775b-11cf-8f52-0040333594a3> DWORD keyType; DWORD nKeys; array TimedFloatKeys keys[nKeys]; }
我们可以看到,除了第一行是首部(header)以外,其他的144行满是一堆template括起来义定的某样内容,而某个template义定的内容互不干相,构结还是非常晰清的。
这就像我们在写C++序程时用的typedefine一样,在义定某种写书的式格。也像C++里头的类,而template的实例为数据对象
我们来看一下这些模板的通用式格:
template <template-name>{ //模板名称 <UUID> //通用独一标记,用来标记一个模板 <member 1>, //成员变量1 ……… <member n>, //成员变量n [restrictions] //模板约束 }
注释经已非常晰清了,其中<template-name>指定模板的名称,这个名称可以括包下画线(“_”),但不能以数字头开。<UUID>表现一个通用独一标记(Universally Unique Identifier,我们在讲DirectInput的时候也提到过),用来标记一个模板,经常使用的式格分别为(8-4-4-16)和(8-4-4-4-12)两种,并且在X文件用中尖括号对(“_”)表现。比如:
<10dd46a9-775b-11cf-8f52-0040333594a3>
然后接下来的就是成员变量了,个数不限,比如像这样,AnimationOptions(动画选项)模板中义定了openclosed和positionquality这两个成员变量:
template AnimationOptions { <e2bf56c0-840f-11cf-8f52-0040333594a3> DWORD openclosed; DWORD positionquality; }
对于可取的成员变量的数据类型,浅墨也为大家理整出来了,可以在下表中取:
可取的数据类型 |
精析 |
WORD |
字类型,用16位表现 |
DWORD |
双字类型,用32位表现 |
FLOAT |
浮点类型 |
DOUBLE |
64位双精度浮点型 |
CHAR |
8位有符号符字类型 |
UCHAR |
8位无符号符字类型 |
BYTE |
8位无符号符字类型 |
STRING |
括包束结符的符字串(char[]) |
CSTRING |
带式格的C符字串 |
array |
指定类型的数组 |
这里的array大家懂得起来许也会涌现差偏,我们提一下。
array数据类型用于义定一个任何效有的数据类型可以表现的数组类型,并且可以指定数组的维度(数组认默维度为1)。X文件中数组的基本语法的义定是这样的:
array <data-type><name>[<dimension-size>];
然后一个义定数组的实例:
template FloatKeys { <10dd46a9-775b-11cf-8f52-0040333594a3> DWORD nValues; array FLOAT values[nValues]; }
最后我们看一下看起来有些秘神的所谓的[restrictions],模板约束。
[restrictions] 表现模板约束,用于指定在模板中可以义定的其他成员变量等。而根据模板约束的不同式形,可以将模板分为以下三大类:
义思名顾,开放式模板是指出了模板本身义定的成员变量以外,还可以向模板中添加其他的成员变量来到达定制模板的的目,在模板中通过方括号对("[ ]")表现。比如:
template Material { <3d82ab4d-62da-11cf-ab39-0020af71e433> ColorRGBA faceColor; FLOAT power; ColorRGB specularColor; ColorRGB emissiveColor; [...] //喏,开放式模板的标识小尾巴 }
约束式模板是指除了模板中义定的成员变量以外,只可以向末班中添加限有的几种数据类型的数据成员,而这些指定可以添加的(俗语说拿了人家offer的)数据类型我们在模板中举列出来。比如这样:
template FileSystem { <f6f23f43-7686-11cf-8f52-0040333594a3> STRING name; [Directory<UUID>,File<UUID>] //喏,约束式模板的标识小尾巴 }
封闭式模板就比拟没有创意了,在它生出的时候就注定了是那幅样子,不能向其中添加其他类型的数据成员。封闭式模板平日表现牢固的数据构结,比如向量、矩阵,颜色等等。仍然是一个例子:
template Coords2d { <f6f23f44-7686-11cf-8f52-0040333594a3> FLOAT u; FLOAT v; } //封闭式模板,直接把小尾巴"[ ]"拿掉了行就。
相信不少友人会把上面的Coords2d一眼成看Cocos2d - -,这里的Coords2d是义定纹理坐标向量的模板名称,不是那个尽人皆知,世态炎凉的2D游戏引擎:)
接着我们看一下约定俗成的经常使用的模板的类型名,像一个小字典一样,X文件中的那些模板名基本上都理整在上面了:
AnimationSet 动画的合组,括包一个或者多个Animation。
Animation 描述一个动画,括包一个或几个AnimationKey
AnimationKey 动画关键帧,义定体具的动作数据,括包一些列旋转、挪动、放缩、矩阵换变。
ColorRGB 义定RGB对象,括包三个Float的值,分别是R、G、B。
ColorRGBA 义定RGBA对象。括包四个Float的值,分别是R、G、B、alpha。
Coords2d 义定纹理坐标向量,括包两个Float值,分别是u、v。
FloatKeys 义定浮点数组,用来义定动画键数值,括包两个部份:浮点值个数,浮点值表列。
Material 义定材质信息,可以被用应到一个完全的Mesh对象,也可以用应到其中的一个面。括包:
1.FaceColor环境光
2.Power镜面反射的强度
3.specularcolor镜面反射等等。
Matrix4X4 义定4X4矩阵,16个浮点数值。
Mesh 义定个Mesh对象,共有9个部份成组:
1、括包的顶点数
2、顶点表列,一个顶点括包三个浮点值
3、面数
4、面的顶点索引表列,个每面括包三个顶点
5、MeshFaceWraps 构结,临时无用
6、MeshTextureCoords纹理坐标,可选
7、MeshNormals 法向,可选
8、MeshVertexColors 顶点颜色,默为认白色
9、MeshMaterialList 材质,不供提的话默为认白色。
MeshFace 面索引,括包两部份:面数,定点索引成构的面数组。
MeshTextureCoords 义定纹理坐标,括包:纹理坐标的个数,纹理坐标(个每纹理坐标有两个浮点值)。
MeshMaterialList 义定材质的用应,括包:多少个材质被用使,材质影响面的个数,面索引。
MeshNormals 义定Mesh的法向量,括包4部份:
1.nNormals法向量的个数=顶点数
2.Normals顶点法向量表列
3.nFaceNormals面的个数
4.FaceNormals面临应的法向量。
MeshVertexColors 指定顶点的颜色取代来原的材质,括包:顶点数目,颜色索引
TextureFilename 纹理的名称,符字串类型。
VertexDuplicationIndices 保存副本,用于精简Mesh的操纵,括包:顶点数,原始顶点数,际实顶点数。
XSkinMeshHeader 描述被导出的SkinMesh关相信息,影响一个顶点的最多换变数目,影响个每面三个顶点的最大换变数目,影响一个顶点的骨骼数。
TimedFloatKeys 间时值,用于Animaterkey中义定间时间隔。
Vector 三维向量,三个浮点值。
SkinWeights 义定骨骼影响权重。括包以下几个部份:骨骼的名字,有多少个权重值,顶点的索引表列等等
首先诉告大家的是,首部部份一般是1行代码就搞定,模板部份一般一百来行代码搞定,而实例化部份一般几万行代码才搞得定。比如,我们次这选的“陆雪琪”的X文件就有63521行代码,以如下VS2010中的截图为证:
当然,这些代码不是我们去写的,而是在三维模建件软如3DS Max和Maya用中可视化的模建环境做出来之后,再导出为X文件式格的。
实例化部份其实就是把第二步中义定的那些模板行进实例化,充填数字,给他们体具的义含。可以这样懂得,模板义定部份就是在“义定类”,而实例化部份就是在“实例化类”。
为了大家懂得更加深刻,我们贴出“陆雪琪”的X文件的148~190行的代码,大家可以配合面前贴出的0~145行代码起一看:
Material Material__26 { 1.000000;1.000000;1.000000;1.000000;; 3.200000; 0.000000;0.000000;0.000000;; 0.000000;0.000000;0.000000;; TextureFilename { "bd378f0.bmp"; } } Material Material__55_Material__29Sub0 { 1.000000;1.000000;1.000000;1.000000;; 3.200000; 0.000000;0.000000;0.000000;; 0.000000;0.000000;0.000000;; TextureFilename { "9496a70.bmp"; } } Material Material__55_QQSub1 { 1.000000;1.000000;1.000000;1.000000;; 3.200000; 0.000000;0.000000;0.000000;; 0.000000;0.000000;0.000000;; TextureFilename { "9622210.bmp"; } } Material Material__55_Material__30Sub2 { 1.000000;1.000000;1.000000;1.000000;; 3.200000; 0.000000;0.000000;0.000000;; 0.000000;0.000000;0.000000;; TextureFilename { "353bd50.bmp"; } }
可以发明,就是在根据面前义定0~145行义定的那些模板,做填空题罢了。
因为骨骼动画内容的特殊性,周边识知太多了,明天只能讲一小部份。为了满意大家的好奇心,我们次这先放出骨骼动画的第一个示例序程。里头有些识知还没讲到,大家可以归去自己研钻,或者是接着追浅墨后续文章的新更。而这篇文章的示例序程所含文件如下:
先给大家露透一下,其实在微软官方的DirectX SDK Samples中经已为我们把骨骼动画类装封好了,在一个名为SkinnedMesh的示例序程中。既然我们关于DirectX的识知都是拜SDK中的档文所赐,我们不妨学一学鲁迅生先教我们的“拿来主义”,直接把微软给我们写的那个骨骼动画类拿来用。
如果你的SDK是安装在D盘,那么这个骨骼动画的微软官方示例序程的路径就是如下:
D:\Program Files\Microsoft DirectX SDK(June 2010)\Samples\C++\Direct3D\SkinnedMesh
微软对Samples中代码的写书方法比拟集密,一般现实代码都放在一个cpp文件中,比如这里的SkinnedMesh示例序程,基本上代码都挤在了一个名为skinnedmesh.cpp的1970行代码的源文件中,在这个源文件中有一个叫CAllocateHierarchy的类,还有几个好用的全局数函,我们直接拿过来用就好了,浅墨为大家整合在了CAllocateHierarchy.h和CAllocateHierarchy.cpp这两个文件中了。
另外,本次用使的”陆雪琪“X文件中对骨骼动画的放存,只放存了一个名为”剑舞“的动画,也就是说用这个X模型中就只有这一个动画。这个动画是浅墨在3DS Max中自己导出的,体具入导方法我们下次再讲(其实就是在panda插件中选项调一下)。在”陆雪琪“X文件的31895行,我们就可以找到这个用AnimationSet 来义定的sworddance(剑舞)的动画集出处,头开部份如下:
因为篇幅原因,更多内容咱们就下次再细讲了,这里贴出细详注释的main.cpp的代码和微软官方Samples中为我们写好的代码CAllocateHierarchy.h,其他的代码大家可以下源代码归去自己磨琢。
好吧,上代码,首先是CAllocateHierarchy.h:
#pragma once //============================================================================= // Desc: CAllocateHierarchy.h // 来自微软官方DirectX SDK Samples中的骨骼动画类 //============================================================================= #include <d3d9.h> #include <d3dx9.h> #include "D3DUtil.h" //----------------------------------------------------------------------------- // Name: struct D3DXFRAME_DERIVED // Desc: 继承自DXDXFRAME构结的构结 //----------------------------------------------------------------------------- struct D3DXFRAME_DERIVED: public D3DXFRAME { D3DXMATRIXA16 CombinedTransformationMatrix; }; //----------------------------------------------------------------------------- // Name: struct D3DXMESHCONTAINER_DERIVED // Desc: 继承自D3DXMESHCONTAINER构结的构结 //----------------------------------------------------------------------------- struct D3DXMESHCONTAINER_DERIVED: public D3DXMESHCONTAINER { LPDIRECT3DTEXTURE9* ppTextures; //纹理数组 LPD3DXMESH pOrigMesh; //原始网格 LPD3DXATTRIBUTERANGE pAttributeTable; DWORD NumAttributeGroups; //属性组数量,即子网格数量 DWORD NumInfl; //个每顶点最多受多少骨骼的影响 LPD3DXBUFFER pBoneCombinationBuf; //骨骼结合表 D3DXMATRIX** ppBoneMatrixPtrs; //放存骨骼的合组换变矩阵 D3DXMATRIX* pBoneOffsetMatrices; //放存骨骼的初始换变矩阵 DWORD NumPaletteEntries; //骨骼数量限上 bool UseSoftwareVP; //标识是不是用使件软顶点理处 }; //----------------------------------------------------------------------------- // Name: class CAllocateHierarchy // Desc: 来自微软官方DirectX SDK Samples中的骨骼动画类,这个类用来从.X文件加载框架次层和网格模型数据 // 心核点: #define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method //----------------------------------------------------------------------------- class CAllocateHierarchy: public ID3DXAllocateHierarchy { public: STDMETHOD(CreateFrame)(THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame); STDMETHOD(CreateMeshContainer)( THIS_ LPCSTR Name, CONST D3DXMESHDATA* pMeshData, CONST D3DXMATERIAL* pMaterials, CONST D3DXEFFECTINSTANCE* pEffectInstances, DWORD NumMaterials, CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer); STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME pFrameToFree); STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER pMeshContainerBase); }; //----------------------------------------------------------------------------- // Desc: 来自微软官方DirectX SDK Samples中的骨骼动画全局数函 //----------------------------------------------------------------------------- void DrawFrame( IDirect3DDevice9* pd3dDevice, LPD3DXFRAME pFrame ); void DrawMeshContainer( IDirect3DDevice9* pd3dDevice, LPD3DXMESHCONTAINER pMeshContainerBase, LPD3DXFRAME pFrameBase ); HRESULT SetupBoneMatrixPointers( LPD3DXFRAME pFrameBase, LPD3DXFRAME pFrameRoot ); void UpdateFrameMatrices( LPD3DXFRAME pFrameBase, LPD3DXMATRIX pParentMatrix );
main.cpp的注释代码格风浅墨改了一下,自己认为工致多了,希望大家会欢喜:)
//-----------------------------------【序程明说】---------------------------------------------- // 【Visual C++】游戏发开系列套配源码五十二 浅墨DirectX教程二十 骨骼动画来袭(一) // VS2010版 // 2013年4月 Create by 浅墨 // 背景音乐素材出处: 最终幻想 Eternal Love (Short Version) // 人物模型素材出处:《诛仙》 陆雪琪 //------------------------------------------------------------------------------------------------ //-----------------------------------【宏义定部份】-------------------------------------------- // 描述:义定一些帮助宏 //------------------------------------------------------------------------------------------------ #define WINDOW_WIDTH 932 //为口窗度宽义定的宏,以便方在此处改修口窗度宽 #define WINDOW_HEIGHT 700 //为口窗高度义定的宏,以便方在此处改修口窗高度 #define WINDOW_TITLE _T("【致我们永不灭熄的游戏发开妄想】浅墨DirectX教程二十 骨骼动画来袭(一)博文套配示例序程 by浅墨") //为口窗标题义定的宏 //-----------------------------------【头文件括包部份】--------------------------------------- // 描述:括包序程所赖依的头文件 //------------------------------------------------------------------------------------------------ #include <d3d9.h> #include <d3dx9.h> #include <tchar.h> #include <time.h> #include "DirectInputClass.h" #include "CameraClass.h" #include "SkyBoxClass.h" #include "SnowParticleClass.h" #include "AllocateHierarchyClass.h" //-----------------------------------【库文件括包部份】--------------------------------------- // 描述:括包序程所赖依的库文件 //------------------------------------------------------------------------------------------------ #pragma comment(lib,"d3d9.lib") #pragma comment(lib,"d3dx9.lib") #pragma comment(lib, "dinput8.lib") // 用使DirectInput必须括包的库文件,意注这里有8 #pragma comment(lib,"dxguid.lib") #pragma comment(lib, "winmm.lib") // 地板的顶点构结 struct CUSTOMVERTEX { FLOAT _x, _y, _z; FLOAT _u, _v ; CUSTOMVERTEX(FLOAT x, FLOAT y, FLOAT z, FLOAT u, FLOAT v) : _x(x), _y(y), _z(z), _u(u), _v(v) {} }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1) //-----------------------------------【全局变量明声部份】------------------------------------- // 描述:全局变量的明声 //------------------------------------------------------------------------------------------------ LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //Direct3D设备对象 LPD3DXFONT g_pTextFPS =NULL; //字体COM接口 LPD3DXFONT g_pTextAdaperName = NULL; // 卡显信息的2D文本 LPD3DXFONT g_pTextHelper = NULL; // 帮助信息的2D文本 LPD3DXFONT g_pTextInfor= NULL; // 制绘信息的2D文本 float g_FPS= 0.0f; //一个浮点型的变量,代表帧速率 wchar_t g_strFPS[50] ={0}; //括包帧速率的符字数组 wchar_t g_strAdapterName[60] ={0}; //括包卡显名称的符字数组 D3DXMATRIX g_matWorld; //天下矩阵 D3DLIGHT9 g_Light; //全局光照 DInputClass* g_pDInput = NULL; //DInputClass类的针指实例 CameraClass* g_pCamera = NULL; //摄像机类的针指实例 SkyBoxClass* g_pSkyBox=NULL; //天空盒类的针指实例 SnowParticleClass* g_pSnowParticles = NULL; //雪花粒子系统的针指实例 //四个和骨骼动画关相的全局变量 LPD3DXFRAME g_pFrameRoot = NULL; D3DXMATRIX* g_pBoneMatrices = NULL; CAllocateHierarchy* g_pAllocateHier = NULL; LPD3DXANIMATIONCONTROLLER g_pAnimController = NULL; LPDIRECT3DVERTEXBUFFER9 g_pFloorVBuffer = NULL; //地板顶点存缓对象 LPDIRECT3DTEXTURE9 g_pFloorTexture = NULL; //地板纹理对象 //-----------------------------------【全局数函明声部份】------------------------------------- // 描述:全局数函明声,避免“未明声的标识”系列误错 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance); HRESULT Objects_Init(); void Direct3D_Render( HWND hwnd,FLOAT fTimeDelta); void Direct3D_Update( HWND hwnd,FLOAT fTimeDelta); void Direct3D_CleanUp( ); float Get_FPS(); void HelpText_Render(HWND hwnd); //-----------------------------------【WinMain( )数函】-------------------------------------- // 描述:Windows用应序程的口入数函,我们的序程从这里开始 //------------------------------------------------------------------------------------------------ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) { //开始计划一个完全的口窗类 WNDCLASSEX wndClass={0} ; //用WINDCLASSEX义定了一个口窗类,即用wndClass实例化了WINDCLASSEX,用于之后口窗的各项初始化 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //置设构结体的字节数巨细 wndClass.style = CS_HREDRAW | CS_VREDRAW; //置设口窗的款式 wndClass.lpfnWndProc = WndProc; //置设指向口窗程过数函的针指 wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; //指定括包口窗程过的序程的实例句柄。 wndClass.hIcon=(HICON)::LoadImage(NULL,_T("GameMedia\\icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //从全局的::LoadImage数函从当地加载自义定ico标图 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定口窗类的光标句柄。 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //为hbrBackground成员指定一个灰色画刷句柄 wndClass.lpszMenuName = NULL; //用一个以空终止的符字串,指定菜单源资的名字。 wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop"); //用一个以空终止的符字串,指定口窗类的名字。 if( !RegisterClassEx( &wndClass ) ) //计划完口窗后,须要对口窗类行进注册,这样才能创立该类型的口窗 return -1; HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE, //见乐闻喜的创立口窗数函CreateWindow WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); //Direct3D源资的初始化,调用失败用messagebox予以显示 if (!(S_OK==Direct3D_Init (hwnd,hInstance))) { MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("浅墨的息消口窗"), 0); //用使MessageBox数函,创立一个息消口窗 } PlaySound(L"GameMedia\\Eternal Love (Short Version).wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 MoveWindow(hwnd,200,10,WINDOW_WIDTH,WINDOW_HEIGHT,true); //调整口窗显示时的位置,口窗左上角位于屏幕坐标(200,10)处 ShowWindow( hwnd, nShowCmd ); //调用Win32数函ShowWindow来显示口窗 UpdateWindow(hwnd); //对口窗行进新更,就像我们买了新房子要装修一样 //行进DirectInput类的初始化 g_pDInput = new DInputClass(); g_pDInput->Init(hwnd,hInstance,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); //息消循环程过 MSG msg = { 0 }; //初始化msg while( msg.message != WM_QUIT ) //用使while循环 { static FLOAT fLastTime = (float)::timeGetTime(); static FLOAT fCurrTime = (float)::timeGetTime(); static FLOAT fTimeDelta = 0.0f; fCurrTime = (float)::timeGetTime(); fTimeDelta = (fCurrTime - fLastTime) / 1000.0f; fLastTime = fCurrTime; if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看用应序程息消队列,有息消时将队列中的息消派发出去。 { TranslateMessage( &msg ); //将虚拟键息消转换为符字息消 DispatchMessage( &msg ); //该数函分发一个息消给口窗序程。 } else { Direct3D_Update(hwnd,fTimeDelta); //调用新更数函,行进画面的新更 Direct3D_Render(hwnd,fTimeDelta); //调用渲染数函,行进画面的渲染 } } UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance); return 0; } //-----------------------------------【WndProc( )数函】-------------------------------------- // 描述:口窗程过数函WndProc,对口窗息消行进理处 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) //口窗程过数函WndProc { switch( message ) //switch句语开始 { case WM_PAINT: // 客户区重绘息消 Direct3D_Render(hwnd,0.0f); //调用Direct3D_Render数函,行进画面的制绘 ValidateRect(hwnd, NULL); // 新更客户区的显示 break; //跳出该switch句语 case WM_KEYDOWN: // 键盘按下息消 if (wParam == VK_ESCAPE) // ESC键 DestroyWindow(hwnd); // 销毁口窗, 并发送一条WM_DESTROY息消 break; case WM_DESTROY: //口窗销毁息消 Direct3D_CleanUp(); //调用Direct3D_CleanUp数函,清算COM接口对象 PostQuitMessage( 0 ); //向系统明表有个线程有终止请求。用来应响WM_DESTROY息消 break; //跳出该switch句语 default: //若上述case条件都不符合,则行执该default句语 return DefWindowProc( hwnd, message, wParam, lParam ); //调用省缺的口窗程过来为用应序程没有理处的口窗息消供提省缺的理处。 } return 0; //畸形出退 } //-----------------------------------【Direct3D_Init( )数函】---------------------------------- // 描述:Direct3D初始化数函,行进Direct3D的初始化 //------------------------------------------------------------------------------------------------ HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance) { //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之一,创接口】:创立Direct3D接口对象, 以便用该Direct3D对象创立Direct3D设备对象 //-------------------------------------------------------------------------------------- LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创立 if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并行进DirectX版本商协 return E_FAIL; //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之二,取信息】:取获件硬设备信息 //-------------------------------------------------------------------------------------- D3DCAPS9 caps; int vp = 0; if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) ) { return E_FAIL; } if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //持支件硬顶点算运,我们就用采件硬顶点算运,妥妥的 else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不持支件硬顶点算运,奈无只好用采件软顶点算运 //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之三,填内容】:充填D3DPRESENT_PARAMETERS构结体 //-------------------------------------------------------------------------------------- D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.BackBufferWidth = WINDOW_WIDTH; d3dpp.BackBufferHeight = WINDOW_HEIGHT; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 2; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = true; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = 0; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之四,创设备】:创立Direct3D设备接口 //-------------------------------------------------------------------------------------- if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_pd3dDevice))) return E_FAIL; //取获卡显信息到g_strAdapterName中,并在卡显名称之前加上“后以卡显型号:”符字串 wchar_t TempName[60]=L"后以卡显型号:"; //义定一个时临符字串,且便方了把"后以卡显型号:"符字串引入我们的的目符字串中 D3DADAPTER_IDENTIFIER9 Adapter; //义定一个D3DADAPTER_IDENTIFIER9构结体,用于存储卡显信息 pD3D->GetAdapterIdentifier(0,0,&Adapter);//调用GetAdapterIdentifier,取获卡显信息 int len = MultiByteToWideChar(CP_ACP,0, Adapter.Description, -1, NULL, 0);//卡显名称当初经已在Adapter.Description中了,但是其为char类型,我们要将其转为wchar_t类型 MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);//这步操纵成完后,g_strAdapterName中就为后以我们的卡显类型名的wchar_t型符字串了 wcscat_s(TempName,g_strAdapterName);//把后以我们的卡显名加到“后以卡显型号:”符字串前面,结果存在TempName中 wcscpy_s(g_strAdapterName,TempName);//把TempName中的结果拷贝到全局变量g_strAdapterName中,功败垂成~ if(!(S_OK==Objects_Init())) return E_FAIL; SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命成完,我们将其释放掉 return S_OK; } //-----------------------------------【Object_Init( )数函】-------------------------------------- // 描述:渲染源资初始化数函,在此数函中行进要被渲染的物体的源资的初始化 //-------------------------------------------------------------------------------------------------- HRESULT Objects_Init() { //创立字体 D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS); D3DXCreateFont(g_pd3dDevice, 20, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"华文中宋", &g_pTextAdaperName); D3DXCreateFont(g_pd3dDevice, 23, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微软雅黑", &g_pTextHelper); D3DXCreateFont(g_pd3dDevice, 26, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"黑体", &g_pTextInfor); // 创立面地顶点存缓 g_pd3dDevice->CreateVertexBuffer(4 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &g_pFloorVBuffer, NULL); CUSTOMVERTEX *pVertices = NULL; g_pFloorVBuffer->Lock(0, 0, (void**)&pVertices, 0); pVertices[0] = CUSTOMVERTEX(-5000.0f, 0.0f, -5000.0f, 0.0f, 30.0f); pVertices[1] = CUSTOMVERTEX(-5000.0f, 0.0f, 5000.0f, 0.0f, 0.0f); pVertices[2] = CUSTOMVERTEX( 5000.0f, 0.0f, -5000.0f, 30.0f, 30.0f); pVertices[3] = CUSTOMVERTEX( 5000.0f, 0.0f, 5000.0f, 30.0f, 0.0f); g_pFloorVBuffer->Unlock(); //创立面地纹理 D3DXCreateTextureFromFile(g_pd3dDevice, L"GameMedia\\wood.jpg", &g_pFloorTexture); g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); g_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // 置设光照 ::ZeroMemory(&g_Light, sizeof(g_Light)); g_Light.Type = D3DLIGHT_DIRECTIONAL; g_Light.Ambient = D3DXCOLOR(0.7f, 0.7f, 0.7f, 1.0f); g_Light.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); g_Light.Specular = D3DXCOLOR(0.9f, 0.9f, 0.9f, 1.0f); g_Light.Direction = D3DXVECTOR3(1.0f, 1.0f, 1.0f); g_pd3dDevice->SetLight(0, &g_Light); g_pd3dDevice->LightEnable(0, true); g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true); g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true); // 创立并初始化虚拟摄像机 g_pCamera = new CameraClass(g_pd3dDevice); g_pCamera->SetCameraPosition(&D3DXVECTOR3(0.0f, 300.0f, -800.0f)); //置设摄像机地点的位置 g_pCamera->SetTargetPosition(&D3DXVECTOR3(0.0f, 400.0f, 0.0f)); //置设目标视察点地点的位置 g_pCamera->SetViewMatrix(); //置设取景换变矩阵 D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 200000.0f); g_pCamera->SetProjMatrix(&matProj); //创立并初始化天空盒 g_pSkyBox = new SkyBoxClass( g_pd3dDevice ); g_pSkyBox->LoadSkyTextureFromFile( L"GameMedia\\frontaw2.jpg", L"GameMedia\\backaw2.jpg", L"GameMedia\\leftaw2.jpg", L"GameMedia\\rightaw2.jpg", L"GameMedia\\topaw2.jpg");//从文件加载前、后、左、右、顶面5个面的纹理图 g_pSkyBox->InitSkyBox(50000); //置设天空盒的边长 //创立并初始化雪花粒子系统 g_pSnowParticles = new SnowParticleClass(g_pd3dDevice); g_pSnowParticles->InitSnowParticle(); // 创立骨骼动画 g_pAllocateHier = new CAllocateHierarchy(); D3DXLoadMeshHierarchyFromX(L"lxq.x", D3DXMESH_MANAGED, g_pd3dDevice, g_pAllocateHier, NULL, &g_pFrameRoot, &g_pAnimController); SetupBoneMatrixPointers(g_pFrameRoot, g_pFrameRoot); //因为这个X文件中唯一一个认默的舞剑动作,所以以下代码可用可用不 // LPD3DXANIMATIONSET pAnimationSet = NULL; // g_pAnimController->GetAnimationSetByName("sworddance", &pAnimationSet); // g_pAnimController->SetTrackAnimationSet((UINT)1.0, pAnimationSet); return S_OK; } //-----------------------------------【Direct3D_Update( )数函】-------------------------------- // 描述:不是即时渲染代码但是须要即时调用的,如按键后的坐标的改更,都放在这里 //-------------------------------------------------------------------------------------------------- void Direct3D_Update( HWND hwnd,FLOAT fTimeDelta) { //用使DirectInput类读取数据 g_pDInput->GetInput(); // 沿摄像机各量分挪动视角 if (g_pDInput->IsKeyDown(DIK_A)) g_pCamera->MoveAlongRightVec(-1.0f); if (g_pDInput->IsKeyDown(DIK_D)) g_pCamera->MoveAlongRightVec( 1.0f); if (g_pDInput->IsKeyDown(DIK_W)) g_pCamera->MoveAlongLookVec( 1.0f); if (g_pDInput->IsKeyDown(DIK_S)) g_pCamera->MoveAlongLookVec(-1.0f); if (g_pDInput->IsKeyDown(DIK_R)) g_pCamera->MoveAlongUpVec( 1.0f); if (g_pDInput->IsKeyDown(DIK_F)) g_pCamera->MoveAlongUpVec(-1.0f); //沿摄像机各量分旋转视角 if (g_pDInput->IsKeyDown(DIK_LEFT)) g_pCamera->RotationUpVec(-0.003f); if (g_pDInput->IsKeyDown(DIK_RIGHT)) g_pCamera->RotationUpVec( 0.003f); if (g_pDInput->IsKeyDown(DIK_UP)) g_pCamera->RotationRightVec(-0.003f); if (g_pDInput->IsKeyDown(DIK_DOWN)) g_pCamera->RotationRightVec( 0.003f); if (g_pDInput->IsKeyDown(DIK_Q)) g_pCamera->RotationLookVec(0.001f); if (g_pDInput->IsKeyDown(DIK_E)) g_pCamera->RotationLookVec( -0.001f); //鼠标制控右向量和上向量的旋转 g_pCamera->RotationUpVec(g_pDInput->MouseDX()* 0.0003f); g_pCamera->RotationRightVec(g_pDInput->MouseDY() * 0.0003f); //鼠标轮滚制控视察点压缩操纵 static FLOAT fPosZ=0.0f; fPosZ += g_pDInput->MouseDZ()*0.03f; //算计并置设取景换变矩阵 D3DXMATRIX matView; g_pCamera->CalculateViewMatrix(&matView); g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //把准确的天下换变矩阵存到g_matWorld中 D3DXMatrixTranslation(&g_matWorld, 0.0f, 0.0f, fPosZ); //以下这段代码用于制限鼠标光标挪动域区 POINT lt,rb; RECT rect; GetClientRect(hwnd,&rect); //获得口窗部内矩形 //将矩形左上点坐标存入lt中 lt.x = rect.left; lt.y = rect.top; //将矩形右下坐标存入rb中 rb.x = rect.right; rb.y = rect.bottom; //将lt和rb的口窗坐标转换为屏幕坐标 ClientToScreen(hwnd,<); ClientToScreen(hwnd,&rb); //以屏幕坐标从新设定矩形域区 rect.left = lt.x; rect.top = lt.y; rect.right = rb.x; rect.bottom = rb.y; //制限鼠标光标挪动域区 ClipCursor(&rect); ShowCursor(false); //隐藏鼠标光标 // 置设骨骼动画的矩阵 D3DXMATRIX matFinal , matScal; D3DXMatrixIdentity(&matFinal); D3DXMatrixScaling(&matScal, 5.0f, 9.0f, 5.0f); matFinal = matScal *matFinal; g_pd3dDevice->SetTransform(D3DTS_WORLD, &matFinal); // 新更骨骼动画 g_pAnimController->AdvanceTime(fTimeDelta, NULL); //置设骨骼动画的间时 UpdateFrameMatrices(g_pFrameRoot, &matFinal); //新更框架中的换变矩阵 } //-----------------------------------【Direct3D_Render( )数函】------------------------------- // 描述:用使Direct3D行进渲染 //-------------------------------------------------------------------------------------------------- void Direct3D_Render(HWND hwnd,FLOAT fTimeDelta) { //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之一】:清屏操纵 //-------------------------------------------------------------------------------------- g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, D3DCOLOR_XRGB(100, 255, 255), 1.0f, 0); //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之二】:开始制绘 //-------------------------------------------------------------------------------------- g_pd3dDevice->BeginScene(); // 开始制绘 //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之三】:正式制绘 //-------------------------------------------------------------------------------------- //-----------------------------【制绘骨骼动画】------------------------ DrawFrame(g_pd3dDevice, g_pFrameRoot); //-----------------------------【制绘地板】----------------------------- D3DXMATRIX matFloor; D3DXMatrixTranslation(&matFloor, 0.0f, 0.0f, 0.0f); g_pd3dDevice->SetTransform(D3DTS_WORLD, &matFloor); g_pd3dDevice->SetStreamSource(0, g_pFloorVBuffer, 0, sizeof(CUSTOMVERTEX)); g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); g_pd3dDevice->SetTexture(0, g_pFloorTexture); g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); //-----------------------------【制绘天空】----------------------------- D3DXMATRIX matSky,matTransSky,matRotSky; D3DXMatrixTranslation(&matTransSky,0.0f,-13000.0f,0.0f); D3DXMatrixRotationY(&matRotSky, -0.00002f*timeGetTime()); //旋转天空网格, 简略模拟云彩动运效果 matSky=matTransSky*matRotSky; g_pSkyBox->RenderSkyBox(&matSky, false); //-----------------------------【制绘雪花粒子系统】------------------------ g_pSnowParticles->UpdateSnowParticle(fTimeDelta); g_pSnowParticles->RenderSnowParticle(); //-----------------------------【制绘文字信息】----------------------------- HelpText_Render(hwnd); //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之四】:束结制绘 //-------------------------------------------------------------------------------------- g_pd3dDevice->EndScene(); // 束结制绘 //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之五】:显示翻转 //-------------------------------------------------------------------------------------- g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // 翻转与显示 } //-----------------------------------【HelpText_Render( )数函】------------------------------- // 描述:装封了帮助信息的数函 //-------------------------------------------------------------------------------------------------- void HelpText_Render(HWND hwnd) { //义定一个矩形,用于取获主口窗矩形 RECT formatRect; GetClientRect(hwnd, &formatRect); //在口窗右上角处,显示每秒帧数 formatRect.top = 5; int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() ); g_pTextFPS->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_RGBA(0,239,136,255)); //显示卡显类型名 g_pTextAdaperName->DrawText(NULL,g_strAdapterName, -1, &formatRect, DT_TOP | DT_LEFT, D3DXCOLOR(1.0f, 0.5f, 0.0f, 1.0f)); // 输出帮助信息 formatRect.left = 0,formatRect.top = 380; g_pTextInfor->DrawText(NULL, L"制控明说:", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(235,123,230,255)); formatRect.top += 35; g_pTextHelper->DrawText(NULL, L" W:向前翔飞 S:向后翔飞 ", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" A:向左翔飞 D:向右翔飞", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" R:垂直向上翔飞 F:垂直向下翔飞", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" Q:向左倾斜 E:向右倾斜", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" 上、下、左、右向方键、鼠标挪动:视角化变 ", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" 鼠标轮滚:人物模型Y轴向方挪动", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); formatRect.top += 25; g_pTextHelper->DrawText(NULL, L" ESC键 : 出退序程", -1, &formatRect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(255,200,0,255)); } //-----------------------------------【Get_FPS( )数函】------------------------------------------ // 描述:用于算计每秒帧速率的一个数函 //-------------------------------------------------------------------------------------------------- float Get_FPS() { //义定四个静态变量 static float fps = 0; //我们须要算计的FPS值 static int frameCount = 0;//帧数 static float currentTime =0.0f;//后以间时 static float lastTime = 0.0f;//持续间时 frameCount++;//每调用一次Get_FPS()数函,帧数自增1 currentTime = timeGetTime()*0.001f;//取获系统间时,其中timeGetTime数函回返的是以毫秒为位单的系统间时,所以须要乘以0.001,到得位单为秒的间时 //如果后以间时减去持续间时大于了1秒钟,就行进一次FPS的算计和持续间时的新更,并将帧数值清零 if(currentTime - lastTime > 1.0f) //将间时制控在1秒钟 { fps = (float)frameCount /(currentTime - lastTime);//算计这1秒钟的FPS值 lastTime = currentTime; //将后以间时currentTime赋给持续间时lastTime,作为下一秒的基准间时 frameCount = 0;//将本次帧数frameCount值清零 } return fps; } //-----------------------------------【Direct3D_CleanUp( )数函】-------------------------------- // 描述:对Direct3D的源资行进清算,释放COM接口对象 //--------------------------------------------------------------------------------------------------- void Direct3D_CleanUp() { //释放COM接口对象 SAFE_DELETE(g_pDInput); SAFE_RELEASE(g_pd3dDevice); SAFE_RELEASE(g_pTextAdaperName) SAFE_RELEASE(g_pTextHelper) SAFE_RELEASE(g_pTextInfor) SAFE_RELEASE(g_pTextFPS) SAFE_RELEASE(g_pd3dDevice) }
背景音乐用使的是来自《最终幻想13》原声带的一曲《Eternal Love (Short Version)》,挺难听的
最后我们看一下行运截图:
、
当然,我们仍然可以像之前的示例序程一样,通过鼠标和键盘来制控视角的挪动,全方位行进视察:
文章最后,依旧是放出本篇文章套配源代码的下载:
: ( 源资上传了一直没显示出来- -我再多上传几回试试。。。。大家急着的话去我源资页看看吧,说不定什么时候就有了。
以上就是本节记笔的全部内容,更多精彩内容,且听下回分解。
浅墨在这里,希望欢喜游戏发开系列文章的友人们能留下你们的评论,每次浅墨登岸客博看到大家的留言的时候会都非常高兴,感到自己正在递传一种仰信,一种精力,感到自己不是一个人在斗战。
文章最后,仍然是【每文一语】栏目,明天的句子是:
面临生活,我们没有选择,但是请一直相信,当初所阅历的一切,都有它存在的意思。
加油:)
下周一,让我们离游戏发开的妄想更近一步。
下周一,游戏发开记笔,我们,不见不散。
大家可以在这里找到浅墨:【浅墨的浪新微博】http://www.weibo.com/u/1723155442
文章结束给大家分享下程序员的一些笑话语录: 一个程序员对自己的未来很迷茫,于是去问上帝。
"万能的上帝呀,请你告诉我,我的未来会怎样?"
上帝说"我的孩子,你去问Lippman,他现在领导的程序员的队伍可能是地球上最大的"
于是他去问Lippman。
Lippman说"程序员的未来就是驾驭程序员"
这个程序员对这个未来不满意,于是他又去问上帝。
"万能的上帝呀,请你告诉我,我的未来会怎样?"
上帝说"我的孩子,你去问Gates,他现在所拥有的财产可能是地球上最多的"
于是他去问Gates。
Gates说"程序员的未来就是榨取程序员"
这个程序员对这个未来不满意,于是他又去问上帝。
"万能的上帝呀,请你告诉我,我的未来会怎样?"
上帝说"我的孩子,你去问侯捷,他写的计算机书的读者可能是地球上最多的"
于是他去问侯捷。
侯捷说"程序员的未来就是诱惑程序员"
这个程序员对这个未来不满意,于是他又去问上帝。
"万能的上帝呀,请你告诉我,我的未来会怎样?"
上帝摇摇头"唉,我的孩子,你还是别当程序员了")