Browse Source

Added CurveBuilder clone.
improving SceneBuilder skinning.

Vicente Penades 6 years ago
parent
commit
52875cb2a4

+ 28 - 1
src/SharpGLTF.Toolkit/Animations/CurveBuilder.cs

@@ -14,15 +14,35 @@ namespace SharpGLTF.Animations
         : ICurveSampler<T>,
         : ICurveSampler<T>,
         IConvertibleCurve<T>
         IConvertibleCurve<T>
     {
     {
+        #region lifecycle
+
+        protected CurveBuilder() { }
+
+        protected CurveBuilder(CurveBuilder<T> other)
+        {
+            foreach (var kvp in other._Keys)
+            {
+                var key = kvp.Key;
+                var val = new _CurveNode<T>(kvp.Value, IsolateValue);
+                this._Keys[key] = val;
+            }
+        }
+
+        public abstract CurveBuilder<T> Clone();
+
+        #endregion
+
         #region data
         #region data
 
 
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
-        internal SortedDictionary<float, _CurveNode<T>> _Keys = new SortedDictionary<float, _CurveNode<T>>();
+        private SortedDictionary<float, _CurveNode<T>> _Keys = new SortedDictionary<float, _CurveNode<T>>();
 
 
         #endregion
         #endregion
 
 
         #region properties
         #region properties
 
 
+        internal IReadOnlyDictionary<float, _CurveNode<T>> _DebugKeys => _Keys;
+
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
         public IReadOnlyCollection<float> Keys => _Keys.Keys;
         public IReadOnlyCollection<float> Keys => _Keys.Keys;
 
 
@@ -292,6 +312,13 @@ namespace SharpGLTF.Animations
     [System.Diagnostics.DebuggerDisplay("{IncomingTangent} -> {Point}[{Degree}] -> {OutgoingTangent}")]
     [System.Diagnostics.DebuggerDisplay("{IncomingTangent} -> {Point}[{Degree}] -> {OutgoingTangent}")]
     struct _CurveNode<T>
     struct _CurveNode<T>
     {
     {
+        public _CurveNode(_CurveNode<T> other, Func<T,T> isolateFunc)
+        {
+            IncomingTangent = isolateFunc(other.IncomingTangent);
+            Point = isolateFunc(other.Point);
+            OutgoingTangent = isolateFunc(other.OutgoingTangent);
+            Degree = other.Degree;
+        }
         public _CurveNode(T value, bool isLinear)
         public _CurveNode(T value, bool isLinear)
         {
         {
             IncomingTangent = default;
             IncomingTangent = default;

+ 51 - 0
src/SharpGLTF.Toolkit/Animations/CurveFactory.cs

@@ -20,6 +20,18 @@ namespace SharpGLTF.Animations
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyVector3))]
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyVector3))]
     sealed class Vector3CurveBuilder : CurveBuilder<Vector3>, ICurveSampler<Vector3>
     sealed class Vector3CurveBuilder : CurveBuilder<Vector3>, ICurveSampler<Vector3>
     {
     {
+        #region lifecycle
+        public Vector3CurveBuilder() { }
+
+        private Vector3CurveBuilder(Vector3CurveBuilder other)
+            : base(other) { }
+
+        public override CurveBuilder<Vector3> Clone() { return new Vector3CurveBuilder(this); }
+
+        #endregion
+
+        #region API
+
         protected override Vector3 IsolateValue(Vector3 value)
         protected override Vector3 IsolateValue(Vector3 value)
         {
         {
             return value;
             return value;
@@ -61,11 +73,26 @@ namespace SharpGLTF.Animations
                     throw new NotSupportedException();
                     throw new NotSupportedException();
             }
             }
         }
         }
