Browse Source

progress on animations, transforms and morphing

Vicente Penades 6 years ago
parent
commit
98a44ac66a

+ 1 - 1
src/SharpGLTF.Core/Schema2/gltf.Mesh.cs

@@ -30,7 +30,7 @@ namespace SharpGLTF.Schema2
 
         public IReadOnlyList<MeshPrimitive> Primitives => _primitives;
 
-        public IReadOnlyList<Single> MorphWeights => _weights.Count == 0 ? null : _weights.Select(item => (Single)item).ToArray();
+        public IReadOnlyList<Single> MorphWeights => _weights.Count == 0 ? null : _weights.Select(item => (Single)item).ToList();
 
         #endregion
 

+ 6 - 4
src/SharpGLTF.Core/Schema2/gltf.Node.cs

@@ -183,12 +183,14 @@ namespace SharpGLTF.Schema2
         /// <returns>A <see cref="Transforms.ITransform"/> object</returns>
         public Transforms.ITransform GetMeshWorldTransform(Animation animation, float time)
         {
-            float[] weights = null; // this.MorphWeights.ToArray();
+            var weights = this.MorphWeights;
 
-            if (weights != null)
+            if (weights != null && weights.Count == 0) weights = null;
+
+            if (weights != null && animation != null)
             {
                 Guard.MustShareLogicalParent(this, animation, nameof(animation));
-
+                
                 // TODO: get input only (time) channel, and create
                 // var mfunc = animation.FindMorphingChannel(this).GetSamplerFunc();
 
@@ -255,7 +257,7 @@ namespace SharpGLTF.Schema2
         /// </summary>
         public IReadOnlyList<Single> MorphWeights
         {
-            get => _weights.Count == 0 ? Mesh?.MorphWeights : _weights.Select(item => (float)item).ToArray();
+            get => _weights.Count == 0 ? Mesh?.MorphWeights : _weights.Select(item => (float)item).ToList();
             set
             {
                 _weights.Clear();

+ 44 - 93
src/SharpGLTF.Core/Transforms/MeshTransforms.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Numerics;
 using System.Text;
 
@@ -11,89 +12,64 @@ namespace SharpGLTF.Transforms
 {
     public interface ITransform
     {
-        V3 TransformPosition(V3 position, (int, float)[] skinWeights);
-        V3 TransformNormal(V3 normal, (int, float)[] skinWeights);
-        V4 TransformTangent(V4 tangent, (int, float)[] skinWeights);
-
-        V3 TransformPosition(V3[] positions, (int, float)[] skinWeights);
-        V3 TransformNormal(V3[] normals, (int, float)[] skinWeights);
-        V4 TransformTangent(V4[] tangents, (int, float)[] skinWeights);
+        V3 TransformPosition(V3 position, V3[] morphTargets, (int, float)[] skinWeights);
+        V3 TransformNormal(V3 normal, V3[] morphTargets, (int, float)[] skinWeights);
+        V4 TransformTangent(V4 tangent, V3[] morphTargets, (int, float)[] skinWeights);
     }
 
     public abstract class MorphTransform
     {
         #region constructor
 
-        protected MorphTransform(float[] morphWeights)
+        protected MorphTransform(IReadOnlyList<float> morphWeights)
         {
-            if (morphWeights == null || morphWeights.Length == 0)
+            if (morphWeights == null || morphWeights.Count == 0)
             {
-                _MorphWeights = _NoWeights;
+                _InvWeight = 1;
                 return;
             }
 
-            _MorphWeights = new float[morphWeights.Length];
-            morphWeights.CopyTo(_MorphWeights, 0);
+            _InvWeight = 1 - morphWeights.Sum();
+            if (_InvWeight == 1) return;
+
+            _MorphWeights = new float[morphWeights.Count];
+
+            for (int i = 0; i < morphWeights.Count; ++i) _MorphWeights[i] = morphWeights[i];
         }
 
         #endregion
 
         #region data
 
+        private readonly float _InvWeight;
         private readonly float[] _MorphWeights;
 
-        private static readonly float[] _NoWeights = new float[] { 1 };
-
         #endregion
 
         #region properties
 
+        public float InverseWeight => _InvWeight;
+
         public IReadOnlyList<float> MorphWeights => _MorphWeights;
 
         #endregion
 
         #region API
 
-        protected V3 MorphPositions(V3[] positions)
+        protected V3 MorphVectors(V3 value, V3[] morphTargets)
         {
-            if (_MorphWeights == null) return positions[0];
-            Guard.IsTrue(_MorphWeights.Length == positions.Length, nameof(positions));
-
-            var p = V3.Zero;
-            for (int i = 0; i < _MorphWeights.Length; ++i)
-            {
-                p += positions[i] * _MorphWeights[i];
-            }
+            if (_InvWeight == 1 || morphTargets == null || morphTargets.Length == 0) return value;
 
-            return p;
-        }
+            Guard.IsTrue(_MorphWeights.Length == morphTargets.Length, nameof(morphTargets));
 
-        protected V3 MorphNormals(V3[] normals)
-        {
-            if (_MorphWeights == null) return normals[0];
-            Guard.IsTrue(_MorphWeights.Length == normals.Length, nameof(normals));
+            var p = value * _InvWeight;
 
-            var n = V3.Zero;
             for (int i = 0; i < _MorphWeights.Length; ++i)
             {
-                n += normals[i] * _MorphWeights[i];
+                p += morphTargets[i] * _MorphWeights[i];
             }
 
-            return V3.Normalize(n);
-        }
-
-        protected V4 MorphTangents(V4[] tangents)
-        {
-            if (_MorphWeights == null) return tangents[0];
-            Guard.IsTrue(_MorphWeights.Length == tangents.Length, nameof(tangents));
-
-            var t = V4.Zero;
-            for (int i = 0; i < _MorphWeights.Length; ++i)
-            {
-                t += tangents[i] * _MorphWeights[i];
-            }
-
-            return t;
+            return p;
         }
 
         #endregion
@@ -101,7 +77,7 @@ namespace SharpGLTF.Transforms
 
     public class StaticTransform : MorphTransform, ITransform
     {
-        public StaticTransform(TRANSFORM xform, params float[] morphWeights)
+        public StaticTransform(TRANSFORM xform, IReadOnlyList<float> morphWeights = null)
             : base(morphWeights)
         {
             _Transform = xform;
@@ -109,50 +85,33 @@ namespace SharpGLTF.Transforms
 
         private readonly TRANSFORM _Transform;
 
-        public V3 TransformPosition(V3 position, (int, float)[] skinWeights)
+        public V3 TransformPosition(V3 position, V3[] morphTargets, (int, float)[] skinWeights)
         {
-            return V3.Transform(position, _Transform);
-        }
-
-        public V3 TransformNormal(V3 normal, (int, float)[] skinWeights)
-        {
-            return V3.Normalize(V3.Transform(normal, _Transform));
-        }
-
-        public V4 TransformTangent(V4 tangent, (int, float)[] skinWeights)
-        {
-            return V4.Transform(tangent, _Transform);
-        }
-
-        public V3 TransformPosition(V3[] positions, (int, float)[] skinWeights)
-        {
-            var position = MorphPositions(positions);
+            position = MorphVectors(position, morphTargets);
 
             return V3.Transform(position, _Transform);
         }
 
-        public V3 TransformNormal(V3[] normals, (int, float)[] skinWeights)
+        public V3 TransformNormal(V3 normal, V3[] morphTargets, (int, float)[] skinWeights)
         {
-            var normal = MorphNormals(normals);
+            normal = MorphVectors(normal, morphTargets);
 
-            return V3.Normalize(V3.TransformNormal(normal, _Transform));
+            return V3.Normalize(V3.Transform(normal, _Transform));
         }
 
-        public V4 TransformTangent(V4[] tangents, (int, float)[] skinWeights)
+        public V4 TransformTangent(V4 tangent, V3[] morphTargets, (int, float)[] skinWeights)
         {
-            var tangent = MorphTangents(tangents);
-
-            var tangentV = new V3(tangent.X, tangent.Y, tangent.Z);
+            var n = MorphVectors(new V3(tangent.X, tangent.Y, tangent.Z), morphTargets);
 
-            tangentV = V3.TransformNormal(tangentV, _Transform);
+            n = V3.Normalize(V3.Transform(n, _Transform));
 
-            return new V4(tangentV, tangent.W);
+            return new V4(n, tangent.W);
         }
     }
 
     public class SkinTransform : MorphTransform, ITransform
     {
-        public SkinTransform(TRANSFORM[] invBindings, TRANSFORM[] xforms, params float[] morphWeights)
+        public SkinTransform(TRANSFORM[] invBindings, TRANSFORM[] xforms, IReadOnlyList<float> morphWeights = null)
             : base(morphWeights)
         {
             Guard.NotNull(invBindings, nameof(invBindings));
@@ -169,8 +128,10 @@ namespace SharpGLTF.Transforms
 
         private readonly TRANSFORM[] _JointTransforms;
 
-        public V3 TransformPosition(V3 localPosition, (int, float)[] skinWeights)
+        public V3 TransformPosition(V3 localPosition, V3[] morphTargets, (int, float)[] skinWeights)
         {
+            localPosition = MorphVectors(localPosition, morphTargets);
+
             var worldPosition = V3.Zero;
 
             foreach (var jw in skinWeights)
@@ -181,8 +142,10 @@ namespace SharpGLTF.Transforms
             return worldPosition;
         }
 
-        public V3 TransformNormal(V3 localNormal, (int, float)[] skinWeights)
+        public V3 TransformNormal(V3 localNormal, V3[] morphTargets, (int, float)[] skinWeights)
         {
+            localNormal = MorphVectors(localNormal, morphTargets);
+
             var worldNormal = V3.Zero;
 
             foreach (var jw in skinWeights)
@@ -193,9 +156,10 @@ namespace SharpGLTF.Transforms
             return V3.Normalize(localNormal);
         }
 
-        public V4 TransformTangent(V4 localTangent, (int, float)[] skinWeights)
+        public V4 TransformTangent(V4 localTangent, V3[] morphTargets, (int, float)[] skinWeights)
         {
-            var localTangentV = new V3(localTangent.X, localTangent.Y, localTangent.Z);
+            var localTangentV = MorphVectors(new V3(localTangent.X, localTangent.Y, localTangent.Z), morphTargets);
+
             var worldTangent = V3.Zero;
 
             foreach (var jw in skinWeights)
@@ -203,22 +167,9 @@ namespace SharpGLTF.Transforms
                 worldTangent += V3.TransformNormal(localTangentV, _JointTransforms[jw.Item1]) * jw.Item2;
             }
 
-            return new V4(worldTangent, localTangentV.Z);
-        }
+            worldTangent = V3.Normalize(worldTangent);
 
-        public V3 TransformPosition(V3[] positions, (int, float)[] skinWeights)
-        {
-            return TransformPosition(MorphPositions(positions), skinWeights);
-        }
-
-        public V3 TransformNormal(V3[] normals, (int, float)[] skinWeights)
-        {
-            return TransformNormal(MorphNormals(normals), skinWeights);
-        }
-
-        public V4 TransformTangent(V4[] tangents, (int, float)[] skinWeights)
-        {
-            return TransformTangent(MorphTangents(tangents), skinWeights);
+            return new V4(worldTangent, localTangent.W);
         }
     }
 }

+ 65 - 3
src/SharpGLTF.Toolkit/Geometry/VertexColumns.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Numerics;
 using System.Text;
+using System.Linq;
 
 namespace SharpGLTF.Geometry
 {
@@ -36,6 +37,19 @@ namespace SharpGLTF.Geometry
         public IList<Vector4> Weights0 { get; set; }
         public IList<Vector4> Weights1 { get; set; }
 
+        public class MorphTarget
+        {
+            public IList<Vector3> Positions { get; set; }
+            public IList<Vector3> Normals { get; set; }
+            public IList<Vector3> Tangents { get; set; }
+        }
+
+        private List<MorphTarget> _MorphTargets;
+
+        private static readonly IReadOnlyList<MorphTarget> _EmptyMorphTargets = new MorphTarget[0];
+
+        public IReadOnlyList<MorphTarget> MorphTargets => _MorphTargets == null ? _EmptyMorphTargets : _MorphTargets;
+
         #endregion
 
         #region API
@@ -109,6 +123,19 @@ namespace SharpGLTF.Geometry
             IsolateNormals();
             IsolateTangents();
 
+            // prepare morph data, if available
+
+            Vector3[] morphPositions = null;
+            Vector3[] morphNormals = null;
+            Vector3[] morphTangents = null;
+
+            if (_MorphTargets != null)
+            {
+                if (_MorphTargets.All(item => item.Positions != null)) morphPositions = new Vector3[this.MorphTargets.Count];
+                if (_MorphTargets.All(item => item.Normals != null)) morphNormals = new Vector3[this.MorphTargets.Count];
+                if (_MorphTargets.All(item => item.Tangents != null)) morphTangents = new Vector3[this.MorphTargets.Count];
+            }
+
             // prepare skinning data, if available
 
             var jw0 = Joints0 != null && Weights0 != null;
@@ -140,20 +167,46 @@ namespace SharpGLTF.Geometry
                     jwjwjwjw[7] = ((int)j.W, w.W);
                 }
 
-                if (Positions != null) Positions[i] = transform.TransformPosition(Positions[i], jwjwjwjw);
-                if (Normals != null) Normals[i] = transform.TransformNormal(Normals[i], jwjwjwjw);
-                if (Tangents != null) Tangents[i] = transform.TransformTangent(Tangents[i], jwjwjwjw);
+                if (Positions != null)
+                {
+                    _FillMorphData(morphPositions, vc => vc.Positions[i]);
+                    Positions[i] = transform.TransformPosition(Positions[i], morphPositions, jwjwjwjw);
+                }
+
+                if (Normals != null)
+                {
+                    _FillMorphData(morphNormals, vc => vc.Normals[i]);
+                    Normals[i] = transform.TransformNormal(Normals[i], morphNormals, jwjwjwjw);
+                }
+
+                if (Tangents != null)
+                {
+                    _FillMorphData(morphTangents, vc => vc.Tangents[i]);
+                    Tangents[i] = transform.TransformTangent(Tangents[i], morphTangents, jwjwjwjw);
+                }
             }
 
             // we've just applied the transform,
             // so we no longer need these columns.
 
+            _MorphTargets = null;
+
             Joints0 = null;
             Joints1 = null;
             Weights0 = null;
             Weights1 = null;
         }
 
+        private void _FillMorphData(Vector3[] array, Func<MorphTarget, Vector3> selector)
+        {
+            if (array == null) return;
+
+            for (int i = 0; i < this._MorphTargets.Count; ++i)
+            {
+                array[i] = selector(this._MorphTargets[i]);
+            }
+        }
+
         public TvG GetVertexGeometry<TvG>(int index)
             where TvG : struct, IVertexGeometry
         {
@@ -234,6 +287,15 @@ namespace SharpGLTF.Geometry
                 );
         }
 
+        public MorphTarget AddMorphTarget()
+        {
+            if (_MorphTargets == null) _MorphTargets = new List<MorphTarget>();
+            var mt = new MorphTarget();
+            _MorphTargets.Add(mt);
+
+            return mt;
+        }
+
         #endregion
     }
 }

+ 0 - 0
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPosition.cs → src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexGeometry.cs


+ 30 - 25
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -332,46 +332,51 @@ namespace SharpGLTF.Schema2
             foreach (var t in triangles)
             {
                 var a = vertices.GetVertex<TvP, TvM>(t.Item1);
-                var b = vertices.GetVertex<TvP, TvM>(t.Item1);
-                var c = vertices.GetVertex<TvP, TvM>(t.Item1);
+                var b = vertices.GetVertex<TvP, TvM>(t.Item2);
+                var c = vertices.GetVertex<TvP, TvM>(t.Item3);
 
                 yield return ((a.Geometry, a.Material), (b.Geometry, b.Material), (c.Geometry, c.Material), prim.Material);
             }
         }
 
