Browse Source

Small code refactoring on some Matrix4x4 methods.
nuget package updates.

Vicente Penades 5 years ago
parent
commit
936e817823

+ 3 - 0
build/SharpGLTF.CodeGen/CodeGen/EmitCSharp.cs

@@ -308,6 +308,9 @@ namespace SharpGLTF.CodeGen
         {
             var sb = new StringBuilder();
 
+            sb.AppendLine("// <auto-generated/>");
+            sb.AppendLine();
+
             sb.AppendLine("//------------------------------------------------------------------------------------------------");
             sb.AppendLine("//      This file has been programatically generated; DON´T EDIT!");
             sb.AppendLine("//------------------------------------------------------------------------------------------------");

+ 1 - 1
build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.2" />    
-    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.1.12" />
+    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.1.15" />
   </ItemGroup>
 
 </Project>

+ 2 - 2
src/Analyzers.props

@@ -10,8 +10,8 @@
   </ItemGroup>
 
   <ItemGroup>    
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.8" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="3.0.0" PrivateAssets="all" />
     <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
   </ItemGroup>
 	

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

@@ -67,7 +67,7 @@ namespace SharpGLTF.Debug
                 if (_Value.Dimensions == Schema2.DimensionType.VEC4) return _Value.AsVector4Array().Cast<Object>().ToArray();
                 if (_Value.Dimensions == Schema2.DimensionType.MAT4) return _Value.AsMatrix4x4Array().Cast<Object>().ToArray();
 
-                var itemByteSz = _Value.ElementByteSize;
+                var itemByteSz = _Value.Format.ByteSize;
                 var byteStride = Math.Max(_Value.SourceBufferView.ByteStride, itemByteSz);
                 var items = new ArraySegment<Byte>[_Value.Count];
 

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.ClearCoat.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.MSFT.textureDDS.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.ModelLightsPunctual.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.NodeLightsPunctual.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.TextureTransform.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.Unlit.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.pbrSpecularGlossiness.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.textureWEBP.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 2 - 0
src/SharpGLTF.Core/Schema2/Generated/gltf.g.cs

@@ -1,3 +1,5 @@
+// <auto-generated/>
+
 //------------------------------------------------------------------------------------------------
 //      This file has been programatically generated; DON´T EDIT!
 //------------------------------------------------------------------------------------------------

+ 0 - 1
src/SharpGLTF.Core/Schema2/gltf.AnimationSampler.cs

@@ -459,5 +459,4 @@ namespace SharpGLTF.Schema2
 
         #endregion
     }
-
 }

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

@@ -213,7 +213,7 @@ namespace SharpGLTF.Schema2
         /// </summary>
         public Matrix4x4 LocalMatrix
         {
-            get => Transforms.AffineTransform.Evaluate(_matrix, _scale, _rotation, _translation);
+            get => Transforms.Matrix4x4Factory.CreateFrom(_matrix, _scale, _rotation, _translation);
             set
             {
                 if (value == Matrix4x4.Identity)
@@ -261,12 +261,12 @@ namespace SharpGLTF.Schema2
             get
             {
                 var vs = VisualParent;
-                return vs == null ? LocalMatrix : Transforms.AffineTransform.LocalToWorld(vs.WorldMatrix, LocalMatrix);
+                return vs == null ? LocalMatrix : Transforms.Matrix4x4Factory.LocalToWorld(vs.WorldMatrix, LocalMatrix);
             }
             set
             {
                 var vs = VisualParent;
-                LocalMatrix = vs == null ? value : Transforms.AffineTransform.WorldToLocal(vs.WorldMatrix, value);
+                LocalMatrix = vs == null ? value : Transforms.Matrix4x4Factory.WorldToLocal(vs.WorldMatrix, value);
             }
         }
 
@@ -298,7 +298,7 @@ namespace SharpGLTF.Schema2
 
             var vs = VisualParent;
             var lm = GetLocalTransform(animation, time).Matrix;
-            return vs == null ? lm : Transforms.AffineTransform.LocalToWorld(vs.GetWorldMatrix(animation, time), lm);
+            return vs == null ? lm : Transforms.Matrix4x4Factory.LocalToWorld(vs.GetWorldMatrix(animation, time), lm);
         }
 
         public IReadOnlyList<Single> GetMorphWeights()

