现在你需要创建一个新的模型处理器(model processor)扩展XNA默认的模型处理器。你将使用这个新模型处理器处理动画模型,提取骨骼和动画,并将它们存储为一个AnimatedModelData对象。
要创建一个新模型处理器你应创建一个叫做AnimatedModelProcessorWin的新的素材管道扩展库(Content Pipeline Extension Library)项目。这个库项目来自于一个新素材处理器类(content processor class),并会自动将素材管道组件(Content Pipeline assembly,Microsoft.Xna. Framework.Content. Pipeline)添加到项目中。因为你将使用AnimatedModelContentWin库(上一部分建立的)存储动画数据,所以你还需要将这个库添加到项目中。代码如下:
[ContentProcessor]
public class ContentProcessor1 : ContentProcessor<TInput, TOutput>
{
public override TOutput Process(TInput input, ContentProcessorContext context)
{
// TODO throw new NotImplementedException();
}
默认素材处理器类扩展了ContentProcessor类,它作为任何素材管道处理器的基类,用来处理类型为Tinput的对象并输出为类型为TOutput的新对象。因为你对创建一个新的素材处理器不感兴趣,只想扩展某些功能,所以你应扩展一个已有的素材处理器而不是ContentProcessor类。在这里,你将扩展ModelProcessor类,它是默认的模型处理器类。你还要把你的新素材处理器类重命名为AnimatedModelProcessor。以下是代码:
[ContentProcessor]
public class AnimatedModelProcessor : ModelProcessor
{
public static string TEXTURES_PATH = "Textures/";
public static string EFFECTS_PATH = "Effects/";
public static string EFFECT_FILENAME = "AnimatedModel.fx";
public override ModelContent Process(NodeContent input, ContentProcessorContext context)
{
protected override MaterialContent ConvertMaterial( MaterialContent material, ContentProcessorContext context)
{
...
}
}
}
在ModelProcessor类中有很多方法可以重写,但处理动画模型只需重写Process和ConvertMaterial方法。主要方法是Process方法,这个方法需要将一个NodeContent对象——包含网格、骨骼和动画——转换为ModelContent对象——存储XNA模型对象的数据。除了Process方法,还要调用ConvertMaterial方法处理模型材质。
重写默认的处理方法
这部分你将重写ModelProcessor类的Process方法,这个方法用以处理模型。你还将创建两个新方法提取模型骨骼和动画:ExtractSkeletonAndAnimations方法和ExtractAnimations方法,ExtractAnimations在ExtractSkeletonAndAnimations方法中调用。以下是代码:
public override ModelContent Process(NodeContent input, ContentProcessorContext context)
{
// Process the model with the default processor
ModelContent model = base.Process(input, context);
// Now extract the model skeleton and all its animations
AnimatedModelData animatedModelData = ExtractSkeletonAndAnimations(input, context);
// Stores the skeletal animation data in the model
Dictionary<string, object> dictionary = new Dictionary<string, object>();
dictionary.Add("AnimatedModelData", animatedModelData);
model.Tag = dictionary;
return model;
}
在Process方法开始,你调用它的基类ModelProcessor,接着,调用ExtractSkeletonAndAnimations方法处理输入的NodeContent并返回一个包含模型骨骼和动画的AnimatedModelData对象。最后,创建一个dictionary将一个字符映射到对象,将AnimatedModelData添加到这个dictionary,将这个dictionary设置在ModelContent对象的Tag属性中。XNA的Model类有一个Tag属性可以将自定义的数据添加到模型中。使用dictionary作为Tag属性,你可以将不同的自定义对象添加到Model类中,并可以实时通过使用string查询到这些对象。
注意你设置在ModelContent对象Tag属性中的数据会一起存储在二进制的XNB文件中,当使用content manager载入模型时这些数据会重新还原。
提取模型骨骼
ExtractSkeletonAndAnimations方法将一个root NodeContent对象作为输入,这个对象可能包含MeshContent和BoneContent作为它的子节点(children)。要提取模型骨骼,你首先要在root NodeContent中找到骨骼的root bone,任何执行深度搜寻depth traverse,创建bone的集合。XNA的MeshHelper类提供了一些方法帮你完成这个处理过程:
// Find the root bone node
BoneContent skeleton = MeshHelper.FindSkeleton(input);
// Transform the hierarchy in a list (depth traversal)
IList<BoneContent> boneList = MeshHelper.FlattenSkeleton(skeleton);
你可以使用MeshHelper类的FindSkeleton方法找到骨骼的root bone。然后你需要使用深度搜索将骨骼树转换为集合。可以使用MeshHelper类的FindSkeleton方法做这件事。结果是bone的集合,每个bone是BoneContent类的一个对象。注意在集合中的bone的顺序和网格顶点的索引顺序是相同的。
对集合中的每个bone,你将它的本地配置(local configuration)、反绝对配置(inverse absolute configuration)和父索引存储在bind pose中。你可以从BoneContent对象的Transform 和AbsoluteTransform属性中读取本地配置和绝对配置,你可以使用XNA中Matrix类的Invert方法计算反绝对配置。
bonesBindPose[i] = boneList[i].Transform;
bonesInverseBindPose[i] = Matrix.Invert(boneList[i].AbsoluteTransform);
以下是ExtractSkeletonAndAnimations 方法的完整代码:
private AnimatedModelData
ExtractSkeletonAndAnimations(NodeContent input, ContentProcessorContext context)
{
// Find the root bone node BoneContent
skeleton = MeshHelper.FindSkeleton(input);
// Transform the hierarchy in a list (depth traversal)
IList<BoneContent> boneList =MeshHelper.FlattenSkeleton(skeleton);
context.Logger.LogImportantMessage("{0} bones found.", boneList.Count);
// Create skeleton bind pose, inverse bind pose, and parent array
Matrix[] bonesBindPose = new Matrix[boneList.Count];
Matrix[] bonesInverseBindPose = new Matrix[boneList.Count];
int[] bonesParentIndex = new int[boneList.Count];
List<string> boneNameList = new List(boneList.Count);
// Extract and store the data needed from the bone list
for (int i= 0; i< boneList.Count; i++)
{
bonesBindPose[i] = boneList[i].Transform;
bonesInverseBindPose[i] = Matrix.Invert(boneList[i].AbsoluteTransform);
int parentIndex =boneNameList.IndexOf(boneList[i].Parent.Name);
bonesParentIndex[i] = parentIndex; boneNameList.Add(boneList[i].Name);
}
// Extract all animations
AnimationData[] animations = ExtractAnimations( skeleton.Animations,boneNameList, context);
return new AnimatedModelData(bonesBindPose, bonesInverseBindPose, bonesParentIndex, animations);
}
在提取模型骨骼后,你需要调用ExtractAnimations方法提取模型动画。
提取模型动画
所有模型动画存储在一个动画dictionary中,这个dictionary映射到一个包含指向一个AnimationContent对象的动画名称、动画数据的字符串。你可以从模型骨骼的root node(类型为BoneContent)的Animations属性中获得动画dictionary。注意素材管道有自己的类存储模型动画数据:AnimationContent、AnimationChannel和AnimationKeyframe类。AnimationContent类存储以一个AnimationChannel对象的数组的形式存储一个完整的模型动画,每个AnimationChannel对象以AnimationKeyframe对象数组的形式存储单个bone的动画。当你将bone一起存储在单个数组中时,XNA的AnimationContent类也会单独存储每个bone的动画。
你可以遍历动画dictionary的AnimationContent对象提取模型动画,对每个找到的动画你需要遍历它们的bone channels(可以从Channels属性中获得),提取所有动画关键帧(可以从Keyframes属性中获得)。下面是ExtractAnimations方法的代码:
private AnimationData[] ExtractAnimations( AnimationContentDictionary animationDictionary,
List<string> boneNameList, ContentProcessorContext context)
{
context.Logger.LogImportantMessage("{0} animations found.",animationDictionary.Count);
AnimationData[] animations = new AnimationData[animationDictionary.Count];
int count = 0;
foreach (AnimationContent animationContent in animationDictionary.Values)
{
// Store all keyframes of the animation
List<Keyframe> keyframes = new List<Keyframe>();
// Go through all animation channels
// Each bone has its own channel
foreach (string animationKey in animationContent.Channels.Keys)
{
AnimationChannel animationChannel =animationContent.Channels[animationKey];
int boneIndex = boneNameList.IndexOf(animationKey);
foreach (AnimationKeyframe keyframe in animationChannel)
keyframes.Add(new Keyframe( keyframe.Time, boneIndex, keyframe.Transform));
}
// Sort all animation frames by time
keyframes.Sort();
animations[count++] = new AnimationData(animationContent.Name, animationContent.Duration, keyframes.ToArray());
}
return animations;
}
当存储了动画的所有关键帧后,你需要将它们排序。因为关键帧是存储在一个List中的,所以你可以使用Sort方法。别忘了你前面在Keyframe类中实现了Icomparable接口,可以使用它们的time属性对关键帧进行排序。
现在你提取了模型骨骼和动画并将它们存储在一个友好的格式中,下面准备写入XNB文件。注意因为List泛型类和Icomparable接口是由.NET Framework而不是XNA提供的,所以你可以在C# 帮助文件中它们的相关信息。
读取和写入自定义数据
你创建的用来存储模型骨骼动画数据的AnimatedModelProcessor使用自定义的对象(AnimatedModelData,AnimationData和Keyframe类)。素材管道需要从二进制文件中读取和写入这些对象,但素材管道不知道如何读取和写入这些自定义对象。
要定义如何读取骨骼动画数据和写入至二进制文件中,你需要为每个用来存储骨骼动画数据的类创建一个content type reader和一个content type writer。这里,你需要为AnimatedModelData,AnimationData和Keyframe类创建一个content type reader和一个content type writer,你可以通过扩展XNA的ContentTypeReader和ContentTypeWriter类创建content type reader和content type writer。
Content Type Writer
要创建content type writer你需要在AnimatedModelProcessorWin项目中添加一个名为AnimatedModelDataWriter的新Content Type Writer。content type writer类只需添加到model processor项目中。你要在content type writer中添加三个新类:KeyframeWriter,AnimationDataWriter和AnimatedModelDataWriter类,这三个类用来为Keyframe,AnimationData和AnimatedModelData类写入数据。每个类都需要扩展ContentTypeWriter类并重写Write方法。
ContentTypeWriter类的Write方法接受两个参数。第一个是ContentWriter,用来将对象的数据写入二进制文件,第二个参数是要写入的对象。在Write方法内,你应使用ContentWriter对象写入所有类中的属性。注意对象的写入顺序是很重要的,它们必须和读取的顺序相同。以下是KeyframeWriter,AnimationDataWriter和AnimatedModelDataWriter类的代码:
[ContentTypeWriter]
public class KeyframeWriter : ContentTypeWriter<Keyframe>
{
protected override void Write(ContentWriter output, Keyframe value)
{
output.WriteObject(value.Time);
output.Write(value.Bone);
output.Write(value.Transform);
}
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return typeof(KeyframeReader).AssemblyQualifiedName;
}
}
[ContentTypeWriter]
public class AnimationDataWriter : ContentTypeWriter<AnimationData>
{
protected override void Write(ContentWriter output, AnimationData value)
{
output.Write(value.Name);
output.WriteObject(value.Duration);
output.WriteObject(value.Keyframes);
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return typeof(AnimationDataReader).AssemblyQualifiedName;
}
}
}
[ContentTypeWriter]
public class AnimatedModelDataWriter : ContentTypeWriter<AnimatedModelData>
{
protected override void Write(ContentWriter output, AnimatedModelData value)
{
output.WriteObject(value.BonesBindPose);
output.WriteObject(value.BonesInverseBindPose);
output.WriteObject(value.BonesParent);
output.WriteObject(value.Animations);
}
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return typeof(AnimatedModelDataReader).AssemblyQualifiedName;
}
}
ContentType Reader
要创建content type reader你需要在AnimatedModelProcessorWin项目中添加一个名为AnimatedModelDataReader的新Content Type Reader。与content type writer类不同,游戏程序需要只需content type reader实时加载动画数据。 你需要创建三个新的类-KeyframeReader,AnimationDataReader和AnimatedModelDataReader-它们用来读取Keyframe,AnimationData和AnimatedModelData类的数据。每个类都需要扩展ContentTypeReader 类并重写Read方法。
ContentTypeReader类的Read方法接受两个参数。第一个是ContentReader,用来从二进制文件读取对象数据,第二个参数是指向对象示例的引用。因为你还没创建对象,所以第二个参数总是null。再次注意读取对象的顺序应与写入的顺序相同。下面是KeyframeReader,AnimationDataReader和AnimatedModelDataReader类的代码:
public class KeyframeReader : ContentTypeReader<Keyframe>
{
protected override Keyframe Read(ContentReader input, Keyframe existingInstance)
{
TimeSpan time = input.ReadObject<TimeSpan>();
int boneIndex = input.ReadInt32();
Matrix transform = input.ReadMatrix();
return new Keyframe(time, boneIndex, transform);
}
}
public class AnimationDataReader : ContentTypeReader<AnimationData>
{
protected override AnimationData Read(ContentReader input, AnimationData existingInstance)
{
string name = input.ReadString();
TimeSpan duration = input.ReadObject<TimeSpan>();
Keyframe[] keyframes = input.ReadObject<Keyframe[]>();
return new AnimationData(name, duration, keyframes);
}
}
public class AnimatedModelDataReader :ContentTypeReader<AnimatedModelData>
{
protected override AnimatedModelData Read(ContentReader input, AnimatedModelData existingInstance)
{
Matrix[] bonesBindPose = input.ReadObject<Matrix[]>();
Matrix[] bonesInverseBindPose = input.ReadObject<Matrix[]>();
int[] bonesParent = input.ReadObject<int[]>();
AnimationData[] animations =input.ReadObject<AnimationData[]>();
return new AnimatedModelData(bonesBindPose, bonesInverseBindPose, bonesParent, animations);
}
}
分享到:
相关推荐
在骨骼动画中,每个模型的关节对应一个骨骼,这些骨骼通过变换矩阵与模型的顶点关联。矩阵可以表示位置、旋转和缩放,是控制骨骼运动的核心工具。 其次,骨骼树结构是骨骼动画的关键部分。每个骨骼都有一个父骨骼和...
### MS3D模型骨骼动画详解 #### 一、模型动画技术概述 模型动画技术是现代三维图形学中不可或缺的一部分,广泛应用于游戏开发、电影制作、虚拟现实等领域。根据实现方式的不同,模型动画主要分为两大类:**顶点...
【中国古风仙侠】unity场景模型+人物模型资源+动作+骨骼动画(约1.5G) 一共2部分资源 一、【中国古风仙侠】unity场景模型+人物模型资源+动作+骨骼动画(约1.5G) Unity中国古风仙侠场景模型、人物模型资源,217个...
MD3骨骼动画模型是计算机图形学中用于游戏和三维应用程序的一种文件格式,它主要用于存储游戏角色或对象的骨架结构和关联的表面纹理数据。这个压缩包包含两个MD3例子,这为学习和理解MD3格式提供了实际操作的基础。 ...
很多人物模型(真实的、卡通的)都带有骨骼动画,资源很全。fbx格式,由于资源比较大,这里给了百度链接
在本主题中,我们将深入探讨如何使用DirectX 3D来实现多骨骼动画网格模型,并通过数字键来控制模型的变化。 首先,理解“骨骼动画”是关键。在3D建模中,骨骼动画是一种技术,它通过连接一系列虚拟骨骼到模型的各个...
在3D模型中,骨骼动画是一种常见的方法,通过将模型的各个部分绑定到虚拟骨骼上,然后移动这些骨骼来实现模型的动态效果。Collada支持这种骨骼绑定,称为“skin”。每个骨骼都有一个变换矩阵,控制着与其关联的几何...
在IT行业中,骨骼动画是一种广泛应用于游戏开发、3D建模和动画制作的技术。它允许开发者创建出逼真的角色动作,使模型具有更多的灵活性和动态表现力。在本例中,"骨骼动画例子"提供了源代码,使得学习者能够深入理解...
骨骼动画是一种在计算机图形学和游戏开发中广泛使用的高级技术,它允许3D模型执行复杂的动作和表情,赋予角色生命和动态。在这个“骨骼动画的Demo”中,我们看到一个实际应用,通过左右下键控制视角变化,以及使用Q...
在3D建模中,骨骼动画是通过将模型绑定到一个虚拟骨骼系统来实现的。每个骨骼都可以独立地进行旋转和平移,这样就可以控制模型的各个部分进行运动。在DirectX9中,我们可以使用Matrix Transformations来表示骨骼的...
看别人的代码 Demo去学习,...我想这应该是最简单的一个3D骨骼动画模型的读取和显示的Demo。其中注释了几个比较关键的地方。包含了几个简单的md5模型,blender模型,blender MD5导出插件。VB.net + OpenTK + Assimp.net
BIP文件是3ds Max中专门用来存储骨骼动画数据的文件格式。它包含了有关角色骨骼结构、关节旋转角度以及时间轴上的动作序列等信息。创建BIP文件的过程通常包括以下几个步骤: 1. **骨骼绑定**:首先,我们需要为3D...
动作混合是骨骼动画中的高级技术,它允许我们同时播放和混合多个动画,创造出复杂的运动效果。例如,角色在挥剑的同时可能会配合身体的微调动作,这些可以通过调整不同动画的权重来实现。通过动作控制器,我们可以...
骨骼动画是通过将3D模型的几何体与虚拟骨骼关联,然后对骨骼进行旋转和移动来实现的。这种方式允许模型在运动时保持其形状的正确性,特别适合人物角色的动作表现。 1. **顶点数据**:MD2模型中的顶点是3D空间中的点...
在骨骼动画中,glut可以帮助我们创建主循环,持续渲染动画帧,并响应键盘和鼠标事件,以便用户可以交互式地控制动画。 骨骼动画的实现步骤通常包括以下几个关键环节: 1. **骨骼结构**:首先需要定义一个骨骼结构...
在3D游戏开发中,骨骼动画(也称为 Skeletal Animation)是一种广泛用于游戏角色动画的技术,它使得3D模型能够展现出各种复杂且流畅的动作。本文将深入探讨骨骼动画的基本原理、工作流程以及在实际项目中的应用。 ...
正确的关节定向是制作骨骼动画的基础,它确保了关节在动画中的正确运动。同时,根关节与整体关节(Body)的连接也是至关重要的,这涉及到角色动画中的动力学和权重分布问题。关节正确的旋转方向对于确保骨骼动画的...
在CocoStudio的骨骼动画中,骨骼具有层级关系,父骨骼的变换会影响其所有子骨骼。API提供了操作单个骨骼的方法,如旋转、平移和缩放,从而实现复杂的动作。 4. **关键帧与插值**: 动画由一系列关键帧组成,每个...
骨骼动画是通过在3D模型上绑定一个骨骼系统,然后对这些骨骼进行关键帧动画来实现的。每个骨骼都可以独立地旋转、移动和缩放,这使得模型的各个部分能够根据骨骼的运动而运动。这种技术的优点在于它能够实现高效且...
在3D游戏开发中,骨骼动画是一种常用的动态表现技术,特别是在角色动画方面。"骨骼动画MDL-3D-OpenGL"这个主题涉及到的是如何使用OpenGL图形库来实现MDL(Model Data Format)格式的3D模型的骨骼动画效果。下面我们...