morphTargetManager.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import { Observer } from "Misc/observable";
  2. import { SmartArray } from "Misc/smartArray";
  3. import { Logger } from "Misc/logger";
  4. import { Nullable } from "types";
  5. import { Scene } from "scene";
  6. import { Engine } from "Engines/engine";
  7. import { Mesh } from "Meshes/mesh";
  8. import { MorphTarget } from "./morphTarget";
  9. /**
  10. * This class is used to deform meshes using morphing between different targets
  11. * @see http://doc.babylonjs.com/how_to/how_to_use_morphtargets
  12. */
  13. export class MorphTargetManager {
  14. private _targets = new Array<MorphTarget>();
  15. private _targetInfluenceChangedObservers = new Array<Nullable<Observer<boolean>>>();
  16. private _targetDataLayoutChangedObservers = new Array<Nullable<Observer<void>>>();
  17. private _activeTargets = new SmartArray<MorphTarget>(16);
  18. private _scene: Nullable<Scene>;
  19. private _influences: Float32Array;
  20. private _supportsNormals = false;
  21. private _supportsTangents = false;
  22. private _vertexCount = 0;
  23. private _uniqueId = 0;
  24. private _tempInfluences = new Array<number>();
  25. /**
  26. * Creates a new MorphTargetManager
  27. * @param scene defines the current scene
  28. */
  29. public constructor(scene: Nullable<Scene> = null) {
  30. if (!scene) {
  31. scene = Engine.LastCreatedScene;
  32. }
  33. this._scene = scene;
  34. if (this._scene) {
  35. this._scene.morphTargetManagers.push(this);
  36. this._uniqueId = this._scene.getUniqueId();
  37. }
  38. }
  39. /**
  40. * Gets the unique ID of this manager
  41. */
  42. public get uniqueId(): number {
  43. return this._uniqueId;
  44. }
  45. /**
  46. * Gets the number of vertices handled by this manager
  47. */
  48. public get vertexCount(): number {
  49. return this._vertexCount;
  50. }
  51. /**
  52. * Gets a boolean indicating if this manager supports morphing of normals
  53. */
  54. public get supportsNormals(): boolean {
  55. return this._supportsNormals;
  56. }
  57. /**
  58. * Gets a boolean indicating if this manager supports morphing of tangents
  59. */
  60. public get supportsTangents(): boolean {
  61. return this._supportsTangents;
  62. }
  63. /**
  64. * Gets the number of targets stored in this manager
  65. */
  66. public get numTargets(): number {
  67. return this._targets.length;
  68. }
  69. /**
  70. * Gets the number of influencers (ie. the number of targets with influences > 0)
  71. */
  72. public get numInfluencers(): number {
  73. return this._activeTargets.length;
  74. }
  75. /**
  76. * Gets the list of influences (one per target)
  77. */
  78. public get influences(): Float32Array {
  79. return this._influences;
  80. }
  81. /**
  82. * Gets the active target at specified index. An active target is a target with an influence > 0
  83. * @param index defines the index to check
  84. * @returns the requested target
  85. */
  86. public getActiveTarget(index: number): MorphTarget {
  87. return this._activeTargets.data[index];
  88. }
  89. /**
  90. * Gets the target at specified index
  91. * @param index defines the index to check
  92. * @returns the requested target
  93. */
  94. public getTarget(index: number): MorphTarget {
  95. return this._targets[index];
  96. }
  97. /**
  98. * Add a new target to this manager
  99. * @param target defines the target to add
  100. */
  101. public addTarget(target: MorphTarget): void {
  102. this._targets.push(target);
  103. this._targetInfluenceChangedObservers.push(target.onInfluenceChanged.add((needUpdate) => {
  104. this._syncActiveTargets(needUpdate);
  105. }));
  106. this._targetDataLayoutChangedObservers.push(target._onDataLayoutChanged.add(() => {
  107. this._syncActiveTargets(true);
  108. }));
  109. this._syncActiveTargets(true);
  110. }
  111. /**
  112. * Removes a target from the manager
  113. * @param target defines the target to remove
  114. */
  115. public removeTarget(target: MorphTarget): void {
  116. var index = this._targets.indexOf(target);
  117. if (index >= 0) {
  118. this._targets.splice(index, 1);
  119. target.onInfluenceChanged.remove(this._targetInfluenceChangedObservers.splice(index, 1)[0]);
  120. target._onDataLayoutChanged.remove(this._targetDataLayoutChangedObservers.splice(index, 1)[0]);
  121. this._syncActiveTargets(true);
  122. }
  123. }
  124. /**
  125. * Serializes the current manager into a Serialization object
  126. * @returns the serialized object
  127. */
  128. public serialize(): any {
  129. var serializationObject: any = {};
  130. serializationObject.id = this.uniqueId;
  131. serializationObject.targets = [];
  132. for (var target of this._targets) {
  133. serializationObject.targets.push(target.serialize());
  134. }
  135. return serializationObject;
  136. }
  137. private _syncActiveTargets(needUpdate: boolean): void {
  138. let influenceCount = 0;
  139. this._activeTargets.reset();
  140. this._supportsNormals = true;
  141. this._supportsTangents = true;
  142. this._vertexCount = 0;
  143. for (var target of this._targets) {
  144. if (target.influence === 0) {
  145. continue;
  146. }
  147. this._activeTargets.push(target);
  148. this._tempInfluences[influenceCount++] = target.influence;
  149. this._supportsNormals = this._supportsNormals && target.hasNormals;
  150. this._supportsTangents = this._supportsTangents && target.hasTangents;
  151. const positions = target.getPositions();
  152. if (positions) {
  153. const vertexCount = positions.length / 3;
  154. if (this._vertexCount === 0) {
  155. this._vertexCount = vertexCount;
  156. }
  157. else if (this._vertexCount !== vertexCount) {
  158. Logger.Error("Incompatible target. Targets must all have the same vertices count.");
  159. return;
  160. }
  161. }
  162. }
  163. if (!this._influences || this._influences.length !== influenceCount) {
  164. this._influences = new Float32Array(influenceCount);
  165. }
  166. for (var index = 0; index < influenceCount; index++) {
  167. this._influences[index] = this._tempInfluences[index];
  168. }
  169. if (needUpdate) {
  170. this.synchronize();
  171. }
  172. }
  173. /**
  174. * Syncrhonize the targets with all the meshes using this morph target manager
  175. */
  176. public synchronize(): void {
  177. if (!this._scene) {
  178. return;
  179. }
  180. // Flag meshes as dirty to resync with the active targets
  181. for (var mesh of this._scene.meshes) {
  182. if ((<any>mesh).morphTargetManager === this) {
  183. (<Mesh>mesh)._syncGeometryWithMorphTargetManager();
  184. }
  185. }
  186. }
  187. // Statics
  188. /**
  189. * Creates a new MorphTargetManager from serialized data
  190. * @param serializationObject defines the serialized data
  191. * @param scene defines the hosting scene
  192. * @returns the new MorphTargetManager
  193. */
  194. public static Parse(serializationObject: any, scene: Scene): MorphTargetManager {
  195. var result = new MorphTargetManager(scene);
  196. result._uniqueId = serializationObject.id;
  197. for (var targetData of serializationObject.targets) {
  198. result.addTarget(MorphTarget.Parse(targetData));
  199. }
  200. return result;
  201. }
  202. }