From 7e5cb70d77544101c7d309c3fbd1bd2e32f26d7d Mon Sep 17 00:00:00 2001 From: jwollen Date: Thu, 24 Apr 2014 10:03:47 +0200 Subject: [PATCH 01/14] [Toolkit] Fixes to ModelCompiler and preparation of data structures Conflicts: Source/Toolkit/SharpDX.Toolkit.Graphics/SharpDX.Toolkit.Graphics.csproj Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs --- .../Model/ModelCompiler.cs | 17 ++- .../Toolkit/SharpDX.Toolkit.Graphics/Model.cs | 5 +- .../ModelAnimation.cs | 39 ++++++ .../ModelAnimationCollection.cs | 42 ++++++ .../SharpDX.Toolkit.Graphics/ModelMeshPart.cs | 5 + .../SharpDX.Toolkit.Graphics/ModelReader.cs | 67 +++++++++- .../ModelSkinnedBone.cs | 31 +++++ .../ModelSkinnedBoneCollection.cs | 55 ++++++++ .../SharpDX.Toolkit.Graphics.csproj | 5 + .../SkinnedEffectInstaller.cs | 100 +++++++++++++++ .../Graphics/ModelData.Animation.cs | 120 ++++++++++++++++++ .../Graphics/ModelData.MeshPart.cs | 7 + .../Graphics/ModelData.SkinnedBone.cs | 52 ++++++++ .../SharpDX.Toolkit/Graphics/ModelData.cs | 32 +++-- .../SharpDX.Toolkit/SharpDX.Toolkit.csproj | 2 + 15 files changed, 557 insertions(+), 22 deletions(-) create mode 100644 Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 9981e82f5..03e59050b 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -665,7 +665,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(); @@ -743,7 +743,11 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) // 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]); } } @@ -794,7 +798,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.Graphics/Model.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs index 3139df2a6..4e82b587b 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs @@ -34,11 +34,12 @@ public class Model : Component public ModelBoneCollection Bones; - //// DISABLE_SKINNED_BONES - //public ModelBoneCollection SkinnedBones; + public ModelSkinnedBoneCollection SkinnedBones; public ModelMeshCollection Meshes; + public ModelAnimationCollection Animations; + public PropertyCollection Properties; /// diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs new file mode 100644 index 000000000..e97e4a66b --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2010-2013 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(); + } + } +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs new file mode 100644 index 000000000..1c8b98581 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2010-2013 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/ModelMeshPart.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs index f1477dfd4..1a0a7742a 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs @@ -56,6 +56,11 @@ public class ModelMeshPart : ComponentBase /// public PropertyCollection Properties; + /// + /// Indices of the models skinned bones that are affecting this part. + /// + public int[] SkinnedBones; + 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..7add76ea7 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,21 @@ 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(); + // Skinned Bones section + BeginChunk("SKIN"); + ReadSkinnedBones(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 +284,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 +358,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 +456,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.InverseBindTransform); + } + protected virtual void ReadMesh(ref ModelMesh mesh) { CurrentMesh = mesh; @@ -460,10 +507,20 @@ protected virtual void ReadMeshPart(ref ModelMeshPart meshPart) vertexBufferRange.Serialize(this); meshPart.VertexBuffer = GetFromList(vertexBufferRange, CurrentMesh.VertexBuffers); + // Skinned bones + Serialize(ref meshPart.SkinnedBones, Serialize); + // 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..6feaf998e --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2010-2013 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 int Index; + + public ModelBone Bone; + + public Matrix InverseBindTransform; + } +} \ 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..12bb39b41 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2010-2013 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..e4c644827 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs @@ -0,0 +1,100 @@ +// Copyright (c) 2010-2013 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.SkinnedBones.Length == 0) + { + 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/Graphics/ModelData.Animation.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs new file mode 100644 index 000000000..5897975d5 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs @@ -0,0 +1,120 @@ +// Copyright (c) 2010-2013 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 children node indices. + /// + 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); + } + } + + /// + /// 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); + } + } + + /// + /// Class KeyFrame + /// + public class KeyFrame : IDataSerializable + { + /// + /// The key time. + /// + public float Time; + + /// + /// The bone transform. + /// + public Matrix 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..fbf3fe9f5 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(); } /// @@ -57,6 +58,11 @@ public MeshPart() /// public Matrix[] BoneOffsetMatrices; + /// + /// Gets the index of the models skinned bones for each bone weight + /// + public List SkinnedBones; + /// /// The attributes attached to this mesh part. /// @@ -67,6 +73,7 @@ void IDataSerializable.Serialize(BinarySerializer serializer) serializer.Serialize(ref MaterialIndex); serializer.Serialize(ref IndexBufferRange); serializer.Serialize(ref VertexBufferRange); + serializer.Serialize(ref SkinnedBones, serializer.Serialize); 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..c3f45a148 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2010-2013 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..28b7521a6 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs @@ -44,9 +44,9 @@ public ModelData() Textures = new List(); Materials = new List(); Bones = new List(); - // DISABLE_SKINNED_BONES - //SkinnedBones = new List(); + SkinnedBones = new List(); Meshes = new List(); + Animations = new List(); Attributes = new PropertyCollection(); } @@ -70,17 +70,21 @@ public ModelData() /// public List Bones; - // DISABLE_SKINNED_BONES - ///// - ///// Gets the bones used to perform skinning animation with this model. - ///// - //public List SkinnedBones; + /// + /// 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 +226,21 @@ 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(); + // 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..9f57b49f8 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj +++ b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj @@ -31,6 +31,8 @@ + + From bb76fe3f6bde4e3989a56cb61c7b77c316425845 Mon Sep 17 00:00:00 2001 From: jwollen Date: Wed, 28 May 2014 10:59:31 +0200 Subject: [PATCH 02/14] [Toolkit] First pass on model animation --- .../Model/ModelCompiler.cs | 151 ++++++++++++++++-- .../Toolkit/SharpDX.Toolkit.Graphics/Model.cs | 15 +- .../SharpDX.Toolkit.Graphics/ModelMesh.cs | 16 +- .../Graphics/ModelData.MeshPart.cs | 5 - 4 files changed, 162 insertions(+), 25 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 03e59050b..0f762856e 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -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) @@ -209,6 +210,9 @@ private void ProcessScene() // Process materials ProcessMaterials(); + + // Process animations + ProcessAnimations(); } private void CollectEmbeddedTextures(Texture[] textures) @@ -399,6 +403,85 @@ 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; + + 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 = Matrix.Scaling(ConvertVector(key.Value)); + } + } + + if (channel.HasRotationKeys) + { + foreach (var key in channel.RotationKeys) + { + var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); + keyFrame.Value *= Matrix.RotationQuaternion(ConvertQuaternion(key.Value)); + } + } + + if (channel.HasPositionKeys) + { + foreach (var key in channel.PositionKeys) + { + var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); + keyFrame.Value *= Matrix.Translation(ConvertVector(key.Value)); + } + } + + 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 = Matrix.Identity }; + keyFrames.AddAfter(node, keyFrame); + return keyFrame; + } + } + + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = Matrix.Identity }; + keyFrames.AddLast(keyFrame); + return keyFrame; + } + private void CollectSkinnedBones() { foreach (var mesh in scene.Meshes) @@ -407,7 +490,7 @@ private void CollectSkinnedBones() { foreach (var bone in mesh.Bones) { - RegisterNode(scene.RootNode.FindNode(bone.Name), skinnedBones); + RegisterNode(scene.RootNode.FindNode(bone.Name), meshNodes); } } } @@ -431,8 +514,6 @@ 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); } @@ -510,7 +591,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) @@ -543,7 +624,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() { @@ -635,23 +716,41 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) var skinningIndices = new Int4[assimpMesh.VertexCount]; var skinningWeights = new Vector4[assimpMesh.VertexCount]; + Matrix meshBindTransform = Matrix.Identity; if (assimpMesh.HasBones) { - meshPart.BoneOffsetMatrices = new Matrix[assimpMesh.BoneCount]; + meshBindTransform = GetAbsoluteTransform(parentNode); + 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); + + // Get or create this skinned bone's global index + int boneIndex; + if (!skinnedBones.TryGetValue(boneNode, out boneIndex)) + { + boneIndex = model.SkinnedBones.Count; + skinnedBones[boneNode] = boneIndex; + + model.SkinnedBones.Add(new ModelData.SkinnedBone + { + BoneIndex = meshNodes[boneNode], + InverseBindTransform = Matrix.Invert(GetAbsoluteTransform(parentNode)) * ConvertMatrix(bone.OffsetMatrix), + }); + } + + // Add the bone index to the mesh part's local bone list + meshPart.SkinnedBones.Add(boneIndex); + if (bone.HasVertexWeights) { - var boneNode = scene.RootNode.FindNode(bone.Name); - var boneIndex = skinnedBones[boneNode]; 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] = i; skinningWeights[weights.VertexID][vertexSkinningCount] = weights.Weight; @@ -686,7 +785,8 @@ 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]; + var position = ConvertVector(assimpMesh.Vertices[i]); + Vector3.TransformCoordinate(ref position, ref meshBindTransform, out position); vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation @@ -695,7 +795,9 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh) // Add normals if (assimpMesh.HasNormals) { - vertexStream.Write(assimpMesh.Normals[i]); + var normal = ConvertVector(assimpMesh.Normals[i]); + Vector3.TransformNormal(ref normal, ref meshBindTransform, out normal); + vertexStream.Write(normal); } // Add colors @@ -736,8 +838,14 @@ 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]); + + Vector3.TransformNormal(ref tangent, ref meshBindTransform, out tangent); + Vector3.TransformNormal(ref bitangent, ref meshBindTransform, out bitangent); + + vertexStream.Write(tangent); + vertexStream.Write(bitangent); } // Add Skinning Indices/Weights @@ -781,6 +889,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; diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs index 4e82b587b..cc65c34a4 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs @@ -165,6 +165,13 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri CopyAbsoluteBoneTransformsTo(new IntPtr(localSharedDrawBoneMatrices)); + var skinningTransforms = new Matrix[SkinnedBones.Count]; + for (int i = 0; i < SkinnedBones.Count; i++) + { + int boneIndex = SkinnedBones[i].Bone.Index; + Matrix.Multiply(ref SkinnedBones[i].InverseBindTransform, ref localSharedDrawBoneMatrices[boneIndex], out skinningTransforms[i]); + } + var defaultParametersContext = default(EffectDefaultParametersContext); for (int i = 0; i < count; i++) @@ -178,7 +185,7 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri Matrix worldTranformed; Matrix.Multiply(ref localSharedDrawBoneMatrices[index], ref world, out worldTranformed); - effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); + effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref world, ref view, ref projection); } else { @@ -196,18 +203,18 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri 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 = world; matrices.View = view; matrices.Projection = projection; } } } - mesh.Draw(context, effectOverride); + mesh.Draw(context, skinningTransforms, effectOverride); } } diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs index 00465c618..b9063a3d0 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs @@ -62,7 +62,7 @@ public void ForEach(Action meshPartFunction) /// The graphics context. /// 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 +73,20 @@ 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.Length; + if (skinnedEffect != null && boneCount > 0) + { + var transforms = new Matrix[boneCount]; + for (int j = 0; j < boneCount; j++) + { + transforms[j] = boneTransforms[part.SkinnedBones[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/ModelData.MeshPart.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs index fbf3fe9f5..ed81e9e72 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs @@ -53,11 +53,6 @@ public MeshPart() /// public BufferRange VertexBufferRange; - /// - /// Gets the offset matrix attached to each bone weights - /// - public Matrix[] BoneOffsetMatrices; - /// /// Gets the index of the models skinned bones for each bone weight /// From e6f37bf20e4da9bdfe0c6171efa6b3bf273fee84 Mon Sep 17 00:00:00 2001 From: jwollen Date: Wed, 28 May 2014 14:24:45 +0200 Subject: [PATCH 03/14] [Toolkit] Changed keyframes to SrtTransforms and added linear keyframe reduction --- .../Model/ModelCompiler.cs | 46 +++++++++++-- .../Graphics/ModelData.Animation.cs | 2 +- .../SharpDX.Toolkit/SharpDX.Toolkit.csproj | 1 + .../Toolkit/SharpDX.Toolkit/SrtTransform.cs | 69 +++++++++++++++++++ 4 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 0f762856e..616d6626b 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -431,7 +431,7 @@ private void ProcessAnimations() foreach (var key in channel.ScalingKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value = Matrix.Scaling(ConvertVector(key.Value)); + keyFrame.Value = new SrtTransform(ConvertVector(key.Value), Quaternion.Identity, Vector3.Zero); } } @@ -440,7 +440,7 @@ private void ProcessAnimations() foreach (var key in channel.RotationKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value *= Matrix.RotationQuaternion(ConvertQuaternion(key.Value)); + keyFrame.Value = new SrtTransform(keyFrame.Value.Scale, ConvertQuaternion(key.Value), Vector3.Zero); } } @@ -449,10 +449,12 @@ private void ProcessAnimations() foreach (var key in channel.PositionKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value *= Matrix.Translation(ConvertVector(key.Value)); + keyFrame.Value = new SrtTransform(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); @@ -471,17 +473,51 @@ private ModelData.KeyFrame GetKeyFrame(float keyTime, LinkedList keyTime) { - keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = Matrix.Identity }; + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = SrtTransform.Identity }; keyFrames.AddAfter(node, keyFrame); return keyFrame; } } - keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = Matrix.Identity }; + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = SrtTransform.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 = SrtTransform.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) diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs index 5897975d5..b054800d7 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs @@ -107,7 +107,7 @@ public class KeyFrame : IDataSerializable /// /// The bone transform. /// - public Matrix Value; + public SrtTransform Value; /// void IDataSerializable.Serialize(BinarySerializer serializer) diff --git a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj index 9f57b49f8..00a85223a 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj +++ b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj @@ -86,6 +86,7 @@ + diff --git a/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs b/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs new file mode 100644 index 000000000..c8d924d3d --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs @@ -0,0 +1,69 @@ +using SharpDX.Serialization; + +namespace SharpDX.Toolkit +{ + public struct SrtTransform : IDataSerializable + { + public Vector3 Scale; + + public Quaternion Rotation; + + public Vector3 Translation; + + public static readonly SrtTransform Identity = new SrtTransform(Vector3.One, Quaternion.Identity, Vector3.Zero); + + public SrtTransform(Vector3 scale, Quaternion rotation, Vector3 translation) + { + Scale = scale; + Rotation = rotation; + Translation = translation; + } + + public SrtTransform(Matrix transform) + { + transform.Decompose(out Scale, out Rotation, out Translation); + } + + public static void Slerp(ref SrtTransform start, ref SrtTransform end, float amount, out SrtTransform 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); + } + + public static SrtTransform Slerp(SrtTransform start, SrtTransform end, float amount) + { + SrtTransform result; + Slerp(ref start, ref end, amount, out result); + return result; + } + + public static explicit operator Matrix(SrtTransform value) + { + return Matrix.Scaling(value.Scale) * Matrix.RotationQuaternion(value.Rotation) * Matrix.Translation(value.Translation); + } + + public void Serialize(BinarySerializer serializer) + { + serializer.Serialize(ref Scale); + serializer.Serialize(ref Rotation); + serializer.Serialize(ref Translation); + } + + public bool Equals(SrtTransform other) + { + return Scale.Equals(other.Scale) && Rotation.Equals(other.Rotation) && Translation.Equals(other.Translation); + } + + public override bool Equals(object value) + { + if (value == null) + return false; + + if (!ReferenceEquals(value.GetType(), typeof(SrtTransform))) + return false; + + return Equals((SrtTransform)value); + } + } +} From 71fa40ec6c77e84a4a2bf0c0c6b949e99ef1a380 Mon Sep 17 00:00:00 2001 From: jwollen Date: Wed, 28 May 2014 15:34:14 +0200 Subject: [PATCH 04/14] [Toolkit] Forced ModelCompiler to collect all bones for now, and updated data structures --- .../SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs | 4 +++- Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs | 2 +- Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs | 9 ++++++++- .../SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 616d6626b..dc7381465 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -550,7 +550,9 @@ private void CollectMeshNodes(Node node) private bool IsModelNode(Node node) { - return meshNodes.ContainsKey(node); + // Return all nodes for now, so we can use model files as skeletons + return true; + //return meshNodes.ContainsKey(node); } private void RegisterNode(Node node, Dictionary nodeMap) diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs index b9063a3d0..79208d646 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs @@ -75,7 +75,7 @@ public void Draw(GraphicsDevice context, Matrix[] boneTransforms, Effect effectO } var skinnedEffect = effect as SkinnedEffect; - int boneCount = part.SkinnedBones.Length; + int boneCount = part.SkinnedBones.Count; if (skinnedEffect != null && boneCount > 0) { var transforms = new Matrix[boneCount]; diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs index 1a0a7742a..e4aff8276 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 { @@ -59,7 +60,13 @@ public class ModelMeshPart : ComponentBase /// /// Indices of the models skinned bones that are affecting this part. /// - public int[] SkinnedBones; + public List SkinnedBones; + + + public bool IsSkinned + { + get { return SkinnedBones != null && SkinnedBones.Count > 0; } + } private Effect effect; diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs index e4c644827..c63fb15bf 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs @@ -57,7 +57,7 @@ protected virtual SkinnedEffect CreateSkinnedEffect() protected override Effect Process(Model model, ModelMeshPart meshPart) { - if (meshPart.SkinnedBones.Length == 0) + if (!meshPart.IsSkinned) { return base.Process(model, meshPart); } From 05d4f67aa03f38706deb1bebad4d0e52f44bd5e9 Mon Sep 17 00:00:00 2001 From: jwollen Date: Tue, 3 Jun 2014 13:30:23 +0200 Subject: [PATCH 05/14] [Toolkit] More documentation and cleanup. --- .../Model/ModelCompiler.cs | 14 +- .../Toolkit/SharpDX.Toolkit.Graphics/Model.cs | 16 +- .../ModelBoneCollection.cs | 2 +- .../SharpDX.Toolkit.Graphics/ModelMesh.cs | 1 + .../SharpDX.Toolkit.Graphics/ModelMeshPart.cs | 7 +- .../Graphics/ModelData.Animation.cs | 56 +---- .../Graphics/ModelData.AnimationChannel.cs | 60 +++++ .../Graphics/ModelData.KeyFrame.cs | 50 +++++ .../SharpDX.Toolkit/SharpDX.Toolkit.csproj | 2 + .../Toolkit/SharpDX.Toolkit/SrtTransform.cs | 206 +++++++++++++++++- 10 files changed, 336 insertions(+), 78 deletions(-) create mode 100644 Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs create mode 100644 Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index dc7381465..37a6eaab1 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(); @@ -413,7 +413,7 @@ private void ProcessAnimations() if (!animation.HasNodeAnimations) continue; - var ticksPerSecond = animation.TicksPerSecond > 0 ? animation.TicksPerSecond : 25.0; + 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 { @@ -526,7 +526,7 @@ private void CollectSkinnedBones() { foreach (var bone in mesh.Bones) { - RegisterNode(scene.RootNode.FindNode(bone.Name), meshNodes); + RegisterNode(scene.RootNode.FindNode(bone.Name), skeletonNodes); } } } @@ -536,7 +536,7 @@ private void CollectMeshNodes(Node node) { if (node.HasMeshes) { - RegisterNode(node, meshNodes); + RegisterNode(node, skeletonNodes); } if (node.HasChildren) @@ -552,7 +552,7 @@ private bool IsModelNode(Node node) { // Return all nodes for now, so we can use model files as skeletons return true; - //return meshNodes.ContainsKey(node); + //return skeletonNodes.ContainsKey(node); } private void RegisterNode(Node node, Dictionary nodeMap) @@ -596,7 +596,7 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) } // 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 @@ -773,7 +773,7 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, model.SkinnedBones.Add(new ModelData.SkinnedBone { - BoneIndex = meshNodes[boneNode], + BoneIndex = skeletonNodes[boneNode], InverseBindTransform = Matrix.Invert(GetAbsoluteTransform(parentNode)) * ConvertMatrix(bone.OffsetMatrix), }); } diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs index cc65c34a4..226f2ba0b 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs @@ -182,10 +182,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 world, 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 localSharedDrawBoneMatrices[index], ref world, out worldTranformed); + effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); + } } else { @@ -207,7 +213,7 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri } else { - matrices.World = world; + matrices.World = effect is SkinnedEffect ? world : worldTranformed; matrices.View = view; matrices.Projection = projection; } 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 79208d646..1ecbc2488 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs @@ -60,6 +60,7 @@ 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, Matrix[] boneTransforms, Effect effectOverride = null) diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs index e4aff8276..e54a9065a 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs @@ -62,7 +62,12 @@ public class ModelMeshPart : ComponentBase /// public List 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; } diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs index b054800d7..228a22c9a 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs @@ -42,7 +42,7 @@ public class Animation : IDataSerializable public float Duration; /// - /// The children node indices. + /// The channels of this animation. /// public List Channels; @@ -62,59 +62,5 @@ void IDataSerializable.Serialize(BinarySerializer serializer) serializer.Serialize(ref Channels); } } - - /// - /// 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); - } - } - - /// - /// Class KeyFrame - /// - public class KeyFrame : IDataSerializable - { - /// - /// The key time. - /// - public float Time; - - /// - /// The bone transform. - /// - public SrtTransform 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.AnimationChannel.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs new file mode 100644 index 000000000..ed31c1e4c --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2010-2013 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..eb63e0da0 --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2010-2013 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 SrtTransform 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/SharpDX.Toolkit.csproj b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj index 00a85223a..8d7972ba5 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj +++ b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj @@ -32,6 +32,7 @@ + @@ -108,6 +109,7 @@ + diff --git a/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs b/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs index c8d924d3d..d6fcb6237 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs +++ b/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs @@ -1,17 +1,59 @@ -using SharpDX.Serialization; +// Copyright (c) 2010-2013 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.Toolkit +namespace SharpDX { - public struct SrtTransform : IDataSerializable + /// + /// Represents a transformation composed of a scaling, rotation and translation operation. + /// + public struct SrtTransform : 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 SrtTransform Identity = new SrtTransform(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 SrtTransform(Vector3 scale, Quaternion rotation, Vector3 translation) { Scale = scale; @@ -19,11 +61,25 @@ public SrtTransform(Vector3 scale, Quaternion rotation, Vector3 translation) Translation = translation; } + /// + /// Initializes a new instance of the struct. + /// + /// The transformation matrix. + /// + /// This constructor is designed to decompose a SRT transformation matrix only. + /// public SrtTransform(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 SrtTransform start, ref SrtTransform end, float amount, out SrtTransform result) { Vector3.Lerp(ref start.Scale, ref end.Scale, amount, out result.Scale); @@ -31,6 +87,13 @@ public static void Slerp(ref SrtTransform start, ref SrtTransform end, float amo 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 SrtTransform Slerp(SrtTransform start, SrtTransform end, float amount) { SrtTransform result; @@ -38,11 +101,112 @@ public static SrtTransform Slerp(SrtTransform start, SrtTransform end, float amo return result; } + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. public static explicit operator Matrix(SrtTransform 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 ==(SrtTransform left, SrtTransform 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 !=(SrtTransform left, SrtTransform 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); @@ -50,20 +214,44 @@ public void Serialize(BinarySerializer serializer) 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 SrtTransform 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(SrtTransform other) { - return Scale.Equals(other.Scale) && Rotation.Equals(other.Rotation) && Translation.Equals(other.Translation); + 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 == null) - return false; - - if (!ReferenceEquals(value.GetType(), typeof(SrtTransform))) + if (!(value is SrtTransform)) return false; - return Equals((SrtTransform)value); + var strongValue = (SrtTransform)value; + return Equals(ref strongValue); } } } From f4a3d1fb53a5685f2d20a5d4ba114eff8e667a1a Mon Sep 17 00:00:00 2001 From: jwollen Date: Wed, 4 Jun 2014 12:02:52 +0200 Subject: [PATCH 06/14] [Toolkit.Compiler] Bounding spheres of skinned meshes are now calculated in mesh space (in default pose), instead of bone space. --- .../Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 37a6eaab1..e383bac18 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -823,12 +823,12 @@ 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 = ConvertVector(assimpMesh.Vertices[i]); - Vector3.TransformCoordinate(ref position, ref meshBindTransform, out position); + Vector3 defaultPosition = ConvertVector(assimpMesh.Vertices[i]); + Vector3 position = Vector3.TransformCoordinate(defaultPosition, meshBindTransform); vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation - boundingPoints[currentBoundingPointIndex++] = new Vector3(position.X, position.Y, position.Z); + boundingPoints[currentBoundingPointIndex++] = assimpMesh.HasBones ? defaultPosition : position; // Add normals if (assimpMesh.HasNormals) From 80d946923e972699269db2eeaf58d7e4a633ed7b Mon Sep 17 00:00:00 2001 From: jwollen Date: Wed, 4 Jun 2014 18:00:18 +0200 Subject: [PATCH 07/14] [Toolkit] Renamed SrtTransform to CompositeTransform --- .../Model/ModelCompiler.cs | 12 ++--- ...{SrtTransform.cs => CompositeTransform.cs} | 48 +++++++++---------- .../Graphics/ModelData.KeyFrame.cs | 2 +- .../SharpDX.Toolkit/SharpDX.Toolkit.csproj | 2 +- 4 files changed, 32 insertions(+), 32 deletions(-) rename Source/Toolkit/SharpDX.Toolkit/{SrtTransform.cs => CompositeTransform.cs} (83%) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index e383bac18..605d11f03 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -431,7 +431,7 @@ private void ProcessAnimations() foreach (var key in channel.ScalingKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value = new SrtTransform(ConvertVector(key.Value), Quaternion.Identity, Vector3.Zero); + keyFrame.Value = new CompositeTransform(ConvertVector(key.Value), Quaternion.Identity, Vector3.Zero); } } @@ -440,7 +440,7 @@ private void ProcessAnimations() foreach (var key in channel.RotationKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value = new SrtTransform(keyFrame.Value.Scale, ConvertQuaternion(key.Value), Vector3.Zero); + keyFrame.Value = new CompositeTransform(keyFrame.Value.Scale, ConvertQuaternion(key.Value), Vector3.Zero); } } @@ -449,7 +449,7 @@ private void ProcessAnimations() foreach (var key in channel.PositionKeys) { var keyFrame = GetKeyFrame((float)(key.Time / ticksPerSecond), keyFrames); - keyFrame.Value = new SrtTransform(keyFrame.Value.Scale, keyFrame.Value.Rotation, ConvertVector(key.Value)); + keyFrame.Value = new CompositeTransform(keyFrame.Value.Scale, keyFrame.Value.Rotation, ConvertVector(key.Value)); } } @@ -473,13 +473,13 @@ private ModelData.KeyFrame GetKeyFrame(float keyTime, LinkedList keyTime) { - keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = SrtTransform.Identity }; + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = CompositeTransform.Identity }; keyFrames.AddAfter(node, keyFrame); return keyFrame; } } - keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = SrtTransform.Identity }; + keyFrame = new ModelData.KeyFrame { Time = keyTime, Value = CompositeTransform.Identity }; keyFrames.AddLast(keyFrame); return keyFrame; } @@ -503,7 +503,7 @@ private void LinearKeyFrameReduction(LinkedList keyFrames) var next = node.Next.Value; var amount = (current.Time - previous.Time) / (next.Time - previous.Time); - var interpolated = SrtTransform.Slerp(previous.Value, next.Value, amount); + var interpolated = CompositeTransform.Slerp(previous.Value, next.Value, amount); var actual = current.Value; diff --git a/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs similarity index 83% rename from Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs rename to Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs index d6fcb6237..b644da53d 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SrtTransform.cs +++ b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs @@ -26,7 +26,7 @@ namespace SharpDX /// /// Represents a transformation composed of a scaling, rotation and translation operation. /// - public struct SrtTransform : IEquatable, IFormattable, IDataSerializable + public struct CompositeTransform : IEquatable, IFormattable, IDataSerializable { /// /// The scaling component of the transformation. @@ -44,17 +44,17 @@ public struct SrtTransform : IEquatable, IFormattable, IDataSerial public Vector3 Translation; /// - /// The identety . + /// The identety . /// - public static readonly SrtTransform Identity = new SrtTransform(Vector3.One, Quaternion.Identity, Vector3.Zero); + public static readonly CompositeTransform Identity = new CompositeTransform(Vector3.One, Quaternion.Identity, Vector3.Zero); /// - /// Initializes a new instance of the struct. + /// 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 SrtTransform(Vector3 scale, Quaternion rotation, Vector3 translation) + public CompositeTransform(Vector3 scale, Quaternion rotation, Vector3 translation) { Scale = scale; Rotation = rotation; @@ -62,13 +62,13 @@ public SrtTransform(Vector3 scale, Quaternion rotation, Vector3 translation) } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The transformation matrix. /// /// This constructor is designed to decompose a SRT transformation matrix only. /// - public SrtTransform(Matrix transform) + public CompositeTransform(Matrix transform) { transform.Decompose(out Scale, out Rotation, out Translation); } @@ -80,7 +80,7 @@ public SrtTransform(Matrix transform) /// 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 SrtTransform start, ref SrtTransform end, float amount, out SrtTransform result) + 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); @@ -94,19 +94,19 @@ public static void Slerp(ref SrtTransform start, ref SrtTransform end, float amo /// End transformation. /// Value between 0 and 1 indicating the weight of . /// The interpolation of the two transformations. - public static SrtTransform Slerp(SrtTransform start, SrtTransform end, float amount) + public static CompositeTransform Slerp(CompositeTransform start, CompositeTransform end, float amount) { - SrtTransform result; + CompositeTransform result; Slerp(ref start, ref end, amount, out result); return result; } /// - /// Performs an explicit conversion from to . + /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. - public static explicit operator Matrix(SrtTransform value) + public static explicit operator Matrix(CompositeTransform value) { return Matrix.Scaling(value.Scale) * Matrix.RotationQuaternion(value.Rotation) * Matrix.Translation(value.Translation); } @@ -117,7 +117,7 @@ public static explicit operator Matrix(SrtTransform value) /// The first value to compare. /// The second value to compare. /// true if has the same value as ; otherwise, false. - public static bool operator ==(SrtTransform left, SrtTransform right) + public static bool operator ==(CompositeTransform left, CompositeTransform right) { return left.Equals(ref right); } @@ -128,7 +128,7 @@ public static explicit operator Matrix(SrtTransform value) /// The first value to compare. /// The second value to compare. /// true if has a different value than ; otherwise, false. - public static bool operator !=(SrtTransform left, SrtTransform right) + public static bool operator !=(CompositeTransform left, CompositeTransform right) { return !left.Equals(ref right); } @@ -215,25 +215,25 @@ public void Serialize(BinarySerializer serializer) } /// - /// Determines whether the specified is equal to this instance. + /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// - /// true if the specified is equal to this instance; otherwise, false. + /// true if the specified is equal to this instance; otherwise, false. /// - public bool Equals(ref SrtTransform other) + 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. + /// Determines whether the specified is equal to this instance. /// - /// The to compare with this instance. + /// The to compare with this instance. /// - /// true if the specified is equal to this instance; otherwise, false. + /// true if the specified is equal to this instance; otherwise, false. /// - public bool Equals(SrtTransform other) + public bool Equals(CompositeTransform other) { return Equals(ref other); } @@ -247,10 +247,10 @@ public bool Equals(SrtTransform other) /// public override bool Equals(object value) { - if (!(value is SrtTransform)) + if (!(value is CompositeTransform)) return false; - var strongValue = (SrtTransform)value; + var strongValue = (CompositeTransform)value; return Equals(ref strongValue); } } diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs index eb63e0da0..f2d8ccd19 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs @@ -37,7 +37,7 @@ public class KeyFrame : IDataSerializable /// /// The bone transform. /// - public SrtTransform Value; + public CompositeTransform Value; /// void IDataSerializable.Serialize(BinarySerializer serializer) diff --git a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj index 8d7972ba5..bf9fdc59b 100644 --- a/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj +++ b/Source/Toolkit/SharpDX.Toolkit/SharpDX.Toolkit.csproj @@ -87,7 +87,7 @@ - + From 556a3529cda27142e317ae2294a4daf446c8640c Mon Sep 17 00:00:00 2001 From: jwollen Date: Thu, 5 Jun 2014 09:36:54 +0200 Subject: [PATCH 08/14] [Toolkit.Compiler] Fixed bind transforms for meshes that have additional tranformations in bind pose --- .../SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 605d11f03..2c84878fb 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -757,26 +757,31 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, Matrix meshBindTransform = Matrix.Identity; if (assimpMesh.HasBones) { - meshBindTransform = GetAbsoluteTransform(parentNode); - for (int i = 0; i < assimpMesh.Bones.Length; i++) { var bone = assimpMesh.Bones[i]; var boneNode = scene.RootNode.FindNode(bone.Name); - // Get or create this skinned bone's global index + // If a bone is used by multiple meshes, their offset matrices may still be different, as the meshes could have additional + // transformations in bind pose. However, this transformation is equal for all offset matrices of the same mesh. int boneIndex; if (!skinnedBones.TryGetValue(boneNode, out boneIndex)) { + // Register each bone only once boneIndex = model.SkinnedBones.Count; skinnedBones[boneNode] = boneIndex; model.SkinnedBones.Add(new ModelData.SkinnedBone { BoneIndex = skeletonNodes[boneNode], - InverseBindTransform = Matrix.Invert(GetAbsoluteTransform(parentNode)) * ConvertMatrix(bone.OffsetMatrix), + InverseBindTransform = ConvertMatrix(bone.OffsetMatrix) }); } + else + { + // If another mesh is using the bone, the difference in offset matrices is baked into the mesh. + meshBindTransform = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(model.SkinnedBones[boneIndex].InverseBindTransform); + } // Add the bone index to the mesh part's local bone list meshPart.SkinnedBones.Add(boneIndex); From 9d712270020c0500b8ec3bffd4e65a47e3730c63 Mon Sep 17 00:00:00 2001 From: jwollen Date: Fri, 6 Jun 2014 15:27:39 +0200 Subject: [PATCH 09/14] [Toolkit.Compiler] Create bones before referencing them, while creating meshes --- .../Model/ModelCompiler.cs | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 2c84878fb..36e5d4d51 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -204,10 +204,12 @@ private void ProcessScene() // Collect meshes and attached nodes CollectMeshNodes(scene.RootNode); - + // Collect nodes CollectNodes(scene.RootNode, null); + CollectMeshes(scene.RootNode); + // Process materials ProcessMaterials(); @@ -571,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); @@ -582,15 +584,15 @@ 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); } @@ -599,15 +601,29 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) 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 @@ -651,7 +667,7 @@ private void CollectNodes(Node node, ModelData.Bone targetParent) { foreach (var subNode in node.Children) { - CollectNodes(subNode, parent); + CollectMeshes(subNode); } } } From 9d59a15659a011b15643f58a5b4fe7cf77073749 Mon Sep 17 00:00:00 2001 From: jwollen Date: Fri, 6 Jun 2014 23:51:48 +0200 Subject: [PATCH 10/14] [Toolkit.Compiler] Fixed bind transform calculation. (This is unwieldy, since Assimp does not provide the actual bind pose) --- .../Model/ModelCompiler.cs | 92 +++++++++++++++++-- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 36e5d4d51..5465bed0f 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -198,6 +198,8 @@ private void ProcessScene() // Collect bones from mesh CollectSkinnedBones(); + CalculateMeshOffsets(); + registeredMeshParts = new List[scene.MeshCount]; CollectEmbeddedTextures(scene.Textures); @@ -520,6 +522,79 @@ private void LinearKeyFrameReduction(LinkedList keyFrames) } } + private Dictionary, Matrix> meshOffsets = new Dictionary, Matrix>(); + private Dictionary referenceMeshes = new Dictionary(); + + private void CalculateMeshOffsets() + { + // For each pair of meshes, get the difference of their offset transforms, based on one of their shared bones (if any) + foreach (var mesh in scene.Meshes) + { + foreach (var otherMesh in scene.Meshes) + { + if (mesh == otherMesh) + break; + + foreach (var bone in mesh.Bones) + { + foreach (var otherBone in otherMesh.Bones) + { + if (bone.Name == otherBone.Name) + { + var offset = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(ConvertMatrix(otherBone.OffsetMatrix)); + meshOffsets[Tuple.Create(mesh, otherMesh)] = offset; + meshOffsets[Tuple.Create(otherMesh, mesh)] = Matrix.Invert(offset); + break; + } + } + } + } + } + + // For each pair of meshes, get the difference of their offset transforms (if they are connected by a chain of offset transforms) + foreach (var start in scene.Meshes) + { + foreach (var mid in scene.Meshes) + { + foreach (var end in scene.Meshes) + { + if (start == end || start == mid || mid == end) + continue; + + var startToEnd = Tuple.Create(start, end); + var startToMid = Tuple.Create(start, mid); + var midToEnd = Tuple.Create(mid, end); + + if (meshOffsets.ContainsKey(startToEnd) || + !meshOffsets.ContainsKey(startToMid) || + !meshOffsets.ContainsKey(midToEnd)) + continue; + + var offset = meshOffsets[startToMid] * meshOffsets[midToEnd]; + meshOffsets[startToEnd] = offset; + meshOffsets[Tuple.Create(end, start)] = Matrix.Invert(offset); + } + } + } + + // Associate each bones with the first mesh that is connected to it by a chain of offset transforms + foreach (var mesh in scene.Meshes) + { + foreach (var otherMesh in scene.Meshes) + { + if (otherMesh != mesh && !meshOffsets.ContainsKey(Tuple.Create(mesh, otherMesh))) + continue; + + foreach (var bone in otherMesh.Bones) + { + var boneNode = scene.RootNode.FindNode(bone.Name); + if (!referenceMeshes.ContainsKey(boneNode)) + referenceMeshes.Add(boneNode, mesh); + } + } + } + } + private void CollectSkinnedBones() { foreach (var mesh in scene.Meshes) @@ -779,25 +854,26 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, var boneNode = scene.RootNode.FindNode(bone.Name); // If a bone is used by multiple meshes, their offset matrices may still be different, as the meshes could have additional - // transformations in bind pose. However, this transformation is equal for all offset matrices of the same mesh. + // transformations in bind pose. We choose one mesh's bind pose as reference, and bake the difference into the current mesh's vertices. + Mesh boneMesh = referenceMeshes[boneNode]; + if (boneMesh != assimpMesh) + { + meshBindTransform = meshOffsets[Tuple.Create(assimpMesh, boneMesh)]; + } + + // Register each bone only once int boneIndex; if (!skinnedBones.TryGetValue(boneNode, out boneIndex)) { - // Register each bone only once boneIndex = model.SkinnedBones.Count; skinnedBones[boneNode] = boneIndex; model.SkinnedBones.Add(new ModelData.SkinnedBone { BoneIndex = skeletonNodes[boneNode], - InverseBindTransform = ConvertMatrix(bone.OffsetMatrix) + InverseBindTransform = Matrix.Invert(meshBindTransform) * ConvertMatrix(bone.OffsetMatrix) }); } - else - { - // If another mesh is using the bone, the difference in offset matrices is baked into the mesh. - meshBindTransform = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(model.SkinnedBones[boneIndex].InverseBindTransform); - } // Add the bone index to the mesh part's local bone list meshPart.SkinnedBones.Add(boneIndex); From c11dd303cb514c869280bfd5f25b946e07b37967 Mon Sep 17 00:00:00 2001 From: jwollen Date: Sat, 7 Jun 2014 00:26:31 +0200 Subject: [PATCH 11/14] [Toolkit.Game] Added AnimationSystem Conflicts: Source/Toolkit/SharpDX.Toolkit.Game/SharpDX.Toolkit.Game.csproj --- .../SharpDX.Toolkit.Game/AnimationSystem.cs | 165 ++++++++++++++++++ .../SharpDX.Toolkit.Game.csproj | 1 + 2 files changed, 166 insertions(+) create mode 100644 Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs diff --git a/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs new file mode 100644 index 000000000..181b5604e --- /dev/null +++ b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs @@ -0,0 +1,165 @@ +// Copyright (c) 2010-2013 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; + } + } +} 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 + From 4fcd95c87e28106246bfbdd9ece6cf3c192ee39d Mon Sep 17 00:00:00 2001 From: jwollen Date: Sat, 7 Jun 2014 09:37:20 +0200 Subject: [PATCH 12/14] [Toolkit.Compiler] Added check for non-existing collections --- .../Model/ModelCompiler.cs | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index 5465bed0f..df4aeb660 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -535,16 +535,19 @@ private void CalculateMeshOffsets() if (mesh == otherMesh) break; - foreach (var bone in mesh.Bones) + if (mesh.HasBones && otherMesh.HasBones) { - foreach (var otherBone in otherMesh.Bones) + foreach (var bone in mesh.Bones) { - if (bone.Name == otherBone.Name) + foreach (var otherBone in otherMesh.Bones) { - var offset = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(ConvertMatrix(otherBone.OffsetMatrix)); - meshOffsets[Tuple.Create(mesh, otherMesh)] = offset; - meshOffsets[Tuple.Create(otherMesh, mesh)] = Matrix.Invert(offset); - break; + if (bone.Name == otherBone.Name) + { + var offset = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(ConvertMatrix(otherBone.OffsetMatrix)); + meshOffsets[Tuple.Create(mesh, otherMesh)] = offset; + meshOffsets[Tuple.Create(otherMesh, mesh)] = Matrix.Invert(offset); + break; + } } } } @@ -585,11 +588,14 @@ private void CalculateMeshOffsets() if (otherMesh != mesh && !meshOffsets.ContainsKey(Tuple.Create(mesh, otherMesh))) continue; - foreach (var bone in otherMesh.Bones) + if (otherMesh.HasBones) { - var boneNode = scene.RootNode.FindNode(bone.Name); - if (!referenceMeshes.ContainsKey(boneNode)) - referenceMeshes.Add(boneNode, mesh); + foreach (var bone in otherMesh.Bones) + { + var boneNode = scene.RootNode.FindNode(bone.Name); + if (!referenceMeshes.ContainsKey(boneNode)) + referenceMeshes.Add(boneNode, mesh); + } } } } From c41159830d5ddc20787844b09107a1e18ca90dac Mon Sep 17 00:00:00 2001 From: jwollen Date: Sat, 7 Jun 2014 20:14:24 +0200 Subject: [PATCH 13/14] [Toolkit.Compiler] Revert to using offset matrices, as "improvised" bind poses didn't achieve the actual intent to 1. Effortlessly support additive animations 2. Share skinning matrices between multiple models Conflicts: Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs --- .../Model/ModelCompiler.cs | 119 ++---------------- .../Toolkit/SharpDX.Toolkit.Graphics/Model.cs | 32 ++--- .../SharpDX.Toolkit.Graphics/ModelMesh.cs | 3 +- .../SharpDX.Toolkit.Graphics/ModelMeshPart.cs | 4 +- .../SharpDX.Toolkit.Graphics/ModelReader.cs | 9 +- .../ModelSkinnedBone.cs | 4 +- .../Graphics/ModelData.MeshPart.cs | 8 +- .../SharpDX.Toolkit/Graphics/ModelData.cs | 11 -- 8 files changed, 34 insertions(+), 156 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs index df4aeb660..8dae2045a 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Compiler/Model/ModelCompiler.cs @@ -198,8 +198,6 @@ private void ProcessScene() // Collect bones from mesh CollectSkinnedBones(); - CalculateMeshOffsets(); - registeredMeshParts = new List[scene.MeshCount]; CollectEmbeddedTextures(scene.Textures); @@ -522,85 +520,6 @@ private void LinearKeyFrameReduction(LinkedList keyFrames) } } - private Dictionary, Matrix> meshOffsets = new Dictionary, Matrix>(); - private Dictionary referenceMeshes = new Dictionary(); - - private void CalculateMeshOffsets() - { - // For each pair of meshes, get the difference of their offset transforms, based on one of their shared bones (if any) - foreach (var mesh in scene.Meshes) - { - foreach (var otherMesh in scene.Meshes) - { - if (mesh == otherMesh) - break; - - if (mesh.HasBones && otherMesh.HasBones) - { - foreach (var bone in mesh.Bones) - { - foreach (var otherBone in otherMesh.Bones) - { - if (bone.Name == otherBone.Name) - { - var offset = ConvertMatrix(bone.OffsetMatrix) * Matrix.Invert(ConvertMatrix(otherBone.OffsetMatrix)); - meshOffsets[Tuple.Create(mesh, otherMesh)] = offset; - meshOffsets[Tuple.Create(otherMesh, mesh)] = Matrix.Invert(offset); - break; - } - } - } - } - } - } - - // For each pair of meshes, get the difference of their offset transforms (if they are connected by a chain of offset transforms) - foreach (var start in scene.Meshes) - { - foreach (var mid in scene.Meshes) - { - foreach (var end in scene.Meshes) - { - if (start == end || start == mid || mid == end) - continue; - - var startToEnd = Tuple.Create(start, end); - var startToMid = Tuple.Create(start, mid); - var midToEnd = Tuple.Create(mid, end); - - if (meshOffsets.ContainsKey(startToEnd) || - !meshOffsets.ContainsKey(startToMid) || - !meshOffsets.ContainsKey(midToEnd)) - continue; - - var offset = meshOffsets[startToMid] * meshOffsets[midToEnd]; - meshOffsets[startToEnd] = offset; - meshOffsets[Tuple.Create(end, start)] = Matrix.Invert(offset); - } - } - } - - // Associate each bones with the first mesh that is connected to it by a chain of offset transforms - foreach (var mesh in scene.Meshes) - { - foreach (var otherMesh in scene.Meshes) - { - if (otherMesh != mesh && !meshOffsets.ContainsKey(Tuple.Create(mesh, otherMesh))) - continue; - - if (otherMesh.HasBones) - { - foreach (var bone in otherMesh.Bones) - { - var boneNode = scene.RootNode.FindNode(bone.Name); - if (!referenceMeshes.ContainsKey(boneNode)) - referenceMeshes.Add(boneNode, mesh); - } - } - } - } - } - private void CollectSkinnedBones() { foreach (var mesh in scene.Meshes) @@ -851,7 +770,6 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, var skinningIndices = new Int4[assimpMesh.VertexCount]; var skinningWeights = new Vector4[assimpMesh.VertexCount]; - Matrix meshBindTransform = Matrix.Identity; if (assimpMesh.HasBones) { for (int i = 0; i < assimpMesh.Bones.Length; i++) @@ -859,39 +777,22 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, var bone = assimpMesh.Bones[i]; var boneNode = scene.RootNode.FindNode(bone.Name); - // If a bone is used by multiple meshes, their offset matrices may still be different, as the meshes could have additional - // transformations in bind pose. We choose one mesh's bind pose as reference, and bake the difference into the current mesh's vertices. - Mesh boneMesh = referenceMeshes[boneNode]; - if (boneMesh != assimpMesh) - { - meshBindTransform = meshOffsets[Tuple.Create(assimpMesh, boneMesh)]; - } - - // Register each bone only once - int boneIndex; - if (!skinnedBones.TryGetValue(boneNode, out boneIndex)) + if (bone.HasVertexWeights) { - boneIndex = model.SkinnedBones.Count; - skinnedBones[boneNode] = boneIndex; + var skinnedBoneIndex = meshPart.SkinnedBones.Count; - model.SkinnedBones.Add(new ModelData.SkinnedBone + meshPart.SkinnedBones.Add(new ModelData.SkinnedBone { BoneIndex = skeletonNodes[boneNode], - InverseBindTransform = Matrix.Invert(meshBindTransform) * ConvertMatrix(bone.OffsetMatrix) + InverseBindTransform = ConvertMatrix(bone.OffsetMatrix) }); - } - - // Add the bone index to the mesh part's local bone list - meshPart.SkinnedBones.Add(boneIndex); - if (bone.HasVertexWeights) - { for (int j = 0; j < bone.VertexWeightCount; j++) { var weights = bone.VertexWeights[j]; var vertexSkinningCount = skinningCount[weights.VertexID]; - skinningIndices[weights.VertexID][vertexSkinningCount] = i; + skinningIndices[weights.VertexID][vertexSkinningCount] = skinnedBoneIndex; skinningWeights[weights.VertexID][vertexSkinningCount] = weights.Weight; @@ -926,18 +827,16 @@ 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++) { - Vector3 defaultPosition = ConvertVector(assimpMesh.Vertices[i]); - Vector3 position = Vector3.TransformCoordinate(defaultPosition, meshBindTransform); + Vector3 position = ConvertVector(assimpMesh.Vertices[i]); vertexStream.Write(position); // Store bounding points for BoundingSphere pre-calculation - boundingPoints[currentBoundingPointIndex++] = assimpMesh.HasBones ? defaultPosition : position; + boundingPoints[currentBoundingPointIndex++] = position; // Add normals if (assimpMesh.HasNormals) { var normal = ConvertVector(assimpMesh.Normals[i]); - Vector3.TransformNormal(ref normal, ref meshBindTransform, out normal); vertexStream.Write(normal); } @@ -981,10 +880,6 @@ private ModelData.MeshPart Process(ModelData.Mesh mesh, Assimp.Mesh assimpMesh, { var tangent = ConvertVector(assimpMesh.Normals[i]); var bitangent = ConvertVector(assimpMesh.Normals[i]); - - Vector3.TransformNormal(ref tangent, ref meshBindTransform, out tangent); - Vector3.TransformNormal(ref bitangent, ref meshBindTransform, out bitangent); - vertexStream.Write(tangent); vertexStream.Write(bitangent); } diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs index 226f2ba0b..55a5f8fc9 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/Model.cs @@ -30,12 +30,12 @@ namespace SharpDX.Toolkit.Graphics [ContentReader(typeof(ModelContentReader))] public class Model : Component { + private static Matrix[] sharedDrawBones; + public MaterialCollection Materials; public ModelBoneCollection Bones; - public ModelSkinnedBoneCollection SkinnedBones; - public ModelMeshCollection Meshes; public ModelAnimationCollection Animations; @@ -161,17 +161,14 @@ 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)); - - var skinningTransforms = new Matrix[SkinnedBones.Count]; - for (int i = 0; i < SkinnedBones.Count; i++) + if (sharedDrawBones == null || sharedDrawBones.Length < boneCount) { - int boneIndex = SkinnedBones[i].Bone.Index; - Matrix.Multiply(ref SkinnedBones[i].InverseBindTransform, ref localSharedDrawBoneMatrices[boneIndex], out skinningTransforms[i]); + sharedDrawBones = new Matrix[boneCount]; } + CopyAbsoluteBoneTransformsTo(sharedDrawBones); + var defaultParametersContext = default(EffectDefaultParametersContext); for (int i = 0; i < count; i++) @@ -189,7 +186,7 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri else { Matrix worldTranformed; - Matrix.Multiply(ref localSharedDrawBoneMatrices[index], ref world, out worldTranformed); + Matrix.Multiply(ref sharedDrawBones[index], ref world, out worldTranformed); effectOverride.DefaultParameters.Apply(ref defaultParametersContext, ref worldTranformed, ref view, ref projection); } } @@ -204,7 +201,7 @@ 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) @@ -220,11 +217,10 @@ public unsafe void Draw(GraphicsDevice context, Matrix world, Matrix view, Matri } } - mesh.Draw(context, skinningTransforms, effectOverride); + mesh.Draw(context, sharedDrawBones, effectOverride); } } - /// /// Calculates the bounds of this model. /// @@ -243,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/ModelMesh.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs index 1ecbc2488..598c5191f 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMesh.cs @@ -82,7 +82,8 @@ public void Draw(GraphicsDevice context, Matrix[] boneTransforms, Effect effectO var transforms = new Matrix[boneCount]; for (int j = 0; j < boneCount; j++) { - transforms[j] = boneTransforms[part.SkinnedBones[j]]; + var skinnedBone = part.SkinnedBones[j]; + Matrix.Multiply(ref skinnedBone.OffsetMatrix, ref boneTransforms[skinnedBone.Bone.Index], out transforms[j]); } skinnedEffect.SetBoneTransforms(transforms); diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs index e54a9065a..c9d16a8c9 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelMeshPart.cs @@ -58,9 +58,9 @@ public class ModelMeshPart : ComponentBase public PropertyCollection Properties; /// - /// Indices of the models skinned bones that are affecting this part. + /// The skinned bones that are affecting this mesh part. /// - public List SkinnedBones; + public ModelSkinnedBoneCollection SkinnedBones; /// /// Gets a value indicating whether this instance contains skinning information. diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs index 7add76ea7..497594384 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelReader.cs @@ -228,11 +228,6 @@ protected virtual void ReadModel(ref Model model) ReadBones(ref model.Bones); EndChunk(); - // Skinned Bones section - BeginChunk("SKIN"); - ReadSkinnedBones(ref model.SkinnedBones); - EndChunk(); - // Mesh section BeginChunk("MESH"); ReadMeshes(ref model.Meshes); @@ -465,7 +460,7 @@ protected virtual void ReadSkinnedBone(ref ModelSkinnedBone skinnedBone) } skinnedBone.Bone = Model.Bones[boneIndex]; - Serialize(ref skinnedBone.InverseBindTransform); + Serialize(ref skinnedBone.OffsetMatrix); } protected virtual void ReadMesh(ref ModelMesh mesh) @@ -508,7 +503,7 @@ protected virtual void ReadMeshPart(ref ModelMeshPart meshPart) meshPart.VertexBuffer = GetFromList(vertexBufferRange, CurrentMesh.VertexBuffers); // Skinned bones - Serialize(ref meshPart.SkinnedBones, Serialize); + ReadSkinnedBones(ref meshPart.SkinnedBones); // Properties ReadProperties(ref meshPart.Properties); diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs index 6feaf998e..feafb344e 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs @@ -22,10 +22,8 @@ namespace SharpDX.Toolkit.Graphics { public class ModelSkinnedBone : ComponentBase { - public int Index; - public ModelBone Bone; - public Matrix InverseBindTransform; + public Matrix OffsetMatrix; } } \ 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 ed81e9e72..9836425fd 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.MeshPart.cs @@ -35,7 +35,7 @@ public sealed class MeshPart : CommonData, IDataSerializable public MeshPart() { Properties = new PropertyCollection(); - SkinnedBones = new List(); + SkinnedBones = new List(); } /// @@ -54,9 +54,9 @@ public MeshPart() public BufferRange VertexBufferRange; /// - /// Gets the index of the models skinned bones for each bone weight + /// Gets the skinned bones. /// - public List SkinnedBones; + public List SkinnedBones; /// /// The attributes attached to this mesh part. @@ -68,7 +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); + serializer.Serialize(ref SkinnedBones); serializer.Serialize(ref Properties); } } diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs index 28b7521a6..19fe124a8 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs @@ -44,7 +44,6 @@ public ModelData() Textures = new List(); Materials = new List(); Bones = new List(); - SkinnedBones = new List(); Meshes = new List(); Animations = new List(); Attributes = new PropertyCollection(); @@ -70,11 +69,6 @@ public ModelData() /// public List Bones; - /// - /// Gets the bones used to perform skinning animation with this model. - /// - public List SkinnedBones; - /// /// Gets the mesh of this model. /// @@ -226,11 +220,6 @@ void IDataSerializable.Serialize(BinarySerializer serializer) serializer.Serialize(ref Bones); serializer.EndChunk(); - // Skinned Bones section - serializer.BeginChunk("SKIN"); - serializer.Serialize(ref SkinnedBones); - serializer.EndChunk(); - // Mesh section serializer.BeginChunk("MESH"); serializer.Serialize(ref Meshes); From 82f6b0352190d80237f976e39197a79b36483707 Mon Sep 17 00:00:00 2001 From: jwollen Date: Fri, 27 Jun 2014 12:12:04 +0200 Subject: [PATCH 14/14] [Toolkit] Updated license date Conflicts: Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.cs --- Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs | 4 ++-- Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs | 4 ++-- .../SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs | 2 +- Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs | 2 +- .../SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs | 2 +- .../SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs | 2 +- Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs | 2 +- .../Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs | 2 +- .../SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs | 2 +- Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs | 2 +- .../Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs index 181b5604e..cea45b0d7 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Game/AnimationSystem.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 @@ -162,4 +162,4 @@ private class AnimationState public int[] ChannelMap; } } -} +} \ No newline at end of file diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs index e97e4a66b..6fce29975 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 @@ -36,4 +36,4 @@ public ModelAnimation() Channels = new List(); } } -} \ No newline at end of file +} diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs index 1c8b98581..01ac62d2f 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelAnimationCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs index feafb344e..cb2819093 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBone.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs index 12bb39b41..8d7a9012c 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/ModelSkinnedBoneCollection.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs index c63fb15bf..dcd98debd 100644 --- a/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs +++ b/Source/Toolkit/SharpDX.Toolkit.Graphics/SkinnedEffectInstaller.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs index b644da53d..e37b3eed5 100644 --- a/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs +++ b/Source/Toolkit/SharpDX.Toolkit/CompositeTransform.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs index 228a22c9a..c7922ae2f 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.Animation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs index ed31c1e4c..2ddc7d59c 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.AnimationChannel.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs index f2d8ccd19..e462b4131 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.KeyFrame.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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 diff --git a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs index c3f45a148..c965e2851 100644 --- a/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs +++ b/Source/Toolkit/SharpDX.Toolkit/Graphics/ModelData.SkinnedBone.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// 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