MeshTransforms.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Text;
  6. using TRANSFORM = System.Numerics.Matrix4x4;
  7. using V3 = System.Numerics.Vector3;
  8. using V4 = System.Numerics.Vector4;
  9. namespace SharpGLTF.Transforms
  10. {
  11. /// <summary>
  12. /// Interface for a mesh transform object
  13. /// </summary>
  14. public interface ITransform
  15. {
  16. /// <summary>
  17. /// Gets a value indicating whether the current <see cref="ITransform"/> will render visible geometry.
  18. /// </summary>
  19. bool Visible { get; }
  20. /// <summary>
  21. /// Gets a value indicating whether the triangles need to be flipped to render correctly.
  22. /// </summary>
  23. bool FlipFaces { get; }
  24. V3 TransformPosition(V3 position, V3[] morphTargets, (int, float)[] skinWeights);
  25. V3 TransformNormal(V3 normal, V3[] morphTargets, (int, float)[] skinWeights);
  26. V4 TransformTangent(V4 tangent, V3[] morphTargets, (int, float)[] skinWeights);
  27. V4 MorphColors(V4 color, V4[] morphTargets);
  28. }
  29. public abstract class MorphTransform
  30. {
  31. #region constructor
  32. protected MorphTransform()
  33. {
  34. Update(SparseWeight8.Create((0, 1)), false);
  35. }
  36. protected MorphTransform(SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
  37. {
  38. Update(morphWeights, useAbsoluteMorphTargets);
  39. }
  40. #endregion
  41. #region data
  42. /// <summary>
  43. /// Represents a sparse collection of weights where:
  44. /// - Index of value <see cref="_COMPLEMENT_INDEX"/> points to the Mesh master positions.
  45. /// - All other indices point to Mesh MorphTarget[index] positions.
  46. /// </summary>
  47. private SparseWeight8 _Weights;
  48. private const int _COMPLEMENT_INDEX = 65536;
  49. /// <summary>
  50. /// True if morph targets represent absolute values.
  51. /// False if morph targets represent values relative to master value.
  52. /// </summary>
  53. private bool _AbsoluteMorphTargets;
  54. #endregion
  55. #region API
  56. public void Update(SparseWeight8 morphWeights, bool useAbsoluteMorphTargets = false)
  57. {
  58. _AbsoluteMorphTargets = useAbsoluteMorphTargets;
  59. if (morphWeights.IsWeightless)
  60. {
  61. _Weights = SparseWeight8.Create((0, 1));
  62. return;
  63. }
  64. _Weights = morphWeights.GetNormalizedWithComplement(_COMPLEMENT_INDEX);
  65. }
  66. protected V3 MorphVectors(V3 value, V3[] morphTargets)
  67. {
  68. if (morphTargets == null) return value;
  69. if (_Weights.Index0 == _COMPLEMENT_INDEX && _Weights.Weight0 == 1) return value;
  70. var p = V3.Zero;
  71. if (_AbsoluteMorphTargets)
  72. {
  73. foreach (var pair in _Weights.GetNonZeroWeights())
  74. {
  75. var val = pair.Item1 == _COMPLEMENT_INDEX ? value : morphTargets[pair.Item1];
  76. p += val * pair.Item2;
  77. }
  78. }
  79. else
  80. {
  81. foreach (var pair in _Weights.GetNonZeroWeights())
  82. {
  83. var val = pair.Item1 == _COMPLEMENT_INDEX ? value : value + morphTargets[pair.Item1];
  84. p += val * pair.Item2;
  85. }
  86. }
  87. return p;
  88. }
  89. protected V4 MorphVectors(V4 value, V4[] morphTargets)
  90. {
  91. if (morphTargets == null) return value;
  92. if (_Weights.Index0 == _COMPLEMENT_INDEX && _Weights.Weight0 == 1) return value;
  93. var p = V4.Zero;
  94. if (_AbsoluteMorphTargets)
  95. {
  96. foreach (var pair in _Weights.GetNonZeroWeights())
  97. {
  98. var val = pair.Item1 == _COMPLEMENT_INDEX ? value : morphTargets[pair.Item1];
  99. p += val * pair.Item2;
  100. }
  101. }
  102. else
  103. {
  104. foreach (var pair in _Weights.GetNonZeroWeights())
  105. {
  106. var val = pair.Item1 == _COMPLEMENT_INDEX ? value : value + morphTargets[pair.Item1];
  107. p += val * pair.Item2;
  108. }
  109. }
  110. return p;
  111. }
  112. public V4 MorphColors(V4 color, V4[] morphTargets)
  113. {
  114. return MorphVectors(color, morphTargets);
  115. }
  116. #endregion
  117. }
  118. public class StaticTransform : MorphTransform, ITransform
  119. {
  120. #region constructor
  121. public StaticTransform(TRANSFORM xform, SparseWeight8 morphWeights, bool useAbsoluteMorphs)
  122. {
  123. Update(xform, morphWeights, useAbsoluteMorphs);
  124. }
  125. #endregion
  126. #region data
  127. private TRANSFORM _Transform;
  128. private Boolean _Visible;
  129. private Boolean _FlipFaces;
  130. #endregion
  131. #region properties
  132. public Boolean Visible => _Visible;
  133. public Boolean FlipFaces => _FlipFaces;
  134. #endregion
  135. #region API
  136. public void Update(TRANSFORM xform, SparseWeight8 morphWeights, bool useAbsoluteMorphs)
  137. {
  138. Update(morphWeights, useAbsoluteMorphs);
  139. _Transform = xform;
  140. // http://m-hikari.com/ija/ija-password-2009/ija-password5-8-2009/hajrizajIJA5-8-2009.pdf
  141. float determinant3x3 =
  142. +(xform.M13 * xform.M21 * xform.M32)
  143. + (xform.M11 * xform.M22 * xform.M33)
  144. + (xform.M12 * xform.M23 * xform.M31)
  145. - (xform.M12 * xform.M21 * xform.M33)
  146. - (xform.M13 * xform.M22 * xform.M31)
  147. - (xform.M11 * xform.M23 * xform.M32);
  148. _Visible = Math.Abs(determinant3x3) > float.Epsilon;
  149. _FlipFaces = determinant3x3 < 0;
  150. }
  151. public V3 TransformPosition(V3 position, V3[] morphTargets, (int, float)[] skinWeights)
  152. {
  153. position = MorphVectors(position, morphTargets);
  154. return V3.Transform(position, _Transform);
  155. }
  156. public V3 TransformNormal(V3 normal, V3[] morphTargets, (int, float)[] skinWeights)
  157. {
  158. normal = MorphVectors(normal, morphTargets);
  159. return V3.Normalize(V3.Transform(normal, _Transform));
  160. }
  161. public V4 TransformTangent(V4 tangent, V3[] morphTargets, (int, float)[] skinWeights)
  162. {
  163. var n = MorphVectors(new V3(tangent.X, tangent.Y, tangent.Z), morphTargets);
  164. n = V3.Normalize(V3.Transform(n, _Transform));
  165. return new V4(n, tangent.W);
  166. }
  167. #endregion
  168. }
  169. public class SkinTransform : MorphTransform, ITransform
  170. {
  171. #region constructor
  172. public SkinTransform(TRANSFORM[] invBindings, TRANSFORM[] worldXforms, SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
  173. {
  174. Update(invBindings, worldXforms, morphWeights, useAbsoluteMorphTargets);
  175. }
  176. #endregion
  177. #region data
  178. private TRANSFORM[] _JointTransforms;
  179. #endregion
  180. #region API
  181. public void Update(TRANSFORM[] invBindings, TRANSFORM[] worldXforms, SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
  182. {
  183. Guard.NotNull(invBindings, nameof(invBindings));
  184. Guard.NotNull(worldXforms, nameof(worldXforms));
  185. Guard.IsTrue(invBindings.Length == worldXforms.Length, nameof(worldXforms), $"{invBindings} and {worldXforms} length mismatch.");
  186. Update(morphWeights, useAbsoluteMorphTargets);
  187. if (_JointTransforms == null || _JointTransforms.Length != invBindings.Length) _JointTransforms = new TRANSFORM[invBindings.Length];
  188. for (int i = 0; i < _JointTransforms.Length; ++i)
  189. {
  190. _JointTransforms[i] = invBindings[i] * worldXforms[i];
  191. }
  192. }
  193. public bool Visible => true;
  194. public bool FlipFaces => false;
  195. public V3 TransformPosition(V3 localPosition, V3[] morphTargets, (int, float)[] skinWeights)
  196. {
  197. Guard.NotNull(skinWeights, nameof(skinWeights));
  198. localPosition = MorphVectors(localPosition, morphTargets);
  199. var worldPosition = V3.Zero;
  200. var wnrm = 1.0f / skinWeights.Sum(item => item.Item2);
  201. foreach (var jw in skinWeights)
  202. {
  203. worldPosition += V3.Transform(localPosition, _JointTransforms[jw.Item1]) * jw.Item2 * wnrm;
  204. }
  205. return worldPosition;
  206. }
  207. public V3 TransformNormal(V3 localNormal, V3[] morphTargets, (int, float)[] skinWeights)
  208. {
  209. Guard.NotNull(skinWeights, nameof(skinWeights));
  210. localNormal = MorphVectors(localNormal, morphTargets);
  211. var worldNormal = V3.Zero;
  212. foreach (var jw in skinWeights)
  213. {
  214. worldNormal += V3.TransformNormal(localNormal, _JointTransforms[jw.Item1]) * jw.Item2;
  215. }
  216. return V3.Normalize(localNormal);
  217. }
  218. public V4 TransformTangent(V4 localTangent, V3[] morphTargets, (int, float)[] skinWeights)
  219. {
  220. Guard.NotNull(skinWeights, nameof(skinWeights));
  221. var localTangentV = MorphVectors(new V3(localTangent.X, localTangent.Y, localTangent.Z), morphTargets);
  222. var worldTangent = V3.Zero;
  223. foreach (var jw in skinWeights)
  224. {
  225. worldTangent += V3.TransformNormal(localTangentV, _JointTransforms[jw.Item1]) * jw.Item2;
  226. }
  227. worldTangent = V3.Normalize(worldTangent);
  228. return new V4(worldTangent, localTangent.W);
  229. }
  230. #endregion
  231. #region helper utilities
  232. /// <summary>
  233. /// Calculates the inverse bind matrix to use for runtime skinning.
  234. /// </summary>
  235. /// <param name="meshWorldTransform">The world space <see cref="TRANSFORM"/> of the mesh at the time of binding (POSE).</param>
  236. /// <param name="jointWorldTransform">The world space <see cref="TRANSFORM"/> of the given bone joint at the time of binding (POSE).</param>
  237. /// <returns>A <see cref="TRANSFORM"/> representing the inverse bind transform.</returns>
  238. public static Matrix4x4 CalculateInverseBinding(Matrix4x4 meshWorldTransform, Matrix4x4 jointWorldTransform)
  239. {
  240. var xform = meshWorldTransform.Inverse();
  241. xform = jointWorldTransform * xform;
  242. return xform.Inverse();
  243. }
  244. #endregion
  245. }
  246. }