<@毛驴:>简单的蒙皮动画
上一篇翻译的文章,主要讲的是表现刚体运动的骨骼动画。相比之下,刚体的运动要简单得多,因为刚体的运动不涉及到形变。组成模型的网格只是在世界坐标系中平移、旋转或者翻转。而仅仅有刚体的骨骼动画对于3D游戏是远远不够的,所以这里我来分享一下本人学习蒙皮动画的成果。
其实蒙皮动画就是更高级的骨骼动画,一样有Frame,同样有父子关系。Frame中有关联矩阵,它代表的是这个Frame和上一级Frame发生相对运动时,它所应遵循的变换规律。所以在更新frame的关联矩阵是,应从根Frame开始以递归方式层层遍历,规则是:新矩阵=父辈矩阵*本身矩阵。
而当Frame的相对位置发生变化时,Mesh不仅仅是空间位置发生变化,还要发生形变。网格模型中的每个顶点不一定都只和一块骨骼关联起来,所以我们要做的就是:对每一个顶点,遍历它的骨骼连接表,找出它相对于每块骨骼的偏移权重(通常由美工建好),将权重乘以骨骼的偏移矩阵,对每个顶点。都将组合完成后的变化矩阵运用到生成新的蒙皮网络模型中。
这就是蒙皮动画的基本原理,下面来看看代码吧:
Tiny.x和Tiny.BMP可以在DirectX的SDK中,下载后一并放在工程文件目录下即可
程序代码:
Code
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace Animation
{
public class Form1 : System.Windows.Forms.Form
{
private Device device = null;
private AnimationRootFrame rootFrame;//使用根Frame来引用整个模型
//用来设置摄像机的位置
private Vector3 objectCenter;//模型的中心
private float objectRadius;//模型包围球的半径
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
public bool InitializeGraphics()
{
// 设置Present Parameters参数,这基本就是最简单的设置方法
PresentParameters presentParams = new PresentParameters();
presentParams.Windowed = true;//应用程序在窗口模式下运行
presentParams.SwapEffect = SwapEffect.Discard;//后台交链缓冲区的设置
presentParams.AutoDepthStencilFormat = DepthFormat.D16;//深度缓存的格式化方式
presentParams.EnableAutoDepthStencil = true;//深度缓存以托管方式运行
bool canDoHardwareSkinning = true;
Caps hardware = Manager.GetDeviceCaps(0,DeviceType.Hardware);//获取当前默认渲染设备(硬件)的性能
if(hardware.MaxVertexBlendMatrices >= 4)//这个模型中,顶点混合矩阵最多为4个
{
//默认情况下,顶点处理方式为软件处理
CreateFlags flags = CreateFlags.SoftwareVertexProcessing;
//如果硬件性能达标,怎改为硬件处理
if(hardware.DeviceCaps.SupportsHardwareTransformAndLight)
flags = CreateFlags.HardwareVertexProcessing;
if(hardware.DeviceCaps.SupportsPureDevice)
flags |= CreateFlags.PureDevice;
device = new Device(0,DeviceType.Hardware,this,flags,presentParams);
}
else
{
canDoHardwareSkinning = false;
device = new Device(0,DeviceType.Reference,this,CreateFlags.SoftwareVertexProcessing,presentParams);
}
Device.IsUsingEventHandlers=false;
//读取文件,开始渲染动画
CreateAnimation(@"..\..\tiny.x",presentParams);
device.DeviceReset += new EventHandler(OnDeviceReset);
OnDeviceReset(device,null);
return canDoHardwareSkinning;
}
private void OnDeviceReset(object sender, EventArgs e)
{
Device dev = (Device)sender;//消息的发送者即渲染设备
//设置view矩阵
Vector3 vEye = new Vector3(0,0,-1.8f*objectRadius);
Vector3 vUp = new Vector3(0,1,0);
dev.Transform.View = Matrix.LookAtLH(vEye,objectCenter,vUp);
//设置Projection矩阵
float aspectRatio = (float)dev.PresentationParameters.BackBufferWidth / (float)dev.PresentationParameters.BackBufferHeight;
dev.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,aspectRatio,objectRadius/64.0f,objectRadius * 200.0f);
//设置灯光效果,这基本是最简单的效果
dev.Lights[0].Type = LightType.Directional;
dev.Lights[0].Direction = new Vector3(0.0f,0.0f,1.0f);
dev.Lights[0].Diffuse = Color.White;
dev.Lights[0].Update();
dev.Lights[0].Enabled = true;
}
//重写Form类的OnPaint方法,但是实际应该叫OnRender方法更好
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
//处理下一刻的Frame和Mesh
ProcessNextFrame();
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);
device.BeginScene();
//渲染蒙皮动画模型
DrawFrame((FrameDerived)rootFrame.FrameHierarchy);
device.EndScene();
device.Present();
this.Invalidate();
}
private void ProcessNextFrame()
{
//设置世界矩阵
Matrix worldMatrix = Matrix.Translation(objectCenter);
device.Transform.World = worldMatrix;
if (rootFrame.AnimationController != null)
rootFrame.AnimationController.AdvanceTime(3.0f, null);//如果想要加快模型步行的速度,可以改变第一个参数的大小,越大则越快
UpdateFrameMatrices((FrameDerived)rootFrame.FrameHierarchy, worldMatrix);//更新Frame矩阵
}
//使用递归调用的方式,更新各个Frame的变换矩阵
private void UpdateFrameMatrices(FrameDerived frame,Matrix parentMatrix)
{
//frame的变化就是将自己的关联矩阵,和父Frame的变换矩阵相乘
frame.CombinedTransformationMatrix = frame.TransformationMatrix * parentMatrix;
if(frame.FrameSibling != null)//处理兄弟frame
UpdateFrameMatrices((FrameDerived)frame.FrameSibling,parentMatrix);
if(frame.FrameFirstChild != null)//处理子frame
UpdateFrameMatrices((FrameDerived)frame.FrameFirstChild,frame.CombinedTransformationMatrix);
}
//开始渲染,同样是递归的方法
private void DrawFrame(FrameDerived frame)
{
//模型是由Mesh表现出来的,所以首先要把Frame所包含的MeshContainer找出来
MeshContainerDerived mesh = (MeshContainerDerived)frame.MeshContainer;
//处理MeshContainer了
while(mesh != null)
{
DrawMeshContainer(mesh, frame);
mesh = (MeshContainerDerived)mesh.NextContainer;
}
//递归处理兄弟Frame
if (frame.FrameSibling != null)
{
DrawFrame((FrameDerived)frame.FrameSibling);
}
//递归处理子Frame
if (frame.FrameFirstChild != null)
{
DrawFrame((FrameDerived)frame.FrameFirstChild);
}
}
//渲染Mesh
private void DrawMeshContainer(MeshContainerDerived mesh, FrameDerived frame)
{
//如果模型是动态的
if(mesh.SkinInformation != null)
{
int attribIdPrev = -1;
//开始渲染,遍历MeshContainer中的各个成员
for(int iattrib = 0; iattrib<mesh.NumberAttributes;iattrib++)
{
//读取骨骼连接表信息,确定将使用的混合权重的数量为numBlend
int numBlend = 0;
BoneCombination[] bones = mesh.GetBones();
for(int i=0; i<mesh.NumberInfluences; i++)
{
if(bones[iattrib].BoneId[i] != -1)
numBlend = i;
}
//这里也是检查渲染设备能力
if(device.DeviceCaps.MaxVertexBlendMatrices >= numBlend +1)
{
//提取Mesh的偏移矩阵和对应Frame的关联矩阵
Matrix[] offsetMatrices = mesh.GetOffsetMatrices();
FrameDerived[] frameMatrices = mesh.GetFrames();
for(int i=0;i<mesh.NumberInfluences;i++)
{
int matrixIndex = bones[iattrib].BoneId[i];
if(matrixIndex != -1)
{
//对应Frame的关联变换矩阵和偏移矩阵的乘积
Matrix tempMatrix = offsetMatrices[matrixIndex]*frameMatrices[matrixIndex].CombinedTransformationMatrix;
//设置索引世界矩阵
device.Transform.SetWorldMatrixByIndex(i,tempMatrix);
}
}
//使用设置好的索引世界矩阵来混合顶点
device.RenderState.VertexBlend = (VertexBlend)numBlend;
//处理Mesh的材质和贴图信息
if((attribIdPrev != bones[iattrib].AttributeId) || (attribIdPrev == -1))
{
device.Material = mesh.GetMaterials()[bones[iattrib].AttributeId].Material3D;
device.SetTexture(0,mesh.GetTextures()[bones[iattrib].AttributeId]);
attribIdPrev = bones[iattrib].AttributeId;
}
mesh.MeshData.Mesh.DrawSubset(iattrib);
}
}
}
//对于静态的Mesh,处理纹理和贴图就可以了
else
{
ExtendedMaterial[] mtrl = mesh.GetMaterials();
for(int iMaterial = 0; iMaterial<mtrl.Length;iMaterial++)
{
device.Material = mtrl[iMaterial].Material3D;
device.SetTexture(0,mesh.GetTextures()[iMaterial]);
mesh.MeshData.Mesh.DrawSubset(iMaterial);
}
}
}
private void CreateAnimation(string file, PresentParameters presentParams)
{
// 声明AllocateHierarchyDerived的实例
AllocateHierarchyDerived alloc = new AllocateHierarchyDerived(this);
// 把模型文件读入缓存
rootFrame = Mesh.LoadHierarchyFromFile(file, MeshFlags.Managed,device, alloc, null);
// 得到模型包围球的半径大小
objectRadius = Frame.CalculateBoundingSphere(rootFrame.FrameHierarchy, out objectCenter);
// 设置动画有关矩阵
SetupBoneMatrices((FrameDerived)rootFrame.FrameHierarchy);
}
private void SetupBoneMatrices(FrameDerived frame)
{
//处理frame的meshcontainer中的矩阵
if (frame.MeshContainer != null)
{
SetupBoneMatrices((MeshContainerDerived)frame.MeshContainer);
}
//兄弟frame
if (frame.FrameSibling != null)
{
SetupBoneMatrices((FrameDerived)frame.FrameSibling);
}
//子frame
if (frame.FrameFirstChild != null)
{
SetupBoneMatrices((FrameDerived)frame.FrameFirstChild);
}
}
//处理mesh的矩阵
private void SetupBoneMatrices(MeshContainerDerived mesh)
{
//只有包含骨骼信息才会有下面的步骤
if (mesh.SkinInformation != null)
{
int numBones = mesh.SkinInformation.NumberBones;
FrameDerived[] frameMatrices = new FrameDerived[numBones];
//遍历frame列表,找到名称相符的frame,并且保存之
for(int i = 0; i< numBones; i++)
{
FrameDerived frame = (FrameDerived)Frame.Find(
rootFrame.FrameHierarchy,
mesh.SkinInformation.GetBoneName(i));
if (frame == null)
throw new ArgumentException();
frameMatrices[i] = frame;
}
mesh.SetFrames(frameMatrices);
}
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(632, 390);
this.Name = "Form1";
this.Text = "Form1";
}
#endregion
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
using (Form1 frm = new Form1())
{
frm.Show();
if (!frm.InitializeGraphics())
{
MessageBox.Show("Your card can not perform skeletal animation on " +
"this file in hardware. This application will run in " +
"ref mode instead.");
}
Application.Run(frm);
}
}
public void GenerateSkinnedMesh(MeshContainerDerived mesh)
{
if (mesh.SkinInformation == null)
throw new ArgumentException();
int numInfl = 0;
BoneCombination[] bones;
//运用骨骼连接表和顶点混合权重产生一个可以被渲染的Mesh
MeshData m = mesh.MeshData;
m.Mesh = mesh.SkinInformation.ConvertToBlendedMesh(m.Mesh, MeshFlags.Managed
| MeshFlags.OptimizeVertexCache, mesh.GetAdjacencyStream(), out numInfl,
out bones);
mesh.NumberInfluences = numInfl;
mesh.SetBones(bones);
mesh.NumberAttributes = bones.Length;
mesh.MeshData = m;
}
}
public class FrameDerived : Frame
{
// 存储关联变换矩阵
private Matrix combined = Matrix.Identity;
public Matrix CombinedTransformationMatrix
{
get { return combined; }
set { combined = value; }
}
}
/// <summary>
/// 继承于MeshContainer
/// </summary>
public class MeshContainerDerived : MeshContainer
{
private Texture[] meshTextures = null;
private int numAttr = 0;
private int numInfl = 0;
private BoneCombination[] bones;
private FrameDerived[] frameMatrices;
private Matrix[] offsetMatrices;
public Texture[] GetTextures() { return meshTextures; }
public void SetTextures(Texture[] textures) { meshTextures = textures; }
public BoneCombination[] GetBones() { return bones; }
public void SetBones(BoneCombination[] b) { bones = b; }
public FrameDerived[] GetFrames() { return frameMatrices; }
public void SetFrames(FrameDerived[] frames) { frameMatrices = frames; }
public Matrix[] GetOffsetMatrices() { return offsetMatrices; }
public void SetOffsetMatrices(Matrix[] matrices) { offsetMatrices = matrices; }
public int NumberAttributes { get { return numAttr; } set { numAttr = value; } }
public int NumberInfluences { get { return numInfl; } set { numInfl = value; } }
}
/// <summary>
/// 继承于AllocateHierarchy
/// </summary>
public class AllocateHierarchyDerived : AllocateHierarchy
{
Form1 app = null;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="parent">得到对应用程序的引用</param>
public AllocateHierarchyDerived(Form1 parent)
{
app = parent;
}
/// <summary>
/// 实例化Frame
/// </summary>
public override Frame CreateFrame(string name)
{
FrameDerived frame = new FrameDerived();
frame.Name = name;
frame.TransformationMatrix = Matrix.Identity;
frame.CombinedTransformationMatrix = Matrix.Identity;
return frame;
}
/// <summary>
/// 实例化MeshContainer
/// </summary>
public override MeshContainer CreateMeshContainer(string name,
MeshData meshData, ExtendedMaterial[] materials,
EffectInstance[] effectInstances,
GraphicsStream adjacency,
SkinInformation skinInfo)
{
//检查mesh信息是否为空
if (meshData.Mesh == null)
throw new ArgumentException();
// 必须为顶点设置格式
if (meshData.Mesh.VertexFormat == VertexFormats.None)
throw new ArgumentException();
MeshContainerDerived mesh = new MeshContainerDerived();
mesh.Name = name;
int numFaces = meshData.Mesh.NumberFaces;
//得到渲染Mesh的设备
Device dev = meshData.Mesh.Device;
// 处理法线
if ((meshData.Mesh.VertexFormat & VertexFormats.Normal) == 0)
{
Mesh tempMesh = meshData.Mesh.Clone(meshData.Mesh.Options.Value,
meshData.Mesh.VertexFormat | VertexFormats.Normal, dev);
meshData.Mesh = tempMesh;
meshData.Mesh.ComputeNormals();
}
//保存材质
mesh.SetMaterials(materials);
mesh.SetAdjacency(adjacency);
Texture[] meshTextures = new Texture[materials.Length];
//增加贴图
for (int i = 0; i < materials.Length; i++)
{
if (materials[i].TextureFilename != null)
{
meshTextures[i] = TextureLoader.FromFile(dev, @"..\..\" +
materials[i].TextureFilename);
}
}
mesh.SetTextures(meshTextures);
mesh.MeshData = meshData;
//如果包含骨骼信息,存储必要的数据
if (skinInfo != null)
{
mesh.SkinInformation = skinInfo;
//骨骼连接表中骨骼个数
int numBones = skinInfo.NumberBones;
//偏移矩阵
Matrix[] offsetMatrices = new Matrix[numBones];
for (int i = 0; i < numBones; i++)
offsetMatrices[i] = skinInfo.GetBoneOffsetMatrix(i);
mesh.SetOffsetMatrices(offsetMatrices);
//进一步为骨骼动画做处理
app.GenerateSkinnedMesh(mesh);
}
return mesh;
}
}
}
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
namespace Animation
{
public class Form1 : System.Windows.Forms.Form
{
private Device device = null;
private AnimationRootFrame rootFrame;//使用根Frame来引用整个模型
//用来设置摄像机的位置
private Vector3 objectCenter;//模型的中心
private float objectRadius;//模型包围球的半径
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
}
public bool InitializeGraphics()
{
// 设置Present Parameters参数,这基本就是最简单的设置方法
PresentParameters presentParams = new PresentParameters();
presentParams.Windowed = true;//应用程序在窗口模式下运行
presentParams.SwapEffect = SwapEffect.Discard;//后台交链缓冲区的设置
presentParams.AutoDepthStencilFormat = DepthFormat.D16;//深度缓存的格式化方式
presentParams.EnableAutoDepthStencil = true;//深度缓存以托管方式运行
bool canDoHardwareSkinning = true;
Caps hardware = Manager.GetDeviceCaps(0,DeviceType.Hardware);//获取当前默认渲染设备(硬件)的性能
if(hardware.MaxVertexBlendMatrices >= 4)//这个模型中,顶点混合矩阵最多为4个
{
//默认情况下,顶点处理方式为软件处理
CreateFlags flags = CreateFlags.SoftwareVertexProcessing;
//如果硬件性能达标,怎改为硬件处理
if(hardware.DeviceCaps.SupportsHardwareTransformAndLight)
flags = CreateFlags.HardwareVertexProcessing;
if(hardware.DeviceCaps.SupportsPureDevice)
flags |= CreateFlags.PureDevice;
device = new Device(0,DeviceType.Hardware,this,flags,presentParams);
}
else
{
canDoHardwareSkinning = false;
device = new Device(0,DeviceType.Reference,this,CreateFlags.SoftwareVertexProcessing,presentParams);
}
Device.IsUsingEventHandlers=false;
//读取文件,开始渲染动画
CreateAnimation(@"..\..\tiny.x",presentParams);
device.DeviceReset += new EventHandler(OnDeviceReset);
OnDeviceReset(device,null);
return canDoHardwareSkinning;
}
private void OnDeviceReset(object sender, EventArgs e)
{
Device dev = (Device)sender;//消息的发送者即渲染设备
//设置view矩阵
Vector3 vEye = new Vector3(0,0,-1.8f*objectRadius);
Vector3 vUp = new Vector3(0,1,0);
dev.Transform.View = Matrix.LookAtLH(vEye,objectCenter,vUp);
//设置Projection矩阵
float aspectRatio = (float)dev.PresentationParameters.BackBufferWidth / (float)dev.PresentationParameters.BackBufferHeight;
dev.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,aspectRatio,objectRadius/64.0f,objectRadius * 200.0f);
//设置灯光效果,这基本是最简单的效果
dev.Lights[0].Type = LightType.Directional;
dev.Lights[0].Direction = new Vector3(0.0f,0.0f,1.0f);
dev.Lights[0].Diffuse = Color.White;
dev.Lights[0].Update();
dev.Lights[0].Enabled = true;
}
//重写Form类的OnPaint方法,但是实际应该叫OnRender方法更好
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
//处理下一刻的Frame和Mesh
ProcessNextFrame();
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);
device.BeginScene();
//渲染蒙皮动画模型
DrawFrame((FrameDerived)rootFrame.FrameHierarchy);
device.EndScene();
device.Present();
this.Invalidate();
}
private void ProcessNextFrame()
{
//设置世界矩阵
Matrix worldMatrix = Matrix.Translation(objectCenter);
device.Transform.World = worldMatrix;
if (rootFrame.AnimationController != null)
rootFrame.AnimationController.AdvanceTime(3.0f, null);//如果想要加快模型步行的速度,可以改变第一个参数的大小,越大则越快
UpdateFrameMatrices((FrameDerived)rootFrame.FrameHierarchy, worldMatrix);//更新Frame矩阵
}
//使用递归调用的方式,更新各个Frame的变换矩阵
private void UpdateFrameMatrices(FrameDerived frame,Matrix parentMatrix)
{
//frame的变化就是将自己的关联矩阵,和父Frame的变换矩阵相乘
frame.CombinedTransformationMatrix = frame.TransformationMatrix * parentMatrix;
if(frame.FrameSibling != null)//处理兄弟frame
UpdateFrameMatrices((FrameDerived)frame.FrameSibling,parentMatrix);
if(frame.FrameFirstChild != null)//处理子frame
UpdateFrameMatrices((FrameDerived)frame.FrameFirstChild,frame.CombinedTransformationMatrix);
}
//开始渲染,同样是递归的方法
private void DrawFrame(FrameDerived frame)
{
//模型是由Mesh表现出来的,所以首先要把Frame所包含的MeshContainer找出来
MeshContainerDerived mesh = (MeshContainerDerived)frame.MeshContainer;
//处理MeshContainer了
while(mesh != null)
{
DrawMeshContainer(mesh, frame);
mesh = (MeshContainerDerived)mesh.NextContainer;
}
//递归处理兄弟Frame
if (frame.FrameSibling != null)
{
DrawFrame((FrameDerived)frame.FrameSibling);
}
//递归处理子Frame
if (frame.FrameFirstChild != null)
{
DrawFrame((FrameDerived)frame.FrameFirstChild);
}
}
//渲染Mesh
private void DrawMeshContainer(MeshContainerDerived mesh, FrameDerived frame)
{
//如果模型是动态的
if(mesh.SkinInformation != null)
{
int attribIdPrev = -1;
//开始渲染,遍历MeshContainer中的各个成员
for(int iattrib = 0; iattrib<mesh.NumberAttributes;iattrib++)
{
//读取骨骼连接表信息,确定将使用的混合权重的数量为numBlend
int numBlend = 0;
BoneCombination[] bones = mesh.GetBones();
for(int i=0; i<mesh.NumberInfluences; i++)
{
if(bones[iattrib].BoneId[i] != -1)
numBlend = i;
}
//这里也是检查渲染设备能力
if(device.DeviceCaps.MaxVertexBlendMatrices >= numBlend +1)
{
//提取Mesh的偏移矩阵和对应Frame的关联矩阵
Matrix[] offsetMatrices = mesh.GetOffsetMatrices();
FrameDerived[] frameMatrices = mesh.GetFrames();
for(int i=0;i<mesh.NumberInfluences;i++)
{
int matrixIndex = bones[iattrib].BoneId[i];
if(matrixIndex != -1)
{
//对应Frame的关联变换矩阵和偏移矩阵的乘积
Matrix tempMatrix = offsetMatrices[matrixIndex]*frameMatrices[matrixIndex].CombinedTransformationMatrix;
//设置索引世界矩阵
device.Transform.SetWorldMatrixByIndex(i,tempMatrix);
}
}
//使用设置好的索引世界矩阵来混合顶点
device.RenderState.VertexBlend = (VertexBlend)numBlend;
//处理Mesh的材质和贴图信息
if((attribIdPrev != bones[iattrib].AttributeId) || (attribIdPrev == -1))
{
device.Material = mesh.GetMaterials()[bones[iattrib].AttributeId].Material3D;
device.SetTexture(0,mesh.GetTextures()[bones[iattrib].AttributeId]);
attribIdPrev = bones[iattrib].AttributeId;
}
mesh.MeshData.Mesh.DrawSubset(iattrib);
}
}
}
//对于静态的Mesh,处理纹理和贴图就可以了
else
{
ExtendedMaterial[] mtrl = mesh.GetMaterials();
for(int iMaterial = 0; iMaterial<mtrl.Length;iMaterial++)
{
device.Material = mtrl[iMaterial].Material3D;
device.SetTexture(0,mesh.GetTextures()[iMaterial]);
mesh.MeshData.Mesh.DrawSubset(iMaterial);
}
}
}
private void CreateAnimation(string file, PresentParameters presentParams)
{
// 声明AllocateHierarchyDerived的实例
AllocateHierarchyDerived alloc = new AllocateHierarchyDerived(this);
// 把模型文件读入缓存
rootFrame = Mesh.LoadHierarchyFromFile(file, MeshFlags.Managed,device, alloc, null);
// 得到模型包围球的半径大小
objectRadius = Frame.CalculateBoundingSphere(rootFrame.FrameHierarchy, out objectCenter);
// 设置动画有关矩阵
SetupBoneMatrices((FrameDerived)rootFrame.FrameHierarchy);
}
private void SetupBoneMatrices(FrameDerived frame)
{
//处理frame的meshcontainer中的矩阵
if (frame.MeshContainer != null)
{
SetupBoneMatrices((MeshContainerDerived)frame.MeshContainer);
}
//兄弟frame
if (frame.FrameSibling != null)
{
SetupBoneMatrices((FrameDerived)frame.FrameSibling);
}
//子frame
if (frame.FrameFirstChild != null)
{
SetupBoneMatrices((FrameDerived)frame.FrameFirstChild);
}
}
//处理mesh的矩阵
private void SetupBoneMatrices(MeshContainerDerived mesh)
{
//只有包含骨骼信息才会有下面的步骤
if (mesh.SkinInformation != null)
{
int numBones = mesh.SkinInformation.NumberBones;
FrameDerived[] frameMatrices = new FrameDerived[numBones];
//遍历frame列表,找到名称相符的frame,并且保存之
for(int i = 0; i< numBones; i++)
{
FrameDerived frame = (FrameDerived)Frame.Find(
rootFrame.FrameHierarchy,
mesh.SkinInformation.GetBoneName(i));
if (frame == null)
throw new ArgumentException();
frameMatrices[i] = frame;
}
mesh.SetFrames(frameMatrices);
}
}
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(632, 390);
this.Name = "Form1";
this.Text = "Form1";
}
#endregion
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
using (Form1 frm = new Form1())
{
frm.Show();
if (!frm.InitializeGraphics())
{
MessageBox.Show("Your card can not perform skeletal animation on " +
"this file in hardware. This application will run in " +
"ref mode instead.");
}
Application.Run(frm);
}
}
public void GenerateSkinnedMesh(MeshContainerDerived mesh)
{
if (mesh.SkinInformation == null)
throw new ArgumentException();
int numInfl = 0;
BoneCombination[] bones;
//运用骨骼连接表和顶点混合权重产生一个可以被渲染的Mesh
MeshData m = mesh.MeshData;
m.Mesh = mesh.SkinInformation.ConvertToBlendedMesh(m.Mesh, MeshFlags.Managed
| MeshFlags.OptimizeVertexCache, mesh.GetAdjacencyStream(), out numInfl,
out bones);
mesh.NumberInfluences = numInfl;
mesh.SetBones(bones);
mesh.NumberAttributes = bones.Length;
mesh.MeshData = m;
}
}
public class FrameDerived : Frame
{
// 存储关联变换矩阵
private Matrix combined = Matrix.Identity;
public Matrix CombinedTransformationMatrix
{
get { return combined; }
set { combined = value; }
}
}
/// <summary>
/// 继承于MeshContainer
/// </summary>
public class MeshContainerDerived : MeshContainer
{
private Texture[] meshTextures = null;
private int numAttr = 0;
private int numInfl = 0;
private BoneCombination[] bones;
private FrameDerived[] frameMatrices;
private Matrix[] offsetMatrices;
public Texture[] GetTextures() { return meshTextures; }
public void SetTextures(Texture[] textures) { meshTextures = textures; }
public BoneCombination[] GetBones() { return bones; }
public void SetBones(BoneCombination[] b) { bones = b; }
public FrameDerived[] GetFrames() { return frameMatrices; }
public void SetFrames(FrameDerived[] frames) { frameMatrices = frames; }
public Matrix[] GetOffsetMatrices() { return offsetMatrices; }
public void SetOffsetMatrices(Matrix[] matrices) { offsetMatrices = matrices; }
public int NumberAttributes { get { return numAttr; } set { numAttr = value; } }
public int NumberInfluences { get { return numInfl; } set { numInfl = value; } }
}
/// <summary>
/// 继承于AllocateHierarchy
/// </summary>
public class AllocateHierarchyDerived : AllocateHierarchy
{
Form1 app = null;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="parent">得到对应用程序的引用</param>
public AllocateHierarchyDerived(Form1 parent)
{
app = parent;
}
/// <summary>
/// 实例化Frame
/// </summary>
public override Frame CreateFrame(string name)
{
FrameDerived frame = new FrameDerived();
frame.Name = name;
frame.TransformationMatrix = Matrix.Identity;
frame.CombinedTransformationMatrix = Matrix.Identity;
return frame;
}
/// <summary>
/// 实例化MeshContainer
/// </summary>
public override MeshContainer CreateMeshContainer(string name,
MeshData meshData, ExtendedMaterial[] materials,
EffectInstance[] effectInstances,
GraphicsStream adjacency,
SkinInformation skinInfo)
{
//检查mesh信息是否为空
if (meshData.Mesh == null)
throw new ArgumentException();
// 必须为顶点设置格式
if (meshData.Mesh.VertexFormat == VertexFormats.None)
throw new ArgumentException();
MeshContainerDerived mesh = new MeshContainerDerived();
mesh.Name = name;
int numFaces = meshData.Mesh.NumberFaces;
//得到渲染Mesh的设备
Device dev = meshData.Mesh.Device;
// 处理法线
if ((meshData.Mesh.VertexFormat & VertexFormats.Normal) == 0)
{
Mesh tempMesh = meshData.Mesh.Clone(meshData.Mesh.Options.Value,
meshData.Mesh.VertexFormat | VertexFormats.Normal, dev);
meshData.Mesh = tempMesh;
meshData.Mesh.ComputeNormals();
}
//保存材质
mesh.SetMaterials(materials);
mesh.SetAdjacency(adjacency);
Texture[] meshTextures = new Texture[materials.Length];
//增加贴图
for (int i = 0; i < materials.Length; i++)
{
if (materials[i].TextureFilename != null)
{
meshTextures[i] = TextureLoader.FromFile(dev, @"..\..\" +
materials[i].TextureFilename);
}
}
mesh.SetTextures(meshTextures);
mesh.MeshData = meshData;
//如果包含骨骼信息,存储必要的数据
if (skinInfo != null)
{
mesh.SkinInformation = skinInfo;
//骨骼连接表中骨骼个数
int numBones = skinInfo.NumberBones;
//偏移矩阵
Matrix[] offsetMatrices = new Matrix[numBones];
for (int i = 0; i < numBones; i++)
offsetMatrices[i] = skinInfo.GetBoneOffsetMatrix(i);
mesh.SetOffsetMatrices(offsetMatrices);
//进一步为骨骼动画做处理
app.GenerateSkinnedMesh(mesh);
}
return mesh;
}
}
}
运行效果: