BabylonExporter.GLTFExporter.Skin.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using BabylonExport.Entities;
  2. using GLTFExport.Entities;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace Max2Babylon
  6. {
  7. partial class BabylonExporter
  8. {
  9. // Bones stored in BabylonSkeleton array are not assumed to be tree-ordered
  10. // Meaning, first element could be a leaf thus resulting in exporting all its ancestors before himself
  11. // Store bones already exported to prevent multiple exportation of same bone
  12. private Dictionary<BabylonBone, GLTFNode> alreadyExportedBones;
  13. private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode)
  14. {
  15. // Skin
  16. GLTFSkin gltfSkin = new GLTFSkin
  17. {
  18. name = babylonSkeleton.name
  19. };
  20. gltfSkin.index = gltf.SkinsList.Count;
  21. gltf.SkinsList.Add(gltfSkin);
  22. var bones = new List<BabylonBone>(babylonSkeleton.bones);
  23. // Compute and store world matrix of each bone
  24. var bonesWorldMatrices = new Dictionary<int, BabylonMatrix>();
  25. foreach (var babylonBone in babylonSkeleton.bones)
  26. {
  27. if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
  28. {
  29. BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones);
  30. bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
  31. }
  32. }
  33. // Buffer
  34. var buffer = GLTFBufferService.Instance.GetBuffer(gltf);
  35. // Accessor - InverseBindMatrices
  36. var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
  37. gltf,
  38. GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
  39. "accessorInverseBindMatrices",
  40. GLTFAccessor.ComponentType.FLOAT,
  41. GLTFAccessor.TypeEnum.MAT4
  42. );
  43. gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;
  44. // World matrix of the node
  45. var invNodeWorldMatrix = BabylonMatrix.Invert(_getNodeWorldMatrix(gltfNode)); // inverted
  46. var gltfJoints = new List<int>();
  47. alreadyExportedBones = new Dictionary<BabylonBone, GLTFNode>();
  48. foreach (var babylonBone in babylonSkeleton.bones)
  49. {
  50. // Export bone as a new node
  51. var gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones);
  52. gltfJoints.Add(gltfBoneNode.index);
  53. // Set this bone as skeleton if it is a root
  54. if (babylonBone.parentBoneIndex == -1)
  55. {
  56. gltfSkin.skeleton = gltfBoneNode.index;
  57. }
  58. // Compute inverseBindMatrice for this bone when attached to this node
  59. var boneLocalMatrix = new BabylonMatrix();
  60. boneLocalMatrix.m = babylonBone.matrix;
  61. BabylonMatrix boneWorldMatrix = null;
  62. if (babylonBone.parentBoneIndex == -1)
  63. {
  64. boneWorldMatrix = boneLocalMatrix;
  65. }
  66. else
  67. {
  68. var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
  69. // Remove scale of parent
  70. // This actually enable to take into account the scale of the bones, except for the root one
  71. parentWorldMatrix = _removeScale(parentWorldMatrix);
  72. boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
  73. }
  74. var inverseBindMatrices = BabylonMatrix.Invert(boneWorldMatrix * invNodeWorldMatrix);
  75. // Populate accessor
  76. List<float> matrix = new List<float>(inverseBindMatrices.m);
  77. matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
  78. accessorInverseBindMatrices.count++;
  79. }
  80. gltfSkin.joints = gltfJoints.ToArray();
  81. return gltfSkin;
  82. }
  83. private GLTFNode _exportBone(BabylonBone babylonBone, GLTF gltf, BabylonSkeleton babylonSkeleton, List<BabylonBone> bones)
  84. {
  85. if (alreadyExportedBones.ContainsKey(babylonBone))
  86. {
  87. return alreadyExportedBones[babylonBone];
  88. }
  89. // Node
  90. var gltfNode = new GLTFNode
  91. {
  92. name = babylonBone.name
  93. };
  94. gltfNode.index = gltf.NodesList.Count;
  95. gltf.NodesList.Add(gltfNode);
  96. alreadyExportedBones.Add(babylonBone, gltfNode);
  97. // Hierarchy
  98. if (babylonBone.parentBoneIndex >= 0)
  99. {
  100. var babylonParentBone = bones.Find(_babylonBone => _babylonBone.index == babylonBone.parentBoneIndex);
  101. var gltfParentNode = _exportBone(babylonParentBone, gltf, babylonSkeleton, bones);
  102. RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as child to " + gltfParentNode.name, 2);
  103. gltfParentNode.ChildrenList.Add(gltfNode.index);
  104. gltfNode.parent = gltfParentNode;
  105. }
  106. else
  107. {
  108. // It's a root node
  109. // Only root nodes are listed in a gltf scene
  110. RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.name + " as root node to scene", 2);
  111. gltf.scenes[0].NodesList.Add(gltfNode.index);
  112. }
  113. // Transform
  114. // Bones transform are exported through translation/rotation/scale rather than matrix
  115. // Because gltf node animation can only target TRS properties, not the matrix one
  116. // Create matrix from array
  117. var babylonMatrix = new BabylonMatrix();
  118. babylonMatrix.m = babylonBone.matrix;
  119. // Decompose matrix into TRS
  120. var translationBabylon = new BabylonVector3();
  121. var rotationQuatBabylon = new BabylonQuaternion();
  122. var scaleBabylon = new BabylonVector3();
  123. babylonMatrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon);
  124. // Store TRS values
  125. gltfNode.translation = translationBabylon.ToArray();
  126. gltfNode.rotation = rotationQuatBabylon.ToArray();
  127. gltfNode.scale = scaleBabylon.ToArray();
  128. // Animations
  129. ExportBoneAnimation(babylonBone, gltf, gltfNode);
  130. return gltfNode;
  131. }
  132. private BabylonMatrix _getNodeLocalMatrix(GLTFNode gltfNode)
  133. {
  134. return BabylonMatrix.Compose(
  135. BabylonVector3.FromArray(gltfNode.scale),
  136. BabylonQuaternion.FromArray(gltfNode.rotation),
  137. BabylonVector3.FromArray(gltfNode.translation)
  138. );
  139. }
  140. private BabylonMatrix _getNodeWorldMatrix(GLTFNode gltfNode)
  141. {
  142. if (gltfNode.parent == null)
  143. {
  144. return _getNodeLocalMatrix(gltfNode);
  145. }
  146. else
  147. {
  148. return _getNodeLocalMatrix(gltfNode) * _getNodeWorldMatrix(gltfNode.parent);
  149. }
  150. }
  151. private BabylonMatrix _getBoneWorldMatrix(BabylonBone babylonBone, List<BabylonBone> bones)
  152. {
  153. var boneLocalMatrix = new BabylonMatrix();
  154. boneLocalMatrix.m = babylonBone.matrix;
  155. if (babylonBone.parentBoneIndex == -1)
  156. {
  157. return boneLocalMatrix;
  158. }
  159. else
  160. {
  161. var parentBabylonBone = bones.Find(bone => bone.index == babylonBone.parentBoneIndex);
  162. var parentWorldMatrix = _getBoneWorldMatrix(parentBabylonBone, bones);
  163. return boneLocalMatrix * parentWorldMatrix;
  164. }
  165. }
  166. private BabylonMatrix _removeScale(BabylonMatrix boneWorldMatrix)
  167. {
  168. var translation = new BabylonVector3();
  169. var rotation = new BabylonQuaternion();
  170. var scale = new BabylonVector3();
  171. boneWorldMatrix.decompose(scale, rotation, translation);
  172. scale.X = 1;
  173. scale.Y = 1;
  174. scale.Z = 1;
  175. return BabylonMatrix.Compose(scale, rotation, translation);
  176. }
  177. }
  178. }