assetContainer.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. import { AbstractScene } from "./abstractScene";
  2. import { Scene } from "./scene";
  3. import { Mesh } from "./Meshes/mesh";
  4. import { TransformNode } from './Meshes/transformNode';
  5. import { Skeleton } from './Bones/skeleton';
  6. import { AnimationGroup } from './Animations/animationGroup';
  7. import { Animatable } from './Animations/animatable';
  8. import { AbstractMesh } from './Meshes/abstractMesh';
  9. import { MultiMaterial } from './Materials/multiMaterial';
  10. import { Material } from './Materials/material';
  11. import { Logger } from './Misc/logger';
  12. import { EngineStore } from './Engines/engineStore';
  13. import { Nullable } from './types';
  14. import { Node } from './node';
  15. /**
  16. * Set of assets to keep when moving a scene into an asset container.
  17. */
  18. export class KeepAssets extends AbstractScene { }
  19. /**
  20. * Class used to store the output of the AssetContainer.instantiateAllMeshesToScene function
  21. */
  22. export class InstantiatedEntries {
  23. /**
  24. * List of new root nodes (eg. nodes with no parent)
  25. */
  26. public rootNodes: TransformNode[] = [];
  27. /**
  28. * List of new skeletons
  29. */
  30. public skeletons: Skeleton[] = [];
  31. /**
  32. * List of new animation groups
  33. */
  34. public animationGroups: AnimationGroup[] = [];
  35. }
  36. /**
  37. * Container with a set of assets that can be added or removed from a scene.
  38. */
  39. export class AssetContainer extends AbstractScene {
  40. private _wasAddedToScene = false;
  41. /**
  42. * The scene the AssetContainer belongs to.
  43. */
  44. public scene: Scene;
  45. /**
  46. * Instantiates an AssetContainer.
  47. * @param scene The scene the AssetContainer belongs to.
  48. */
  49. constructor(scene: Scene) {
  50. super();
  51. this.scene = scene;
  52. this["sounds"] = [];
  53. this["effectLayers"] = [];
  54. this["layers"] = [];
  55. this["lensFlareSystems"] = [];
  56. this["proceduralTextures"] = [];
  57. this["reflectionProbes"] = [];
  58. scene.onDisposeObservable.add(() => {
  59. if (!this._wasAddedToScene) {
  60. this.dispose();
  61. }
  62. });
  63. }
  64. /**
  65. * Instantiate or clone all meshes and add the new ones to the scene.
  66. * Skeletons and animation groups will all be cloned
  67. * @param nameFunction defines an optional function used to get new names for clones
  68. * @param cloneMaterials defines an optional boolean that defines if materials must be cloned as well (false by default)
  69. * @returns a list of rootNodes, skeletons and aniamtion groups that were duplicated
  70. */
  71. public instantiateModelsToScene(nameFunction?: (sourceName: string) => string, cloneMaterials = false): InstantiatedEntries {
  72. let convertionMap: {[key: number]: number} = {};
  73. let storeMap: {[key: number]: any} = {};
  74. let result = new InstantiatedEntries();
  75. let alreadySwappedSkeletons: Skeleton[] = [];
  76. let alreadySwappedMaterials: Material[] = [];
  77. let options = {
  78. doNotInstantiate: true
  79. };
  80. let onClone = (source: TransformNode, clone: TransformNode) => {
  81. convertionMap[source.uniqueId] = clone.uniqueId;
  82. storeMap[clone.uniqueId] = clone;
  83. if (nameFunction) {
  84. clone.name = nameFunction(source.name);
  85. }
  86. if (clone instanceof Mesh) {
  87. let clonedMesh = clone as Mesh;
  88. if (clonedMesh.morphTargetManager) {
  89. let oldMorphTargetManager = (source as Mesh).morphTargetManager!;
  90. clonedMesh.morphTargetManager = oldMorphTargetManager.clone();
  91. for (var index = 0; index < oldMorphTargetManager.numTargets; index++) {
  92. let oldTarget = oldMorphTargetManager.getTarget(index);
  93. let newTarget = clonedMesh.morphTargetManager.getTarget(index);
  94. convertionMap[oldTarget.uniqueId] = newTarget.uniqueId;
  95. storeMap[newTarget.uniqueId] = newTarget;
  96. }
  97. }
  98. }
  99. };
  100. this.transformNodes.forEach((o) => {
  101. if (!o.parent) {
  102. let newOne = o.instantiateHierarchy(null, options, (source, clone) => {
  103. onClone(source, clone);
  104. });
  105. if (newOne) {
  106. result.rootNodes.push(newOne);
  107. }
  108. }
  109. });
  110. this.meshes.forEach((o) => {
  111. if (!o.parent) {
  112. let newOne = o.instantiateHierarchy(null, options, (source, clone) => {
  113. onClone(source, clone);
  114. if ((clone as any).material) {
  115. let mesh = clone as AbstractMesh;
  116. if (mesh.material) {
  117. if (cloneMaterials) {
  118. let sourceMaterial = (source as AbstractMesh).material!;
  119. if (alreadySwappedMaterials.indexOf(sourceMaterial) === -1) {
  120. let swap = sourceMaterial.clone(nameFunction ? nameFunction(sourceMaterial.name) : "Clone of " + sourceMaterial.name)!;
  121. alreadySwappedMaterials.push(sourceMaterial);
  122. convertionMap[sourceMaterial.uniqueId] = swap.uniqueId;
  123. storeMap[swap.uniqueId] = swap;
  124. if (sourceMaterial.getClassName() === "MultiMaterial") {
  125. let multi = sourceMaterial as MultiMaterial;
  126. for (var material of multi.subMaterials) {
  127. if (!material) {
  128. continue;
  129. }
  130. swap = material.clone(nameFunction ? nameFunction(material.name) : "Clone of " + material.name)!;
  131. alreadySwappedMaterials.push(material);
  132. convertionMap[material.uniqueId] = swap.uniqueId;
  133. storeMap[swap.uniqueId] = swap;
  134. }
  135. multi.subMaterials = multi.subMaterials.map((m) => m && storeMap[convertionMap[m.uniqueId]]);
  136. }
  137. }
  138. if (mesh.getClassName() !== "InstancedMesh") {
  139. mesh.material = storeMap[convertionMap[sourceMaterial.uniqueId]];
  140. }
  141. } else {
  142. if (mesh.material.getClassName() === "MultiMaterial") {
  143. if (this.scene.multiMaterials.indexOf(mesh.material as MultiMaterial) === -1) {
  144. this.scene.addMultiMaterial(mesh.material as MultiMaterial);
  145. }
  146. } else {
  147. if (this.scene.materials.indexOf(mesh.material) === -1) {
  148. this.scene.addMaterial(mesh.material);
  149. }
  150. }
  151. }
  152. }
  153. }
  154. });
  155. if (newOne) {
  156. result.rootNodes.push(newOne);
  157. }
  158. }
  159. });
  160. this.skeletons.forEach((s) => {
  161. let clone = s.clone(nameFunction ? nameFunction(s.name) : "Clone of " + s.name);
  162. if (s.overrideMesh) {
  163. clone.overrideMesh = storeMap[convertionMap[s.overrideMesh.uniqueId]];
  164. }
  165. for (var m of this.meshes) {
  166. if (m.skeleton === s && !m.isAnInstance) {
  167. let copy = storeMap[convertionMap[m.uniqueId]];
  168. (copy as Mesh).skeleton = clone;
  169. if (alreadySwappedSkeletons.indexOf(clone) !== -1) {
  170. continue;
  171. }
  172. alreadySwappedSkeletons.push(clone);
  173. // Check if bones are mesh linked
  174. for (var bone of clone.bones) {
  175. if (bone._linkedTransformNode) {
  176. bone._linkedTransformNode = storeMap[convertionMap[bone._linkedTransformNode.uniqueId]];
  177. }
  178. }
  179. }
  180. }
  181. result.skeletons.push(clone);
  182. });
  183. this.animationGroups.forEach((o) => {
  184. let clone = o.clone(o.name, (oldTarget) => {
  185. let newTarget = storeMap[convertionMap[oldTarget.uniqueId]];
  186. return newTarget || oldTarget;
  187. });
  188. result.animationGroups.push(clone);
  189. });
  190. return result;
  191. }
  192. /**
  193. * Adds all the assets from the container to the scene.
  194. */
  195. public addAllToScene() {
  196. this._wasAddedToScene = true;
  197. this.cameras.forEach((o) => {
  198. this.scene.addCamera(o);
  199. });
  200. this.lights.forEach((o) => {
  201. this.scene.addLight(o);
  202. });
  203. this.meshes.forEach((o) => {
  204. this.scene.addMesh(o);
  205. });
  206. this.skeletons.forEach((o) => {
  207. this.scene.addSkeleton(o);
  208. });
  209. this.animations.forEach((o) => {
  210. this.scene.addAnimation(o);
  211. });
  212. this.animationGroups.forEach((o) => {
  213. this.scene.addAnimationGroup(o);
  214. });
  215. this.multiMaterials.forEach((o) => {
  216. this.scene.addMultiMaterial(o);
  217. });
  218. this.materials.forEach((o) => {
  219. this.scene.addMaterial(o);
  220. });
  221. this.morphTargetManagers.forEach((o) => {
  222. this.scene.addMorphTargetManager(o);
  223. });
  224. this.geometries.forEach((o) => {
  225. this.scene.addGeometry(o);
  226. });
  227. this.transformNodes.forEach((o) => {
  228. this.scene.addTransformNode(o);
  229. });
  230. this.actionManagers.forEach((o) => {
  231. this.scene.addActionManager(o);
  232. });
  233. this.textures.forEach((o) => {
  234. this.scene.addTexture(o);
  235. });
  236. this.reflectionProbes.forEach((o) => {
  237. this.scene.addReflectionProbe(o);
  238. });
  239. if (this.environmentTexture) {
  240. this.scene.environmentTexture = this.environmentTexture;
  241. }
  242. for (let component of this.scene._serializableComponents) {
  243. component.addFromContainer(this);
  244. }
  245. }
  246. /**
  247. * Removes all the assets in the container from the scene
  248. */
  249. public removeAllFromScene() {
  250. this._wasAddedToScene = false;
  251. this.cameras.forEach((o) => {
  252. this.scene.removeCamera(o);
  253. });
  254. this.lights.forEach((o) => {
  255. this.scene.removeLight(o);
  256. });
  257. this.meshes.forEach((o) => {
  258. this.scene.removeMesh(o);
  259. });
  260. this.skeletons.forEach((o) => {
  261. this.scene.removeSkeleton(o);
  262. });
  263. this.animations.forEach((o) => {
  264. this.scene.removeAnimation(o);
  265. });
  266. this.animationGroups.forEach((o) => {
  267. this.scene.removeAnimationGroup(o);
  268. });
  269. this.multiMaterials.forEach((o) => {
  270. this.scene.removeMultiMaterial(o);
  271. });
  272. this.materials.forEach((o) => {
  273. this.scene.removeMaterial(o);
  274. });
  275. this.morphTargetManagers.forEach((o) => {
  276. this.scene.removeMorphTargetManager(o);
  277. });
  278. this.geometries.forEach((o) => {
  279. this.scene.removeGeometry(o);
  280. });
  281. this.transformNodes.forEach((o) => {
  282. this.scene.removeTransformNode(o);
  283. });
  284. this.actionManagers.forEach((o) => {
  285. this.scene.removeActionManager(o);
  286. });
  287. this.textures.forEach((o) => {
  288. this.scene.removeTexture(o);
  289. });
  290. this.reflectionProbes.forEach((o) => {
  291. this.scene.removeReflectionProbe(o);
  292. });
  293. if (this.environmentTexture === this.scene.environmentTexture) {
  294. this.scene.environmentTexture = null;
  295. }
  296. for (let component of this.scene._serializableComponents) {
  297. component.removeFromContainer(this);
  298. }
  299. }
  300. /**
  301. * Disposes all the assets in the container
  302. */
  303. public dispose() {
  304. this.cameras.forEach((o) => {
  305. o.dispose();
  306. });
  307. this.cameras = [];
  308. this.lights.forEach((o) => {
  309. o.dispose();
  310. });
  311. this.lights = [];
  312. this.meshes.forEach((o) => {
  313. o.dispose();
  314. });
  315. this.meshes = [];
  316. this.skeletons.forEach((o) => {
  317. o.dispose();
  318. });
  319. this.skeletons = [];
  320. this.animationGroups.forEach((o) => {
  321. o.dispose();
  322. });
  323. this.animationGroups = [];
  324. this.multiMaterials.forEach((o) => {
  325. o.dispose();
  326. });
  327. this.multiMaterials = [];
  328. this.materials.forEach((o) => {
  329. o.dispose();
  330. });
  331. this.materials = [];
  332. this.geometries.forEach((o) => {
  333. o.dispose();
  334. });
  335. this.geometries = [];
  336. this.transformNodes.forEach((o) => {
  337. o.dispose();
  338. });
  339. this.transformNodes = [];
  340. this.actionManagers.forEach((o) => {
  341. o.dispose();
  342. });
  343. this.actionManagers = [];
  344. this.textures.forEach((o) => {
  345. o.dispose();
  346. });
  347. this.textures = [];
  348. this.reflectionProbes.forEach((o) => {
  349. o.dispose();
  350. });
  351. this.reflectionProbes = [];
  352. if (this.environmentTexture) {
  353. this.environmentTexture.dispose();
  354. this.environmentTexture = null;
  355. }
  356. for (let component of this.scene._serializableComponents) {
  357. component.removeFromContainer(this, true);
  358. }
  359. }
  360. private _moveAssets<T>(sourceAssets: T[], targetAssets: T[], keepAssets: T[]): void {
  361. if (!sourceAssets) {
  362. return;
  363. }
  364. for (let asset of sourceAssets) {
  365. let move = true;
  366. if (keepAssets) {
  367. for (let keepAsset of keepAssets) {
  368. if (asset === keepAsset) {
  369. move = false;
  370. break;
  371. }
  372. }
  373. }
  374. if (move) {
  375. targetAssets.push(asset);
  376. }
  377. }
  378. }
  379. /**
  380. * Removes all the assets contained in the scene and adds them to the container.
  381. * @param keepAssets Set of assets to keep in the scene. (default: empty)
  382. */
  383. public moveAllFromScene(keepAssets?: KeepAssets): void {
  384. this._wasAddedToScene = false;
  385. if (keepAssets === undefined) {
  386. keepAssets = new KeepAssets();
  387. }
  388. for (let key in this) {
  389. if (this.hasOwnProperty(key)) {
  390. (<any>this)[key] = (<any>this)[key] || (key === "environmentTexture" ? null : []);
  391. this._moveAssets((<any>this.scene)[key], (<any>this)[key], (<any>keepAssets)[key]);
  392. }
  393. }
  394. this.environmentTexture = this.scene.environmentTexture;
  395. this.removeAllFromScene();
  396. }
  397. /**
  398. * Adds all meshes in the asset container to a root mesh that can be used to position all the contained meshes. The root mesh is then added to the front of the meshes in the assetContainer.
  399. * @returns the root mesh
  400. */
  401. public createRootMesh() {
  402. var rootMesh = new Mesh("assetContainerRootMesh", this.scene);
  403. this.meshes.forEach((m) => {
  404. if (!m.parent) {
  405. rootMesh.addChild(m);
  406. }
  407. });
  408. this.meshes.unshift(rootMesh);
  409. return rootMesh;
  410. }
  411. /**
  412. * Merge animations (direct and animation groups) from this asset container into a scene
  413. * @param scene is the instance of BABYLON.Scene to append to (default: last created scene)
  414. * @param animatables set of animatables to retarget to a node from the scene
  415. * @param targetConverter defines a function used to convert animation targets from the asset container to the scene (default: search node by name)
  416. * @returns an array of the new AnimationGroup added to the scene (empty array if none)
  417. */
  418. public mergeAnimationsTo(scene: Nullable<Scene> = EngineStore.LastCreatedScene, animatables: Animatable[], targetConverter: Nullable<(target: any) => Nullable<Node>> = null): AnimationGroup[] {
  419. if (!scene) {
  420. Logger.Error("No scene available to merge animations to");
  421. return [];
  422. }
  423. let _targetConverter = targetConverter ? targetConverter : (target: any) => {
  424. let node = null;
  425. const targetProperty = target.animations.length ? target.animations[0].targetProperty : "";
  426. /*
  427. BabylonJS adds special naming to targets that are children of nodes.
  428. This name attempts to remove that special naming to get the parent nodes name in case the target
  429. can't be found in the node tree
  430. Ex: Torso_primitive0 likely points to a Mesh primitive. We take away primitive0 and are left with "Torso" which is the name
  431. of the primitive's parent.
  432. */
  433. const name = target.name.split(".").join("").split("_primitive")[0];
  434. switch (targetProperty) {
  435. case "position":
  436. case "rotationQuaternion":
  437. node = scene.getTransformNodeByName(target.name) || scene.getTransformNodeByName(name);
  438. break;
  439. case "influence":
  440. node = scene.getMorphTargetByName(target.name) || scene.getMorphTargetByName(name);
  441. break;
  442. default:
  443. node = scene.getNodeByName(target.name) || scene.getNodeByName(name);
  444. }
  445. return node;
  446. };
  447. // Copy new node animations
  448. let nodesInAC = this.getNodes();
  449. nodesInAC.forEach((nodeInAC) => {
  450. let nodeInScene = _targetConverter(nodeInAC);
  451. if (nodeInScene !== null) {
  452. // Remove old animations with same target property as a new one
  453. for (let animationInAC of nodeInAC.animations) {
  454. // Doing treatment on an array for safety measure
  455. let animationsWithSameProperty = nodeInScene.animations.filter((animationInScene) => {
  456. return animationInScene.targetProperty === animationInAC.targetProperty;
  457. });
  458. for (let animationWithSameProperty of animationsWithSameProperty) {
  459. const index = nodeInScene.animations.indexOf(animationWithSameProperty, 0);
  460. if (index > -1) {
  461. nodeInScene.animations.splice(index, 1);
  462. }
  463. }
  464. }
  465. // Append new animations
  466. nodeInScene.animations = nodeInScene.animations.concat(nodeInAC.animations);
  467. }
  468. });
  469. let newAnimationGroups = new Array<AnimationGroup>();
  470. // Copy new animation groups
  471. this.animationGroups.slice().forEach((animationGroupInAC) => {
  472. // Clone the animation group and all its animatables
  473. newAnimationGroups.push(animationGroupInAC.clone(animationGroupInAC.name, _targetConverter));
  474. // Remove animatables related to the asset container
  475. animationGroupInAC.animatables.forEach((animatable) => {
  476. animatable.stop();
  477. });
  478. });
  479. // Retarget animatables
  480. animatables.forEach((animatable) => {
  481. let target = _targetConverter(animatable.target);
  482. if (target) {
  483. // Clone the animatable and retarget it
  484. scene.beginAnimation(target, animatable.fromFrame, animatable.toFrame, animatable.loopAnimation, animatable.speedRatio, animatable.onAnimationEnd ? animatable.onAnimationEnd : undefined, undefined, true, undefined, animatable.onAnimationLoop ? animatable.onAnimationLoop : undefined);
  485. // Stop animation for the target in the asset container
  486. scene.stopAnimation(animatable.target);
  487. }
  488. });
  489. return newAnimationGroups;
  490. }
  491. }