+
+        #endregion
     }
     }
 
 
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyQuaternion))]
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyQuaternion))]
     sealed class QuaternionCurveBuilder : CurveBuilder<Quaternion>, ICurveSampler<Quaternion>
     sealed class QuaternionCurveBuilder : CurveBuilder<Quaternion>, ICurveSampler<Quaternion>
     {
     {
+        #region lifecycle
+
+        public QuaternionCurveBuilder() { }
+
+        private QuaternionCurveBuilder(QuaternionCurveBuilder other)
+            : base(other) { }
+
+        public override CurveBuilder<Quaternion> Clone() { return new QuaternionCurveBuilder(this); }
+
+        #endregion
+
+        #region API
+
         protected override Quaternion IsolateValue(Quaternion value)
         protected override Quaternion IsolateValue(Quaternion value)
         {
         {
             return value;
             return value;
@@ -107,14 +134,36 @@ namespace SharpGLTF.Animations
                     throw new NotSupportedException();
                     throw new NotSupportedException();
             }
             }
         }
         }
+
+        #endregion
     }
     }
 
 
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyArray))]
     [System.Diagnostics.DebuggerTypeProxy(typeof(Debug._CurveBuilderDebugProxyArray))]
     sealed class ArrayCurveBuilder : CurveBuilder<Single[]>, ICurveSampler<Single[]>
     sealed class ArrayCurveBuilder : CurveBuilder<Single[]>, ICurveSampler<Single[]>
     {
     {
+        #region lifecycle
+
+        public ArrayCurveBuilder() { }
+
+        private ArrayCurveBuilder(ArrayCurveBuilder other)
+            : base(other)
+        {
+            System.Diagnostics.Debug.Assert(other._ValueLength == this._ValueLength);
+        }
+
+        public override CurveBuilder<Single[]> Clone() { return new ArrayCurveBuilder(this); }
+
+        #endregion
+
+        #region data
+
         // the first "CheckValue" will fix any further calls to this value.
         // the first "CheckValue" will fix any further calls to this value.
         private int _ValueLength = 0;
         private int _ValueLength = 0;
 
 
+        #endregion
+
+        #region API
+
         protected override Single[] IsolateValue(Single[] value)
         protected override Single[] IsolateValue(Single[] value)
         {
         {
             Guard.NotNull(value, nameof(value));
             Guard.NotNull(value, nameof(value));
@@ -164,5 +213,7 @@ namespace SharpGLTF.Animations
                     throw new NotSupportedException();
                     throw new NotSupportedException();
             }
             }
         }
         }
+
+        #endregion
     }
     }
 }
 }

+ 1 - 1
src/SharpGLTF.Toolkit/Debug/DebugViews.cs