-        private static TvP _Transform<TvP>(TvP p, (int, float)[] jointweights, Transforms.ITransform xform)
-            where TvP : struct, Geometry.VertexTypes.IVertexGeometry
+        public static Geometry.VertexColumns GetVertexColumns(this MeshPrimitive primitive)
         {
-            p.SetPosition(xform.TransformPosition(p.GetPosition(), jointweights));
-            if (p.TryGetNormal(out Vector3 n)) p.SetNormal(xform.TransformNormal(n, jointweights));
-            if (p.TryGetTangent(out Vector4 t)) p.SetTangent(xform.TransformTangent(t, jointweights));
+            var columns = new Geometry.VertexColumns();
 
-            return p;
-        }
+            _CopyTo(primitive.VertexAccessors, columns);
 
-        public static Geometry.VertexColumns GetVertexColumns(this MeshPrimitive primitive)
-        {
-            var vertexAccessors = primitive.VertexAccessors;
+            for (int i = 0; i < primitive.MorphTargetsCount; ++i)
+            {
+                _CopyTo(primitive.GetMorphTargetAccessors(i), columns.AddMorphTarget());
+            }
 
-            var columns = new Geometry.VertexColumns();
+            return columns;
+        }
 
-            if (vertexAccessors.ContainsKey("POSITION")) columns.Positions = vertexAccessors["POSITION"].AsVector3Array();
-            if (vertexAccessors.ContainsKey("NORMAL")) columns.Normals = vertexAccessors["NORMAL"].AsVector3Array();
-            if (vertexAccessors.ContainsKey("TANGENT")) columns.Tangents = vertexAccessors["TANGENT"].AsVector4Array();
+        private static void _CopyTo(IReadOnlyDictionary<string, Accessor> vertexAccessors, Geometry.VertexColumns dstColumns)
+        {
+            if (vertexAccessors.ContainsKey("POSITION")) dstColumns.Positions = vertexAccessors["POSITION"].AsVector3Array();
+            if (vertexAccessors.ContainsKey("NORMAL")) dstColumns.Normals = vertexAccessors["NORMAL"].AsVector3Array();
+            if (vertexAccessors.ContainsKey("TANGENT")) dstColumns.Tangents = vertexAccessors["TANGENT"].AsVector4Array();
 
-            if (vertexAccessors.ContainsKey("COLOR_0")) columns.Colors0 = vertexAccessors["COLOR_0"].AsColorArray();
-            if (vertexAccessors.ContainsKey("COLOR_1")) columns.Colors1 = vertexAccessors["COLOR_1"].AsColorArray();
+            if (vertexAccessors.ContainsKey("COLOR_0")) dstColumns.Colors0 = vertexAccessors["COLOR_0"].AsColorArray();
+            if (vertexAccessors.ContainsKey("COLOR_1")) dstColumns.Colors1 = vertexAccessors["COLOR_1"].AsColorArray();
 
-            if (vertexAccessors.ContainsKey("TEXCOORD_0")) columns.Textures0 = vertexAccessors["TEXCOORD_0"].AsVector2Array();
-            if (vertexAccessors.ContainsKey("TEXCOORD_1")) columns.Textures1 = vertexAccessors["TEXCOORD_1"].AsVector2Array();
+            if (vertexAccessors.ContainsKey("TEXCOORD_0")) dstColumns.Textures0 = vertexAccessors["TEXCOORD_0"].AsVector2Array();
+            if (vertexAccessors.ContainsKey("TEXCOORD_1")) dstColumns.Textures1 = vertexAccessors["TEXCOORD_1"].AsVector2Array();
 
-            if (vertexAccessors.ContainsKey("JOINTS_0")) columns.Joints0 = vertexAccessors["JOINTS_0"].AsVector4Array();
-            if (vertexAccessors.ContainsKey("JOINTS_1")) columns.Joints1 = vertexAccessors["JOINTS_1"].AsVector4Array();
+            if (vertexAccessors.ContainsKey("JOINTS_0")) dstColumns.Joints0 = vertexAccessors["JOINTS_0"].AsVector4Array();
+            if (vertexAccessors.ContainsKey("JOINTS_1")) dstColumns.Joints1 = vertexAccessors["JOINTS_1"].AsVector4Array();
 
-            if (vertexAccessors.ContainsKey("WEIGHTS_0")) columns.Weights0 = vertexAccessors["WEIGHTS_0"].AsVector4Array();
-            if (vertexAccessors.ContainsKey("WEIGHTS_1")) columns.Weights1 = vertexAccessors["WEIGHTS_1"].AsVector4Array();
+            if (vertexAccessors.ContainsKey("WEIGHTS_0")) dstColumns.Weights0 = vertexAccessors["WEIGHTS_0"].AsVector4Array();
+            if (vertexAccessors.ContainsKey("WEIGHTS_1")) dstColumns.Weights1 = vertexAccessors["WEIGHTS_1"].AsVector4Array();
+        }
 
