morphTargetManager.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 { EngineStore } from "../Engines/engineStore";
  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 _supportsUVs = false;
  23. private _vertexCount = 0;
  24. private _uniqueId = 0;
  25. private _tempInfluences = new Array<number>();
  26. /**
  27. * Gets or sets a boolean indicating if normals must be morphed
  28. */
  29. public enableNormalMorphing = true;
  30. /**
  31. * Gets or sets a boolean indicating if tangents must be morphed
  32. */
  33. public enableTangentMorphing = true;
  34. /**
  35. * Gets or sets a boolean indicating if UV must be morphed
  36. */
  37. public enableUVMorphing = true;
  38. /**
  39. * Creates a new MorphTargetManager
  40. * @param scene defines the current scene
  41. */
  42. public constructor(scene: Nullable<Scene> = null) {
  43. if (!scene) {
  44. scene = EngineStore.LastCreatedScene;
  45. }
  46. this._scene = scene;
  47. if (this._scene) {
  48. this._scene.morphTargetManagers.push(this);
  49. this._uniqueId = this._scene.getUniqueId();
  50. }
  51. }
  52. /**
  53. * Gets the unique ID of this manager
  54. */
  55. public get uniqueId(): number {
  56. return this._uniqueId;
  57. }
  58. /**
  59. * Gets the number of vertices handled by this manager
  60. */
  61. public get vertexCount(): number {
  62. return this._vertexCount;
  63. }
  64. /**
  65. * Gets a boolean indicating if this manager supports morphing of normals
  66. */
  67. public get supportsNormals(): boolean {
  68. return this._supportsNormals && this.enableNormalMorphing;
  69. }
  70. /**
  71. * Gets a boolean indicating if this manager supports morphing of tangents
  72. */
  73. public get supportsTangents(): boolean {
  74. return this._supportsTangents && this.enableTangentMorphing;
  75. }
  76. /**
  77. * Gets a boolean indicating if this manager supports morphing of texture coordinates
  78. */
  79. public get supportsUVs(): boolean {
  80. return this._supportsUVs && this.enableUVMorphing;
  81. }
  82. /**
  83. * Gets the number of targets stored in this manager
  84. */
  85. public get numTargets(): number {
  86. return this._targets.length;
  87. }
  88. /**
  89. * Gets the number of influencers (ie. the number of targets with influences > 0)
  90. */
  91. public get numInfluencers(): number {
  92. return this._activeTargets.length;
  93. }
  94. /**
  95. * Gets the list of influences (one per target)
  96. */
  97. public get influences(): Float32Array {
  98. return this._influences;
  99. }
  100. /**
  101. * Gets the active target at specified index. An active target is a target with an influence > 0
  102. * @param index defines the index to check
  103. * @returns the requested target
  104. */
  105. public getActiveTarget(index: number): MorphTarget {
  106. return this._activeTargets.data[index];
  107. }
  108. /**
  109. * Gets the target at specified index
  110. * @param index defines the index to check
  111. * @returns the requested target
  112. */
  113. public getTarget(index: number): MorphTarget {
  114. return this._targets[index];
  115. }
  116. /**
  117. * Add a new target to this manager
  118. * @param target defines the target to add
  119. */
  120. public addTarget(target: MorphTarget): void {
  121. this._targets.push(target);
  122. this._targetInfluenceChangedObservers.push(target.onInfluenceChanged.add((needUpdate) => {
  123. this._syncActiveTargets(needUpdate);
  124. }));
  125. this._targetDataLayoutChangedObservers.push(target._onDataLayoutChanged.add(() => {
  126. this._syncActiveTargets(true);
  127. }));
  128. this._syncActiveTargets(true);
  129. }
  130. /**
  131. * Removes a target from the manager
  132. * @param target defines the target to remove
  133. */
  134. public removeTarget(target: MorphTarget): void {
  135. var index = this._targets.indexOf(target);
  136. if (index >= 0) {
  137. this._targets.splice(index, 1);
  138. target.onInfluenceChanged.remove(this._targetInfluenceChangedObservers.splice(index, 1)[0]);
  139. target._onDataLayoutChanged.remove(this._targetDataLayoutChangedObservers.splice(index, 1)[0]);
  140. this._syncActiveTargets(true);
  141. }
  142. }
  143. /**
  144. * Clone the current manager
  145. * @returns a new MorphTargetManager
  146. */
  147. public clone(): MorphTargetManager {
  148. let copy = new MorphTargetManager(this._scene);
  149. for (var target of this._targets) {
  150. copy.addTarget(target.clone());
  151. }
  152. copy.enableNormalMorphing = this.enableNormalMorphing;
  153. copy.enableTangentMorphing = this.enableTangentMorphing;
  154. copy.enableUVMorphing = this.enableUVMorphing;
  155. return copy;
  156. }
  157. /**
  158. * Serializes the current manager into a Serialization object
  159. * @returns the serialized object
  160. */
  161. public serialize(): any {
  162. var serializationObject: any = {};
  163. serializationObject.id = this.uniqueId;
  164. serializationObject.targets = [];
  165. for (var target of this._targets) {
  166. serializationObject.targets.push(target.serialize());
  167. }
  168. return serializationObject;
  169. }
  170. private _syncActiveTargets(needUpdate: boolean): void {
  171. let influenceCount = 0;
  172. this._activeTargets.reset();
  173. this._supportsNormals = true;
  174. this._supportsTangents = true;
  175. this._supportsUVs = true;
  176. this._vertexCount = 0;
  177. for (var target of this._targets) {
  178. if (target.influence === 0) {
  179. continue;
  180. }
  181. this._activeTargets.push(target);
  182. this._tempInfluences[influenceCount++] = target.influence;
  183. this._supportsNormals = this._supportsNormals && target.hasNormals;
  184. this._supportsTangents = this._supportsTangents && target.hasTangents;
  185. this._supportsUVs = this._supportsUVs && target.hasUVs;
  186. const positions = target.getPositions();
  187. if (positions) {
  188. const vertexCount = positions.length / 3;
  189. if (this._vertexCount === 0) {
  190. this._vertexCount = vertexCount;
  191. }
  192. else if (this._vertexCount !== vertexCount) {
  193. Logger.Error("Incompatible target. Targets must all have the same vertices count.");
  194. return;
  195. }
  196. }
  197. }
  198. if (!this._influences || this._influences.length !== influenceCount) {
  199. this._influences = new Float32Array(influenceCount);
  200. }
  201. for (var index = 0; index < influenceCount; index++) {
  202. this._influences[index] = this._tempInfluences[index];
  203. }
  204. if (needUpdate) {
  205. this.synchronize();
  206. }
  207. }
  208. /**
  209. * Syncrhonize the targets with all the meshes using this morph target manager
  210. */
  211. public synchronize(): void {
  212. if (!this._scene) {
  213. return;
  214. }
  215. // Flag meshes as dirty to resync with the active targets
  216. for (var mesh of this._scene.meshes) {
  217. if ((<any>mesh).morphTargetManager === this) {
  218. (<Mesh>mesh)._syncGeometryWithMorphTargetManager();
  219. }
  220. }
  221. }
  222. // Statics
  223. /**
  224. * Creates a new MorphTargetManager from serialized data
  225. * @param serializationObject defines the serialized data
  226. * @param scene defines the hosting scene
  227. * @returns the new MorphTargetManager
  228. */
  229. public static Parse(serializationObject: any, scene: Scene): MorphTargetManager {
  230. var result = new MorphTargetManager(scene);
  231. result._uniqueId = serializationObject.id;
  232. for (var targetData of serializationObject.targets) {
  233. result.addTarget(MorphTarget.Parse(targetData));
  234. }
  235. return result;
  236. }
  237. }