skeletonViewer.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { Color3, Vector3, Matrix, Tmp } from "Math";
  2. import { Scene } from "scene";
  3. import { Nullable } from "types";
  4. import { LinesMesh } from "Mesh";
  5. import { Skeleton, Bone } from "Bones";
  6. import { AbstractMesh } from "Mesh";
  7. import { UtilityLayerRenderer } from "index";
  8. /**
  9. * Class used to render a debug view of a given skeleton
  10. * @see http://www.babylonjs-playground.com/#1BZJVJ#8
  11. */
  12. export class SkeletonViewer {
  13. /** Gets or sets the color used to render the skeleton */
  14. public color: Color3 = Color3.White();
  15. private _scene: Scene;
  16. private _debugLines = new Array<Array<Vector3>>();
  17. private _debugMesh: Nullable<LinesMesh>;
  18. private _isEnabled = false;
  19. private _renderFunction: () => void;
  20. public get debugMesh(): Nullable<LinesMesh> {
  21. return this._debugMesh;
  22. }
  23. /**
  24. * Creates a new SkeletonViewer
  25. * @param skeleton defines the skeleton to render
  26. * @param mesh defines the mesh attached to the skeleton
  27. * @param scene defines the hosting scene
  28. * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
  29. * @param renderingGroupId defines the rendering group id to use with the viewer
  30. * @param utilityLayerRenderer defines an optional utility layer to render the helper on
  31. */
  32. constructor(
  33. /** defines the skeleton to render */
  34. public skeleton: Skeleton,
  35. /** defines the mesh attached to the skeleton */
  36. public mesh: AbstractMesh,
  37. scene: Scene,
  38. /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default) */
  39. public autoUpdateBonesMatrices = true,
  40. /** defines the rendering group id to use with the viewer */
  41. public renderingGroupId = 1,
  42. /** defines an optional utility layer to render the helper on */
  43. public utilityLayerRenderer?: UtilityLayerRenderer
  44. ) {
  45. this._scene = scene;
  46. this.update();
  47. this._renderFunction = this.update.bind(this);
  48. }
  49. /** Gets or sets a boolean indicating if the viewer is enabled */
  50. public set isEnabled(value: boolean) {
  51. if (this._isEnabled === value) {
  52. return;
  53. }
  54. this._isEnabled = value;
  55. if (value) {
  56. this._scene.registerBeforeRender(this._renderFunction);
  57. } else {
  58. this._scene.unregisterBeforeRender(this._renderFunction);
  59. }
  60. }
  61. public get isEnabled(): boolean {
  62. return this._isEnabled;
  63. }
  64. private _getBonePosition(position: Vector3, bone: Bone, meshMat: Matrix, x = 0, y = 0, z = 0): void {
  65. var tmat = Tmp.Matrix[0];
  66. var parentBone = bone.getParent();
  67. tmat.copyFrom(bone.getLocalMatrix());
  68. if (x !== 0 || y !== 0 || z !== 0) {
  69. var tmat2 = Tmp.Matrix[1];
  70. Matrix.IdentityToRef(tmat2);
  71. tmat2.setTranslationFromFloats(x, y, z);
  72. tmat2.multiplyToRef(tmat, tmat);
  73. }
  74. if (parentBone) {
  75. tmat.multiplyToRef(parentBone.getAbsoluteTransform(), tmat);
  76. }
  77. tmat.multiplyToRef(meshMat, tmat);
  78. position.x = tmat.m[12];
  79. position.y = tmat.m[13];
  80. position.z = tmat.m[14];
  81. }
  82. private _getLinesForBonesWithLength(bones: Bone[], meshMat: Matrix): void {
  83. var len = bones.length;
  84. var meshPos = this.mesh.position;
  85. for (var i = 0; i < len; i++) {
  86. var bone = bones[i];
  87. var points = this._debugLines[i];
  88. if (!points) {
  89. points = [Vector3.Zero(), Vector3.Zero()];
  90. this._debugLines[i] = points;
  91. }
  92. this._getBonePosition(points[0], bone, meshMat);
  93. this._getBonePosition(points[1], bone, meshMat, 0, bone.length, 0);
  94. points[0].subtractInPlace(meshPos);
  95. points[1].subtractInPlace(meshPos);
  96. }
  97. }
  98. private _getLinesForBonesNoLength(bones: Bone[], meshMat: Matrix): void {
  99. var len = bones.length;
  100. var boneNum = 0;
  101. var meshPos = this.mesh.position;
  102. for (var i = len - 1; i >= 0; i--) {
  103. var childBone = bones[i];
  104. var parentBone = childBone.getParent();
  105. if (!parentBone) {
  106. continue;
  107. }
  108. var points = this._debugLines[boneNum];
  109. if (!points) {
  110. points = [Vector3.Zero(), Vector3.Zero()];
  111. this._debugLines[boneNum] = points;
  112. }
  113. childBone.getAbsolutePositionToRef(this.mesh, points[0]);
  114. parentBone.getAbsolutePositionToRef(this.mesh, points[1]);
  115. points[0].subtractInPlace(meshPos);
  116. points[1].subtractInPlace(meshPos);
  117. boneNum++;
  118. }
  119. }
  120. /** Update the viewer to sync with current skeleton state */
  121. public update() {
  122. if (this.autoUpdateBonesMatrices) {
  123. this.skeleton.computeAbsoluteTransforms();
  124. }
  125. if (this.skeleton.bones[0].length === undefined) {
  126. this._getLinesForBonesNoLength(this.skeleton.bones, this.mesh.getWorldMatrix());
  127. } else {
  128. this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
  129. }
  130. const targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
  131. if (!this._debugMesh) {
  132. this._debugMesh = MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
  133. this._debugMesh.renderingGroupId = this.renderingGroupId;
  134. } else {
  135. MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, targetScene);
  136. }
  137. this._debugMesh.position.copyFrom(this.mesh.position);
  138. this._debugMesh.color = this.color;
  139. }
  140. /** Release associated resources */
  141. public dispose() {
  142. if (this._debugMesh) {
  143. this.isEnabled = false;
  144. this._debugMesh.dispose();
  145. this._debugMesh = null;
  146. }
  147. }
  148. }