-            return columns;
+        private static void _CopyTo(IReadOnlyDictionary<string, Accessor> vertexAccessors, Geometry.VertexColumns.MorphTarget dstColumns)
+        {
+            if (vertexAccessors.ContainsKey("POSITION")) dstColumns.Positions = vertexAccessors["POSITION"].AsVector3Array();
+            if (vertexAccessors.ContainsKey("NORMAL")) dstColumns.Normals = vertexAccessors["NORMAL"].AsVector3Array();
+            if (vertexAccessors.ContainsKey("TANGENT")) dstColumns.Tangents = vertexAccessors["TANGENT"].AsVector3Array();
         }
 
         public static IEnumerable<(int, int, int)> GetTriangleIndices(this MeshPrimitive primitive)

+ 25 - 3
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -112,7 +112,8 @@ namespace SharpGLTF.Schema2.LoadAndSave
         [Test]
         public void LoadUnlitModel()
         {
-            var f = TestFiles.GetSampleModelsPaths()
+            var f = TestFiles
+                .GetSampleModelsPaths()
                 .FirstOrDefault(item => item.EndsWith(@"UnlitTest\glTF-Binary\UnlitTest.glb"));
 
             var model = ModelRoot.Load(f);
@@ -130,7 +131,8 @@ namespace SharpGLTF.Schema2.LoadAndSave
         [Test]
         public void LoadLightsModel()
         {
-            var f = TestFiles.GetSchemaExtensionsModelsPaths()
+            var f = TestFiles
+                .GetSchemaExtensionsModelsPaths()
                 .FirstOrDefault(item => item.EndsWith("lights.gltf"));
 
             var model = ModelRoot.Load(f);
@@ -145,7 +147,9 @@ namespace SharpGLTF.Schema2.LoadAndSave
         [Test]
         public void LoadSparseModel()
         {
-            var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.Contains("SimpleSparseAccessor.gltf"));
+            var path = TestFiles
+                .GetSampleModelsPaths()
+                .FirstOrDefault(item => item.Contains("SimpleSparseAccessor.gltf"));
 
             var model = ModelRoot.Load(path);
             Assert.NotNull(model);
@@ -158,5 +162,23 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             var positions = accessor.AsVector3Array();
         }
+
+        [Test]
+        public void LoadMorphTargetModel()
+        {
+            var path = TestFiles
+                .GetSampleModelsPaths()
+                .FirstOrDefault(item => item.Contains("MorphPrimitivesTest.glb"));
+
+            var model = ModelRoot.Load(path);
+            Assert.NotNull(model);
+
+            var triangles = model.DefaultScene
+                .Triangulate<Geometry.VertexTypes.VertexPosition, Geometry.VertexTypes.VertexEmpty>(null, 0)
+                .ToArray();
+
+            model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".obj"));
+            model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(path), ".glb"));
+        }
     }
 }