diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 9981e82f5..8dae2045a 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -38,7 +38,7 @@ public sealed class ModelCompiler private Assimp.Scene scene; private ModelData model; private List[] registeredMeshParts; - private readonly Dictionary meshNodes = new Dictionary(); + private readonly Dictionary skeletonNodes = new Dictionary(); private readonly Dictionary skinnedBones = new Dictionary(); @@ -163,10 +163,11 @@ private ContentCompilerResult CompileFromFileInternal(string fileName, ModelComp } var importer = new AssimpImporter(); + importer.SetConfig(new Assimp.Configs.MaxBoneCountConfig(72)); //importer.SetConfig(new NormalSmoothingAngleConfig(66.0f)); // Steps for Direct3D Right-Handed, should we make this configurable? - var steps = PostProcessSteps.FlipUVs | PostProcessSteps.FlipWindingOrder; + var steps = PostProcessSteps.FlipUVs | PostProcessSteps.FlipWindingOrder | PostProcessSteps.LimitBoneWeights | PostProcessSteps.SplitByBoneCount; // Setup quality switch (compilerOptions.Quality) @@ -203,12 +204,17 @@ private void ProcessScene() // Collect meshes and attached nodes CollectMeshNodes(scene.RootNode); - + // Collect nodes CollectNodes(scene.RootNode, null); + CollectMeshes(scene.RootNode); + // Process materials ProcessMaterials(); + + // Process animations + ProcessAnimations(); } private void CollectEmbeddedTextures(Texture[] textures) @@ -399,6 +405,121 @@ private ModelData.Material Process(Material rawMaterial) return material; } + private void ProcessAnimations() + { + if (!scene.HasAnimations) + return; + + foreach (var animation in scene.Animations) + { + if (!animation.HasNodeAnimations) + continue; + + var ticksPerSecond = animation.TicksPerSecond > 0 ? animation.TicksPerSecond : 25.0; // Is this format dependent? At least some .x files behave strangely. + + var animationData = new ModelData.Animation + { + Name = animation.Name, + Duration = (float)(animation.DurationInTicks / ticksPerSecond) + }; + model.Animations.Add(animationData); + + foreach (var channel in animation.NodeAnimationChannels) + { + var keyFrames = new LinkedList(); + + if (channel.HasScalingKeys) + { + foreach (var key in channel.ScalingKeys) + { + var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); + keyFrame.Value = new CompositeTransform(ConvertVector(key.Value), Quaternion.Identity, Vector3.Zero); + } + } + + if (channel.HasRotationKeys) + { + foreach (var key in channel.RotationKeys) + { + var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); + keyFrame.Value = new CompositeTransform(keyFrame.Value.Scale, ConvertQuaternion(key.Value), Vector3.Zero); + } + } + + if (channel.HasPositionKeys) + { + foreach (var key in channel.PositionKeys) + { + var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); + keyFrame.Value = new CompositeTransform(keyFrame.Value.Scale, keyFrame.Value.Rotation, ConvertVector(key.Value)); + } + } + + LinearKeyFrameReduction(keyFrames); + + var channelData = new ModelData.AnimationChannel { BoneName = channel.NodeName }; + channelData.KeyFrames.AddRange(keyFrames); + animationData.Channels.Add(channelData); + } + } + } + + private ModelData.KeyFrame GetKeyFrame(float keyTime, LinkedList keyFrames) + { + ModelData.KeyFrame keyFrame; + + for (var node = keyFrames.First; node != null; node = node.Next) + { + if (MathUtil.NearEqual((float)keyTime, node.Value.Time)) + return node.Value; + + if (node.Value.Time > keyTime) + { + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = CompositeTransform.Identity }; + keyFrames.AddAfter(node, keyFrame); + return keyFrame; + } + } + + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = CompositeTransform.Identity }; + keyFrames.AddLast(keyFrame); + return keyFrame; + } + + private const float TranslationThreshold = 1e-8f; + + private const float RotationThreshold = 0.9999999f; + + private void LinearKeyFrameReduction(LinkedList keyFrames) + { + if (keyFrames.Count < 3) + return; + + var node = keyFrames.First.Next; + while (node.Next != null) + { + var nextNode = node.Next; + + var previous = node.Previous.Value; + var current = node.Value; + var next = node.Next.Value; + + var amount = (current.Time - previous.Time) / (next.Time - previous.Time); + var interpolated = CompositeTransform.Slerp(previous.Value, next.Value, amount); + + var actual = current.Value; + + if ((interpolated.Translation - actual.Translation).LengthSquared() < TranslationThreshold && + Quaternion.Dot(interpolated.Rotation, actual.Rotation) > RotationThreshold && + (interpolated.Scale - actual.Scale).LengthSquared() < TranslationThreshold) + { + keyFrames.Remove(node); + } + + node = nextNode; + } + } + private void CollectSkinnedBones() { foreach (var mesh in scene.Meshes) @@ -407,7 +528,7 @@ private void CollectSkinnedBones() { foreach (var bone in mesh.Bones) { - RegisterNode(scene.RootNode.FindNode(bone.Name), skinnedBones); + RegisterNode(scene.RootNode.FindNode(bone.Name), skeletonNodes); } } } @@ -417,7 +538,7 @@ private void CollectMeshNodes(Node node) { if (node.HasMeshes) { - RegisterNode(node, meshNodes); + RegisterNode(node, skeletonNodes); } if (node.HasChildren) @@ -431,9 +552,9 @@ private void CollectMeshNodes(Node node) private bool IsModelNode(Node node) { - // Disable Skinned bones for this version - //return meshNodes.ContainsKey(node) || skinnedBones.ContainsKey(node); - return meshNodes.ContainsKey(node); + // Return all nodes for now, so we can use model files as skeletons + return true; + //return skeletonNodes.ContainsKey(node); } private void RegisterNode(Node node, Dictionary nodeMap) @@ -452,7 +573,7 @@ private void RegisterNode(Node node, Dictionary nodeMap) node = node.Parent; } } - + private void CollectNodes(Node node, ModelData.Bone targetParent) { bool isModelNode = IsModelNode(node); @@ -463,32 +584,46 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) } var parent = new ModelData.Bone - { - Index = model.Bones.Count, - Name = node.Name, - ParentIndex = targetParent == null ? -1 : targetParent.Index, - Transform = ConvertMatrix(node.Transform), - Children = new List() - }; + { + Index = model.Bones.Count, + Name = node.Name, + ParentIndex = targetParent == null ? -1 : targetParent.Index, + Transform = ConvertMatrix(node.Transform), + Children = new List() + }; - if (targetParent!= null && targetParent.Children != null) + if (targetParent != null && targetParent.Children != null) { targetParent.Children.Add(parent.Index); } // Associate created bones with local index - meshNodes[node] = model.Bones.Count; + skeletonNodes[node] = model.Bones.Count; model.Bones.Add(parent); - // if node has meshes, create a new scene object for it - if( node.MeshCount > 0) + // continue for all child nodes + if (node.HasChildren) + { + foreach (var subNode in node.Children) + { + CollectNodes(subNode, parent); + } + } + } + + private void CollectMeshes(Node node) + { + var boneIndex = skeletonNodes[node]; + var bone = model.Bones[boneIndex]; + + if (node.HasMeshes) { var mesh = new ModelData.Mesh - { - Name = parent.Name, - ParentBoneIndex = parent.Index, - MeshParts = new List() - }; + { + Name = bone.Name, + ParentBoneIndex = bone.Index, + MeshParts = new List() + }; model.Meshes.Add(mesh); // Precalculate the number of vertices for bounding sphere calculation @@ -510,7 +645,7 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) for (int i = 0; i < node.MeshCount; i++) { var meshIndex = node.MeshIndices[i]; - var meshPart = Process(mesh, scene.Meshes[meshIndex]); + var meshPart = Process(mesh, scene.Meshes[meshIndex], node); var meshToPartList = registeredMeshParts[meshIndex]; if (meshToPartList == null) @@ -532,7 +667,7 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) { foreach (var subNode in node.Children) { - CollectNodes(subNode, parent); + CollectMeshes(subNode); } } } @@ -543,7 +678,7 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) - private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) + private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, Node parentNode) { var meshPart = new ModelData.MeshPart() { @@ -637,21 +772,27 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) if (assimpMesh.HasBones) { - meshPart.BoneOffsetMatrices = new Matrix[assimpMesh.BoneCount]; for (int i = 0; i < assimpMesh.Bones.Length; i++) { var bone = assimpMesh.Bones[i]; - meshPart.BoneOffsetMatrices[i] = ConvertMatrix(bone.OffsetMatrix); + var boneNode = scene.RootNode.FindNode(bone.Name); + if (bone.HasVertexWeights) { - var boneNode = scene.RootNode.FindNode(bone.Name); - var boneIndex = skinnedBones[boneNode]; + var skinnedBoneIndex = meshPart.SkinnedBones.Count; + + meshPart.SkinnedBones.Add(new ModelData.SkinnedBone + { + BoneIndex = skeletonNodes[boneNode], + InverseBindTransform = ConvertMatrix(bone.OffsetMatrix) + }); + for (int j = 0; j < bone.VertexWeightCount; j++) { var weights = bone.VertexWeights[j]; var vertexSkinningCount = skinningCount[weights.VertexID]; - skinningIndices[weights.VertexID][vertexSkinningCount] = boneIndex; + skinningIndices[weights.VertexID][vertexSkinningCount] = skinnedBoneIndex; skinningWeights[weights.VertexID][vertexSkinningCount] = weights.Weight; @@ -665,7 +806,7 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) if (hasWeights) { layout.Add(VertexElement.BlendIndices(Format.R16G16B16A16_SInt, vertexBufferElementSize)); - vertexBufferElementSize += Utilities.SizeOf(); + vertexBufferElementSize += 8; layout.Add(VertexElement.BlendWeights(Format.R32G32B32A32_Float, vertexBufferElementSize)); vertexBufferElementSize += Utilities.SizeOf(); @@ -686,16 +827,17 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) var vertexStream = DataStream.Create(vertexBuffer.Buffer, true, true); for (int i = 0; i < assimpMesh.VertexCount; i++) { - var position = assimpMesh.Vertices[i]; + Vector3 position = ConvertVector(assimpMesh.Vertices[i]); vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation - boundingPoints[currentBoundingPointIndex++] = new Vector3(position.X, position.Y, position.Z); + boundingPoints[currentBoundingPointIndex++] = position; // Add normals if (assimpMesh.HasNormals) { - vertexStream.Write(assimpMesh.Normals[i]); + var normal = ConvertVector(assimpMesh.Normals[i]); + vertexStream.Write(normal); } // Add colors @@ -736,14 +878,20 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) // Add tangent / bitangent if (assimpMesh.HasTangentBasis) { - vertexStream.Write(assimpMesh.Tangents[i]); - vertexStream.Write(assimpMesh.BiTangents[i]); + var tangent = ConvertVector(assimpMesh.Normals[i]); + var bitangent = ConvertVector(assimpMesh.Normals[i]); + vertexStream.Write(tangent); + vertexStream.Write(bitangent); } // Add Skinning Indices/Weights if (assimpMesh.HasBones && hasWeights) { - vertexStream.Write(skinningIndices[i]); + var vertexSkinndingIndices = skinningIndices[i]; + vertexStream.Write((short)vertexSkinndingIndices.X); + vertexStream.Write((short)vertexSkinndingIndices.Y); + vertexStream.Write((short)vertexSkinndingIndices.Z); + vertexStream.Write((short)vertexSkinndingIndices.W); vertexStream.Write(skinningWeights[i]); } } @@ -777,6 +925,19 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) return meshPart; } + private static Matrix GetAbsoluteTransform(Node node) + { + Matrix transform = ConvertMatrix(node.Transform); + + while (node.Parent != null) + { + node = node.Parent; + transform *= ConvertMatrix(node.Transform); + } + + return transform; + } + private unsafe static SharpDX.Vector2 ConvertVector(Vector2D value) { return *(SharpDX.Vector2*)&value; @@ -794,7 +955,14 @@ private unsafe static SharpDX.Color4 ConvertColor(Color4D value) private unsafe static Matrix ConvertMatrix(Matrix4x4 sourceMatrix) { - return *(Matrix*)&sourceMatrix; + // Assimp uses column vectors + return Matrix.Transpose(*(Matrix*)&sourceMatrix); + } + + private static SharpDX.Quaternion ConvertQuaternion(Assimp.Quaternion value) + { + // Assimp quaternions are stored in wxyz order + return new SharpDX.Quaternion(value.X, value.Y, value.Z, value.W); } private TextureAddressMode ConvertWrapMode(TextureWrapMode wrapMode) diff --git a/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs new file mode 100644 index 000000000..cea45b0d7 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs @@ -0,0 +1,165 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using SharpDX.Toolkit.Graphics; + +using System; +using System.Collections.Generic; + +namespace SharpDX.Toolkit +{ + /// + /// Manages and drives skeletal animations. + /// + public class AnimationSystem : GameSystem + { + private Dictionary animationStates; + + /// + /// Initializes a new instance of the class. + /// + /// The game. + public AnimationSystem(Game game) + : base(game) + { + animationStates = new Dictionary(); + Enabled = true; + } + + /// + /// Starts a new animation. + /// + /// The animated model. + /// The animation to play. + public void StartAnimation(Model model, ModelAnimation animation) + { + if (model == null) + { + throw new ArgumentNullException("model"); + } + + if (animation == null) + { + throw new ArgumentNullException("animation"); + } + + animationStates[model] = new AnimationState + { + Model = model, + Animation = animation, + ChannelMap = BuildChannelMap(model, animation) + }; + } + + /// + /// Stop the animation of a model. + /// + /// The animated model. + public void StopAnimation(Model model) + { + if (model == null) + { + throw new ArgumentNullException("model"); + } + + animationStates.Remove(model); + } + + public override void Update(GameTime gameTime) + { + foreach (var state in animationStates.Values) + { + var animation = state.Animation; + + if (MathUtil.IsZero(animation.Duration)) + { + state.CurrentTime = 0.0f; + } + else + { + state.CurrentTime += (float)gameTime.ElapsedGameTime.TotalSeconds; + state.CurrentTime %= animation.Duration; + } + + for (int i = 0; i < animation.Channels.Count; i++) + { + var boneIndex = state.ChannelMap[i]; + if (boneIndex < 0) + { + continue; + } + + var bone = state.Model.Bones[boneIndex]; + var keyFrames = animation.Channels[i].KeyFrames; + + int nextIndex = 0; + int lastIndex = keyFrames.Count - 1; + + while (nextIndex < lastIndex && state.CurrentTime >= keyFrames[nextIndex].Time) + { + nextIndex++; + } + + var nextKeyFrame = keyFrames[nextIndex]; + + if (nextIndex == 0) + { + bone.Transform = (Matrix)nextKeyFrame.Value; + } + else + { + var previousKeyFrame = keyFrames[nextIndex - 1]; + var amount = (state.CurrentTime - previousKeyFrame.Time) / (nextKeyFrame.Time - previousKeyFrame.Time); + + bone.Transform = (Matrix)CompositeTransform.Slerp(previousKeyFrame.Value, nextKeyFrame.Value, amount); + } + } + } + } + + private int[] BuildChannelMap(Model model, ModelAnimation animation) + { + var channelMap = new int[animation.Channels.Count]; + + for (int i = 0; i < animation.Channels.Count; i++) + { + channelMap[i] = -1; + foreach (var bone in model.Bones) + { + if (bone.Name == animation.Channels[i].BoneName) + { + channelMap[i] = bone.Index; + break; + } + } + } + + return channelMap; + } + + private class AnimationState + { + public float CurrentTime; + public Model Model; + public ModelAnimation Animation; + public int[] ChannelMap; + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Game/SharpDX.Toolkit.Game.csproj b/Source/Toolkit/SharpDX.Toolkit.Game/SharpDX.Toolkit.Game.csproj index 9691728d4..ebbbed9db 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Game/SharpDX.Toolkit.Game.csproj +++ b/Source/Toolkit/SharpDX.Toolkit.Game/SharpDX.Toolkit.Game.csproj @@ -22,6 +22,7 @@ Properties\SharedAssemblyInfo.cs + diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs index 3139df2a6..55a5f8fc9 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs @@ -30,15 +30,16 @@ namespace SharpDX.Toolkit.Graphics [ContentReader(typeof(ModelContentReader))] public class Model : Component { + private static Matrix[] sharedDrawBones; + public MaterialCollection Materials; public ModelBoneCollection Bones; - //// DISABLE_SKINNED_BONES - //public ModelBoneCollection SkinnedBones; - public ModelMeshCollection Meshes; + public ModelAnimationCollection Animations; + public PropertyCollection Properties; /// @@ -160,9 +161,13 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri { int count = Meshes.Count; int boneCount = Bones.Count; - Matrix* localSharedDrawBoneMatrices = stackalloc Matrix[boneCount]; // TODO use a global cache as BoneCount could generate a StackOverflow - CopyAbsoluteBoneTransformsTo(new IntPtr(localSharedDrawBoneMatrices)); + if (sharedDrawBones == null || sharedDrawBones.Length < boneCount) + { + sharedDrawBones = new Matrix[boneCount]; + } + + CopyAbsoluteBoneTransformsTo(sharedDrawBones); var defaultParametersContext = default(EffectDefaultParametersContext); @@ -174,10 +179,16 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri if (effectOverride != null) { - Matrix worldTranformed; - Matrix.Multiply(ref localSharedDrawBoneMatrices[index], ref world, out worldTranformed); - - effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); + if (effectOverride is SkinnedEffect) + { + effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref world, ref view, ref projection); + } + else + { + Matrix worldTranformed; + Matrix.Multiply(ref sharedDrawBones[index], ref world, out worldTranformed); + effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); + } } else { @@ -190,27 +201,26 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri } Matrix worldTranformed; - Matrix.Multiply(ref localSharedDrawBoneMatrices[index], ref world, out worldTranformed); + Matrix.Multiply(ref sharedDrawBones[index], ref world, out worldTranformed); var matrices = effect as IEffectMatrices; if (matrices == null) { - effect.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); + effect.DefaultParameters.Apply(ref defaultParametersContext, ref world, ref view, ref projection); } else { - matrices.World = worldTranformed; + matrices.World = effect is SkinnedEffect ? world : worldTranformed; matrices.View = view; matrices.Projection = projection; } } } - mesh.Draw(context, effectOverride); + mesh.Draw(context, sharedDrawBones, effectOverride); } } - /// /// Calculates the bounds of this model. /// @@ -229,16 +239,20 @@ public unsafe BoundingSphere CalculateBounds(Matrix world) { int count = Meshes.Count; int boneCount = Bones.Count; - Matrix* localSharedDrawBoneMatrices = stackalloc Matrix[boneCount]; // TODO use a global cache as BoneCount could generate a StackOverflow - CopyAbsoluteBoneTransformsTo(new IntPtr(localSharedDrawBoneMatrices)); + if (sharedDrawBones == null || sharedDrawBones.Length < boneCount) + { + sharedDrawBones = new Matrix[boneCount]; + } + + CopyAbsoluteBoneTransformsTo(sharedDrawBones); var defaultSphere = new BoundingSphere(Vector3.Zero, 0.0f); for (int i = 0; i < count; i++) { var mesh = Meshes[i]; int index = mesh.ParentBone.Index; Matrix result; - Matrix.Multiply(ref localSharedDrawBoneMatrices[index], ref world, out result); + Matrix.Multiply(ref sharedDrawBones[index], ref world, out result); var meshSphere = mesh.BoundingSphere; Vector3.TransformCoordinate(ref meshSphere.Center, ref result, out meshSphere.Center); diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs new file mode 100644 index 000000000..6fce29975 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace SharpDX.Toolkit.Graphics +{ + public class ModelAnimation : ComponentBase + { + public string Name; + + public float Duration; + + public List Channels; + + public ModelAnimation() + { + Channels = new List(); + } + } +} diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs new file mode 100644 index 000000000..01ac62d2f --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace SharpDX.Toolkit.Graphics +{ + public class ModelAnimationCollection : List + { + public ModelAnimationCollection() + { + } + + public ModelAnimationCollection(int capacity) + : base(capacity) + { + } + + public ModelAnimationCollection(IEnumerable collection) + : base(collection) + { + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelBoneCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelBoneCollection.cs index d12bbfbd5..eda949594 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelBoneCollection.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelBoneCollection.cs @@ -35,7 +35,7 @@ public ModelBoneCollection() } /// - /// Initializes a new instance of the class that is empty and has the specified initial capacity. + /// Initializes a new instance of the class that is empty and has the specified initial capacity. /// /// The number of elements that the new list can initially store. public ModelBoneCollection(int capacity) diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs index 00465c618..598c5191f 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs @@ -60,9 +60,10 @@ public void ForEach(Action meshPartFunction) /// Draws all of the ModelMeshPart objects in this mesh, using their current Effect settings. /// /// The graphics context. + /// The model's bone transforms. /// The effect to use instead of the effect attached to each mesh part. Default is null (use Effect in MeshPart) /// Model has no effect - public void Draw(GraphicsDevice context, Effect effectOverride = null) + public void Draw(GraphicsDevice context, Matrix[] boneTransforms, Effect effectOverride = null) { int count = this.MeshParts.Count; for (int i = 0; i < count; i++) @@ -73,6 +74,21 @@ public void Draw(GraphicsDevice context, Effect effectOverride = null) { throw new InvalidOperationException("ModelMeshPart has no effect and effectOverride is null"); } + + var skinnedEffect = effect as SkinnedEffect; + int boneCount = part.SkinnedBones.Count; + if (skinnedEffect != null && boneCount > 0) + { + var transforms = new Matrix[boneCount]; + for (int j = 0; j < boneCount; j++) + { + var skinnedBone = part.SkinnedBones[j]; + Matrix.Multiply(ref skinnedBone.OffsetMatrix, ref boneTransforms[skinnedBone.Bone.Index], out transforms[j]); + } + + skinnedEffect.SetBoneTransforms(transforms); + } + int passCount = effect.CurrentTechnique.Passes.Count; for (int j = 0; j < passCount; j++) { diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs index f1477dfd4..c9d16a8c9 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs @@ -17,6 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System.Collections.Generic; namespace SharpDX.Toolkit.Graphics { @@ -56,6 +57,22 @@ public class ModelMeshPart : ComponentBase /// public PropertyCollection Properties; + /// + /// The skinned bones that are affecting this mesh part. + /// + public ModelSkinnedBoneCollection SkinnedBones; + + /// + /// Gets a value indicating whether this instance contains skinning information. + /// + /// + /// true if this instance contains skinning information; otherwise, false. + /// + public bool IsSkinned + { + get { return SkinnedBones != null && SkinnedBones.Count > 0; } + } + private Effect effect; /// Gets or sets the material Effect for this mesh part. Reference page contains code sample. diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs index 356962a04..497594384 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs @@ -97,6 +97,11 @@ protected virtual ModelBone CreateModelBone() return new ModelBone(); } + protected virtual ModelSkinnedBone CreateModelSkinnedBone() + { + return new ModelSkinnedBone(); + } + protected virtual ModelMesh CreateModelMesh() { return new ModelMesh(); @@ -107,6 +112,11 @@ protected virtual ModelMeshPart CreateModelMeshPart() return new ModelMeshPart(); } + protected virtual ModelAnimation CreateModelAnimation() + { + return new ModelAnimation(); + } + protected virtual MaterialCollection CreateModelMaterialCollection(int capacity) { return new MaterialCollection(capacity); @@ -117,6 +127,11 @@ protected virtual ModelBoneCollection CreateModelBoneCollection(int capacity) return new ModelBoneCollection(capacity); } + protected virtual ModelSkinnedBoneCollection CreateModelSkinnedBoneCollection(int capacity) + { + return new ModelSkinnedBoneCollection(capacity); + } + protected virtual ModelMeshCollection CreateModelMeshCollection(int capacity) { return new ModelMeshCollection(capacity); @@ -137,6 +152,11 @@ protected virtual PropertyCollection CreatePropertyCollection(int capacity) return new PropertyCollection(capacity); } + protected virtual ModelAnimationCollection CreateModelAnimationCollection(int capacity) + { + return new ModelAnimationCollection(capacity); + } + protected virtual PropertyCollection CreateMaterialPropertyCollection(int capacity) { return new PropertyCollection(capacity); @@ -208,17 +228,16 @@ protected virtual void ReadModel(ref Model model) ReadBones(ref model.Bones); EndChunk(); - //// DISABLE_SKINNED_BONES - //// Skinned Bones section - //BeginChunk("SKIN"); - //ReadBones(ref model.SkinnedBones); - //EndChunk(); - // Mesh section BeginChunk("MESH"); ReadMeshes(ref model.Meshes); EndChunk(); + // Animation section + BeginChunk("ANIM"); + ReadAnimations(ref model.Animations); + EndChunk(); + // Serialize attributes ReadProperties(ref model.Properties); @@ -260,6 +279,12 @@ protected virtual void ReadBones(ref ModelBoneCollection bones) } } + protected virtual void ReadSkinnedBones(ref ModelSkinnedBoneCollection skinnedBones) + { + // Read all bones + ReadList(ref skinnedBones, CreateModelSkinnedBoneCollection, CreateModelSkinnedBone, ReadSkinnedBone); + } + public delegate PropertyKey NameToPropertyKeyDelegate(string name); protected virtual void ReadProperties(ref PropertyCollection properties) @@ -328,6 +353,11 @@ protected virtual void ReadIndexBuffers(ref BufferCollection list) } } + protected virtual void ReadAnimations(ref ModelAnimationCollection meshes) + { + ReadList(ref meshes, CreateModelAnimationCollection, CreateModelAnimation, ReadAnimation); + } + protected virtual void ReadMaterial(ref Material material) { var textureStackCount = Reader.ReadInt32(); @@ -421,6 +451,18 @@ protected virtual void ReadBone(ref ModelBone bone) } } + protected virtual void ReadSkinnedBone(ref ModelSkinnedBone skinnedBone) + { + var boneIndex = Reader.ReadInt32(); + if (boneIndex > Model.Bones.Count) + { + throw new InvalidOperationException("Invalid bone index"); + } + skinnedBone.Bone = Model.Bones[boneIndex]; + + Serialize(ref skinnedBone.OffsetMatrix); + } + protected virtual void ReadMesh(ref ModelMesh mesh) { CurrentMesh = mesh; @@ -460,10 +502,20 @@ protected virtual void ReadMeshPart(ref ModelMeshPart meshPart) vertexBufferRange.Serialize(this); meshPart.VertexBuffer = GetFromList(vertexBufferRange, CurrentMesh.VertexBuffers); + // Skinned bones + ReadSkinnedBones(ref meshPart.SkinnedBones); + // Properties ReadProperties(ref meshPart.Properties); } + protected virtual void ReadAnimation(ref ModelAnimation animation) + { + Serialize(ref animation.Name); + Serialize(ref animation.Duration); + Serialize(ref animation.Channels); + } + protected virtual void ReadVertexBuffer(ref VertexBufferBinding vertexBufferBinding) { // Read the number of vertices diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs new file mode 100644 index 000000000..cb2819093 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +namespace SharpDX.Toolkit.Graphics +{ + public class ModelSkinnedBone : ComponentBase + { + public ModelBone Bone; + + public Matrix OffsetMatrix; + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs new file mode 100644 index 000000000..8d7a9012c --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; + +using SharpDX.Serialization; + +namespace SharpDX.Toolkit.Graphics +{ + public class ModelSkinnedBoneCollection : List + { + /// + /// Initializes a new instance of the class. + /// + public ModelSkinnedBoneCollection() + { + } + + /// + /// Initializes a new instance of the class that is empty and has the specified initial capacity. + /// + /// The number of elements that the new list can initially store. + public ModelSkinnedBoneCollection(int capacity) + : base(capacity) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The collection. + public ModelSkinnedBoneCollection(IEnumerable collection) + : base(collection) + { + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/SharpDX.Toolkit.Graphics.csproj b/Source/Toolkit/SharpDX.Toolkit.Graphics/SharpDX.Toolkit.Graphics.csproj index 62e40e7a9..7a4cca1c9 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/SharpDX.Toolkit.Graphics.csproj +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/SharpDX.Toolkit.Graphics.csproj @@ -37,7 +37,12 @@ + + + + + diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs new file mode 100644 index 000000000..dcd98debd --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +namespace SharpDX.Toolkit.Graphics +{ + /// + /// Implementation of the for the . + /// + /// + /// This effect installer uses if the model supports skeletal animation and otherwise. + /// + public class SkinnedEffectInstaller : BasicEffectInstaller + { + /// + /// Initializes a new instance of the class. + /// + /// The services. + public SkinnedEffectInstaller(IServiceRegistry services) + : base(services) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + public SkinnedEffectInstaller(GraphicsDevice graphicsDevice) + : base(graphicsDevice) + { + } + + /// + /// Creates an instance of , overridable in case a user would want to subclass . + /// + /// A new instance of BasicEffect. + protected virtual SkinnedEffect CreateSkinnedEffect() + { + return new SkinnedEffect(GraphicsDevice); + } + + protected override Effect Process(Model model, ModelMeshPart meshPart) + { + if (!meshPart.IsSkinned) + { + return base.Process(model, meshPart); + } + + var effect = CreateSkinnedEffect(); + + var material = meshPart.Material; + + // Setup ColorDiffuse + if (material.HasProperty(MaterialKeys.ColorDiffuse)) + { + effect.DiffuseColor = material.GetProperty(MaterialKeys.ColorDiffuse); + } + + // Setup ColorSpecular + if (material.HasProperty(MaterialKeys.ColorSpecular)) + { + effect.SpecularColor = material.GetProperty(MaterialKeys.ColorSpecular); + } + + // Setup ColorEmissive + if (material.HasProperty(MaterialKeys.ColorEmissive)) + { + effect.EmissiveColor = material.GetProperty(MaterialKeys.ColorEmissive); + } + + if (material.HasProperty(TextureKeys.DiffuseTexture)) + { + var diffuseTextureStack = material.GetProperty(TextureKeys.DiffuseTexture); + if (diffuseTextureStack.Count > 0) + { + var diffuseTexture = diffuseTextureStack[0]; + effect.Texture = (Texture2D)diffuseTexture.Texture; + } + } + + return effect; + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs new file mode 100644 index 000000000..e37b3eed5 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs @@ -0,0 +1,257 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using SharpDX.Serialization; +using System; +using System.Globalization; + +namespace SharpDX +{ + /// + /// Represents a transformation composed of a scaling, rotation and translation operation. + /// + public struct CompositeTransform : IEquatable, IFormattable, IDataSerializable + { + /// + /// The scaling component of the transformation. + /// + public Vector3 Scale; + + /// + /// The rotation component of the transformation. + /// + public Quaternion Rotation; + + /// + /// The translation component of the transformation. + /// + public Vector3 Translation; + + /// + /// The identety . + /// + public static readonly CompositeTransform Identity = new CompositeTransform(Vector3.One, Quaternion.Identity, Vector3.Zero); + + /// + /// Initializes a new instance of the struct. + /// + /// The scaling component of the transformation. + /// The rotation component of the transformation. + /// The translation component of the transformation. + public CompositeTransform(Vector3 scale, Quaternion rotation, Vector3 translation) + { + Scale = scale; + Rotation = rotation; + Translation = translation; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The transformation matrix. + /// + /// This constructor is designed to decompose a SRT transformation matrix only. + /// + public CompositeTransform(Matrix transform) + { + transform.Decompose(out Scale, out Rotation, out Translation); + } + + /// + /// Interpolates between two transformation, using spherical linear interpolation for rotations and linear interpolation for scaling and translation. + /// + /// Start transformation. + /// End transformation. + /// Value between 0 and 1 indicating the weight of . + /// When the method completes, contains the interpolation of the two transformations. + public static void Slerp(ref CompositeTransform start, ref CompositeTransform end, float amount, out CompositeTransform result) + { + Vector3.Lerp(ref start.Scale, ref end.Scale, amount, out result.Scale); + Quaternion.Slerp(ref start.Rotation, ref end.Rotation, amount, out result.Rotation); + Vector3.Lerp(ref start.Translation, ref end.Translation, amount, out result.Translation); + } + + /// + /// Interpolates between two transformation, using spherical linear interpolation for rotations and linear interpolation for scaling and translation. + /// + /// Start transformation. + /// End transformation. + /// Value between 0 and 1 indicating the weight of . + /// The interpolation of the two transformations. + public static CompositeTransform Slerp(CompositeTransform start, CompositeTransform end, float amount) + { + CompositeTransform result; + Slerp(ref start, ref end, amount, out result); + return result; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Matrix(CompositeTransform value) + { + return Matrix.Scaling(value.Scale) * Matrix.RotationQuaternion(value.Rotation) * Matrix.Translation(value.Translation); + } + + /// + /// Tests for equality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has the same value as ; otherwise, false. + public static bool operator ==(CompositeTransform left, CompositeTransform right) + { + return left.Equals(ref right); + } + + /// + /// Tests for inequality between two objects. + /// + /// The first value to compare. + /// The second value to compare. + /// true if has a different value than ; otherwise, false. + public static bool operator !=(CompositeTransform left, CompositeTransform right) + { + return !left.Equals(ref right); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "Scale:{0} Rotation:{1} Translation:{2}", Scale.ToString(), Rotation.ToString(), Translation.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + if (format == null) + return ToString(); + + return string.Format(CultureInfo.CurrentCulture, "Scale:{0} Rotation:{1} Translation:{2}", Scale.ToString(format, CultureInfo.CurrentCulture), + Rotation.ToString(format, CultureInfo.CurrentCulture), Translation.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format(formatProvider, "Scale:{0} Rotation:{1} Translation:{2}", Scale.ToString(), Rotation.ToString(), Translation.ToString()); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (format == null) + return ToString(formatProvider); + + return string.Format(formatProvider, "Scale:{0} Rotation:{1} Translation:{2}", Scale.ToString(format, formatProvider), + Rotation.ToString(format, formatProvider), Translation.ToString(format, formatProvider)); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + unchecked + { + var hashCode = Scale.GetHashCode(); + hashCode = (hashCode * 397) ^ Rotation.GetHashCode(); + hashCode = (hashCode * 397) ^ Translation.GetHashCode(); + return hashCode; + } + } + + /// + public void Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref Scale); + serializer.Serialize(ref Rotation); + serializer.Serialize(ref Translation); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(ref CompositeTransform other) + { + return Scale.Equals(ref other.Scale) && Rotation.Equals(ref other.Rotation) && Translation.Equals(ref other.Translation); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(CompositeTransform other) + { + return Equals(ref other); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object value) + { + if (!(value is CompositeTransform)) + return false; + + var strongValue = (CompositeTransform)value; + return Equals(ref strongValue); + } + } +} diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs new file mode 100644 index 000000000..c7922ae2f --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; + +using SharpDX.Serialization; + +namespace SharpDX.Toolkit.Graphics +{ + public sealed partial class ModelData + { + /// + /// Class Animation + /// + public class Animation : IDataSerializable + { + /// + /// The name of this animation. + /// + public string Name; + + /// + /// Total total animation duration. + /// + public float Duration; + + /// + /// The channels of this animation. + /// + public List Channels; + + /// + /// Initializes a new instance of the class. + /// + public Animation() + { + Channels = new List(); + } + + /// + void IDataSerializable.Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref Name); + serializer.Serialize(ref Duration); + serializer.Serialize(ref Channels); + } + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs new file mode 100644 index 000000000..2ddc7d59c --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; + +using SharpDX.Serialization; + +namespace SharpDX.Toolkit.Graphics +{ + public sealed partial class ModelData + { + /// + /// Class AnimationChannel + /// + public class AnimationChannel : IDataSerializable + { + /// + /// The name of the animated bone. + /// + public string BoneName; + + /// + /// The key frames of this animation. + /// + public List KeyFrames; + + /// + /// Initializes a new instance of the class. + /// + public AnimationChannel() + { + KeyFrames = new List(); + } + + /// + void IDataSerializable.Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref BoneName); + serializer.Serialize(ref KeyFrames); + } + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs new file mode 100644 index 000000000..e462b4131 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2010-2014 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using SharpDX.Serialization; + +namespace SharpDX.Toolkit.Graphics +{ + public sealed partial class ModelData + { + /// + /// Class KeyFrame + /// + public class KeyFrame : IDataSerializable + { + /// + /// The key time. + /// + public float Time; + + /// + /// The bone transform. + /// + public CompositeTransform Value; + + /// + void IDataSerializable.Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref Time); + serializer.Serialize(ref Value); + } + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs index c334253ca..9836425fd 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs @@ -35,6 +35,7 @@ public sealed class MeshPart : CommonData, IDataSerializable public MeshPart() { Properties = new PropertyCollection(); + SkinnedBones = new List(); } /// @@ -53,9 +54,9 @@ public MeshPart() public BufferRange VertexBufferRange; /// - /// Gets the offset matrix attached to each bone weights + /// Gets the skinned bones. /// - public Matrix[] BoneOffsetMatrices; + public List SkinnedBones; /// /// The attributes attached to this mesh part. @@ -67,6 +68,7 @@ void IDataSerializable.Serialize(BinarySerializer serializer) serializer.Serialize(ref MaterialIndex); serializer.Serialize(ref IndexBufferRange); serializer.Serialize(ref VertexBufferRange); + serializer.Serialize(ref SkinnedBones); serializer.Serialize(ref Properties); } } diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs new file mode 100644 index 000000000..c965e2851 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2010-2015 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; + +using SharpDX.Serialization; + +namespace SharpDX.Toolkit.Graphics +{ + public sealed partial class ModelData + { + /// + /// Class SkinnedBone + /// + public class SkinnedBone : IDataSerializable + { + /// + /// Index of the skinned bone. + /// + public int BoneIndex; + + /// + /// The absolute transform that takes the skinned vertices in bind pose from world space to bone space. + /// + public Matrix InverseBindTransform; + + /// + void IDataSerializable.Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref BoneIndex); + serializer.Serialize(ref InverseBindTransform); + } + } + } +} diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs index 5618b4ea5..19fe124a8 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs @@ -44,9 +44,8 @@ public ModelData() Textures = new List(); Materials = new List(); Bones = new List(); - // DISABLE_SKINNED_BONES - //SkinnedBones = new List(); Meshes = new List(); + Animations = new List(); Attributes = new PropertyCollection(); } @@ -70,17 +69,16 @@ public ModelData() /// public List Bones; - // DISABLE_SKINNED_BONES - ///// - ///// Gets the bones used to perform skinning animation with this model. - ///// - //public List SkinnedBones; - /// /// Gets the mesh of this model. /// public List Meshes; + /// + /// Gets the mesh of this model. + /// + public List Animations; + /// /// Gets the attributes attached to this instance. /// @@ -222,17 +220,16 @@ void IDataSerializable.Serialize(BinarySerializer serializer) serializer.Serialize(ref Bones); serializer.EndChunk(); - //// DISABLE_SKINNED_BONES - //// Skinned Bones section - //serializer.BeginChunk("SKIN"); - //serializer.Serialize(ref SkinnedBones); - //serializer.EndChunk(); - // Mesh section serializer.BeginChunk("MESH"); serializer.Serialize(ref Meshes); serializer.EndChunk(); + // Animation section + serializer.BeginChunk("ANIM"); + serializer.Serialize(ref Animations); + serializer.EndChunk(); + // Serialize attributes serializer.Serialize(ref Attributes); diff --git a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj index d99988131..bf9fdc59b 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj +++ b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj @@ -31,6 +31,9 @@ + + + @@ -84,6 +87,7 @@ + @@ -105,6 +109,7 @@ +