instancedMesh.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. import { Matrix, TmpVectors } from "../Maths/math.vector.js";
  2. import { Logger } from "../Misc/logger.js";
  3. import { AbstractMesh } from "../Meshes/abstractMesh.js";
  4. import { Mesh } from "../Meshes/mesh.js";
  5. import { DeepCopier } from "../Misc/deepCopier.js";
  6. import { TransformNode } from "./transformNode.js";
  7. import { VertexBuffer } from "../Buffers/buffer.js";
  8. import { Tools } from "../Misc/tools.js";
  9. Mesh._instancedMeshFactory = (name, mesh) => {
  10. const instance = new InstancedMesh(name, mesh);
  11. if (mesh.instancedBuffers) {
  12. instance.instancedBuffers = {};
  13. for (const key in mesh.instancedBuffers) {
  14. instance.instancedBuffers[key] = mesh.instancedBuffers[key];
  15. }
  16. }
  17. return instance;
  18. };
  19. /**
  20. * Creates an instance based on a source mesh.
  21. */
  22. export class InstancedMesh extends AbstractMesh {
  23. /**
  24. * Creates a new InstancedMesh object from the mesh source.
  25. * @param name defines the name of the instance
  26. * @param source the mesh to create the instance from
  27. */
  28. constructor(name, source) {
  29. super(name, source.getScene());
  30. /** @internal */
  31. this._indexInSourceMeshInstanceArray = -1;
  32. /** @internal */
  33. this._distanceToCamera = 0;
  34. source.addInstance(this);
  35. this._sourceMesh = source;
  36. this._unIndexed = source._unIndexed;
  37. this.position.copyFrom(source.position);
  38. this.rotation.copyFrom(source.rotation);
  39. this.scaling.copyFrom(source.scaling);
  40. if (source.rotationQuaternion) {
  41. this.rotationQuaternion = source.rotationQuaternion.clone();
  42. }
  43. this.animations = source.animations.slice();
  44. for (const range of source.getAnimationRanges()) {
  45. if (range != null) {
  46. this.createAnimationRange(range.name, range.from, range.to);
  47. }
  48. }
  49. this.infiniteDistance = source.infiniteDistance;
  50. this.setPivotMatrix(source.getPivotMatrix());
  51. this.refreshBoundingInfo(true, true);
  52. this._syncSubMeshes();
  53. }
  54. /**
  55. * @returns the string "InstancedMesh".
  56. */
  57. getClassName() {
  58. return "InstancedMesh";
  59. }
  60. /** Gets the list of lights affecting that mesh */
  61. get lightSources() {
  62. return this._sourceMesh._lightSources;
  63. }
  64. _resyncLightSources() {
  65. // Do nothing as all the work will be done by source mesh
  66. }
  67. _resyncLightSource() {
  68. // Do nothing as all the work will be done by source mesh
  69. }
  70. _removeLightSource() {
  71. // Do nothing as all the work will be done by source mesh
  72. }
  73. // Methods
  74. /**
  75. * If the source mesh receives shadows
  76. */
  77. get receiveShadows() {
  78. return this._sourceMesh.receiveShadows;
  79. }
  80. set receiveShadows(_value) {
  81. if (this._sourceMesh?.receiveShadows !== _value) {
  82. Tools.Warn("Setting receiveShadows on an instanced mesh has no effect");
  83. }
  84. }
  85. /**
  86. * The material of the source mesh
  87. */
  88. get material() {
  89. return this._sourceMesh.material;
  90. }
  91. set material(_value) {
  92. if (this._sourceMesh?.material !== _value) {
  93. Tools.Warn("Setting material on an instanced mesh has no effect");
  94. }
  95. }
  96. /**
  97. * Visibility of the source mesh
  98. */
  99. get visibility() {
  100. return this._sourceMesh.visibility;
  101. }
  102. set visibility(_value) {
  103. if (this._sourceMesh?.visibility !== _value) {
  104. Tools.Warn("Setting visibility on an instanced mesh has no effect");
  105. }
  106. }
  107. /**
  108. * Skeleton of the source mesh
  109. */
  110. get skeleton() {
  111. return this._sourceMesh.skeleton;
  112. }
  113. set skeleton(_value) {
  114. if (this._sourceMesh?.skeleton !== _value) {
  115. Tools.Warn("Setting skeleton on an instanced mesh has no effect");
  116. }
  117. }
  118. /**
  119. * Rendering ground id of the source mesh
  120. */
  121. get renderingGroupId() {
  122. return this._sourceMesh.renderingGroupId;
  123. }
  124. set renderingGroupId(value) {
  125. if (!this._sourceMesh || value === this._sourceMesh.renderingGroupId) {
  126. return;
  127. }
  128. //no-op with warning
  129. Logger.Warn("Note - setting renderingGroupId of an instanced mesh has no effect on the scene");
  130. }
  131. /**
  132. * @returns the total number of vertices (integer).
  133. */
  134. getTotalVertices() {
  135. return this._sourceMesh ? this._sourceMesh.getTotalVertices() : 0;
  136. }
  137. /**
  138. * Returns a positive integer : the total number of indices in this mesh geometry.
  139. * @returns the number of indices or zero if the mesh has no geometry.
  140. */
  141. getTotalIndices() {
  142. return this._sourceMesh.getTotalIndices();
  143. }
  144. /**
  145. * The source mesh of the instance
  146. */
  147. get sourceMesh() {
  148. return this._sourceMesh;
  149. }
  150. /**
  151. * Creates a new InstancedMesh object from the mesh model.
  152. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/copies/instances
  153. * @param name defines the name of the new instance
  154. * @returns a new InstancedMesh
  155. */
  156. createInstance(name) {
  157. return this._sourceMesh.createInstance(name);
  158. }
  159. /**
  160. * Is this node ready to be used/rendered
  161. * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
  162. * @returns {boolean} is it ready
  163. */
  164. isReady(completeCheck = false) {
  165. return this._sourceMesh.isReady(completeCheck, true);
  166. }
  167. /**
  168. * Returns an array of integers or a typed array (Int32Array, Uint32Array, Uint16Array) populated with the mesh indices.
  169. * @param kind kind of verticies to retrieve (eg. positions, normals, uvs, etc.)
  170. * @param copyWhenShared If true (default false) and and if the mesh geometry is shared among some other meshes, the returned array is a copy of the internal one.
  171. * @param forceCopy defines a boolean forcing the copy of the buffer no matter what the value of copyWhenShared is
  172. * @returns a float array or a Float32Array of the requested kind of data : positions, normals, uvs, etc.
  173. */
  174. getVerticesData(kind, copyWhenShared, forceCopy) {
  175. return this._sourceMesh.getVerticesData(kind, copyWhenShared, forceCopy);
  176. }
  177. /**
  178. * Sets the vertex data of the mesh geometry for the requested `kind`.
  179. * If the mesh has no geometry, a new Geometry object is set to the mesh and then passed this vertex data.
  180. * The `data` are either a numeric array either a Float32Array.
  181. * The parameter `updatable` is passed as is to the underlying Geometry object constructor (if initially none) or updater.
  182. * The parameter `stride` is an optional positive integer, it is usually automatically deducted from the `kind` (3 for positions or normals, 2 for UV, etc).
  183. * Note that a new underlying VertexBuffer object is created each call.
  184. * If the `kind` is the `PositionKind`, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
  185. *
  186. * Possible `kind` values :
  187. * - VertexBuffer.PositionKind
  188. * - VertexBuffer.UVKind
  189. * - VertexBuffer.UV2Kind
  190. * - VertexBuffer.UV3Kind
  191. * - VertexBuffer.UV4Kind
  192. * - VertexBuffer.UV5Kind
  193. * - VertexBuffer.UV6Kind
  194. * - VertexBuffer.ColorKind
  195. * - VertexBuffer.MatricesIndicesKind
  196. * - VertexBuffer.MatricesIndicesExtraKind
  197. * - VertexBuffer.MatricesWeightsKind
  198. * - VertexBuffer.MatricesWeightsExtraKind
  199. *
  200. * Returns the Mesh.
  201. * @param kind defines vertex data kind
  202. * @param data defines the data source
  203. * @param updatable defines if the data must be flagged as updatable (false as default)
  204. * @param stride defines the vertex stride (optional)
  205. * @returns the current mesh
  206. */
  207. setVerticesData(kind, data, updatable, stride) {
  208. if (this.sourceMesh) {
  209. this.sourceMesh.setVerticesData(kind, data, updatable, stride);
  210. }
  211. return this.sourceMesh;
  212. }
  213. /**
  214. * Updates the existing vertex data of the mesh geometry for the requested `kind`.
  215. * If the mesh has no geometry, it is simply returned as it is.
  216. * The `data` are either a numeric array either a Float32Array.
  217. * No new underlying VertexBuffer object is created.
  218. * If the `kind` is the `PositionKind` and if `updateExtends` is true, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
  219. * If the parameter `makeItUnique` is true, a new global geometry is created from this positions and is set to the mesh.
  220. *
  221. * Possible `kind` values :
  222. * - VertexBuffer.PositionKind
  223. * - VertexBuffer.UVKind
  224. * - VertexBuffer.UV2Kind
  225. * - VertexBuffer.UV3Kind
  226. * - VertexBuffer.UV4Kind
  227. * - VertexBuffer.UV5Kind
  228. * - VertexBuffer.UV6Kind
  229. * - VertexBuffer.ColorKind
  230. * - VertexBuffer.MatricesIndicesKind
  231. * - VertexBuffer.MatricesIndicesExtraKind
  232. * - VertexBuffer.MatricesWeightsKind
  233. * - VertexBuffer.MatricesWeightsExtraKind
  234. *
  235. * Returns the Mesh.
  236. * @param kind defines vertex data kind
  237. * @param data defines the data source
  238. * @param updateExtends defines if extends info of the mesh must be updated (can be null). This is mostly useful for "position" kind
  239. * @param makeItUnique defines it the updated vertex buffer must be flagged as unique (false by default)
  240. * @returns the source mesh
  241. */
  242. updateVerticesData(kind, data, updateExtends, makeItUnique) {
  243. if (this.sourceMesh) {
  244. this.sourceMesh.updateVerticesData(kind, data, updateExtends, makeItUnique);
  245. }
  246. return this.sourceMesh;
  247. }
  248. /**
  249. * Sets the mesh indices.
  250. * Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array).
  251. * If the mesh has no geometry, a new Geometry object is created and set to the mesh.
  252. * This method creates a new index buffer each call.
  253. * Returns the Mesh.
  254. * @param indices the source data
  255. * @param totalVertices defines the total number of vertices referenced by indices (could be null)
  256. * @returns source mesh
  257. */
  258. setIndices(indices, totalVertices = null) {
  259. if (this.sourceMesh) {
  260. this.sourceMesh.setIndices(indices, totalVertices);
  261. }
  262. return this.sourceMesh;
  263. }
  264. /**
  265. * Boolean : True if the mesh owns the requested kind of data.
  266. * @param kind defines which buffer to check (positions, indices, normals, etc). Possible `kind` values :
  267. * - VertexBuffer.PositionKind
  268. * - VertexBuffer.UVKind
  269. * - VertexBuffer.UV2Kind
  270. * - VertexBuffer.UV3Kind
  271. * - VertexBuffer.UV4Kind
  272. * - VertexBuffer.UV5Kind
  273. * - VertexBuffer.UV6Kind
  274. * - VertexBuffer.ColorKind
  275. * - VertexBuffer.MatricesIndicesKind
  276. * - VertexBuffer.MatricesIndicesExtraKind
  277. * - VertexBuffer.MatricesWeightsKind
  278. * - VertexBuffer.MatricesWeightsExtraKind
  279. * @returns true if data kind is present
  280. */
  281. isVerticesDataPresent(kind) {
  282. return this._sourceMesh.isVerticesDataPresent(kind);
  283. }
  284. /**
  285. * @returns an array of indices (IndicesArray).
  286. */
  287. getIndices() {
  288. return this._sourceMesh.getIndices();
  289. }
  290. get _positions() {
  291. return this._sourceMesh._positions;
  292. }
  293. /**
  294. * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
  295. * This means the mesh underlying bounding box and sphere are recomputed.
  296. * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
  297. * @param applyMorph defines whether to apply the morph target before computing the bounding info
  298. * @returns the current mesh
  299. */
  300. refreshBoundingInfo(applySkeleton = false, applyMorph = false) {
  301. if (this.hasBoundingInfo && this.getBoundingInfo().isLocked) {
  302. return this;
  303. }
  304. const bias = this._sourceMesh.geometry ? this._sourceMesh.geometry.boundingBias : null;
  305. this._refreshBoundingInfo(this._sourceMesh._getPositionData(applySkeleton, applyMorph), bias);
  306. return this;
  307. }
  308. /** @internal */
  309. _preActivate() {
  310. if (this._currentLOD) {
  311. this._currentLOD._preActivate();
  312. }
  313. return this;
  314. }
  315. /**
  316. * @internal
  317. */
  318. _activate(renderId, intermediateRendering) {
  319. super._activate(renderId, intermediateRendering);
  320. if (!this._sourceMesh.subMeshes) {
  321. Logger.Warn("Instances should only be created for meshes with geometry.");
  322. }
  323. if (this._currentLOD) {
  324. const differentSign = this._currentLOD._getWorldMatrixDeterminant() >= 0 !== this._getWorldMatrixDeterminant() >= 0;
  325. if (differentSign) {
  326. this._internalAbstractMeshDataInfo._actAsRegularMesh = true;
  327. return true;
  328. }
  329. this._internalAbstractMeshDataInfo._actAsRegularMesh = false;
  330. this._currentLOD._registerInstanceForRenderId(this, renderId);
  331. if (intermediateRendering) {
  332. if (!this._currentLOD._internalAbstractMeshDataInfo._isActiveIntermediate) {
  333. this._currentLOD._internalAbstractMeshDataInfo._onlyForInstancesIntermediate = true;
  334. return true;
  335. }
  336. }
  337. else {
  338. if (!this._currentLOD._internalAbstractMeshDataInfo._isActive) {
  339. this._currentLOD._internalAbstractMeshDataInfo._onlyForInstances = true;
  340. return true;
  341. }
  342. }
  343. }
  344. return false;
  345. }
  346. /** @internal */
  347. _postActivate() {
  348. if (this._sourceMesh.edgesShareWithInstances && this._sourceMesh._edgesRenderer && this._sourceMesh._edgesRenderer.isEnabled && this._sourceMesh._renderingGroup) {
  349. // we are using the edge renderer of the source mesh
  350. this._sourceMesh._renderingGroup._edgesRenderers.pushNoDuplicate(this._sourceMesh._edgesRenderer);
  351. this._sourceMesh._edgesRenderer.customInstances.push(this.getWorldMatrix());
  352. }
  353. else if (this._edgesRenderer && this._edgesRenderer.isEnabled && this._sourceMesh._renderingGroup) {
  354. // we are using the edge renderer defined for this instance
  355. this._sourceMesh._renderingGroup._edgesRenderers.push(this._edgesRenderer);
  356. }
  357. }
  358. getWorldMatrix() {
  359. if (this._currentLOD && this._currentLOD.billboardMode !== TransformNode.BILLBOARDMODE_NONE && this._currentLOD._masterMesh !== this) {
  360. if (!this._billboardWorldMatrix) {
  361. this._billboardWorldMatrix = new Matrix();
  362. }
  363. const tempMaster = this._currentLOD._masterMesh;
  364. this._currentLOD._masterMesh = this;
  365. TmpVectors.Vector3[7].copyFrom(this._currentLOD.position);
  366. this._currentLOD.position.set(0, 0, 0);
  367. this._billboardWorldMatrix.copyFrom(this._currentLOD.computeWorldMatrix(true));
  368. this._currentLOD.position.copyFrom(TmpVectors.Vector3[7]);
  369. this._currentLOD._masterMesh = tempMaster;
  370. return this._billboardWorldMatrix;
  371. }
  372. return super.getWorldMatrix();
  373. }
  374. get isAnInstance() {
  375. return true;
  376. }
  377. /**
  378. * Returns the current associated LOD AbstractMesh.
  379. * @param camera defines the camera to use to pick the LOD level
  380. * @returns a Mesh or `null` if no LOD is associated with the AbstractMesh
  381. */
  382. getLOD(camera) {
  383. if (!camera) {
  384. return this;
  385. }
  386. const sourceMeshLODLevels = this.sourceMesh.getLODLevels();
  387. if (!sourceMeshLODLevels || sourceMeshLODLevels.length === 0) {
  388. this._currentLOD = this.sourceMesh;
  389. }
  390. else {
  391. const boundingInfo = this.getBoundingInfo();
  392. this._currentLOD = this.sourceMesh.getLOD(camera, boundingInfo.boundingSphere);
  393. }
  394. return this._currentLOD;
  395. }
  396. /**
  397. * @internal
  398. */
  399. _preActivateForIntermediateRendering(renderId) {
  400. return this.sourceMesh._preActivateForIntermediateRendering(renderId);
  401. }
  402. /** @internal */
  403. _syncSubMeshes() {
  404. this.releaseSubMeshes();
  405. if (this._sourceMesh.subMeshes) {
  406. for (let index = 0; index < this._sourceMesh.subMeshes.length; index++) {
  407. this._sourceMesh.subMeshes[index].clone(this, this._sourceMesh);
  408. }
  409. }
  410. return this;
  411. }
  412. /** @internal */
  413. _generatePointsArray() {
  414. return this._sourceMesh._generatePointsArray();
  415. }
  416. /** @internal */
  417. _updateBoundingInfo() {
  418. if (this.hasBoundingInfo) {
  419. this.getBoundingInfo().update(this.worldMatrixFromCache);
  420. }
  421. else {
  422. this.buildBoundingInfo(this.absolutePosition, this.absolutePosition, this.worldMatrixFromCache);
  423. }
  424. this._updateSubMeshesBoundingInfo(this.worldMatrixFromCache);
  425. return this;
  426. }
  427. /**
  428. * Creates a new InstancedMesh from the current mesh.
  429. *
  430. * Returns the clone.
  431. * @param name the cloned mesh name
  432. * @param newParent the optional Node to parent the clone to.
  433. * @param doNotCloneChildren if `true` the model children aren't cloned.
  434. * @param newSourceMesh if set this mesh will be used as the source mesh instead of ths instance's one
  435. * @returns the clone
  436. */
  437. clone(name, newParent = null, doNotCloneChildren, newSourceMesh) {
  438. const result = (newSourceMesh || this._sourceMesh).createInstance(name);
  439. // Deep copy
  440. DeepCopier.DeepCopy(this, result, [
  441. "name",
  442. "subMeshes",
  443. "uniqueId",
  444. "parent",
  445. "lightSources",
  446. "receiveShadows",
  447. "material",
  448. "visibility",
  449. "skeleton",
  450. "sourceMesh",
  451. "isAnInstance",
  452. "facetNb",
  453. "isFacetDataEnabled",
  454. "isBlocked",
  455. "useBones",
  456. "hasInstances",
  457. "collider",
  458. "edgesRenderer",
  459. "forward",
  460. "up",
  461. "right",
  462. "absolutePosition",
  463. "absoluteScaling",
  464. "absoluteRotationQuaternion",
  465. "isWorldMatrixFrozen",
  466. "nonUniformScaling",
  467. "behaviors",
  468. "worldMatrixFromCache",
  469. "hasThinInstances",
  470. "hasBoundingInfo",
  471. ], []);
  472. // Bounding info
  473. this.refreshBoundingInfo();
  474. // Parent
  475. if (newParent) {
  476. result.parent = newParent;
  477. }
  478. if (!doNotCloneChildren) {
  479. // Children
  480. for (let index = 0; index < this.getScene().meshes.length; index++) {
  481. const mesh = this.getScene().meshes[index];
  482. if (mesh.parent === this) {
  483. mesh.clone(mesh.name, result);
  484. }
  485. }
  486. }
  487. result.computeWorldMatrix(true);
  488. this.onClonedObservable.notifyObservers(result);
  489. return result;
  490. }
  491. /**
  492. * Disposes the InstancedMesh.
  493. * Returns nothing.
  494. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  495. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  496. */
  497. dispose(doNotRecurse, disposeMaterialAndTextures = false) {
  498. // Remove from mesh
  499. this._sourceMesh.removeInstance(this);
  500. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  501. }
  502. /**
  503. * @internal
  504. */
  505. _serializeAsParent(serializationObject) {
  506. super._serializeAsParent(serializationObject);
  507. serializationObject.parentId = this._sourceMesh.uniqueId;
  508. serializationObject.parentInstanceIndex = this._indexInSourceMeshInstanceArray;
  509. }
  510. /**
  511. * Instantiate (when possible) or clone that node with its hierarchy
  512. * @param newParent defines the new parent to use for the instance (or clone)
  513. * @param options defines options to configure how copy is done
  514. * @param options.doNotInstantiate defines if the model must be instantiated or just cloned
  515. * @param options.newSourcedMesh newSourcedMesh the new source mesh for the instance (or clone)
  516. * @param onNewNodeCreated defines an option callback to call when a clone or an instance is created
  517. * @returns an instance (or a clone) of the current node with its hierarchy
  518. */
  519. instantiateHierarchy(newParent = null, options, onNewNodeCreated) {
  520. const clone = this.clone("Clone of " + (this.name || this.id), newParent || this.parent, true, options && options.newSourcedMesh);
  521. if (clone) {
  522. if (onNewNodeCreated) {
  523. onNewNodeCreated(this, clone);
  524. }
  525. }
  526. for (const child of this.getChildTransformNodes(true)) {
  527. child.instantiateHierarchy(clone, options, onNewNodeCreated);
  528. }
  529. return clone;
  530. }
  531. }
  532. Mesh.prototype.registerInstancedBuffer = function (kind, stride) {
  533. // Remove existing one
  534. this._userInstancedBuffersStorage?.vertexBuffers[kind]?.dispose();
  535. // Creates the instancedBuffer field if not present
  536. if (!this.instancedBuffers) {
  537. this.instancedBuffers = {};
  538. for (const instance of this.instances) {
  539. instance.instancedBuffers = {};
  540. }
  541. }
  542. if (!this._userInstancedBuffersStorage) {
  543. this._userInstancedBuffersStorage = {
  544. data: {},
  545. vertexBuffers: {},
  546. strides: {},
  547. sizes: {},
  548. vertexArrayObjects: this.getEngine().getCaps().vertexArrayObject ? {} : undefined,
  549. };
  550. }
  551. // Creates an empty property for this kind
  552. this.instancedBuffers[kind] = null;
  553. this._userInstancedBuffersStorage.strides[kind] = stride;
  554. this._userInstancedBuffersStorage.sizes[kind] = stride * 32; // Initial size
  555. this._userInstancedBuffersStorage.data[kind] = new Float32Array(this._userInstancedBuffersStorage.sizes[kind]);
  556. this._userInstancedBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), this._userInstancedBuffersStorage.data[kind], kind, true, false, stride, true);
  557. for (const instance of this.instances) {
  558. instance.instancedBuffers[kind] = null;
  559. }
  560. this._invalidateInstanceVertexArrayObject();
  561. this._markSubMeshesAsAttributesDirty();
  562. };
  563. Mesh.prototype._processInstancedBuffers = function (visibleInstances, renderSelf) {
  564. const instanceCount = visibleInstances ? visibleInstances.length : 0;
  565. for (const kind in this.instancedBuffers) {
  566. let size = this._userInstancedBuffersStorage.sizes[kind];
  567. const stride = this._userInstancedBuffersStorage.strides[kind];
  568. // Resize if required
  569. const expectedSize = (instanceCount + 1) * stride;
  570. while (size < expectedSize) {
  571. size *= 2;
  572. }
  573. if (this._userInstancedBuffersStorage.data[kind].length != size) {
  574. this._userInstancedBuffersStorage.data[kind] = new Float32Array(size);
  575. this._userInstancedBuffersStorage.sizes[kind] = size;
  576. if (this._userInstancedBuffersStorage.vertexBuffers[kind]) {
  577. this._userInstancedBuffersStorage.vertexBuffers[kind].dispose();
  578. this._userInstancedBuffersStorage.vertexBuffers[kind] = null;
  579. }
  580. }
  581. const data = this._userInstancedBuffersStorage.data[kind];
  582. // Update data buffer
  583. let offset = 0;
  584. if (renderSelf) {
  585. const value = this.instancedBuffers[kind];
  586. if (value.toArray) {
  587. value.toArray(data, offset);
  588. }
  589. else if (value.copyToArray) {
  590. value.copyToArray(data, offset);
  591. }
  592. else {
  593. data[offset] = value;
  594. }
  595. offset += stride;
  596. }
  597. for (let instanceIndex = 0; instanceIndex < instanceCount; instanceIndex++) {
  598. const instance = visibleInstances[instanceIndex];
  599. const value = instance.instancedBuffers[kind];
  600. if (value.toArray) {
  601. value.toArray(data, offset);
  602. }
  603. else if (value.copyToArray) {
  604. value.copyToArray(data, offset);
  605. }
  606. else {
  607. data[offset] = value;
  608. }
  609. offset += stride;
  610. }
  611. // Update vertex buffer
  612. if (!this._userInstancedBuffersStorage.vertexBuffers[kind]) {
  613. this._userInstancedBuffersStorage.vertexBuffers[kind] = new VertexBuffer(this.getEngine(), this._userInstancedBuffersStorage.data[kind], kind, true, false, stride, true);
  614. this._invalidateInstanceVertexArrayObject();
  615. }
  616. else {
  617. this._userInstancedBuffersStorage.vertexBuffers[kind].updateDirectly(data, 0);
  618. }
  619. }
  620. };
  621. Mesh.prototype._invalidateInstanceVertexArrayObject = function () {
  622. if (!this._userInstancedBuffersStorage || this._userInstancedBuffersStorage.vertexArrayObjects === undefined) {
  623. return;
  624. }
  625. for (const kind in this._userInstancedBuffersStorage.vertexArrayObjects) {
  626. this.getEngine().releaseVertexArrayObject(this._userInstancedBuffersStorage.vertexArrayObjects[kind]);
  627. }
  628. this._userInstancedBuffersStorage.vertexArrayObjects = {};
  629. };
  630. Mesh.prototype._disposeInstanceSpecificData = function () {
  631. if (this._instanceDataStorage.instancesBuffer) {
  632. this._instanceDataStorage.instancesBuffer.dispose();
  633. this._instanceDataStorage.instancesBuffer = null;
  634. }
  635. while (this.instances.length) {
  636. this.instances[0].dispose();
  637. }
  638. for (const kind in this.instancedBuffers) {
  639. if (this._userInstancedBuffersStorage.vertexBuffers[kind]) {
  640. this._userInstancedBuffersStorage.vertexBuffers[kind].dispose();
  641. }
  642. }
  643. this._invalidateInstanceVertexArrayObject();
  644. this.instancedBuffers = {};
  645. };
  646. //# sourceMappingURL=instancedMesh.js.map