subMesh.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. import { VertexBuffer } from "../Buffers/buffer.js";
  2. import { IntersectionInfo } from "../Collisions/intersectionInfo.js";
  3. import { BoundingInfo } from "../Culling/boundingInfo.js";
  4. import { extractMinAndMaxIndexed } from "../Maths/math.functions.js";
  5. import { DrawWrapper } from "../Materials/drawWrapper.js";
  6. /**
  7. * Defines a subdivision inside a mesh
  8. */
  9. export class SubMesh {
  10. /**
  11. * Gets material defines used by the effect associated to the sub mesh
  12. */
  13. get materialDefines() {
  14. return this._mainDrawWrapperOverride ? this._mainDrawWrapperOverride.defines : this._getDrawWrapper()?.defines;
  15. }
  16. /**
  17. * Sets material defines used by the effect associated to the sub mesh
  18. */
  19. set materialDefines(defines) {
  20. const drawWrapper = this._mainDrawWrapperOverride ?? this._getDrawWrapper(undefined, true);
  21. drawWrapper.defines = defines;
  22. }
  23. /**
  24. * @internal
  25. */
  26. _getDrawWrapper(passId, createIfNotExisting = false) {
  27. passId = passId ?? this._engine.currentRenderPassId;
  28. let drawWrapper = this._drawWrappers[passId];
  29. if (!drawWrapper && createIfNotExisting) {
  30. this._drawWrappers[passId] = drawWrapper = new DrawWrapper(this._mesh.getScene().getEngine());
  31. }
  32. return drawWrapper;
  33. }
  34. /**
  35. * @internal
  36. */
  37. _removeDrawWrapper(passId, disposeWrapper = true) {
  38. if (disposeWrapper) {
  39. this._drawWrappers[passId]?.dispose();
  40. }
  41. this._drawWrappers[passId] = undefined;
  42. }
  43. /**
  44. * Gets associated (main) effect (possibly the effect override if defined)
  45. */
  46. get effect() {
  47. return this._mainDrawWrapperOverride ? this._mainDrawWrapperOverride.effect : this._getDrawWrapper()?.effect ?? null;
  48. }
  49. /** @internal */
  50. get _drawWrapper() {
  51. return this._mainDrawWrapperOverride ?? this._getDrawWrapper(undefined, true);
  52. }
  53. /** @internal */
  54. get _drawWrapperOverride() {
  55. return this._mainDrawWrapperOverride;
  56. }
  57. /**
  58. * @internal
  59. */
  60. _setMainDrawWrapperOverride(wrapper) {
  61. this._mainDrawWrapperOverride = wrapper;
  62. }
  63. /**
  64. * Sets associated effect (effect used to render this submesh)
  65. * @param effect defines the effect to associate with
  66. * @param defines defines the set of defines used to compile this effect
  67. * @param materialContext material context associated to the effect
  68. * @param resetContext true to reset the draw context
  69. */
  70. setEffect(effect, defines = null, materialContext, resetContext = true) {
  71. const drawWrapper = this._drawWrapper;
  72. drawWrapper.setEffect(effect, defines, resetContext);
  73. if (materialContext !== undefined) {
  74. drawWrapper.materialContext = materialContext;
  75. }
  76. if (!effect) {
  77. drawWrapper.defines = null;
  78. drawWrapper.materialContext = undefined;
  79. }
  80. }
  81. /**
  82. * Resets the draw wrappers cache
  83. * @param passId If provided, releases only the draw wrapper corresponding to this render pass id
  84. */
  85. resetDrawCache(passId) {
  86. if (this._drawWrappers) {
  87. if (passId !== undefined) {
  88. this._removeDrawWrapper(passId);
  89. return;
  90. }
  91. else {
  92. for (const drawWrapper of this._drawWrappers) {
  93. drawWrapper?.dispose();
  94. }
  95. }
  96. }
  97. this._drawWrappers = [];
  98. }
  99. /**
  100. * Add a new submesh to a mesh
  101. * @param materialIndex defines the material index to use
  102. * @param verticesStart defines vertex index start
  103. * @param verticesCount defines vertices count
  104. * @param indexStart defines index start
  105. * @param indexCount defines indices count
  106. * @param mesh defines the parent mesh
  107. * @param renderingMesh defines an optional rendering mesh
  108. * @param createBoundingBox defines if bounding box should be created for this submesh
  109. * @returns the new submesh
  110. */
  111. static AddToMesh(materialIndex, verticesStart, verticesCount, indexStart, indexCount, mesh, renderingMesh, createBoundingBox = true) {
  112. return new SubMesh(materialIndex, verticesStart, verticesCount, indexStart, indexCount, mesh, renderingMesh, createBoundingBox);
  113. }
  114. /**
  115. * Creates a new submesh
  116. * @param materialIndex defines the material index to use
  117. * @param verticesStart defines vertex index start
  118. * @param verticesCount defines vertices count
  119. * @param indexStart defines index start
  120. * @param indexCount defines indices count
  121. * @param mesh defines the parent mesh
  122. * @param renderingMesh defines an optional rendering mesh
  123. * @param createBoundingBox defines if bounding box should be created for this submesh
  124. * @param addToMesh defines a boolean indicating that the submesh must be added to the mesh.subMeshes array (true by default)
  125. */
  126. constructor(
  127. /** the material index to use */
  128. materialIndex,
  129. /** vertex index start */
  130. verticesStart,
  131. /** vertices count */
  132. verticesCount,
  133. /** index start */
  134. indexStart,
  135. /** indices count */
  136. indexCount, mesh, renderingMesh, createBoundingBox = true, addToMesh = true) {
  137. this.materialIndex = materialIndex;
  138. this.verticesStart = verticesStart;
  139. this.verticesCount = verticesCount;
  140. this.indexStart = indexStart;
  141. this.indexCount = indexCount;
  142. this._mainDrawWrapperOverride = null;
  143. /** @internal */
  144. this._linesIndexCount = 0;
  145. this._linesIndexBuffer = null;
  146. /** @internal */
  147. this._lastColliderWorldVertices = null;
  148. /** @internal */
  149. this._lastColliderTransformMatrix = null;
  150. /** @internal */
  151. this._wasDispatched = false;
  152. /** @internal */
  153. this._renderId = 0;
  154. /** @internal */
  155. this._alphaIndex = 0;
  156. /** @internal */
  157. this._distanceToCamera = 0;
  158. this._currentMaterial = null;
  159. this._mesh = mesh;
  160. this._renderingMesh = renderingMesh || mesh;
  161. if (addToMesh) {
  162. mesh.subMeshes.push(this);
  163. }
  164. this._engine = this._mesh.getScene().getEngine();
  165. this.resetDrawCache();
  166. this._trianglePlanes = [];
  167. this._id = mesh.subMeshes.length - 1;
  168. if (createBoundingBox) {
  169. this.refreshBoundingInfo();
  170. mesh.computeWorldMatrix(true);
  171. }
  172. }
  173. /**
  174. * Returns true if this submesh covers the entire parent mesh
  175. * @ignorenaming
  176. */
  177. // eslint-disable-next-line @typescript-eslint/naming-convention
  178. get IsGlobal() {
  179. return this.verticesStart === 0 && this.verticesCount === this._mesh.getTotalVertices() && this.indexStart === 0 && this.indexCount === this._mesh.getTotalIndices();
  180. }
  181. /**
  182. * Returns the submesh BoundingInfo object
  183. * @returns current bounding info (or mesh's one if the submesh is global)
  184. */
  185. getBoundingInfo() {
  186. if (this.IsGlobal || this._mesh.hasThinInstances) {
  187. return this._mesh.getBoundingInfo();
  188. }
  189. return this._boundingInfo;
  190. }
  191. /**
  192. * Sets the submesh BoundingInfo
  193. * @param boundingInfo defines the new bounding info to use
  194. * @returns the SubMesh
  195. */
  196. setBoundingInfo(boundingInfo) {
  197. this._boundingInfo = boundingInfo;
  198. return this;
  199. }
  200. /**
  201. * Returns the mesh of the current submesh
  202. * @returns the parent mesh
  203. */
  204. getMesh() {
  205. return this._mesh;
  206. }
  207. /**
  208. * Returns the rendering mesh of the submesh
  209. * @returns the rendering mesh (could be different from parent mesh)
  210. */
  211. getRenderingMesh() {
  212. return this._renderingMesh;
  213. }
  214. /**
  215. * Returns the replacement mesh of the submesh
  216. * @returns the replacement mesh (could be different from parent mesh)
  217. */
  218. getReplacementMesh() {
  219. return this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh ? this._mesh : null;
  220. }
  221. /**
  222. * Returns the effective mesh of the submesh
  223. * @returns the effective mesh (could be different from parent mesh)
  224. */
  225. getEffectiveMesh() {
  226. const replacementMesh = this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh ? this._mesh : null;
  227. return replacementMesh ? replacementMesh : this._renderingMesh;
  228. }
  229. /**
  230. * Returns the submesh material
  231. * @param getDefaultMaterial Defines whether or not to get the default material if nothing has been defined.
  232. * @returns null or the current material
  233. */
  234. getMaterial(getDefaultMaterial = true) {
  235. const rootMaterial = this._renderingMesh.getMaterialForRenderPass(this._engine.currentRenderPassId) ?? this._renderingMesh.material;
  236. if (!rootMaterial) {
  237. return getDefaultMaterial ? this._mesh.getScene().defaultMaterial : null;
  238. }
  239. else if (this._isMultiMaterial(rootMaterial)) {
  240. const effectiveMaterial = rootMaterial.getSubMaterial(this.materialIndex);
  241. if (this._currentMaterial !== effectiveMaterial) {
  242. this._currentMaterial = effectiveMaterial;
  243. this.resetDrawCache();
  244. }
  245. return effectiveMaterial;
  246. }
  247. return rootMaterial;
  248. }
  249. _isMultiMaterial(material) {
  250. return material.getSubMaterial !== undefined;
  251. }
  252. // Methods
  253. /**
  254. * Sets a new updated BoundingInfo object to the submesh
  255. * @param data defines an optional position array to use to determine the bounding info
  256. * @returns the SubMesh
  257. */
  258. refreshBoundingInfo(data = null) {
  259. this._lastColliderWorldVertices = null;
  260. if (this.IsGlobal || !this._renderingMesh || !this._renderingMesh.geometry) {
  261. return this;
  262. }
  263. if (!data) {
  264. data = this._renderingMesh.getVerticesData(VertexBuffer.PositionKind);
  265. }
  266. if (!data) {
  267. this._boundingInfo = this._mesh.getBoundingInfo();
  268. return this;
  269. }
  270. const indices = this._renderingMesh.getIndices();
  271. let extend;
  272. //is this the only submesh?
  273. if (this.indexStart === 0 && this.indexCount === indices.length) {
  274. const boundingInfo = this._renderingMesh.getBoundingInfo();
  275. //the rendering mesh's bounding info can be used, it is the standard submesh for all indices.
  276. extend = { minimum: boundingInfo.minimum.clone(), maximum: boundingInfo.maximum.clone() };
  277. }
  278. else {
  279. extend = extractMinAndMaxIndexed(data, indices, this.indexStart, this.indexCount, this._renderingMesh.geometry.boundingBias);
  280. }
  281. if (this._boundingInfo) {
  282. this._boundingInfo.reConstruct(extend.minimum, extend.maximum);
  283. }
  284. else {
  285. this._boundingInfo = new BoundingInfo(extend.minimum, extend.maximum);
  286. }
  287. return this;
  288. }
  289. /**
  290. * @internal
  291. */
  292. _checkCollision(collider) {
  293. const boundingInfo = this.getBoundingInfo();
  294. return boundingInfo._checkCollision(collider);
  295. }
  296. /**
  297. * Updates the submesh BoundingInfo
  298. * @param world defines the world matrix to use to update the bounding info
  299. * @returns the submesh
  300. */
  301. updateBoundingInfo(world) {
  302. let boundingInfo = this.getBoundingInfo();
  303. if (!boundingInfo) {
  304. this.refreshBoundingInfo();
  305. boundingInfo = this.getBoundingInfo();
  306. }
  307. if (boundingInfo) {
  308. boundingInfo.update(world);
  309. }
  310. return this;
  311. }
  312. /**
  313. * True is the submesh bounding box intersects the frustum defined by the passed array of planes.
  314. * @param frustumPlanes defines the frustum planes
  315. * @returns true if the submesh is intersecting with the frustum
  316. */
  317. isInFrustum(frustumPlanes) {
  318. const boundingInfo = this.getBoundingInfo();
  319. if (!boundingInfo) {
  320. return false;
  321. }
  322. return boundingInfo.isInFrustum(frustumPlanes, this._mesh.cullingStrategy);
  323. }
  324. /**
  325. * True is the submesh bounding box is completely inside the frustum defined by the passed array of planes
  326. * @param frustumPlanes defines the frustum planes
  327. * @returns true if the submesh is inside the frustum
  328. */
  329. isCompletelyInFrustum(frustumPlanes) {
  330. const boundingInfo = this.getBoundingInfo();
  331. if (!boundingInfo) {
  332. return false;
  333. }
  334. return boundingInfo.isCompletelyInFrustum(frustumPlanes);
  335. }
  336. /**
  337. * Renders the submesh
  338. * @param enableAlphaMode defines if alpha needs to be used
  339. * @returns the submesh
  340. */
  341. render(enableAlphaMode) {
  342. this._renderingMesh.render(this, enableAlphaMode, this._mesh._internalAbstractMeshDataInfo._actAsRegularMesh ? this._mesh : undefined);
  343. return this;
  344. }
  345. /**
  346. * @internal
  347. */
  348. _getLinesIndexBuffer(indices, engine) {
  349. if (!this._linesIndexBuffer) {
  350. const linesIndices = [];
  351. for (let index = this.indexStart; index < this.indexStart + this.indexCount; index += 3) {
  352. linesIndices.push(indices[index], indices[index + 1], indices[index + 1], indices[index + 2], indices[index + 2], indices[index]);
  353. }
  354. this._linesIndexBuffer = engine.createIndexBuffer(linesIndices);
  355. this._linesIndexCount = linesIndices.length;
  356. }
  357. return this._linesIndexBuffer;
  358. }
  359. /**
  360. * Checks if the submesh intersects with a ray
  361. * @param ray defines the ray to test
  362. * @returns true is the passed ray intersects the submesh bounding box
  363. */
  364. canIntersects(ray) {
  365. const boundingInfo = this.getBoundingInfo();
  366. if (!boundingInfo) {
  367. return false;
  368. }
  369. return ray.intersectsBox(boundingInfo.boundingBox);
  370. }
  371. /**
  372. * Intersects current submesh with a ray
  373. * @param ray defines the ray to test
  374. * @param positions defines mesh's positions array
  375. * @param indices defines mesh's indices array
  376. * @param fastCheck defines if the first intersection will be used (and not the closest)
  377. * @param trianglePredicate defines an optional predicate used to select faces when a mesh intersection is detected
  378. * @returns intersection info or null if no intersection
  379. */
  380. intersects(ray, positions, indices, fastCheck, trianglePredicate) {
  381. const material = this.getMaterial();
  382. if (!material) {
  383. return null;
  384. }
  385. let step = 3;
  386. let checkStopper = false;
  387. switch (material.fillMode) {
  388. case 3:
  389. case 5:
  390. case 6:
  391. case 8:
  392. return null;
  393. case 7:
  394. step = 1;
  395. checkStopper = true;
  396. break;
  397. default:
  398. break;
  399. }
  400. // LineMesh first as it's also a Mesh...
  401. if (material.fillMode === 4) {
  402. // Check if mesh is unindexed
  403. if (!indices.length) {
  404. return this._intersectUnIndexedLines(ray, positions, indices, this._mesh.intersectionThreshold, fastCheck);
  405. }
  406. return this._intersectLines(ray, positions, indices, this._mesh.intersectionThreshold, fastCheck);
  407. }
  408. else {
  409. // Check if mesh is unindexed
  410. if (!indices.length && this._mesh._unIndexed) {
  411. return this._intersectUnIndexedTriangles(ray, positions, indices, fastCheck, trianglePredicate);
  412. }
  413. return this._intersectTriangles(ray, positions, indices, step, checkStopper, fastCheck, trianglePredicate);
  414. }
  415. }
  416. /**
  417. * @internal
  418. */
  419. _intersectLines(ray, positions, indices, intersectionThreshold, fastCheck) {
  420. let intersectInfo = null;
  421. // Line test
  422. for (let index = this.indexStart; index < this.indexStart + this.indexCount; index += 2) {
  423. const p0 = positions[indices[index]];
  424. const p1 = positions[indices[index + 1]];
  425. const length = ray.intersectionSegment(p0, p1, intersectionThreshold);
  426. if (length < 0) {
  427. continue;
  428. }
  429. if (fastCheck || !intersectInfo || length < intersectInfo.distance) {
  430. intersectInfo = new IntersectionInfo(null, null, length);
  431. intersectInfo.faceId = index / 2;
  432. if (fastCheck) {
  433. break;
  434. }
  435. }
  436. }
  437. return intersectInfo;
  438. }
  439. /**
  440. * @internal
  441. */
  442. _intersectUnIndexedLines(ray, positions, indices, intersectionThreshold, fastCheck) {
  443. let intersectInfo = null;
  444. // Line test
  445. for (let index = this.verticesStart; index < this.verticesStart + this.verticesCount; index += 2) {
  446. const p0 = positions[index];
  447. const p1 = positions[index + 1];
  448. const length = ray.intersectionSegment(p0, p1, intersectionThreshold);
  449. if (length < 0) {
  450. continue;
  451. }
  452. if (fastCheck || !intersectInfo || length < intersectInfo.distance) {
  453. intersectInfo = new IntersectionInfo(null, null, length);
  454. intersectInfo.faceId = index / 2;
  455. if (fastCheck) {
  456. break;
  457. }
  458. }
  459. }
  460. return intersectInfo;
  461. }
  462. /**
  463. * @internal
  464. */
  465. _intersectTriangles(ray, positions, indices, step, checkStopper, fastCheck, trianglePredicate) {
  466. let intersectInfo = null;
  467. // Triangles test
  468. let faceId = -1;
  469. for (let index = this.indexStart; index < this.indexStart + this.indexCount - (3 - step); index += step) {
  470. faceId++;
  471. const indexA = indices[index];
  472. const indexB = indices[index + 1];
  473. const indexC = indices[index + 2];
  474. if (checkStopper && indexC === 0xffffffff) {
  475. index += 2;
  476. continue;
  477. }
  478. const p0 = positions[indexA];
  479. const p1 = positions[indexB];
  480. const p2 = positions[indexC];
  481. // stay defensive and don't check against undefined positions.
  482. if (!p0 || !p1 || !p2) {
  483. continue;
  484. }
  485. if (trianglePredicate && !trianglePredicate(p0, p1, p2, ray, indexA, indexB, indexC)) {
  486. continue;
  487. }
  488. const currentIntersectInfo = ray.intersectsTriangle(p0, p1, p2);
  489. if (currentIntersectInfo) {
  490. if (currentIntersectInfo.distance < 0) {
  491. continue;
  492. }
  493. if (fastCheck || !intersectInfo || currentIntersectInfo.distance < intersectInfo.distance) {
  494. intersectInfo = currentIntersectInfo;
  495. intersectInfo.faceId = faceId;
  496. if (fastCheck) {
  497. break;
  498. }
  499. }
  500. }
  501. }
  502. return intersectInfo;
  503. }
  504. /**
  505. * @internal
  506. */
  507. _intersectUnIndexedTriangles(ray, positions, indices, fastCheck, trianglePredicate) {
  508. let intersectInfo = null;
  509. // Triangles test
  510. for (let index = this.verticesStart; index < this.verticesStart + this.verticesCount; index += 3) {
  511. const p0 = positions[index];
  512. const p1 = positions[index + 1];
  513. const p2 = positions[index + 2];
  514. if (trianglePredicate && !trianglePredicate(p0, p1, p2, ray, -1, -1, -1)) {
  515. continue;
  516. }
  517. const currentIntersectInfo = ray.intersectsTriangle(p0, p1, p2);
  518. if (currentIntersectInfo) {
  519. if (currentIntersectInfo.distance < 0) {
  520. continue;
  521. }
  522. if (fastCheck || !intersectInfo || currentIntersectInfo.distance < intersectInfo.distance) {
  523. intersectInfo = currentIntersectInfo;
  524. intersectInfo.faceId = index / 3;
  525. if (fastCheck) {
  526. break;
  527. }
  528. }
  529. }
  530. }
  531. return intersectInfo;
  532. }
  533. /** @internal */
  534. _rebuild() {
  535. if (this._linesIndexBuffer) {
  536. this._linesIndexBuffer = null;
  537. }
  538. }
  539. // Clone
  540. /**
  541. * Creates a new submesh from the passed mesh
  542. * @param newMesh defines the new hosting mesh
  543. * @param newRenderingMesh defines an optional rendering mesh
  544. * @returns the new submesh
  545. */
  546. clone(newMesh, newRenderingMesh) {
  547. const result = new SubMesh(this.materialIndex, this.verticesStart, this.verticesCount, this.indexStart, this.indexCount, newMesh, newRenderingMesh, false);
  548. if (!this.IsGlobal) {
  549. const boundingInfo = this.getBoundingInfo();
  550. if (!boundingInfo) {
  551. return result;
  552. }
  553. result._boundingInfo = new BoundingInfo(boundingInfo.minimum, boundingInfo.maximum);
  554. }
  555. return result;
  556. }
  557. // Dispose
  558. /**
  559. * Release associated resources
  560. */
  561. dispose() {
  562. if (this._linesIndexBuffer) {
  563. this._mesh.getScene().getEngine()._releaseBuffer(this._linesIndexBuffer);
  564. this._linesIndexBuffer = null;
  565. }
  566. // Remove from mesh
  567. const index = this._mesh.subMeshes.indexOf(this);
  568. this._mesh.subMeshes.splice(index, 1);
  569. this.resetDrawCache();
  570. }
  571. /**
  572. * Gets the class name
  573. * @returns the string "SubMesh".
  574. */
  575. getClassName() {
  576. return "SubMesh";
  577. }
  578. // Statics
  579. /**
  580. * Creates a new submesh from indices data
  581. * @param materialIndex the index of the main mesh material
  582. * @param startIndex the index where to start the copy in the mesh indices array
  583. * @param indexCount the number of indices to copy then from the startIndex
  584. * @param mesh the main mesh to create the submesh from
  585. * @param renderingMesh the optional rendering mesh
  586. * @param createBoundingBox defines if bounding box should be created for this submesh
  587. * @returns a new submesh
  588. */
  589. static CreateFromIndices(materialIndex, startIndex, indexCount, mesh, renderingMesh, createBoundingBox = true) {
  590. let minVertexIndex = Number.MAX_VALUE;
  591. let maxVertexIndex = -Number.MAX_VALUE;
  592. const whatWillRender = renderingMesh || mesh;
  593. const indices = whatWillRender.getIndices();
  594. for (let index = startIndex; index < startIndex + indexCount; index++) {
  595. const vertexIndex = indices[index];
  596. if (vertexIndex < minVertexIndex) {
  597. minVertexIndex = vertexIndex;
  598. }
  599. if (vertexIndex > maxVertexIndex) {
  600. maxVertexIndex = vertexIndex;
  601. }
  602. }
  603. return new SubMesh(materialIndex, minVertexIndex, maxVertexIndex - minVertexIndex + 1, startIndex, indexCount, mesh, renderingMesh, createBoundingBox);
  604. }
  605. }
  606. //# sourceMappingURL=subMesh.js.map