skeletonViewer.ts 6.8 KB

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