| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Numerics;
- using System.Text;
- using TRANSFORM = System.Numerics.Matrix4x4;
- using V3 = System.Numerics.Vector3;
- using V4 = System.Numerics.Vector4;
- namespace SharpGLTF.Transforms
- {
- /// <summary>
- /// Interface for a mesh transform object
- /// </summary>
- public interface ITransform
- {
- /// <summary>
- /// Gets a value indicating whether the current <see cref="ITransform"/> will render visible geometry.
- /// </summary>
- bool Visible { get; }
- /// <summary>
- /// Gets a value indicating whether the triangles need to be flipped to render correctly.
- /// </summary>
- bool FlipFaces { get; }
- 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);
- V4 MorphColors(V4 color, V4[] morphTargets);
- }
- public abstract class MorphTransform
- {
- #region constructor
- protected MorphTransform()
- {
- Update(SparseWeight8.Create((0, 1)), false);
- }
- protected MorphTransform(SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
- {
- Update(morphWeights, useAbsoluteMorphTargets);
- }
- #endregion
- #region data
- /// <summary>
- /// Represents a sparse collection of weights where:
- /// - Index of value <see cref="_COMPLEMENT_INDEX"/> points to the Mesh master positions.
- /// - All other indices point to Mesh MorphTarget[index] positions.
- /// </summary>
- private SparseWeight8 _Weights;
- private const int _COMPLEMENT_INDEX = 65536;
- /// <summary>
- /// True if morph targets represent absolute values.
- /// False if morph targets represent values relative to master value.
- /// </summary>
- private bool _AbsoluteMorphTargets;
- #endregion
- #region API
- public void Update(SparseWeight8 morphWeights, bool useAbsoluteMorphTargets = false)
- {
- _AbsoluteMorphTargets = useAbsoluteMorphTargets;
- if (morphWeights.IsWeightless)
- {
- _Weights = SparseWeight8.Create((0, 1));
- return;
- }
- _Weights = morphWeights.GetNormalizedWithComplement(_COMPLEMENT_INDEX);
- }
- protected V3 MorphVectors(V3 value, V3[] morphTargets)
- {
- if (morphTargets == null) return value;
- if (_Weights.Index0 == _COMPLEMENT_INDEX && _Weights.Weight0 == 1) return value;
- var p = V3.Zero;
- if (_AbsoluteMorphTargets)
- {
- foreach (var pair in _Weights.GetNonZeroWeights())
- {
- var val = pair.Item1 == _COMPLEMENT_INDEX ? value : morphTargets[pair.Item1];
- p += val * pair.Item2;
- }
- }
- else
- {
- foreach (var pair in _Weights.GetNonZeroWeights())
- {
- var val = pair.Item1 == _COMPLEMENT_INDEX ? value : value + morphTargets[pair.Item1];
- p += val * pair.Item2;
- }
- }
- return p;
- }
- protected V4 MorphVectors(V4 value, V4[] morphTargets)
- {
- if (morphTargets == null) return value;
- if (_Weights.Index0 == _COMPLEMENT_INDEX && _Weights.Weight0 == 1) return value;
- var p = V4.Zero;
- if (_AbsoluteMorphTargets)
- {
- foreach (var pair in _Weights.GetNonZeroWeights())
- {
- var val = pair.Item1 == _COMPLEMENT_INDEX ? value : morphTargets[pair.Item1];
- p += val * pair.Item2;
- }
- }
- else
- {
- foreach (var pair in _Weights.GetNonZeroWeights())
- {
- var val = pair.Item1 == _COMPLEMENT_INDEX ? value : value + morphTargets[pair.Item1];
- p += val * pair.Item2;
- }
- }
- return p;
- }
- public V4 MorphColors(V4 color, V4[] morphTargets)
- {
- return MorphVectors(color, morphTargets);
- }
- #endregion
- }
- public class StaticTransform : MorphTransform, ITransform
- {
- #region constructor
- public StaticTransform(TRANSFORM xform, SparseWeight8 morphWeights, bool useAbsoluteMorphs)
- {
- Update(xform, morphWeights, useAbsoluteMorphs);
- }
- #endregion
- #region data
- private TRANSFORM _Transform;
- private Boolean _Visible;
- private Boolean _FlipFaces;
- #endregion
- #region properties
- public Boolean Visible => _Visible;
- public Boolean FlipFaces => _FlipFaces;
- #endregion
- #region API
- public void Update(TRANSFORM xform, SparseWeight8 morphWeights, bool useAbsoluteMorphs)
- {
- Update(morphWeights, useAbsoluteMorphs);
- _Transform = xform;
- // http://m-hikari.com/ija/ija-password-2009/ija-password5-8-2009/hajrizajIJA5-8-2009.pdf
- float determinant3x3 =
- +(xform.M13 * xform.M21 * xform.M32)
- + (xform.M11 * xform.M22 * xform.M33)
- + (xform.M12 * xform.M23 * xform.M31)
- - (xform.M12 * xform.M21 * xform.M33)
- - (xform.M13 * xform.M22 * xform.M31)
- - (xform.M11 * xform.M23 * xform.M32);
- _Visible = Math.Abs(determinant3x3) > float.Epsilon;
- _FlipFaces = determinant3x3 < 0;
- }
- public V3 TransformPosition(V3 position, V3[] morphTargets, (int, float)[] skinWeights)
- {
- position = MorphVectors(position, morphTargets);
- return V3.Transform(position, _Transform);
- }
- public V3 TransformNormal(V3 normal, V3[] morphTargets, (int, float)[] skinWeights)
- {
- normal = MorphVectors(normal, morphTargets);
- return V3.Normalize(V3.Transform(normal, _Transform));
- }
- public V4 TransformTangent(V4 tangent, V3[] morphTargets, (int, float)[] skinWeights)
- {
- var n = MorphVectors(new V3(tangent.X, tangent.Y, tangent.Z), morphTargets);
- n = V3.Normalize(V3.Transform(n, _Transform));
- return new V4(n, tangent.W);
- }
- #endregion
- }
- public class SkinTransform : MorphTransform, ITransform
- {
- #region constructor
- public SkinTransform(TRANSFORM[] invBindings, TRANSFORM[] worldXforms, SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
- {
- Update(invBindings, worldXforms, morphWeights, useAbsoluteMorphTargets);
- }
- #endregion
- #region data
- private TRANSFORM[] _JointTransforms;
- #endregion
- #region API
- public void Update(TRANSFORM[] invBindings, TRANSFORM[] worldXforms, SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
- {
- Guard.NotNull(invBindings, nameof(invBindings));
- Guard.NotNull(worldXforms, nameof(worldXforms));
- Guard.IsTrue(invBindings.Length == worldXforms.Length, nameof(worldXforms), $"{invBindings} and {worldXforms} length mismatch.");
- Update(morphWeights, useAbsoluteMorphTargets);
- if (_JointTransforms == null || _JointTransforms.Length != invBindings.Length) _JointTransforms = new TRANSFORM[invBindings.Length];
- for (int i = 0; i < _JointTransforms.Length; ++i)
- {
- _JointTransforms[i] = invBindings[i] * worldXforms[i];
- }
- }
- public bool Visible => true;
- public bool FlipFaces => false;
- public V3 TransformPosition(V3 localPosition, V3[] morphTargets, (int, float)[] skinWeights)
- {
- Guard.NotNull(skinWeights, nameof(skinWeights));
- localPosition = MorphVectors(localPosition, morphTargets);
- var worldPosition = V3.Zero;
- var wnrm = 1.0f / skinWeights.Sum(item => item.Item2);
- foreach (var jw in skinWeights)
- {
- worldPosition += V3.Transform(localPosition, _JointTransforms[jw.Item1]) * jw.Item2 * wnrm;
- }
- return worldPosition;
- }
- public V3 TransformNormal(V3 localNormal, V3[] morphTargets, (int, float)[] skinWeights)
- {
- Guard.NotNull(skinWeights, nameof(skinWeights));
- localNormal = MorphVectors(localNormal, morphTargets);
- var worldNormal = V3.Zero;
- foreach (var jw in skinWeights)
- {
- worldNormal += V3.TransformNormal(localNormal, _JointTransforms[jw.Item1]) * jw.Item2;
- }
- return V3.Normalize(localNormal);
- }
- public V4 TransformTangent(V4 localTangent, V3[] morphTargets, (int, float)[] skinWeights)
- {
- Guard.NotNull(skinWeights, nameof(skinWeights));
- var localTangentV = MorphVectors(new V3(localTangent.X, localTangent.Y, localTangent.Z), morphTargets);
- var worldTangent = V3.Zero;
- foreach (var jw in skinWeights)
- {
- worldTangent += V3.TransformNormal(localTangentV, _JointTransforms[jw.Item1]) * jw.Item2;
- }
- worldTangent = V3.Normalize(worldTangent);
- return new V4(worldTangent, localTangent.W);
- }
- #endregion
- #region helper utilities
- /// <summary>
- /// Calculates the inverse bind matrix to use for runtime skinning.
- /// </summary>
- /// <param name="meshWorldTransform">The world space <see cref="TRANSFORM"/> of the mesh at the time of binding (POSE).</param>
- /// <param name="jointWorldTransform">The world space <see cref="TRANSFORM"/> of the given bone joint at the time of binding (POSE).</param>
- /// <returns>A <see cref="TRANSFORM"/> representing the inverse bind transform.</returns>
- public static Matrix4x4 CalculateInverseBinding(Matrix4x4 meshWorldTransform, Matrix4x4 jointWorldTransform)
- {
- var xform = meshWorldTransform.Inverse();
- xform = jointWorldTransform * xform;
- return xform.Inverse();
- }
- #endregion
- }
- }
|