skeletonViewer.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. import { Vector3, Matrix, TmpVectors, Quaternion } 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 { Mesh } from "../Meshes/mesh";
  9. import { LinesMesh } from "../Meshes/linesMesh";
  10. import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
  11. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  12. import { Material } from '../Materials/material';
  13. import { StandardMaterial } from '../Materials/standardMaterial';
  14. import { ShaderMaterial } from '../Materials/shaderMaterial';
  15. import { DynamicTexture } from '../Materials/Textures/dynamicTexture';
  16. import { VertexBuffer } from '../Meshes/buffer';
  17. import { Effect } from '../Materials/effect';
  18. import { ISkeletonViewerOptions, IBoneWeightShaderOptions, ISkeletonMapShaderOptions, ISkeletonMapShaderColorMapKnot } from './ISkeletonViewer';
  19. import { Observer } from '../Misc/observable';
  20. import { SphereBuilder } from '../Meshes/Builders/sphereBuilder';
  21. import { ShapeBuilder } from '../Meshes/Builders/shapeBuilder';
  22. /**
  23. * Class used to render a debug view of a given skeleton
  24. * @see http://www.babylonjs-playground.com/#1BZJVJ#8
  25. */
  26. export class SkeletonViewer {
  27. /** public Display constants BABYLON.SkeletonViewer.DISPLAY_LINES */
  28. public static readonly DISPLAY_LINES = 0;
  29. /** public Display constants BABYLON.SkeletonViewer.DISPLAY_SPHERES */
  30. public static readonly DISPLAY_SPHERES = 1;
  31. /** public Display constants BABYLON.SkeletonViewer.DISPLAY_SPHERE_AND_SPURS */
  32. public static readonly DISPLAY_SPHERE_AND_SPURS = 2;
  33. /** public static method to create a BoneWeight Shader
  34. * @param options The constructor options
  35. * @param scene The scene that the shader is scoped to
  36. * @returns The created ShaderMaterial
  37. * @see http://www.babylonjs-playground.com/#1BZJVJ#395
  38. */
  39. static CreateBoneWeightShader(options: IBoneWeightShaderOptions, scene: Scene): ShaderMaterial {
  40. let skeleton: Skeleton = options.skeleton;
  41. let colorBase: Color3 = options.colorBase ?? Color3.Black();
  42. let colorZero: Color3 = options.colorZero ?? Color3.Blue();
  43. let colorQuarter: Color3 = options.colorQuarter ?? Color3.Green();
  44. let colorHalf: Color3 = options.colorHalf ?? Color3.Yellow();
  45. let colorFull: Color3 = options.colorFull ?? Color3.Red();
  46. let targetBoneIndex: number = options.targetBoneIndex ?? 0;
  47. Effect.ShadersStore['boneWeights:' + skeleton.name + "VertexShader"] =
  48. `precision highp float;
  49. attribute vec3 position;
  50. attribute vec2 uv;
  51. uniform mat4 view;
  52. uniform mat4 projection;
  53. uniform mat4 worldViewProjection;
  54. #include<bonesDeclaration>
  55. #include<instancesDeclaration>
  56. varying vec3 vColor;
  57. uniform vec3 colorBase;
  58. uniform vec3 colorZero;
  59. uniform vec3 colorQuarter;
  60. uniform vec3 colorHalf;
  61. uniform vec3 colorFull;
  62. uniform float targetBoneIndex;
  63. void main() {
  64. vec3 positionUpdated = position;
  65. #include<instancesVertex>
  66. #include<bonesVertex>
  67. vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0);
  68. vec3 color = colorBase;
  69. float totalWeight = 0.;
  70. if(matricesIndices[0] == targetBoneIndex && matricesWeights[0] > 0.){
  71. totalWeight += matricesWeights[0];
  72. }
  73. if(matricesIndices[1] == targetBoneIndex && matricesWeights[1] > 0.){
  74. totalWeight += matricesWeights[1];
  75. }
  76. if(matricesIndices[2] == targetBoneIndex && matricesWeights[2] > 0.){
  77. totalWeight += matricesWeights[2];
  78. }
  79. if(matricesIndices[3] == targetBoneIndex && matricesWeights[3] > 0.){
  80. totalWeight += matricesWeights[3];
  81. }
  82. color = mix(color, colorZero, smoothstep(0., 0.25, totalWeight));
  83. color = mix(color, colorQuarter, smoothstep(0.25, 0.5, totalWeight));
  84. color = mix(color, colorHalf, smoothstep(0.5, 0.75, totalWeight));
  85. color = mix(color, colorFull, smoothstep(0.75, 1.0, totalWeight));
  86. vColor = color;
  87. gl_Position = projection * view * worldPos;
  88. }`;
  89. Effect.ShadersStore['boneWeights:' + skeleton.name + "FragmentShader"] =
  90. `
  91. precision highp float;
  92. varying vec3 vPosition;
  93. varying vec3 vColor;
  94. void main() {
  95. vec4 color = vec4(vColor, 1.0);
  96. gl_FragColor = color;
  97. }
  98. `;
  99. let shader: ShaderMaterial = new ShaderMaterial('boneWeight:' + skeleton.name, scene,
  100. {
  101. vertex: 'boneWeights:' + skeleton.name,
  102. fragment: 'boneWeights:' + skeleton.name
  103. },
  104. {
  105. attributes: ['position', 'normal'],
  106. uniforms: [
  107. 'world', 'worldView', 'worldViewProjection', 'view', 'projection', 'viewProjection',
  108. 'colorBase', 'colorZero', 'colorQuarter', 'colorHalf', 'colorFull', 'targetBoneIndex'
  109. ]
  110. });
  111. shader.setColor3('colorBase', colorBase);
  112. shader.setColor3('colorZero', colorZero);
  113. shader.setColor3('colorQuarter', colorQuarter);
  114. shader.setColor3('colorHalf', colorHalf);
  115. shader.setColor3('colorFull', colorFull);
  116. shader.setFloat('targetBoneIndex', targetBoneIndex);
  117. shader.getClassName = (): string => {
  118. return "BoneWeightShader";
  119. };
  120. shader.transparencyMode = Material.MATERIAL_OPAQUE;
  121. return shader;
  122. }
  123. /** public static method to create a BoneWeight Shader
  124. * @param options The constructor options
  125. * @param scene The scene that the shader is scoped to
  126. * @returns The created ShaderMaterial
  127. */
  128. static CreateSkeletonMapShader(options: ISkeletonMapShaderOptions, scene: Scene) {
  129. let skeleton: Skeleton = options.skeleton;
  130. let colorMap: ISkeletonMapShaderColorMapKnot[] = options.colorMap ?? [
  131. {
  132. color: new Color3(1, 0.38, 0.18),
  133. location : 0
  134. },
  135. {
  136. color: new Color3(.59, 0.18, 1.00),
  137. location : 0.2
  138. },
  139. {
  140. color: new Color3(0.59, 1, 0.18),
  141. location : 0.4
  142. },
  143. {
  144. color: new Color3(1, 0.87, 0.17),
  145. location : 0.6
  146. },
  147. {
  148. color: new Color3(1, 0.17, 0.42),
  149. location : 0.8
  150. },
  151. {
  152. color: new Color3(0.17, 0.68, 1.0),
  153. location : 1.0
  154. }
  155. ];
  156. let bufferWidth: number = skeleton.bones.length + 1;
  157. let colorMapBuffer: number[] = SkeletonViewer._CreateBoneMapColorBuffer(bufferWidth, colorMap, scene);
  158. let shader = new ShaderMaterial('boneWeights:' + skeleton.name, scene,
  159. {
  160. vertexSource:
  161. `precision highp float;
  162. attribute vec3 position;
  163. attribute vec2 uv;
  164. uniform mat4 view;
  165. uniform mat4 projection;
  166. uniform mat4 worldViewProjection;
  167. uniform float colorMap[` + ((skeleton.bones.length) * 4) + `];
  168. #include<bonesDeclaration>
  169. #include<instancesDeclaration>
  170. varying vec3 vColor;
  171. void main() {
  172. vec3 positionUpdated = position;
  173. #include<instancesVertex>
  174. #include<bonesVertex>
  175. vec3 color = vec3(0.);
  176. bool first = true;
  177. for (int i = 0; i < 4; i++) {
  178. int boneIdx = int(matricesIndices[i]);
  179. float boneWgt = matricesWeights[i];
  180. vec3 c = vec3(colorMap[boneIdx * 4 + 0], colorMap[boneIdx * 4 + 1], colorMap[boneIdx * 4 + 2]);
  181. if (boneWgt > 0.) {
  182. if (first) {
  183. first = false;
  184. color = c;
  185. } else {
  186. color = mix(color, c, boneWgt);
  187. }
  188. }
  189. }
  190. vColor = color;
  191. vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0);
  192. gl_Position = projection * view * worldPos;
  193. }`,
  194. fragmentSource:
  195. `
  196. precision highp float;
  197. varying vec3 vColor;
  198. void main() {
  199. vec4 color = vec4( vColor, 1.0 );
  200. gl_FragColor = color;
  201. }
  202. `
  203. },
  204. {
  205. attributes: ['position', 'normal'],
  206. uniforms: [
  207. 'world', 'worldView', 'worldViewProjection', 'view', 'projection', 'viewProjection',
  208. 'colorMap'
  209. ]
  210. });
  211. shader.setFloats('colorMap', colorMapBuffer);
  212. shader.getClassName = (): string => {
  213. return "SkeletonMapShader";
  214. };
  215. shader.transparencyMode = Material.MATERIAL_OPAQUE;
  216. return shader;
  217. }
  218. /** private static method to create a BoneWeight Shader
  219. * @param size The size of the buffer to create (usually the bone count)
  220. * @param colorMap The gradient data to generate
  221. * @param scene The scene that the shader is scoped to
  222. * @returns an Array of floats from the color gradient values
  223. */
  224. private static _CreateBoneMapColorBuffer(size: number, colorMap: ISkeletonMapShaderColorMapKnot[], scene: Scene) {
  225. let tempGrad = new DynamicTexture('temp', {width: size, height: 1}, scene, false);
  226. let ctx = tempGrad.getContext();
  227. let grad = ctx.createLinearGradient(0, 0, size, 0);
  228. colorMap.forEach((stop) => {
  229. grad.addColorStop(stop.location, stop.color.toHexString());
  230. });
  231. ctx.fillStyle = grad;
  232. ctx.fillRect(0, 0, size, 1);
  233. tempGrad.update();
  234. let buffer: number[] = [];
  235. let data: Uint8ClampedArray = ctx.getImageData(0, 0, size, 1).data;
  236. let rUnit = 1 / 255;
  237. for (let i = 0; i < data.length; i++) {
  238. buffer.push(data[i] * rUnit);
  239. }
  240. tempGrad.dispose();
  241. return buffer;
  242. }
  243. /** If SkeletonViewer scene scope. */
  244. private _scene : Scene;
  245. /** Gets or sets the color used to render the skeleton */
  246. public color: Color3 = Color3.White();
  247. /** Array of the points of the skeleton fo the line view. */
  248. private _debugLines = new Array<Array<Vector3>>();
  249. /** The SkeletonViewers Mesh. */
  250. private _debugMesh: Nullable<LinesMesh>;
  251. /** If SkeletonViewer is enabled. */
  252. private _isEnabled = false;
  253. /** If SkeletonViewer is ready. */
  254. private _ready : boolean;
  255. /** SkeletonViewer render observable. */
  256. private _obs: Nullable<Observer<Scene>> = null;
  257. /** The Utility Layer to render the gizmos in. */
  258. private _utilityLayer: Nullable<UtilityLayerRenderer>;
  259. private _boneIndices: Set<number>;
  260. /** Gets the Scene. */
  261. get scene(): Scene {
  262. return this._scene;
  263. }
  264. /** Gets the utilityLayer. */
  265. get utilityLayer(): Nullable<UtilityLayerRenderer> {
  266. return this._utilityLayer;
  267. }
  268. /** Checks Ready Status. */
  269. get isReady(): Boolean {
  270. return this._ready;
  271. }
  272. /** Sets Ready Status. */
  273. set ready(value: boolean) {
  274. this._ready = value;
  275. }
  276. /** Gets the debugMesh */
  277. get debugMesh(): Nullable<AbstractMesh> | Nullable<LinesMesh> {
  278. return this._debugMesh;
  279. }
  280. /** Sets the debugMesh */
  281. set debugMesh(value: Nullable<AbstractMesh> | Nullable<LinesMesh>) {
  282. this._debugMesh = (value as any);
  283. }
  284. /** Gets the material */
  285. get material(): StandardMaterial {
  286. return this.material;
  287. }
  288. /** Sets the material */
  289. set material(value: StandardMaterial) {
  290. this.material = value;
  291. }
  292. /** Gets the material */
  293. get displayMode(): number {
  294. return this.options.displayMode || SkeletonViewer.DISPLAY_LINES;
  295. }
  296. /** Sets the material */
  297. set displayMode(value: number) {
  298. if (value > SkeletonViewer.DISPLAY_SPHERE_AND_SPURS) {
  299. value = SkeletonViewer.DISPLAY_LINES;
  300. }
  301. this.options.displayMode = value;
  302. }
  303. /**
  304. * Creates a new SkeletonViewer
  305. * @param skeleton defines the skeleton to render
  306. * @param mesh defines the mesh attached to the skeleton
  307. * @param scene defines the hosting scene
  308. * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
  309. * @param renderingGroupId defines the rendering group id to use with the viewer
  310. * @param options All of the extra constructor options for the SkeletonViewer
  311. */
  312. constructor(
  313. /** defines the skeleton to render */
  314. public skeleton: Skeleton,
  315. /** defines the mesh attached to the skeleton */
  316. public mesh: AbstractMesh,
  317. /** The Scene scope*/
  318. scene: Scene,
  319. /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default) */
  320. public autoUpdateBonesMatrices: boolean = true,
  321. /** defines the rendering group id to use with the viewer */
  322. public renderingGroupId: number = 3,
  323. /** is the options for the viewer */
  324. public options: Partial<ISkeletonViewerOptions> = {}
  325. ) {
  326. this._scene = scene;
  327. this._ready = false;
  328. //Defaults
  329. options.pauseAnimations = options.pauseAnimations ?? true;
  330. options.currentStateIsRestPose = options.currentStateIsRestPose ?? false;
  331. options.returnToRest = (options.currentStateIsRestPose) ? false : options.returnToRest ?? (options.currentStateIsRestPose) ? false : true;
  332. options.displayMode = options.displayMode ?? SkeletonViewer.DISPLAY_LINES;
  333. options.displayOptions = options.displayOptions ?? {};
  334. options.displayOptions.midStep = options.displayOptions.midStep ?? 0.235;
  335. options.displayOptions.midStepFactor = options.displayOptions.midStepFactor ?? 0.155;
  336. options.displayOptions.sphereBaseSize = options.displayOptions.sphereBaseSize ?? 0.15;
  337. options.displayOptions.sphereScaleUnit = options.displayOptions.sphereScaleUnit ?? 2;
  338. options.displayOptions.sphereFactor = options.displayOptions.sphereFactor ?? 0.865;
  339. options.computeBonesUsingShaders = options.computeBonesUsingShaders ?? true;
  340. options.useAllBones = options.useAllBones ?? true;
  341. const initialMeshBoneIndices = mesh.getVerticesData(VertexBuffer.MatricesIndicesKind);
  342. const initialMeshBoneWeights = mesh.getVerticesData(VertexBuffer.MatricesWeightsKind);
  343. this._boneIndices = new Set();
  344. if (!options.useAllBones) {
  345. if (initialMeshBoneIndices && initialMeshBoneWeights) {
  346. for (let i = 0; i < initialMeshBoneIndices.length; ++i) {
  347. const index = initialMeshBoneIndices[i], weight = initialMeshBoneWeights[i];
  348. if (weight !== 0) {
  349. this._boneIndices.add(index);
  350. }
  351. }
  352. }
  353. }
  354. /* Create Utility Layer */
  355. this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
  356. this._utilityLayer.pickUtilitySceneFirst = false;
  357. this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
  358. let displayMode = this.options.displayMode || 0;
  359. if (displayMode > SkeletonViewer.DISPLAY_SPHERE_AND_SPURS) {
  360. displayMode = SkeletonViewer.DISPLAY_LINES;
  361. }
  362. this.displayMode = displayMode;
  363. //Prep the Systems
  364. this.update();
  365. this._bindObs();
  366. }
  367. /** The Dynamic bindings for the update functions */
  368. private _bindObs(): void {
  369. switch (this.displayMode){
  370. case SkeletonViewer.DISPLAY_LINES: {
  371. this._obs = this.scene.onBeforeRenderObservable.add(() => {
  372. this._displayLinesUpdate();
  373. });
  374. break;
  375. }
  376. }
  377. }
  378. /** Update the viewer to sync with current skeleton state, only used to manually update. */
  379. public update(): void {
  380. switch (this.displayMode){
  381. case SkeletonViewer.DISPLAY_LINES: {
  382. this._displayLinesUpdate();
  383. break;
  384. }
  385. case SkeletonViewer.DISPLAY_SPHERES: {
  386. this._buildSpheresAndSpurs(true);
  387. break;
  388. }
  389. case SkeletonViewer.DISPLAY_SPHERE_AND_SPURS: {
  390. this._buildSpheresAndSpurs(false);
  391. break;
  392. }
  393. }
  394. }
  395. /** Gets or sets a boolean indicating if the viewer is enabled */
  396. public set isEnabled(value: boolean) {
  397. if (this.isEnabled === value) {
  398. return;
  399. }
  400. this._isEnabled = value;
  401. if (this.debugMesh) {
  402. this.debugMesh.setEnabled(value);
  403. }
  404. if (value && !this._obs) {
  405. this._bindObs();
  406. } else if (!value && this._obs) {
  407. this.scene.onBeforeRenderObservable.remove(this._obs);
  408. this._obs = null;
  409. }
  410. }
  411. public get isEnabled(): boolean {
  412. return this._isEnabled;
  413. }
  414. private _getBonePosition(position: Vector3, bone: Bone, meshMat: Matrix, x = 0, y = 0, z = 0): void {
  415. var tmat = TmpVectors.Matrix[0];
  416. var parentBone = bone.getParent();
  417. tmat.copyFrom(bone.getLocalMatrix());
  418. if (x !== 0 || y !== 0 || z !== 0) {
  419. var tmat2 = TmpVectors.Matrix[1];
  420. Matrix.IdentityToRef(tmat2);
  421. tmat2.setTranslationFromFloats(x, y, z);
  422. tmat2.multiplyToRef(tmat, tmat);
  423. }
  424. if (parentBone) {
  425. tmat.multiplyToRef(parentBone.getAbsoluteTransform(), tmat);
  426. }
  427. tmat.multiplyToRef(meshMat, tmat);
  428. position.x = tmat.m[12];
  429. position.y = tmat.m[13];
  430. position.z = tmat.m[14];
  431. }
  432. private _getLinesForBonesWithLength(bones: Bone[], meshMat: Matrix): void {
  433. var len = bones.length;
  434. let mesh = this.mesh._effectiveMesh;
  435. var meshPos = mesh.position;
  436. let idx = 0;
  437. for (var i = 0; i < len; i++) {
  438. var bone = bones[i];
  439. var points = this._debugLines[idx];
  440. if (bone._index === -1 || (!this._boneIndices.has(bone.getIndex()) && !this.options.useAllBones)) {
  441. continue;
  442. }
  443. if (!points) {
  444. points = [Vector3.Zero(), Vector3.Zero()];
  445. this._debugLines[idx] = points;
  446. }
  447. this._getBonePosition(points[0], bone, meshMat);
  448. this._getBonePosition(points[1], bone, meshMat, 0, bone.length, 0);
  449. points[0].subtractInPlace(meshPos);
  450. points[1].subtractInPlace(meshPos);
  451. idx++;
  452. }
  453. }
  454. private _getLinesForBonesNoLength(bones: Bone[]): void {
  455. var len = bones.length;
  456. var boneNum = 0;
  457. let mesh = this.mesh._effectiveMesh;
  458. var meshPos = mesh.position;
  459. for (var i = len - 1; i >= 0; i--) {
  460. var childBone = bones[i];
  461. var parentBone = childBone.getParent();
  462. if (!parentBone || (!this._boneIndices.has(childBone.getIndex()) && !this.options.useAllBones)) {
  463. continue;
  464. }
  465. var points = this._debugLines[boneNum];
  466. if (!points) {
  467. points = [Vector3.Zero(), Vector3.Zero()];
  468. this._debugLines[boneNum] = points;
  469. }
  470. childBone.getAbsolutePositionToRef(mesh, points[0]);
  471. parentBone.getAbsolutePositionToRef(mesh, points[1]);
  472. points[0].subtractInPlace(meshPos);
  473. points[1].subtractInPlace(meshPos);
  474. boneNum++;
  475. }
  476. }
  477. /** function to revert the mesh and scene back to the initial state. */
  478. private _revert(animationState: boolean): void {
  479. if (this.options.pauseAnimations) {
  480. this.scene.animationsEnabled = animationState;
  481. }
  482. }
  483. /** function to build and bind sphere joint points and spur bone representations. */
  484. private _buildSpheresAndSpurs(spheresOnly = true): void {
  485. if (this._debugMesh) {
  486. this._debugMesh.dispose();
  487. this._debugMesh = null;
  488. this.ready = false;
  489. }
  490. this._ready = false;
  491. let scene = this.scene;
  492. let bones: Bone[] = this.skeleton.bones;
  493. let spheres: Array<[Mesh, Bone]> = [];
  494. let spurs: Mesh[] = [];
  495. const animationState = scene.animationsEnabled;
  496. try {
  497. if (this.options.pauseAnimations) {
  498. scene.animationsEnabled = false;
  499. }
  500. if (this.options.currentStateIsRestPose) {
  501. let _s = Vector3.Zero();
  502. let _r = Quaternion.Identity();
  503. let _t = Vector3.Zero();
  504. this.skeleton.bones.forEach((b) => {
  505. b.getLocalMatrix().decompose(_s, _r, _t);
  506. b.setRestPose(Matrix.Compose(_s, _r, _t));
  507. });
  508. }else {
  509. if (this.options.returnToRest) {
  510. this.skeleton.returnToRest();
  511. }
  512. }
  513. if (this.autoUpdateBonesMatrices) {
  514. this.skeleton.computeAbsoluteTransforms();
  515. }
  516. let longestBoneLength = Number.NEGATIVE_INFINITY;
  517. let getAbsoluteRestPose = function(bone: Nullable<Bone>, matrix: Matrix) {
  518. if (bone === null || bone._index === -1) {
  519. matrix.copyFrom(Matrix.Identity());
  520. return;
  521. }
  522. getAbsoluteRestPose(bone.getParent(), matrix);
  523. bone.getBindPose().multiplyToRef(matrix, matrix);
  524. return;
  525. };
  526. let displayOptions = this.options.displayOptions || {};
  527. for (let i = 0; i < bones.length; i++) {
  528. let bone = bones[i];
  529. if (bone._index === -1 || (!this._boneIndices.has(bone.getIndex()) && !this.options.useAllBones)) {
  530. continue;
  531. }
  532. let boneAbsoluteRestTransform = new Matrix();
  533. getAbsoluteRestPose(bone, boneAbsoluteRestTransform);
  534. let anchorPoint = new Vector3();
  535. boneAbsoluteRestTransform.decompose(undefined, undefined, anchorPoint);
  536. bone.children.forEach((bc, i) => {
  537. let childAbsoluteRestTransform : Matrix = new Matrix();
  538. bc.getRestPose().multiplyToRef(boneAbsoluteRestTransform, childAbsoluteRestTransform);
  539. let childPoint = new Vector3();
  540. childAbsoluteRestTransform.decompose(undefined, undefined, childPoint);
  541. let distanceFromParent = Vector3.Distance(anchorPoint, childPoint);
  542. if (distanceFromParent > longestBoneLength) {
  543. longestBoneLength = distanceFromParent;
  544. }
  545. if (spheresOnly) {
  546. return;
  547. }
  548. let dir = childPoint.clone().subtract(anchorPoint.clone());
  549. let h = dir.length();
  550. let up = dir.normalize().scale(h);
  551. let midStep = displayOptions.midStep || 0.165;
  552. let midStepFactor = displayOptions.midStepFactor || 0.215;
  553. let up0 = up.scale(midStep);
  554. let spur = ShapeBuilder.ExtrudeShapeCustom('skeletonViewer',
  555. {
  556. shape: [
  557. new Vector3(1, -1, 0),
  558. new Vector3(1, 1, 0),
  559. new Vector3(-1, 1, 0),
  560. new Vector3(-1, -1, 0),
  561. new Vector3(1, -1, 0)
  562. ],
  563. path: [ Vector3.Zero(), up0, up ],
  564. scaleFunction:
  565. (i: number) => {
  566. switch (i){
  567. case 0:
  568. case 2:
  569. return 0;
  570. case 1:
  571. return h * midStepFactor;
  572. }
  573. return 0;
  574. },
  575. sideOrientation: Mesh.DEFAULTSIDE,
  576. updatable: false
  577. }, scene);
  578. spur.convertToFlatShadedMesh();
  579. let numVertices = spur.getTotalVertices();
  580. let mwk: number[] = [], mik: number[] = [];
  581. for (let i = 0; i < numVertices; i++) {
  582. mwk.push(1, 0, 0, 0);
  583. mik.push(bone.getIndex(), 0, 0, 0);
  584. }
  585. spur.position = anchorPoint.clone();
  586. spur.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
  587. spur.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
  588. spurs.push(spur);
  589. });
  590. let sphereBaseSize = displayOptions.sphereBaseSize || 0.2;
  591. let sphere = SphereBuilder.CreateSphere('skeletonViewer', {
  592. segments: 6,
  593. diameter: sphereBaseSize,
  594. updatable: true
  595. }, scene);
  596. const numVertices = sphere.getTotalVertices();
  597. let mwk: number[] = [], mik: number[] = [];
  598. for (let i = 0; i < numVertices; i++) {
  599. mwk.push(1, 0, 0, 0);
  600. mik.push(bone.getIndex(), 0, 0, 0);
  601. }
  602. sphere.setVerticesData(VertexBuffer.MatricesWeightsKind, mwk, false);
  603. sphere.setVerticesData(VertexBuffer.MatricesIndicesKind, mik, false);
  604. sphere.position = anchorPoint.clone();
  605. spheres.push([sphere, bone]);
  606. }
  607. let sphereScaleUnit = displayOptions.sphereScaleUnit || 2;
  608. let sphereFactor = displayOptions.sphereFactor || 0.85;
  609. const meshes = [];
  610. for (let i = 0; i < spheres.length; i++) {
  611. let [sphere, bone] = spheres[i];
  612. let scale = 1 / (sphereScaleUnit / longestBoneLength);
  613. let _stepsOut = 0;
  614. let _b = bone;
  615. while ((_b.getParent()) && (_b.getParent() as Bone).getIndex() !== -1) {
  616. _stepsOut++;
  617. _b = (_b.getParent() as Bone);
  618. }
  619. sphere.scaling.scaleInPlace(scale * Math.pow(sphereFactor, _stepsOut));
  620. meshes.push(sphere);
  621. }
  622. this.debugMesh = Mesh.MergeMeshes(meshes.concat(spurs), true, true);
  623. if (this.debugMesh) {
  624. this.debugMesh.renderingGroupId = this.renderingGroupId;
  625. this.debugMesh.skeleton = this.skeleton;
  626. this.debugMesh.parent = this.mesh;
  627. this.debugMesh.computeBonesUsingShaders = this.options.computeBonesUsingShaders ?? true;
  628. this.debugMesh.alwaysSelectAsActiveMesh = true;
  629. }
  630. this._revert(animationState);
  631. this.ready = true;
  632. } catch (err) {
  633. console.error(err);
  634. this._revert(animationState);
  635. this.dispose();
  636. }
  637. }
  638. /** Update the viewer to sync with current skeleton state, only used for the line display. */
  639. private _displayLinesUpdate(): void {
  640. if (!this._utilityLayer) {
  641. return;
  642. }
  643. if (this.autoUpdateBonesMatrices) {
  644. this.skeleton.computeAbsoluteTransforms();
  645. }
  646. let mesh = this.mesh._effectiveMesh;
  647. if (this.skeleton.bones[0].length === undefined) {
  648. this._getLinesForBonesNoLength(this.skeleton.bones);
  649. } else {
  650. this._getLinesForBonesWithLength(this.skeleton.bones, mesh.getWorldMatrix());
  651. }
  652. const targetScene = this._utilityLayer.utilityLayerScene;
  653. if (targetScene) {
  654. if (!this._debugMesh) {
  655. this._debugMesh = LinesBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
  656. this._debugMesh.renderingGroupId = this.renderingGroupId;
  657. } else {
  658. LinesBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, targetScene);
  659. }
  660. this._debugMesh.position.copyFrom(this.mesh.position);
  661. this._debugMesh.color = this.color;
  662. }
  663. }
  664. /** Changes the displayMode of the skeleton viewer
  665. * @param mode The displayMode numerical value
  666. */
  667. public changeDisplayMode(mode: number): void {
  668. let wasEnabled = (this.isEnabled) ? true : false;
  669. if (this.displayMode !== mode) {
  670. this.isEnabled = false;
  671. if (this._debugMesh) {
  672. this._debugMesh.dispose();
  673. this._debugMesh = null;
  674. this.ready = false;
  675. }
  676. this.displayMode = mode;
  677. this.update();
  678. this._bindObs();
  679. this.isEnabled = wasEnabled;
  680. }
  681. }
  682. /** Changes the displayMode of the skeleton viewer
  683. * @param option String of the option name
  684. * @param value The numerical option value
  685. */
  686. public changeDisplayOptions(option: string, value: number): void {
  687. let wasEnabled = (this.isEnabled) ? true : false;
  688. (this.options.displayOptions as any)[option] = value;
  689. this.isEnabled = false;
  690. if (this._debugMesh) {
  691. this._debugMesh.dispose();
  692. this._debugMesh = null;
  693. this.ready = false;
  694. }
  695. this.update();
  696. this._bindObs();
  697. this.isEnabled = wasEnabled;
  698. }
  699. /** Release associated resources */
  700. public dispose(): void {
  701. this.isEnabled = false;
  702. if (this._debugMesh) {
  703. this._debugMesh.dispose();
  704. this._debugMesh = null;
  705. }
  706. if (this._utilityLayer) {
  707. this._utilityLayer.dispose();
  708. this._utilityLayer = null;
  709. }
  710. this.ready = false;
  711. }
  712. }