+ 0 - 25
src/SharpGLTF.Core/Transforms/AffineTransform.cs

@@ -113,31 +113,6 @@ namespace SharpGLTF.Transforms
 
         #region API
 
-        /// <summary>
-        /// Evaluates a <see cref="Matrix4x4"/> transform based on the available parameters.
-        /// </summary>
-        /// <param name="transform">A <see cref="Matrix4x4"/> instance, or null.</param>
-        /// <param name="scale">A <see cref="Vector3"/> instance, or null.</param>
-        /// <param name="rotation">A <see cref="Quaternion"/> instance, or null.</param>
-        /// <param name="translation">A <see cref="Vector3"/> instance, or null.</param>
-        /// <returns>A <see cref="Matrix4x4"/> transform.</returns>
-        public static Matrix4x4 Evaluate(Matrix4x4? transform, Vector3? scale, Quaternion? rotation, Vector3? translation)
-        {
-            if (transform.HasValue) return transform.Value;
-
-            return new AffineTransform(null, scale, rotation, translation).Matrix;
-        }
-
-        public static Matrix4x4 LocalToWorld(Matrix4x4 parentWorld, Matrix4x4 childLocal)
-        {
-            return childLocal * parentWorld;
-        }
-
-        public static Matrix4x4 WorldToLocal(Matrix4x4 parentWorld, Matrix4x4 childWorld)
-        {
-            return childWorld * parentWorld.Inverse();
-        }
-
         public static AffineTransform Blend(ReadOnlySpan<AffineTransform> transforms, ReadOnlySpan<float> weights)
         {
             var s = Vector3.Zero;

+ 132 - 0
src/SharpGLTF.Core/Transforms/Matrix4x4Factory.cs

@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Transforms
+{
+    public static class Matrix4x4Factory
+    {
+        /// <summary>
+        /// Evaluates a <see cref="Matrix4x4"/> transform based on the available parameters.
+        /// </summary>
+        /// <param name="transform">A <see cref="Matrix4x4"/> instance, or null.</param>
+        /// <param name="scale">A <see cref="Vector3"/> instance, or null.</param>
+        /// <param name="rotation">A <see cref="Quaternion"/> instance, or null.</param>
+        /// <param name="translation">A <see cref="Vector3"/> instance, or null.</param>
+        /// <returns>A <see cref="Matrix4x4"/> transform.</returns>
+        public static Matrix4x4 CreateFrom(Matrix4x4? transform, Vector3? scale, Quaternion? rotation, Vector3? translation)
+        {
+            if (transform.HasValue)
+            {
+                // scale, rotation and translation should be null at this point.
+                return transform.Value;
+            }
+
+            return new AffineTransform(null, scale, rotation, translation).Matrix;
+        }
+
+        public static Matrix4x4 LocalToWorld(Matrix4x4 parentWorld, Matrix4x4 childLocal)
+        {
+            return childLocal * parentWorld;
+        }
+
+        public static Matrix4x4 WorldToLocal(Matrix4x4 parentWorld, Matrix4x4 childWorld)
+        {
+            return childWorld * parentWorld.Inverse();
+        }
+
+        /// <summary>
+        /// Normalizes the axis of the given matrix, to make it orthogonal.
+        /// </summary>
+        /// <param name="xform">The <see cref="Matrix4x4"/> to normalize.</param>
+        public static void NormalizeMatrix(ref Matrix4x4 xform)
+        {
+            var vx = new Vector3(xform.M11, xform.M12, xform.M13);
+            var vy = new Vector3(xform.M21, xform.M22, xform.M23);
+            var vz = new Vector3(xform.M31, xform.M32, xform.M33);
+
+            var lx = vx.Length();
+            var ly = vy.Length();
+            var lz = vz.Length();
+
+            // normalize axis vectors
+            vx /= lx;
+            vy /= ly;
+            vz /= lz;
+
+            // determine the skew of each axis (the smaller, the more orthogonal the axis is)
+            var kxy = Math.Abs(Vector3.Dot(vx, vy));
+            var kxz = Math.Abs(Vector3.Dot(vx, vz));
+            var kyz = Math.Abs(Vector3.Dot(vy, vz));
+
+            var kx = kxy + kxz;
+            var ky = kxy + kyz;
+            var kz = kxz + kyz;
+
+            // we will use the axis with less skew as our fixed pivot.
+
+            // axis X as pivot
+            if (kx < ky && kx < kz)
+            {
+                if (ky < kz)
+                {
+                    vz = Vector3.Cross(vx, vy);
+                    vy = Vector3.Cross(vz, vx);
+                }
+                else
+                {
+                    vy = Vector3.Cross(vz, vx);
+                    vz = Vector3.Cross(vx, vy);
+                }
+            }
+
+            // axis Y as pivot
+            else if (ky < kx && ky < kz)
+            {
+                if (kx < kz)
+                {
+                    vz = Vector3.Cross(vx, vy);
+                    vx = Vector3.Cross(vy, vz);
+                }
+                else
+                {
+                    vx = Vector3.Cross(vy, vz);
+                    vz = Vector3.Cross(vx, vy);
+                }
+            }
+
+            // axis z as pivot
+            else
+            {
+                if (kx < ky)
+                {
+                    vy = Vector3.Cross(vz, vx);
+                    vx = Vector3.Cross(vy, vz);
+                }
+                else
+                {
+                    vx = Vector3.Cross(vy, vz);
+                    vy = Vector3.Cross(vz, vx);
+                }
+            }
+
+            // restore axes original lengths
+            vx *= lx;
+            vy *= ly;
+            vz *= lz;
+
+            xform.M11 = vx.X;
+            xform.M12 = vx.Y;
+            xform.M13 = vx.Z;
+
+            xform.M21 = vy.X;
+            xform.M22 = vy.Y;
+            xform.M23 = vy.Z;
+
+            xform.M31 = vz.X;
+            xform.M32 = vz.Y;
+            xform.M33 = vz.Z;
+        }
+    }
+}

+ 4 - 4
src/SharpGLTF.Toolkit/Scenes/NodeBuilder.cs

@@ -103,7 +103,7 @@ namespace SharpGLTF.Scenes
         /// </summary>
         public Matrix4x4 LocalMatrix
         {
-            get => Transforms.AffineTransform.Evaluate(_Matrix, Scale?.Value, Rotation?.Value, Translation?.Value);
+            get => Transforms.Matrix4x4Factory.CreateFrom(_Matrix, Scale?.Value, Rotation?.Value, Translation?.Value);
             set
             {
                 if (HasAnimations) { _DecomposeMatrix(value); return; }
@@ -166,12 +166,12 @@ namespace SharpGLTF.Scenes
             get
             {
                 var vs = this.Parent;
-                return vs == null ? LocalMatrix : Transforms.AffineTransform.LocalToWorld(vs.WorldMatrix, LocalMatrix);
+                return vs == null ? LocalMatrix : Transforms.Matrix4x4Factory.LocalToWorld(vs.WorldMatrix, LocalMatrix);
             }
             set
             {
                 var vs = this.Parent;
-                LocalMatrix = vs == null ? value : Transforms.AffineTransform.WorldToLocal(vs.WorldMatrix, value);
+                LocalMatrix = vs == null ? value : Transforms.Matrix4x4Factory.WorldToLocal(vs.WorldMatrix, value);
             }
         }
 
@@ -383,7 +383,7 @@ namespace SharpGLTF.Scenes
 
             var vs = Parent;
             var lm = GetLocalTransform(animationTrack, time).Matrix;
-            return vs == null ? lm : Transforms.AffineTransform.LocalToWorld(vs.GetWorldMatrix(animationTrack, time), lm);
+            return vs == null ? lm : Transforms.Matrix4x4Factory.LocalToWorld(vs.GetWorldMatrix(animationTrack, time), lm);
         }
 
         public Matrix4x4 GetInverseBindMatrix(Matrix4x4? meshWorldMatrix = null)

+ 1 - 0
tests/SharpGLTF.NUnit/NumericsAssert.cs

@@ -7,6 +7,7 @@ using NUnit.Framework;
 
 namespace SharpGLTF
 {
+    [System.Diagnostics.DebuggerStepThrough]
     public static class NumericsAssert
     {
         public static double UnitError(this Vector3 v) { return v.LengthError(1); }

+ 1 - 1
tests/SharpGLTF.Tests/SharpGLTF.Core.Tests.csproj

@@ -26,7 +26,7 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
   </ItemGroup>  
 
 </Project>

+ 55 - 1
tests/SharpGLTF.Tests/Transforms/InverseBindMatrixTest.cs

@@ -33,5 +33,59 @@ namespace SharpGLTF.Transforms
         }
 
 
-    }
+        [Test]
+        public void TestMatrixNormalization()
+        {
+            void testMatrix(Matrix4x4 m, float tolerance = 0)
+            {
+                var o = m;
+
+                Matrix4x4Factory.NormalizeMatrix(ref m);
+
+                NumericsAssert.AreEqual(o, m, tolerance);
+
+                Assert.IsTrue(Matrix4x4.Decompose(m, out _, out _, out _));
+                Assert.IsTrue(Matrix4x4.Invert(m, out _));
+            }
+
+            void testSkewed(Func<Matrix4x4, Matrix4x4> mf, float tolerance = 0)
+            {
+                var m = Matrix4x4.Identity;
+
+                var o = m = mf(m);
+
+                Assert.IsFalse(Matrix4x4.Decompose(m, out _, out _, out _));
+
+                Matrix4x4Factory.NormalizeMatrix(ref m);
+
+                NumericsAssert.AreEqual(o, m, tolerance);                
+
+                Assert.IsTrue(Matrix4x4.Decompose(m, out _, out _, out _));
+                Assert.IsTrue(Matrix4x4.Invert(m, out _));               
+            }
+            
+            testSkewed(m => { m.M12 += 0.34f; return m; }, 0.34f);
+            testSkewed(m => { m.M13 += 0.34f; return m; }, 0.34f);
+            testSkewed(m => { m.M21 += 0.34f; return m; }, 0.34f);
+            testSkewed(m => { m.M23 += 0.34f; return m; }, 0.34f);
+            testSkewed(m => { m.M31 += 0.34f; return m; }, 0.34f);
+            testSkewed(m => { m.M32 += 0.34f; return m; }, 0.34f);
+
+            testSkewed(m => { m.M12 += 0.1f; m.M23 -= 0.1f; m.M31 += 0.05f; return m; }, 0.20f);
+
+            // test normalization with uneven scaling
+
+            testMatrix(Matrix4x4.CreateScale(0.0001f) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3), 0.0001f);
+            testMatrix(Matrix4x4.CreateScale(1000) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3), 0.0001f);
+
+            var SxR = Matrix4x4.CreateScale(5, 1, 1) * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3);   // Decomposable
+            var RxS = Matrix4x4.CreateFromYawPitchRoll(1, 2, 3) * Matrix4x4.CreateScale(5, 1, 1);   // Not Decomposable            
+
+            Assert.IsTrue(Matrix4x4.Decompose(SxR, out _, out _, out _));
+            testMatrix(SxR, 0.0001f);
+
+            Assert.IsFalse(Matrix4x4.Decompose(RxS, out _, out _, out _));
+            testMatrix(RxS, 100);           
+        }
+    }    
 }

+ 1 - 1
tests/SharpGLTF.Toolkit.Tests/SharpGLTF.Toolkit.Tests.csproj

@@ -26,7 +26,7 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
   </ItemGroup>  
 
 </Project>