skeletonViewer.ts 6.9 KB

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