@@ -21,7 +21,7 @@ namespace SharpGLTF.Debug
         {
         {
             Animations._CurveNode<T>? prev = null;
             Animations._CurveNode<T>? prev = null;
 
 
-            foreach (var kvp in curve._Keys)
+            foreach (var kvp in curve._DebugKeys)
             {
             {
                 if (prev.HasValue)
                 if (prev.HasValue)
                 {
                 {

+ 13 - 2
src/SharpGLTF.Toolkit/Scenes/Content.cs

@@ -112,6 +112,14 @@ namespace SharpGLTF.Scenes
         public SkinTransformer(MESHBUILDER mesh, NodeBuilder[] joints)
         public SkinTransformer(MESHBUILDER mesh, NodeBuilder[] joints)
         {
         {
             _Target = new MeshContent(mesh);
             _Target = new MeshContent(mesh);
+            _TargetBindMatrix = null;
+            _Joints.AddRange(joints);
+        }
+
+        public SkinTransformer(MESHBUILDER mesh, Matrix4x4 meshBindMatrix, NodeBuilder[] joints)
+        {
+            _Target = new MeshContent(mesh);
+            _TargetBindMatrix = meshBindMatrix;
             _Joints.AddRange(joints);
             _Joints.AddRange(joints);
         }
         }
 
 
@@ -120,6 +128,7 @@ namespace SharpGLTF.Scenes
         #region data
         #region data
 
 
         private IRenderableContent _Target; // Can be either a morphController or a mesh
         private IRenderableContent _Target; // Can be either a morphController or a mesh
+        private Matrix4x4? _TargetBindMatrix;
 
 
         // condition: all NodeBuilder objects must have the same root.
         // condition: all NodeBuilder objects must have the same root.
         private readonly List<NodeBuilder> _Joints = new List<NodeBuilder>();
         private readonly List<NodeBuilder> _Joints = new List<NodeBuilder>();
@@ -136,9 +145,11 @@ namespace SharpGLTF.Scenes
         {
         {
             var skinnedMeshNode = dstScene.CreateNode();
             var skinnedMeshNode = dstScene.CreateNode();
 
 
-            var skinnedJoints = _Joints.Select(j => context.GetNode(j)).ToArray();
+            var skinnedJoints = _Joints
+                .Select(j => context.GetNode(j))
+                .ToArray();
 
 
-            skinnedMeshNode.WithSkinBinding(skinnedJoints);
+            skinnedMeshNode.WithSkinBinding(_TargetBindMatrix ?? Matrix4x4.Identity, skinnedJoints);
 
 
             _Target.Setup(skinnedMeshNode, context);
             _Target.Setup(skinnedMeshNode, context);
         }
         }

+ 8 - 0
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.cs

@@ -47,6 +47,14 @@ namespace SharpGLTF.Scenes
             _Instances.Add(instance);
             _Instances.Add(instance);
         }
         }
 
 
+        public void AddSkinnedMesh(MESHBUILDER mesh, Matrix4x4 meshBindMatrix, params NodeBuilder[] joints)
+        {
+            var instance = new InstanceBuilder(this);
+            instance.Content = new SkinTransformer(mesh, meshBindMatrix, joints);
+
+            _Instances.Add(instance);
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 108 - 0
tests/SharpGLTF.Tests/Scenes/SceneBuilderTests.cs

@@ -199,5 +199,113 @@ namespace SharpGLTF.Scenes
             scene.AttachToCurrentTest("skinned.glb");
             scene.AttachToCurrentTest("skinned.glb");
             scene.AttachToCurrentTest("skinned.gltf");
             scene.AttachToCurrentTest("skinned.gltf");
         }
         }
+
+        [Test]
+        public void CreateDoubleSkinnedScene()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+            TestContext.CurrentContext.AttachGltfValidatorLinks();
+
+            // create two materials
+
+            var pink = new MaterialBuilder("material1")
+                .WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 0, 1, 1))
+                .WithDoubleSide(true);
+
+            var yellow = new MaterialBuilder("material2")
+                .WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 1, 0, 1))
+                .WithDoubleSide(true);
+
+            // create the mesh            
+
+            const int jointIdx0 = 0; // index of joint node 0
+            const int jointIdx1 = 1; // index of joint node 1
+            const int jointIdx2 = 2; // index of joint node 2
+
+            var v1 = new SKINNEDVERTEX(new Vector3(-10, 0, +10), (jointIdx0, 1));
+            var v2 = new SKINNEDVERTEX(new Vector3(+10, 0, +10), (jointIdx0, 1));
+            var v3 = new SKINNEDVERTEX(new Vector3(+10, 0, -10), (jointIdx0, 1));
+            var v4 = new SKINNEDVERTEX(new Vector3(-10, 0, -10), (jointIdx0, 1));
+
+            var v5 = new SKINNEDVERTEX(new Vector3(-10, 40, +10), (jointIdx0, 0.5f), (jointIdx1, 0.5f));
+            var v6 = new SKINNEDVERTEX(new Vector3(+10, 40, +10), (jointIdx0, 0.5f), (jointIdx1, 0.5f));
+            var v7 = new SKINNEDVERTEX(new Vector3(+10, 40, -10), (jointIdx0, 0.5f), (jointIdx1, 0.5f));
+            var v8 = new SKINNEDVERTEX(new Vector3(-10, 40, -10), (jointIdx0, 0.5f), (jointIdx1, 0.5f));
+
+            var v9 = new SKINNEDVERTEX(new Vector3(-5, 80, +5), (jointIdx2, 1));
+            var v10 = new SKINNEDVERTEX(new Vector3(+5, 80, +5), (jointIdx2, 1));
+            var v11 = new SKINNEDVERTEX(new Vector3(+5, 80, -5), (jointIdx2, 1));
+            var v12 = new SKINNEDVERTEX(new Vector3(-5, 80, -5), (jointIdx2, 1));
+
+            var mesh = SKINNEDVERTEX.CreateCompatibleMesh("mesh1");
+
+            #if DEBUG
+            mesh.VertexPreprocessor.SetDebugPreprocessors();
+            #else
+            mesh.VertexPreprocessor.SetSanitizerPreprocessors();
+            #endif
+
+            mesh.UsePrimitive(pink).AddConvexPolygon(v1, v2, v6, v5);
+            mesh.UsePrimitive(pink).AddConvexPolygon(v2, v3, v7, v6);
+            mesh.UsePrimitive(pink).AddConvexPolygon(v3, v4, v8, v7);
+            mesh.UsePrimitive(pink).AddConvexPolygon(v4, v1, v5, v8);
+
+            mesh.UsePrimitive(yellow).AddConvexPolygon(v5, v6, v10, v9);
+            mesh.UsePrimitive(yellow).AddConvexPolygon(v6, v7, v11, v10);
+            mesh.UsePrimitive(yellow).AddConvexPolygon(v7, v8, v12, v11);
+            mesh.UsePrimitive(yellow).AddConvexPolygon(v8, v5, v9, v12);
+
+            mesh.Validate();
+
+            // create the skeleton armature 1 for the skinned mesh.
+
+            var armature1 = new NodeBuilder("Skeleton1");
+            var joint0 = armature1.CreateNode("Joint 0").WithLocalTranslation(new Vector3(0, 0, 0)); // jointIdx0
+            var joint1 = joint0.CreateNode("Joint 1").WithLocalTranslation(new Vector3(0, 40, 0));  // jointIdx1
+            var joint2 = joint1.CreateNode("Joint 2").WithLocalTranslation(new Vector3(0, 40, 0));  // jointIdx2
+
+            joint1.UseRotation("Base Track")
+                .WithPoint(1, Quaternion.Identity)
+                .WithPoint(2, Quaternion.CreateFromYawPitchRoll(0, 1, 0))
+                .WithPoint(3, Quaternion.CreateFromYawPitchRoll(0, 0, 1))
+                .WithPoint(4, Quaternion.Identity);
+
+            // create the skeleton armature 2 for the skinned mesh.
+
+            var armature2 = new NodeBuilder("Skeleton2").WithLocalTranslation(new Vector3(100,0,0));
+            var joint3 = armature2.CreateNode("Joint 3").WithLocalTranslation(new Vector3(0, 0, 0)); // jointIdx0
+            var joint4 = joint3.CreateNode("Joint 4").WithLocalTranslation(new Vector3(0, 40, 0));  // jointIdx1
+            var joint5 = joint4.CreateNode("Joint 5").WithLocalTranslation(new Vector3(0, 40, 0));  // jointIdx2
+
+            joint4.UseRotation("Base Track")
+                .WithPoint(1, Quaternion.Identity)
+                .WithPoint(2, Quaternion.CreateFromYawPitchRoll(0, 1, 0))
+                .WithPoint(3, Quaternion.CreateFromYawPitchRoll(0, 0, 1))
+                .WithPoint(4, Quaternion.Identity);
+
+            // create scene
+
+            var scene = new SceneBuilder();
+
+            scene.AddSkinnedMesh
+                (
+                mesh,
+                joint0, // joint used for skinning joint index 0
+                joint1, // joint used for skinning joint index 1
+                joint2  // joint used for skinning joint index 2
+                );
+
+            scene.AddSkinnedMesh
+                (
+                mesh,
+                armature2.WorldMatrix,
+                joint3, // joint used for skinning joint index 0
+                joint4, // joint used for skinning joint index 1
+                joint5  // joint used for skinning joint index 2
+                );
+
+            scene.AttachToCurrentTest("skinned.glb");
+            scene.AttachToCurrentTest("skinned.gltf");
+        }
     }
     }
 }
 }