assetContainer.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. import { AbstractScene } from "./abstractScene.js";
  2. import { Mesh } from "./Meshes/mesh.js";
  3. import { TransformNode } from "./Meshes/transformNode.js";
  4. import { AbstractMesh } from "./Meshes/abstractMesh.js";
  5. import { Logger } from "./Misc/logger.js";
  6. import { EngineStore } from "./Engines/engineStore.js";
  7. import { InstancedMesh } from "./Meshes/instancedMesh.js";
  8. import { Light } from "./Lights/light.js";
  9. import { Camera } from "./Cameras/camera.js";
  10. import { Tools } from "./Misc/tools.js";
  11. /**
  12. * Set of assets to keep when moving a scene into an asset container.
  13. */
  14. export class KeepAssets extends AbstractScene {
  15. }
  16. /**
  17. * Class used to store the output of the AssetContainer.instantiateAllMeshesToScene function
  18. */
  19. export class InstantiatedEntries {
  20. constructor() {
  21. /**
  22. * List of new root nodes (eg. nodes with no parent)
  23. */
  24. this.rootNodes = [];
  25. /**
  26. * List of new skeletons
  27. */
  28. this.skeletons = [];
  29. /**
  30. * List of new animation groups
  31. */
  32. this.animationGroups = [];
  33. }
  34. /**
  35. * Disposes the instantiated entries from the scene
  36. */
  37. dispose() {
  38. this.rootNodes.slice(0).forEach((o) => {
  39. o.dispose();
  40. });
  41. this.rootNodes.length = 0;
  42. this.skeletons.slice(0).forEach((o) => {
  43. o.dispose();
  44. });
  45. this.skeletons.length = 0;
  46. this.animationGroups.slice(0).forEach((o) => {
  47. o.dispose();
  48. });
  49. this.animationGroups.length = 0;
  50. }
  51. }
  52. /**
  53. * Container with a set of assets that can be added or removed from a scene.
  54. */
  55. export class AssetContainer extends AbstractScene {
  56. /**
  57. * Instantiates an AssetContainer.
  58. * @param scene The scene the AssetContainer belongs to.
  59. */
  60. constructor(scene) {
  61. super();
  62. this._wasAddedToScene = false;
  63. scene = scene || EngineStore.LastCreatedScene;
  64. if (!scene) {
  65. return;
  66. }
  67. this.scene = scene;
  68. this["sounds"] = [];
  69. this["effectLayers"] = [];
  70. this["layers"] = [];
  71. this["lensFlareSystems"] = [];
  72. this["proceduralTextures"] = [];
  73. this["reflectionProbes"] = [];
  74. scene.onDisposeObservable.add(() => {
  75. if (!this._wasAddedToScene) {
  76. this.dispose();
  77. }
  78. });
  79. this._onContextRestoredObserver = scene.getEngine().onContextRestoredObservable.add(() => {
  80. for (const geometry of this.geometries) {
  81. geometry._rebuild();
  82. }
  83. for (const mesh of this.meshes) {
  84. mesh._rebuild();
  85. }
  86. for (const system of this.particleSystems) {
  87. system.rebuild();
  88. }
  89. for (const texture of this.textures) {
  90. texture._rebuild();
  91. }
  92. });
  93. }
  94. /**
  95. * Given a list of nodes, return a topological sorting of them.
  96. * @param nodes
  97. * @returns a sorted array of nodes
  98. */
  99. _topologicalSort(nodes) {
  100. const nodesUidMap = new Map();
  101. for (const node of nodes) {
  102. nodesUidMap.set(node.uniqueId, node);
  103. }
  104. const dependencyGraph = {
  105. dependsOn: new Map(),
  106. dependedBy: new Map(), // given a node id, what are the ids of the nodes that depend on it
  107. };
  108. // Build the dependency graph given the list of nodes
  109. // First pass: Initialize the empty dependency graph
  110. for (const node of nodes) {
  111. const nodeId = node.uniqueId;
  112. dependencyGraph.dependsOn.set(nodeId, new Set());
  113. dependencyGraph.dependedBy.set(nodeId, new Set());
  114. }
  115. // Second pass: Populate the dependency graph. We assume that we
  116. // don't need to check for cycles here, as the scene graph cannot
  117. // contain cycles. Our graph also already contains all transitive
  118. // dependencies because getDescendants returns the transitive
  119. // dependencies by default.
  120. for (const node of nodes) {
  121. const nodeId = node.uniqueId;
  122. const dependsOn = dependencyGraph.dependsOn.get(nodeId);
  123. if (node instanceof InstancedMesh) {
  124. const masterMesh = node.sourceMesh;
  125. if (nodesUidMap.has(masterMesh.uniqueId)) {
  126. dependsOn.add(masterMesh.uniqueId);
  127. dependencyGraph.dependedBy.get(masterMesh.uniqueId).add(nodeId);
  128. }
  129. }
  130. const dependedBy = dependencyGraph.dependedBy.get(nodeId);
  131. for (const child of node.getDescendants()) {
  132. const childId = child.uniqueId;
  133. if (nodesUidMap.has(childId)) {
  134. dependedBy.add(childId);
  135. const childDependsOn = dependencyGraph.dependsOn.get(childId);
  136. childDependsOn.add(nodeId);
  137. }
  138. }
  139. }
  140. // Third pass: Topological sort
  141. const sortedNodes = [];
  142. // First: Find all nodes that have no dependencies
  143. const leaves = [];
  144. for (const node of nodes) {
  145. const nodeId = node.uniqueId;
  146. if (dependencyGraph.dependsOn.get(nodeId).size === 0) {
  147. leaves.push(node);
  148. nodesUidMap.delete(nodeId);
  149. }
  150. }
  151. const visitList = leaves;
  152. while (visitList.length > 0) {
  153. const nodeToVisit = visitList.shift();
  154. sortedNodes.push(nodeToVisit);
  155. // Remove the node from the dependency graph
  156. // When a node is visited, we know that dependsOn is empty.
  157. // So we only need to remove the node from dependedBy.
  158. const dependedByVisitedNode = dependencyGraph.dependedBy.get(nodeToVisit.uniqueId);
  159. // Array.from(x.values()) is to make the TS compiler happy
  160. for (const dependedByVisitedNodeId of Array.from(dependedByVisitedNode.values())) {
  161. const dependsOnDependedByVisitedNode = dependencyGraph.dependsOn.get(dependedByVisitedNodeId);
  162. dependsOnDependedByVisitedNode.delete(nodeToVisit.uniqueId);
  163. if (dependsOnDependedByVisitedNode.size === 0 && nodesUidMap.get(dependedByVisitedNodeId)) {
  164. visitList.push(nodesUidMap.get(dependedByVisitedNodeId));
  165. nodesUidMap.delete(dependedByVisitedNodeId);
  166. }
  167. }
  168. }
  169. if (nodesUidMap.size > 0) {
  170. Logger.Error("SceneSerializer._topologicalSort: There were unvisited nodes:");
  171. nodesUidMap.forEach((node) => Logger.Error(node.name));
  172. }
  173. return sortedNodes;
  174. }
  175. _addNodeAndDescendantsToList(list, addedIds, rootNode, predicate) {
  176. if (!rootNode || (predicate && !predicate(rootNode)) || addedIds.has(rootNode.uniqueId)) {
  177. return;
  178. }
  179. list.push(rootNode);
  180. addedIds.add(rootNode.uniqueId);
  181. for (const child of rootNode.getDescendants(true)) {
  182. this._addNodeAndDescendantsToList(list, addedIds, child, predicate);
  183. }
  184. }
  185. /**
  186. * Check if a specific node is contained in this asset container.
  187. * @param node the node to check
  188. * @returns true if the node is contained in this container, otherwise false.
  189. */
  190. _isNodeInContainer(node) {
  191. if (node instanceof AbstractMesh && this.meshes.indexOf(node) !== -1) {
  192. return true;
  193. }
  194. if (node instanceof TransformNode && this.transformNodes.indexOf(node) !== -1) {
  195. return true;
  196. }
  197. if (node instanceof Light && this.lights.indexOf(node) !== -1) {
  198. return true;
  199. }
  200. if (node instanceof Camera && this.cameras.indexOf(node) !== -1) {
  201. return true;
  202. }
  203. return false;
  204. }
  205. /**
  206. * For every node in the scene, check if its parent node is also in the scene.
  207. * @returns true if every node's parent is also in the scene, otherwise false.
  208. */
  209. _isValidHierarchy() {
  210. for (const node of this.meshes) {
  211. if (node.parent && !this._isNodeInContainer(node.parent)) {
  212. Logger.Warn(`Node ${node.name} has a parent that is not in the container.`);
  213. return false;
  214. }
  215. }
  216. for (const node of this.transformNodes) {
  217. if (node.parent && !this._isNodeInContainer(node.parent)) {
  218. Logger.Warn(`Node ${node.name} has a parent that is not in the container.`);
  219. return false;
  220. }
  221. }
  222. for (const node of this.lights) {
  223. if (node.parent && !this._isNodeInContainer(node.parent)) {
  224. Logger.Warn(`Node ${node.name} has a parent that is not in the container.`);
  225. return false;
  226. }
  227. }
  228. for (const node of this.cameras) {
  229. if (node.parent && !this._isNodeInContainer(node.parent)) {
  230. Logger.Warn(`Node ${node.name} has a parent that is not in the container.`);
  231. return false;
  232. }
  233. }
  234. return true;
  235. }
  236. /**
  237. * Instantiate or clone all meshes and add the new ones to the scene.
  238. * Skeletons and animation groups will all be cloned
  239. * @param nameFunction defines an optional function used to get new names for clones
  240. * @param cloneMaterials defines an optional boolean that defines if materials must be cloned as well (false by default)
  241. * @param options defines an optional list of options to control how to instantiate / clone models
  242. * @param options.doNotInstantiate defines if the model must be instantiated or just cloned
  243. * @param options.predicate defines a predicate used to filter whih mesh to instantiate/clone
  244. * @returns a list of rootNodes, skeletons and animation groups that were duplicated
  245. */
  246. instantiateModelsToScene(nameFunction, cloneMaterials = false, options) {
  247. if (!this._isValidHierarchy()) {
  248. Tools.Warn("SceneSerializer.InstantiateModelsToScene: The Asset Container hierarchy is not valid.");
  249. }
  250. const conversionMap = {};
  251. const storeMap = {};
  252. const result = new InstantiatedEntries();
  253. const alreadySwappedSkeletons = [];
  254. const alreadySwappedMaterials = [];
  255. const localOptions = {
  256. doNotInstantiate: true,
  257. ...options,
  258. };
  259. const onClone = (source, clone) => {
  260. conversionMap[source.uniqueId] = clone.uniqueId;
  261. storeMap[clone.uniqueId] = clone;
  262. if (nameFunction) {
  263. clone.name = nameFunction(source.name);
  264. }
  265. if (clone instanceof Mesh) {
  266. const clonedMesh = clone;
  267. if (clonedMesh.morphTargetManager) {
  268. const oldMorphTargetManager = source.morphTargetManager;
  269. clonedMesh.morphTargetManager = oldMorphTargetManager.clone();
  270. for (let index = 0; index < oldMorphTargetManager.numTargets; index++) {
  271. const oldTarget = oldMorphTargetManager.getTarget(index);
  272. const newTarget = clonedMesh.morphTargetManager.getTarget(index);
  273. conversionMap[oldTarget.uniqueId] = newTarget.uniqueId;
  274. storeMap[newTarget.uniqueId] = newTarget;
  275. }
  276. }
  277. }
  278. };
  279. const nodesToSort = [];
  280. const idsOnSortList = new Set();
  281. for (const transformNode of this.transformNodes) {
  282. if (transformNode.parent === null) {
  283. this._addNodeAndDescendantsToList(nodesToSort, idsOnSortList, transformNode, localOptions.predicate);
  284. }
  285. }
  286. for (const mesh of this.meshes) {
  287. if (mesh.parent === null) {
  288. this._addNodeAndDescendantsToList(nodesToSort, idsOnSortList, mesh, localOptions.predicate);
  289. }
  290. }
  291. // Topologically sort nodes by parenting/instancing relationships so that all resources are in place
  292. // when a given node is instantiated.
  293. const sortedNodes = this._topologicalSort(nodesToSort);
  294. const onNewCreated = (source, clone) => {
  295. onClone(source, clone);
  296. if (source.parent) {
  297. const replicatedParentId = conversionMap[source.parent.uniqueId];
  298. const replicatedParent = storeMap[replicatedParentId];
  299. if (replicatedParent) {
  300. clone.parent = replicatedParent;
  301. }
  302. else {
  303. clone.parent = source.parent;
  304. }
  305. }
  306. if (clone.position && source.position) {
  307. clone.position.copyFrom(source.position);
  308. }
  309. if (clone.rotationQuaternion && source.rotationQuaternion) {
  310. clone.rotationQuaternion.copyFrom(source.rotationQuaternion);
  311. }
  312. if (clone.rotation && source.rotation) {
  313. clone.rotation.copyFrom(source.rotation);
  314. }
  315. if (clone.scaling && source.scaling) {
  316. clone.scaling.copyFrom(source.scaling);
  317. }
  318. if (clone.material) {
  319. const mesh = clone;
  320. if (mesh.material) {
  321. if (cloneMaterials) {
  322. const sourceMaterial = source.material;
  323. if (alreadySwappedMaterials.indexOf(sourceMaterial) === -1) {
  324. let swap = sourceMaterial.clone(nameFunction ? nameFunction(sourceMaterial.name) : "Clone of " + sourceMaterial.name);
  325. alreadySwappedMaterials.push(sourceMaterial);
  326. conversionMap[sourceMaterial.uniqueId] = swap.uniqueId;
  327. storeMap[swap.uniqueId] = swap;
  328. if (sourceMaterial.getClassName() === "MultiMaterial") {
  329. const multi = sourceMaterial;
  330. for (const material of multi.subMaterials) {
  331. if (!material) {
  332. continue;
  333. }
  334. swap = material.clone(nameFunction ? nameFunction(material.name) : "Clone of " + material.name);
  335. alreadySwappedMaterials.push(material);
  336. conversionMap[material.uniqueId] = swap.uniqueId;
  337. storeMap[swap.uniqueId] = swap;
  338. }
  339. multi.subMaterials = multi.subMaterials.map((m) => m && storeMap[conversionMap[m.uniqueId]]);
  340. }
  341. }
  342. if (mesh.getClassName() !== "InstancedMesh") {
  343. mesh.material = storeMap[conversionMap[sourceMaterial.uniqueId]];
  344. }
  345. }
  346. else {
  347. if (mesh.material.getClassName() === "MultiMaterial") {
  348. if (this.scene.multiMaterials.indexOf(mesh.material) === -1) {
  349. this.scene.addMultiMaterial(mesh.material);
  350. }
  351. }
  352. else {
  353. if (this.scene.materials.indexOf(mesh.material) === -1) {
  354. this.scene.addMaterial(mesh.material);
  355. }
  356. }
  357. }
  358. }
  359. }
  360. if (clone.parent === null) {
  361. result.rootNodes.push(clone);
  362. }
  363. };
  364. sortedNodes.forEach((node) => {
  365. if (node.getClassName() === "InstancedMesh") {
  366. const instancedNode = node;
  367. const sourceMesh = instancedNode.sourceMesh;
  368. const replicatedSourceId = conversionMap[sourceMesh.uniqueId];
  369. const replicatedSource = typeof replicatedSourceId === "number" ? storeMap[replicatedSourceId] : sourceMesh;
  370. const replicatedInstancedNode = replicatedSource.createInstance(instancedNode.name);
  371. onNewCreated(instancedNode, replicatedInstancedNode);
  372. }
  373. else {
  374. // Mesh or TransformNode
  375. let canInstance = true;
  376. if (node.getClassName() === "TransformNode" ||
  377. node.getClassName() === "Node" ||
  378. node.skeleton ||
  379. !node.getTotalVertices ||
  380. node.getTotalVertices() === 0) {
  381. // Transform nodes, skinned meshes, and meshes with no vertices can never be instanced!
  382. canInstance = false;
  383. }
  384. else if (localOptions.doNotInstantiate) {
  385. if (typeof localOptions.doNotInstantiate === "function") {
  386. canInstance = !localOptions.doNotInstantiate(node);
  387. }
  388. else {
  389. canInstance = !localOptions.doNotInstantiate;
  390. }
  391. }
  392. const replicatedNode = canInstance ? node.createInstance(`instance of ${node.name}`) : node.clone(`Clone of ${node.name}`, null, true);
  393. if (!replicatedNode) {
  394. throw new Error(`Could not clone or instantiate node on Asset Container ${node.name}`);
  395. }
  396. onNewCreated(node, replicatedNode);
  397. }
  398. });
  399. this.skeletons.forEach((s) => {
  400. if (localOptions.predicate && !localOptions.predicate(s)) {
  401. return;
  402. }
  403. const clone = s.clone(nameFunction ? nameFunction(s.name) : "Clone of " + s.name);
  404. for (const m of this.meshes) {
  405. if (m.skeleton === s && !m.isAnInstance) {
  406. const copy = storeMap[conversionMap[m.uniqueId]];
  407. if (!copy || copy.isAnInstance) {
  408. continue;
  409. }
  410. copy.skeleton = clone;
  411. if (alreadySwappedSkeletons.indexOf(clone) !== -1) {
  412. continue;
  413. }
  414. alreadySwappedSkeletons.push(clone);
  415. // Check if bones are mesh linked
  416. for (const bone of clone.bones) {
  417. if (bone._linkedTransformNode) {
  418. bone._linkedTransformNode = storeMap[conversionMap[bone._linkedTransformNode.uniqueId]];
  419. }
  420. }
  421. }
  422. }
  423. result.skeletons.push(clone);
  424. });
  425. this.animationGroups.forEach((o) => {
  426. if (localOptions.predicate && !localOptions.predicate(o)) {
  427. return;
  428. }
  429. const clone = o.clone(nameFunction ? nameFunction(o.name) : "Clone of " + o.name, (oldTarget) => {
  430. const newTarget = storeMap[conversionMap[oldTarget.uniqueId]];
  431. return newTarget || oldTarget;
  432. });
  433. result.animationGroups.push(clone);
  434. });
  435. return result;
  436. }
  437. /**
  438. * Adds all the assets from the container to the scene.
  439. */
  440. addAllToScene() {
  441. if (this._wasAddedToScene) {
  442. return;
  443. }
  444. if (!this._isValidHierarchy()) {
  445. Tools.Warn("SceneSerializer.addAllToScene: The Asset Container hierarchy is not valid.");
  446. }
  447. this._wasAddedToScene = true;
  448. this.addToScene(null);
  449. if (this.environmentTexture) {
  450. this.scene.environmentTexture = this.environmentTexture;
  451. }
  452. for (const component of this.scene._serializableComponents) {
  453. component.addFromContainer(this);
  454. }
  455. this.scene.getEngine().onContextRestoredObservable.remove(this._onContextRestoredObserver);
  456. this._onContextRestoredObserver = null;
  457. }
  458. /**
  459. * Adds assets from the container to the scene.
  460. * @param predicate defines a predicate used to select which entity will be added (can be null)
  461. */
  462. addToScene(predicate = null) {
  463. const addedNodes = [];
  464. this.cameras.forEach((o) => {
  465. if (predicate && !predicate(o)) {
  466. return;
  467. }
  468. this.scene.addCamera(o);
  469. addedNodes.push(o);
  470. });
  471. this.lights.forEach((o) => {
  472. if (predicate && !predicate(o)) {
  473. return;
  474. }
  475. this.scene.addLight(o);
  476. addedNodes.push(o);
  477. });
  478. this.meshes.forEach((o) => {
  479. if (predicate && !predicate(o)) {
  480. return;
  481. }
  482. this.scene.addMesh(o);
  483. addedNodes.push(o);
  484. });
  485. this.skeletons.forEach((o) => {
  486. if (predicate && !predicate(o)) {
  487. return;
  488. }
  489. this.scene.addSkeleton(o);
  490. });
  491. this.animations.forEach((o) => {
  492. if (predicate && !predicate(o)) {
  493. return;
  494. }
  495. this.scene.addAnimation(o);
  496. });
  497. this.animationGroups.forEach((o) => {
  498. if (predicate && !predicate(o)) {
  499. return;
  500. }
  501. this.scene.addAnimationGroup(o);
  502. });
  503. this.multiMaterials.forEach((o) => {
  504. if (predicate && !predicate(o)) {
  505. return;
  506. }
  507. this.scene.addMultiMaterial(o);
  508. });
  509. this.materials.forEach((o) => {
  510. if (predicate && !predicate(o)) {
  511. return;
  512. }
  513. this.scene.addMaterial(o);
  514. });
  515. this.morphTargetManagers.forEach((o) => {
  516. if (predicate && !predicate(o)) {
  517. return;
  518. }
  519. this.scene.addMorphTargetManager(o);
  520. });
  521. this.geometries.forEach((o) => {
  522. if (predicate && !predicate(o)) {
  523. return;
  524. }
  525. this.scene.addGeometry(o);
  526. });
  527. this.transformNodes.forEach((o) => {
  528. if (predicate && !predicate(o)) {
  529. return;
  530. }
  531. this.scene.addTransformNode(o);
  532. addedNodes.push(o);
  533. });
  534. this.actionManagers.forEach((o) => {
  535. if (predicate && !predicate(o)) {
  536. return;
  537. }
  538. this.scene.addActionManager(o);
  539. });
  540. this.textures.forEach((o) => {
  541. if (predicate && !predicate(o)) {
  542. return;
  543. }
  544. this.scene.addTexture(o);
  545. });
  546. this.reflectionProbes.forEach((o) => {
  547. if (predicate && !predicate(o)) {
  548. return;
  549. }
  550. this.scene.addReflectionProbe(o);
  551. });
  552. for (const addedNode of addedNodes) {
  553. // If node was added to the scene, but parent is not in the scene, break the relationship
  554. if (addedNode.parent && this.scene.getNodes().indexOf(addedNode.parent) === -1) {
  555. // Use setParent to keep transform if possible
  556. if (addedNode.setParent) {
  557. addedNode.setParent(null);
  558. }
  559. else {
  560. addedNode.parent = null;
  561. }
  562. }
  563. }
  564. }
  565. /**
  566. * Removes all the assets in the container from the scene
  567. */
  568. removeAllFromScene() {
  569. if (!this._isValidHierarchy()) {
  570. Tools.Warn("SceneSerializer.removeAllFromScene: The Asset Container hierarchy is not valid.");
  571. }
  572. this._wasAddedToScene = false;
  573. this.removeFromScene(null);
  574. if (this.environmentTexture === this.scene.environmentTexture) {
  575. this.scene.environmentTexture = null;
  576. }
  577. for (const component of this.scene._serializableComponents) {
  578. component.removeFromContainer(this);
  579. }
  580. }
  581. /**
  582. * Removes assets in the container from the scene
  583. * @param predicate defines a predicate used to select which entity will be added (can be null)
  584. */
  585. removeFromScene(predicate = null) {
  586. this.cameras.forEach((o) => {
  587. if (predicate && !predicate(o)) {
  588. return;
  589. }
  590. this.scene.removeCamera(o);
  591. });
  592. this.lights.forEach((o) => {
  593. if (predicate && !predicate(o)) {
  594. return;
  595. }
  596. this.scene.removeLight(o);
  597. });
  598. this.meshes.forEach((o) => {
  599. if (predicate && !predicate(o)) {
  600. return;
  601. }
  602. this.scene.removeMesh(o, true);
  603. });
  604. this.skeletons.forEach((o) => {
  605. if (predicate && !predicate(o)) {
  606. return;
  607. }
  608. this.scene.removeSkeleton(o);
  609. });
  610. this.animations.forEach((o) => {
  611. if (predicate && !predicate(o)) {
  612. return;
  613. }
  614. this.scene.removeAnimation(o);
  615. });
  616. this.animationGroups.forEach((o) => {
  617. if (predicate && !predicate(o)) {
  618. return;
  619. }
  620. this.scene.removeAnimationGroup(o);
  621. });
  622. this.multiMaterials.forEach((o) => {
  623. if (predicate && !predicate(o)) {
  624. return;
  625. }
  626. this.scene.removeMultiMaterial(o);
  627. });
  628. this.materials.forEach((o) => {
  629. if (predicate && !predicate(o)) {
  630. return;
  631. }
  632. this.scene.removeMaterial(o);
  633. });
  634. this.morphTargetManagers.forEach((o) => {
  635. if (predicate && !predicate(o)) {
  636. return;
  637. }
  638. this.scene.removeMorphTargetManager(o);
  639. });
  640. this.geometries.forEach((o) => {
  641. if (predicate && !predicate(o)) {
  642. return;
  643. }
  644. this.scene.removeGeometry(o);
  645. });
  646. this.transformNodes.forEach((o) => {
  647. if (predicate && !predicate(o)) {
  648. return;
  649. }
  650. this.scene.removeTransformNode(o);
  651. });
  652. this.actionManagers.forEach((o) => {
  653. if (predicate && !predicate(o)) {
  654. return;
  655. }
  656. this.scene.removeActionManager(o);
  657. });
  658. this.textures.forEach((o) => {
  659. if (predicate && !predicate(o)) {
  660. return;
  661. }
  662. this.scene.removeTexture(o);
  663. });
  664. this.reflectionProbes.forEach((o) => {
  665. if (predicate && !predicate(o)) {
  666. return;
  667. }
  668. this.scene.removeReflectionProbe(o);
  669. });
  670. }
  671. /**
  672. * Disposes all the assets in the container
  673. */
  674. dispose() {
  675. this.cameras.slice(0).forEach((o) => {
  676. o.dispose();
  677. });
  678. this.cameras.length = 0;
  679. this.lights.slice(0).forEach((o) => {
  680. o.dispose();
  681. });
  682. this.lights.length = 0;
  683. this.meshes.slice(0).forEach((o) => {
  684. o.dispose();
  685. });
  686. this.meshes.length = 0;
  687. this.skeletons.slice(0).forEach((o) => {
  688. o.dispose();
  689. });
  690. this.skeletons.length = 0;
  691. this.animationGroups.slice(0).forEach((o) => {
  692. o.dispose();
  693. });
  694. this.animationGroups.length = 0;
  695. this.multiMaterials.slice(0).forEach((o) => {
  696. o.dispose();
  697. });
  698. this.multiMaterials.length = 0;
  699. this.materials.slice(0).forEach((o) => {
  700. o.dispose();
  701. });
  702. this.materials.length = 0;
  703. this.geometries.slice(0).forEach((o) => {
  704. o.dispose();
  705. });
  706. this.geometries.length = 0;
  707. this.transformNodes.slice(0).forEach((o) => {
  708. o.dispose();
  709. });
  710. this.transformNodes.length = 0;
  711. this.actionManagers.slice(0).forEach((o) => {
  712. o.dispose();
  713. });
  714. this.actionManagers.length = 0;
  715. this.textures.slice(0).forEach((o) => {
  716. o.dispose();
  717. });
  718. this.textures.length = 0;
  719. this.reflectionProbes.slice(0).forEach((o) => {
  720. o.dispose();
  721. });
  722. this.reflectionProbes.length = 0;
  723. this.morphTargetManagers.slice(0).forEach((o) => {
  724. o.dispose();
  725. });
  726. this.morphTargetManagers.length = 0;
  727. if (this.environmentTexture) {
  728. this.environmentTexture.dispose();
  729. this.environmentTexture = null;
  730. }
  731. for (const component of this.scene._serializableComponents) {
  732. component.removeFromContainer(this, true);
  733. }
  734. if (this._onContextRestoredObserver) {
  735. this.scene.getEngine().onContextRestoredObservable.remove(this._onContextRestoredObserver);
  736. this._onContextRestoredObserver = null;
  737. }
  738. }
  739. _moveAssets(sourceAssets, targetAssets, keepAssets) {
  740. if (!sourceAssets || !targetAssets) {
  741. return;
  742. }
  743. for (const asset of sourceAssets) {
  744. let move = true;
  745. if (keepAssets) {
  746. for (const keepAsset of keepAssets) {
  747. if (asset === keepAsset) {
  748. move = false;
  749. break;
  750. }
  751. }
  752. }
  753. if (move) {
  754. targetAssets.push(asset);
  755. asset._parentContainer = this;
  756. }
  757. }
  758. }
  759. /**
  760. * Removes all the assets contained in the scene and adds them to the container.
  761. * @param keepAssets Set of assets to keep in the scene. (default: empty)
  762. */
  763. moveAllFromScene(keepAssets) {
  764. this._wasAddedToScene = false;
  765. if (keepAssets === undefined) {
  766. keepAssets = new KeepAssets();
  767. }
  768. for (const key in this) {
  769. if (Object.prototype.hasOwnProperty.call(this, key)) {
  770. this[key] = this[key] || (key === "_environmentTexture" ? null : []);
  771. this._moveAssets(this.scene[key], this[key], keepAssets[key]);
  772. }
  773. }
  774. this.environmentTexture = this.scene.environmentTexture;
  775. this.removeAllFromScene();
  776. }
  777. /**
  778. * 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.
  779. * @returns the root mesh
  780. */
  781. createRootMesh() {
  782. const rootMesh = new Mesh("assetContainerRootMesh", this.scene);
  783. this.meshes.forEach((m) => {
  784. if (!m.parent) {
  785. rootMesh.addChild(m);
  786. }
  787. });
  788. this.meshes.unshift(rootMesh);
  789. return rootMesh;
  790. }
  791. /**
  792. * Merge animations (direct and animation groups) from this asset container into a scene
  793. * @param scene is the instance of BABYLON.Scene to append to (default: last created scene)
  794. * @param animatables set of animatables to retarget to a node from the scene
  795. * @param targetConverter defines a function used to convert animation targets from the asset container to the scene (default: search node by name)
  796. * @returns an array of the new AnimationGroup added to the scene (empty array if none)
  797. */
  798. mergeAnimationsTo(scene = EngineStore.LastCreatedScene, animatables, targetConverter = null) {
  799. if (!scene) {
  800. Logger.Error("No scene available to merge animations to");
  801. return [];
  802. }
  803. const _targetConverter = targetConverter
  804. ? targetConverter
  805. : (target) => {
  806. let node = null;
  807. const targetProperty = target.animations.length ? target.animations[0].targetProperty : "";
  808. /*
  809. BabylonJS adds special naming to targets that are children of nodes.
  810. This name attempts to remove that special naming to get the parent nodes name in case the target
  811. can't be found in the node tree
  812. Ex: Torso_primitive0 likely points to a Mesh primitive. We take away primitive0 and are left with "Torso" which is the name
  813. of the primitive's parent.
  814. */
  815. const name = target.name.split(".").join("").split("_primitive")[0];
  816. switch (targetProperty) {
  817. case "position":
  818. case "rotationQuaternion":
  819. node = scene.getTransformNodeByName(target.name) || scene.getTransformNodeByName(name);
  820. break;
  821. case "influence":
  822. node = scene.getMorphTargetByName(target.name) || scene.getMorphTargetByName(name);
  823. break;
  824. default:
  825. node = scene.getNodeByName(target.name) || scene.getNodeByName(name);
  826. }
  827. return node;
  828. };
  829. // Copy new node animations
  830. const nodesInAC = this.getNodes();
  831. nodesInAC.forEach((nodeInAC) => {
  832. const nodeInScene = _targetConverter(nodeInAC);
  833. if (nodeInScene !== null) {
  834. // Remove old animations with same target property as a new one
  835. for (const animationInAC of nodeInAC.animations) {
  836. // Doing treatment on an array for safety measure
  837. const animationsWithSameProperty = nodeInScene.animations.filter((animationInScene) => {
  838. return animationInScene.targetProperty === animationInAC.targetProperty;
  839. });
  840. for (const animationWithSameProperty of animationsWithSameProperty) {
  841. const index = nodeInScene.animations.indexOf(animationWithSameProperty, 0);
  842. if (index > -1) {
  843. nodeInScene.animations.splice(index, 1);
  844. }
  845. }
  846. }
  847. // Append new animations
  848. nodeInScene.animations = nodeInScene.animations.concat(nodeInAC.animations);
  849. }
  850. });
  851. const newAnimationGroups = [];
  852. // Copy new animation groups
  853. this.animationGroups.slice().forEach((animationGroupInAC) => {
  854. // Clone the animation group and all its animatables
  855. newAnimationGroups.push(animationGroupInAC.clone(animationGroupInAC.name, _targetConverter));
  856. // Remove animatables related to the asset container
  857. animationGroupInAC.animatables.forEach((animatable) => {
  858. animatable.stop();
  859. });
  860. });
  861. // Retarget animatables
  862. animatables.forEach((animatable) => {
  863. const target = _targetConverter(animatable.target);
  864. if (target) {
  865. // Clone the animatable and retarget it
  866. scene.beginAnimation(target, animatable.fromFrame, animatable.toFrame, animatable.loopAnimation, animatable.speedRatio, animatable.onAnimationEnd ? animatable.onAnimationEnd : undefined, undefined, true, undefined, animatable.onAnimationLoop ? animatable.onAnimationLoop : undefined);
  867. // Stop animation for the target in the asset container
  868. scene.stopAnimation(animatable.target);
  869. }
  870. });
  871. return newAnimationGroups;
  872. }
  873. /**
  874. * @since 6.15.0
  875. * This method checks for any node that has no parent
  876. * and is not in the rootNodes array, and adds the node
  877. * there, if so.
  878. */
  879. populateRootNodes() {
  880. this.rootNodes.length = 0;
  881. this.meshes.forEach((m) => {
  882. if (!m.parent && this.rootNodes.indexOf(m) === -1) {
  883. this.rootNodes.push(m);
  884. }
  885. });
  886. this.transformNodes.forEach((t) => {
  887. if (!t.parent && this.rootNodes.indexOf(t) === -1) {
  888. this.rootNodes.push(t);
  889. }
  890. });
  891. this.lights.forEach((l) => {
  892. if (!l.parent && this.rootNodes.indexOf(l) === -1) {
  893. this.rootNodes.push(l);
  894. }
  895. });
  896. this.cameras.forEach((c) => {
  897. if (!c.parent && this.rootNodes.indexOf(c) === -1) {
  898. this.rootNodes.push(c);
  899. }
  900. });
  901. }
  902. /**
  903. * @since 6.26.0
  904. * Given a root asset, this method will traverse its hierarchy and add it, its children and any materials/skeletons/animation groups to the container.
  905. * @param root root node
  906. */
  907. addAllAssetsToContainer(root) {
  908. if (!root) {
  909. return;
  910. }
  911. const nodesToVisit = [];
  912. const visitedNodes = new Set();
  913. nodesToVisit.push(root);
  914. while (nodesToVisit.length > 0) {
  915. const nodeToVisit = nodesToVisit.pop();
  916. if (nodeToVisit instanceof Mesh) {
  917. if (nodeToVisit.geometry && this.geometries.indexOf(nodeToVisit.geometry) === -1) {
  918. this.geometries.push(nodeToVisit.geometry);
  919. }
  920. this.meshes.push(nodeToVisit);
  921. }
  922. else if (nodeToVisit instanceof TransformNode) {
  923. this.transformNodes.push(nodeToVisit);
  924. }
  925. else if (nodeToVisit instanceof Light) {
  926. this.lights.push(nodeToVisit);
  927. }
  928. else if (nodeToVisit instanceof Camera) {
  929. this.cameras.push(nodeToVisit);
  930. }
  931. if (nodeToVisit instanceof AbstractMesh) {
  932. if (nodeToVisit.material && this.materials.indexOf(nodeToVisit.material) === -1) {
  933. this.materials.push(nodeToVisit.material);
  934. for (const texture of nodeToVisit.material.getActiveTextures()) {
  935. if (this.textures.indexOf(texture) === -1) {
  936. this.textures.push(texture);
  937. }
  938. }
  939. }
  940. if (nodeToVisit.skeleton && this.skeletons.indexOf(nodeToVisit.skeleton) === -1) {
  941. this.skeletons.push(nodeToVisit.skeleton);
  942. }
  943. if (nodeToVisit.morphTargetManager && this.morphTargetManagers.indexOf(nodeToVisit.morphTargetManager) === -1) {
  944. this.morphTargetManagers.push(nodeToVisit.morphTargetManager);
  945. }
  946. }
  947. for (const child of nodeToVisit.getChildren()) {
  948. if (!visitedNodes.has(child)) {
  949. nodesToVisit.push(child);
  950. }
  951. }
  952. visitedNodes.add(nodeToVisit);
  953. }
  954. this.populateRootNodes();
  955. }
  956. }
  957. //# sourceMappingURL=assetContainer.js.map