UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

943 lines (942 loc) 105 kB
import { __esDecorate, __runInitializers } from "../tslib.es6.js"; import { Vector3, Vector4, TmpVectors } from "../Maths/math.vector.pure.js"; import { VertexBuffer, VertexBufferDeduceStride } from "../Buffers/buffer.pure.js"; import { _WarnImport } from "../Misc/devTools.js"; import { Color4 } from "../Maths/math.color.pure.js"; import { Logger } from "../Misc/logger.js"; import { nativeOverride } from "../Misc/decorators.js"; import { makeSyncFunction, runCoroutineSync } from "../Misc/coroutine.js"; import { RuntimeError, ErrorCodes } from "../Misc/error.js"; import { SubMesh } from "./subMesh.js"; /** Class used to attach material info to sub section of a vertex data class */ export class VertexDataMaterialInfo { } /** * This class contains the various kinds of data on every vertex of a mesh used in determining its shape and appearance */ let VertexData = (() => { var _a; let _staticExtraInitializers = []; let _static__TransformVector3Coordinates_decorators; let _static__TransformVector3Normals_decorators; let _static__TransformVector4Normals_decorators; let _static__FlipFaces_decorators; return _a = class VertexData { /** * Creates a new VertexData */ constructor() { /** * Gets the unique ID of this vertex Data */ this.uniqueId = 0; /** * Metadata used to store contextual values */ this.metadata = {}; this._applyTo = makeSyncFunction(this._applyToCoroutine.bind(this)); this.uniqueId = _a._UniqueIdGenerator; _a._UniqueIdGenerator++; } /** * Uses the passed data array to set the set the values for the specified kind of data * @param data a linear array of floating numbers * @param kind the type of data that is being set, eg positions, colors etc */ set(data, kind) { if (!data.length) { Logger.Warn(`Setting vertex data kind '${kind}' with an empty array`); } switch (kind) { case VertexBuffer.PositionKind: this.positions = data; break; case VertexBuffer.NormalKind: this.normals = data; break; case VertexBuffer.TangentKind: this.tangents = data; break; case VertexBuffer.UVKind: this.uvs = data; break; case VertexBuffer.UV2Kind: this.uvs2 = data; break; case VertexBuffer.UV3Kind: this.uvs3 = data; break; case VertexBuffer.UV4Kind: this.uvs4 = data; break; case VertexBuffer.UV5Kind: this.uvs5 = data; break; case VertexBuffer.UV6Kind: this.uvs6 = data; break; case VertexBuffer.ColorKind: this.colors = data; break; case VertexBuffer.MatricesIndicesKind: this.matricesIndices = data; break; case VertexBuffer.MatricesWeightsKind: this.matricesWeights = data; break; case VertexBuffer.MatricesIndicesExtraKind: this.matricesIndicesExtra = data; break; case VertexBuffer.MatricesWeightsExtraKind: this.matricesWeightsExtra = data; break; } } /** * Associates the vertexData to the passed Mesh. * Sets it as updatable or not (default `false`) * @param mesh the mesh the vertexData is applied to * @param updatable when used and having the value true allows new data to update the vertexData * @returns the VertexData */ applyToMesh(mesh, updatable) { this._applyTo(mesh, updatable, false); return this; } /** * Associates the vertexData to the passed Geometry. * Sets it as updatable or not (default `false`) * @param geometry the geometry the vertexData is applied to * @param updatable when used and having the value true allows new data to update the vertexData * @returns VertexData */ applyToGeometry(geometry, updatable) { this._applyTo(geometry, updatable, false); return this; } /** * Updates the associated mesh * @param mesh the mesh to be updated * @returns VertexData */ updateMesh(mesh) { this._update(mesh); return this; } /** * Updates the associated geometry * @param geometry the geometry to be updated * @returns VertexData. */ updateGeometry(geometry) { this._update(geometry); return this; } /** * @internal */ *_applyToCoroutine(meshOrGeometry, updatable = false, isAsync) { if (this.positions) { meshOrGeometry.setVerticesData(VertexBuffer.PositionKind, this.positions, updatable); if (isAsync) { yield; } } if (this.normals) { meshOrGeometry.setVerticesData(VertexBuffer.NormalKind, this.normals, updatable); if (isAsync) { yield; } } if (this.tangents) { meshOrGeometry.setVerticesData(VertexBuffer.TangentKind, this.tangents, updatable); if (isAsync) { yield; } } if (this.uvs) { meshOrGeometry.setVerticesData(VertexBuffer.UVKind, this.uvs, updatable); if (isAsync) { yield; } } if (this.uvs2) { meshOrGeometry.setVerticesData(VertexBuffer.UV2Kind, this.uvs2, updatable); if (isAsync) { yield; } } if (this.uvs3) { meshOrGeometry.setVerticesData(VertexBuffer.UV3Kind, this.uvs3, updatable); if (isAsync) { yield; } } if (this.uvs4) { meshOrGeometry.setVerticesData(VertexBuffer.UV4Kind, this.uvs4, updatable); if (isAsync) { yield; } } if (this.uvs5) { meshOrGeometry.setVerticesData(VertexBuffer.UV5Kind, this.uvs5, updatable); if (isAsync) { yield; } } if (this.uvs6) { meshOrGeometry.setVerticesData(VertexBuffer.UV6Kind, this.uvs6, updatable); if (isAsync) { yield; } } if (this.colors) { const stride = this.positions && this.colors.length === this.positions.length ? 3 : 4; meshOrGeometry.setVerticesData(VertexBuffer.ColorKind, this.colors, updatable, stride); if (this.hasVertexAlpha && meshOrGeometry.hasVertexAlpha !== undefined) { meshOrGeometry.hasVertexAlpha = true; } if (isAsync) { yield; } } if (this.matricesIndices) { meshOrGeometry.setVerticesData(VertexBuffer.MatricesIndicesKind, this.matricesIndices, updatable); if (isAsync) { yield; } } if (this.matricesWeights) { meshOrGeometry.setVerticesData(VertexBuffer.MatricesWeightsKind, this.matricesWeights, updatable); if (isAsync) { yield; } } if (this.matricesIndicesExtra) { meshOrGeometry.setVerticesData(VertexBuffer.MatricesIndicesExtraKind, this.matricesIndicesExtra, updatable); if (isAsync) { yield; } } if (this.matricesWeightsExtra) { meshOrGeometry.setVerticesData(VertexBuffer.MatricesWeightsExtraKind, this.matricesWeightsExtra, updatable); if (isAsync) { yield; } } if (this.indices) { meshOrGeometry.setIndices(this.indices, null, updatable); if (isAsync) { yield; } } else { meshOrGeometry.setIndices([], null); } if (meshOrGeometry.subMeshes && this.materialInfos && this.materialInfos.length > 1) { const mesh = meshOrGeometry; mesh.subMeshes = []; for (const matInfo of this.materialInfos) { new SubMesh(matInfo.materialIndex, matInfo.verticesStart, matInfo.verticesCount, matInfo.indexStart, matInfo.indexCount, mesh); } } return this; } _update(meshOrGeometry, updateExtends, makeItUnique) { if (this.positions) { meshOrGeometry.updateVerticesData(VertexBuffer.PositionKind, this.positions, updateExtends, makeItUnique); } if (this.normals) { meshOrGeometry.updateVerticesData(VertexBuffer.NormalKind, this.normals, updateExtends, makeItUnique); } if (this.tangents) { meshOrGeometry.updateVerticesData(VertexBuffer.TangentKind, this.tangents, updateExtends, makeItUnique); } if (this.uvs) { meshOrGeometry.updateVerticesData(VertexBuffer.UVKind, this.uvs, updateExtends, makeItUnique); } if (this.uvs2) { meshOrGeometry.updateVerticesData(VertexBuffer.UV2Kind, this.uvs2, updateExtends, makeItUnique); } if (this.uvs3) { meshOrGeometry.updateVerticesData(VertexBuffer.UV3Kind, this.uvs3, updateExtends, makeItUnique); } if (this.uvs4) { meshOrGeometry.updateVerticesData(VertexBuffer.UV4Kind, this.uvs4, updateExtends, makeItUnique); } if (this.uvs5) { meshOrGeometry.updateVerticesData(VertexBuffer.UV5Kind, this.uvs5, updateExtends, makeItUnique); } if (this.uvs6) { meshOrGeometry.updateVerticesData(VertexBuffer.UV6Kind, this.uvs6, updateExtends, makeItUnique); } if (this.colors) { meshOrGeometry.updateVerticesData(VertexBuffer.ColorKind, this.colors, updateExtends, makeItUnique); } if (this.matricesIndices) { meshOrGeometry.updateVerticesData(VertexBuffer.MatricesIndicesKind, this.matricesIndices, updateExtends, makeItUnique); } if (this.matricesWeights) { meshOrGeometry.updateVerticesData(VertexBuffer.MatricesWeightsKind, this.matricesWeights, updateExtends, makeItUnique); } if (this.matricesIndicesExtra) { meshOrGeometry.updateVerticesData(VertexBuffer.MatricesIndicesExtraKind, this.matricesIndicesExtra, updateExtends, makeItUnique); } if (this.matricesWeightsExtra) { meshOrGeometry.updateVerticesData(VertexBuffer.MatricesWeightsExtraKind, this.matricesWeightsExtra, updateExtends, makeItUnique); } if (this.indices) { meshOrGeometry.setIndices(this.indices, null); } return this; } static _TransformVector3Coordinates(coordinates, transformation, offset = 0, length = coordinates.length) { const coordinate = TmpVectors.Vector3[0]; const transformedCoordinate = TmpVectors.Vector3[1]; for (let index = offset; index < offset + length; index += 3) { Vector3.FromArrayToRef(coordinates, index, coordinate); Vector3.TransformCoordinatesToRef(coordinate, transformation, transformedCoordinate); coordinates[index] = transformedCoordinate.x; coordinates[index + 1] = transformedCoordinate.y; coordinates[index + 2] = transformedCoordinate.z; } } static _TransformVector3Normals(normals, transformation, offset = 0, length = normals.length) { const normal = TmpVectors.Vector3[0]; const transformedNormal = TmpVectors.Vector3[1]; for (let index = offset; index < offset + length; index += 3) { Vector3.FromArrayToRef(normals, index, normal); Vector3.TransformNormalToRef(normal, transformation, transformedNormal); normals[index] = transformedNormal.x; normals[index + 1] = transformedNormal.y; normals[index + 2] = transformedNormal.z; } } static _TransformVector4Normals(normals, transformation, offset = 0, length = normals.length) { const normal = TmpVectors.Vector4[0]; const transformedNormal = TmpVectors.Vector4[1]; for (let index = offset; index < offset + length; index += 4) { Vector4.FromArrayToRef(normals, index, normal); Vector4.TransformNormalToRef(normal, transformation, transformedNormal); normals[index] = transformedNormal.x; normals[index + 1] = transformedNormal.y; normals[index + 2] = transformedNormal.z; normals[index + 3] = transformedNormal.w; } } static _FlipFaces(indices, offset = 0, length = indices.length) { for (let index = offset; index < offset + length; index += 3) { const tmp = indices[index + 1]; indices[index + 1] = indices[index + 2]; indices[index + 2] = tmp; } } /** * Transforms each position and each normal of the vertexData according to the passed Matrix * @param matrix the transforming matrix * @returns the VertexData */ transform(matrix) { const flip = matrix.determinant() < 0; if (this.positions) { _a._TransformVector3Coordinates(this.positions, matrix); } if (this.normals) { _a._TransformVector3Normals(this.normals, matrix); } if (this.tangents) { _a._TransformVector4Normals(this.tangents, matrix); } if (flip && this.indices) { _a._FlipFaces(this.indices); } return this; } /** * Generates an array of vertex data where each vertex data only has one material info * @returns An array of VertexData */ // eslint-disable-next-line @typescript-eslint/naming-convention splitBasedOnMaterialID() { if (!this.materialInfos || this.materialInfos.length < 2) { return [this]; } const result = []; for (const materialInfo of this.materialInfos) { const vertexData = new _a(); if (this.positions) { vertexData.positions = this.positions.slice(materialInfo.verticesStart * 3, (materialInfo.verticesCount + materialInfo.verticesStart) * 3); } if (this.normals) { vertexData.normals = this.normals.slice(materialInfo.verticesStart * 3, (materialInfo.verticesCount + materialInfo.verticesStart) * 3); } if (this.tangents) { vertexData.tangents = this.tangents.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.colors) { vertexData.colors = this.colors.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.uvs) { vertexData.uvs = this.uvs.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.uvs2) { vertexData.uvs2 = this.uvs2.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.uvs3) { vertexData.uvs3 = this.uvs3.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.uvs4) { vertexData.uvs4 = this.uvs4.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.uvs5) { vertexData.uvs5 = this.uvs5.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.uvs6) { vertexData.uvs6 = this.uvs6.slice(materialInfo.verticesStart * 2, (materialInfo.verticesCount + materialInfo.verticesStart) * 2); } if (this.matricesIndices) { vertexData.matricesIndices = this.matricesIndices.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.matricesIndicesExtra) { vertexData.matricesIndicesExtra = this.matricesIndicesExtra.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.matricesWeights) { vertexData.matricesWeights = this.matricesWeights.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.matricesWeightsExtra) { vertexData.matricesWeightsExtra = this.matricesWeightsExtra.slice(materialInfo.verticesStart * 4, (materialInfo.verticesCount + materialInfo.verticesStart) * 4); } if (this.indices) { vertexData.indices = []; for (let index = materialInfo.indexStart; index < materialInfo.indexStart + materialInfo.indexCount; index++) { vertexData.indices.push(this.indices[index] - materialInfo.verticesStart); } } const newMaterialInfo = new VertexDataMaterialInfo(); newMaterialInfo.indexStart = 0; newMaterialInfo.indexCount = vertexData.indices ? vertexData.indices.length : 0; newMaterialInfo.materialIndex = materialInfo.materialIndex; newMaterialInfo.verticesStart = 0; newMaterialInfo.verticesCount = (vertexData.positions ? vertexData.positions.length : 0) / 3; vertexData.materialInfos = [newMaterialInfo]; result.push(vertexData); } return result; } /** * Merges the passed VertexData into the current one * @param others the VertexData to be merged into the current one * @param use32BitsIndices defines a boolean indicating if indices must be store in a 32 bits array * @param forceCloneIndices defines a boolean indicating if indices are forced to be cloned * @param mergeMaterialIds defines a boolean indicating if we need to merge the material infos * @param enableCompletion defines a boolean indicating if the vertex data should be completed to be compatible * @returns the modified VertexData */ merge(others, use32BitsIndices = false, forceCloneIndices = false, mergeMaterialIds = false, enableCompletion = false) { const vertexDatas = Array.isArray(others) ? others.map((other) => { return { vertexData: other }; }) : [{ vertexData: others }]; return runCoroutineSync(this._mergeCoroutine(undefined, vertexDatas, use32BitsIndices, false, forceCloneIndices, mergeMaterialIds, enableCompletion)); } /** * @internal */ *_mergeCoroutine(transform, vertexDatas, use32BitsIndices = false, isAsync, forceCloneIndices, mergeMaterialIds = false, enableCompletion = false) { this._validate(); let others = vertexDatas.map((vertexData) => vertexData.vertexData); // eslint-disable-next-line @typescript-eslint/no-this-alias let root = this; if (enableCompletion) { // First let's make sure we have the max set of attributes on the main vertex data for (const other of others) { if (!other) { continue; } other._validate(); if (!this.normals && other.normals) { this.normals = new Float32Array(this.positions.length); } if (!this.tangents && other.tangents) { this.tangents = new Float32Array((this.positions.length / 3) * 4); } if (!this.uvs && other.uvs) { this.uvs = new Float32Array((this.positions.length / 3) * 2); } if (!this.uvs2 && other.uvs2) { this.uvs2 = new Float32Array((this.positions.length / 3) * 2); } if (!this.uvs3 && other.uvs3) { this.uvs3 = new Float32Array((this.positions.length / 3) * 2); } if (!this.uvs4 && other.uvs4) { this.uvs4 = new Float32Array((this.positions.length / 3) * 2); } if (!this.uvs5 && other.uvs5) { this.uvs5 = new Float32Array((this.positions.length / 3) * 2); } if (!this.uvs6 && other.uvs6) { this.uvs6 = new Float32Array((this.positions.length / 3) * 2); } if (!this.colors && other.colors) { this.colors = new Float32Array((this.positions.length / 3) * 4); this.colors.fill(1); // Set to white by default } if (!this.matricesIndices && other.matricesIndices) { this.matricesIndices = new Float32Array((this.positions.length / 3) * 4); } if (!this.matricesWeights && other.matricesWeights) { this.matricesWeights = new Float32Array((this.positions.length / 3) * 4); } if (!this.matricesIndicesExtra && other.matricesIndicesExtra) { this.matricesIndicesExtra = new Float32Array((this.positions.length / 3) * 4); } if (!this.matricesWeightsExtra && other.matricesWeightsExtra) { this.matricesWeightsExtra = new Float32Array((this.positions.length / 3) * 4); } } } for (const other of others) { if (!other) { continue; } if (!enableCompletion) { other._validate(); if (!this.normals !== !other.normals || !this.tangents !== !other.tangents || !this.uvs !== !other.uvs || !this.uvs2 !== !other.uvs2 || !this.uvs3 !== !other.uvs3 || !this.uvs4 !== !other.uvs4 || !this.uvs5 !== !other.uvs5 || !this.uvs6 !== !other.uvs6 || !this.colors !== !other.colors || !this.matricesIndices !== !other.matricesIndices || !this.matricesWeights !== !other.matricesWeights || !this.matricesIndicesExtra !== !other.matricesIndicesExtra || !this.matricesWeightsExtra !== !other.matricesWeightsExtra) { throw new Error("Cannot merge vertex data that do not have the same set of attributes"); } } else { // Align the others with main set of attributes if (this.normals && !other.normals) { other.normals = new Float32Array(other.positions.length); } if (this.tangents && !other.tangents) { other.tangents = new Float32Array((other.positions.length / 3) * 4); } if (this.uvs && !other.uvs) { other.uvs = new Float32Array((other.positions.length / 3) * 2); } if (this.uvs2 && !other.uvs2) { other.uvs2 = new Float32Array((other.positions.length / 3) * 2); } if (this.uvs3 && !other.uvs3) { other.uvs3 = new Float32Array((other.positions.length / 3) * 2); } if (this.uvs4 && !other.uvs4) { other.uvs4 = new Float32Array((other.positions.length / 3) * 2); } if (this.uvs5 && !other.uvs5) { other.uvs5 = new Float32Array((other.positions.length / 3) * 2); } if (this.uvs6 && !other.uvs6) { other.uvs6 = new Float32Array((other.positions.length / 3) * 2); } if (this.colors && !other.colors) { other.colors = new Float32Array((other.positions.length / 3) * 4); other.colors.fill(1); // Set to white by default } if (this.matricesIndices && !other.matricesIndices) { other.matricesIndices = new Float32Array((other.positions.length / 3) * 4); } if (this.matricesWeights && !other.matricesWeights) { other.matricesWeights = new Float32Array((other.positions.length / 3) * 4); } if (this.matricesIndicesExtra && !other.matricesIndicesExtra) { other.matricesIndicesExtra = new Float32Array((other.positions.length / 3) * 4); } if (this.matricesWeightsExtra && !other.matricesWeightsExtra) { other.matricesWeightsExtra = new Float32Array((other.positions.length / 3) * 4); } } } if (mergeMaterialIds) { // Merge material infos let materialIndex; let indexOffset = 0; let vertexOffset = 0; const materialInfos = []; let currentMaterialInfo = null; const vertexDataList = []; // We need to split vertexData with more than one materialInfo for (const split of this.splitBasedOnMaterialID()) { vertexDataList.push({ vertexData: split, transform: transform }); } for (const data of vertexDatas) { if (!data.vertexData) { continue; } for (const split of data.vertexData.splitBasedOnMaterialID()) { vertexDataList.push({ vertexData: split, transform: data.transform }); } } // Sort by material IDs vertexDataList.sort((a, b) => { const matInfoA = a.vertexData.materialInfos ? a.vertexData.materialInfos[0].materialIndex : 0; const matInfoB = b.vertexData.materialInfos ? b.vertexData.materialInfos[0].materialIndex : 0; if (matInfoA > matInfoB) { return 1; } if (matInfoA === matInfoB) { return 0; } return -1; }); // Build the new material info for (const vertexDataSource of vertexDataList) { const vertexData = vertexDataSource.vertexData; if (vertexData.materialInfos) { materialIndex = vertexData.materialInfos[0].materialIndex; } else { materialIndex = 0; } if (currentMaterialInfo && currentMaterialInfo.materialIndex === materialIndex) { currentMaterialInfo.indexCount += vertexData.indices.length; currentMaterialInfo.verticesCount += vertexData.positions.length / 3; } else { const materialInfo = new VertexDataMaterialInfo(); materialInfo.materialIndex = materialIndex; materialInfo.indexStart = indexOffset; materialInfo.indexCount = vertexData.indices.length; materialInfo.verticesStart = vertexOffset; materialInfo.verticesCount = vertexData.positions.length / 3; materialInfos.push(materialInfo); currentMaterialInfo = materialInfo; } indexOffset += vertexData.indices.length; vertexOffset += vertexData.positions.length / 3; } // Extract sorted values const first = vertexDataList.splice(0, 1)[0]; root = first.vertexData; transform = first.transform; others = vertexDataList.map((v) => v.vertexData); vertexDatas = vertexDataList; this.materialInfos = materialInfos; } // Merge geometries const totalIndices = others.reduce((indexSum, vertexData) => indexSum + (vertexData.indices?.length ?? 0), root.indices?.length ?? 0); const sliceIndices = forceCloneIndices || others.some((vertexData) => vertexData.indices === root.indices); let indices = sliceIndices ? root.indices?.slice() : root.indices; if (totalIndices > 0) { let indicesOffset = indices?.length ?? 0; if (!indices) { indices = new Array(totalIndices); } if (indices.length !== totalIndices) { if (Array.isArray(indices)) { indices.length = totalIndices; } else { const temp = use32BitsIndices || indices instanceof Uint32Array ? new Uint32Array(totalIndices) : new Uint16Array(totalIndices); temp.set(indices); indices = temp; } if (transform && transform.determinant() < 0) { _a._FlipFaces(indices, 0, indicesOffset); } } let positionsOffset = root.positions ? root.positions.length / 3 : 0; for (const { vertexData: other, transform } of vertexDatas) { if (other.indices) { for (let index = 0; index < other.indices.length; index++) { indices[indicesOffset + index] = other.indices[index] + positionsOffset; } if (transform && transform.determinant() < 0) { _a._FlipFaces(indices, indicesOffset, other.indices.length); } // The call to _validate already checked for positions positionsOffset += other.positions.length / 3; indicesOffset += other.indices.length; if (isAsync) { yield; } } } } this.indices = indices; this.positions = _a._MergeElement(VertexBuffer.PositionKind, root.positions, transform, vertexDatas.map((other) => [other.vertexData.positions, other.transform])); if (isAsync) { yield; } if (root.normals) { this.normals = _a._MergeElement(VertexBuffer.NormalKind, root.normals, transform, vertexDatas.map((other) => [other.vertexData.normals, other.transform])); if (isAsync) { yield; } } if (root.tangents) { this.tangents = _a._MergeElement(VertexBuffer.TangentKind, root.tangents, transform, vertexDatas.map((other) => [other.vertexData.tangents, other.transform])); if (isAsync) { yield; } } if (root.uvs) { this.uvs = _a._MergeElement(VertexBuffer.UVKind, root.uvs, transform, vertexDatas.map((other) => [other.vertexData.uvs, other.transform])); if (isAsync) { yield; } } if (root.uvs2) { this.uvs2 = _a._MergeElement(VertexBuffer.UV2Kind, root.uvs2, transform, vertexDatas.map((other) => [other.vertexData.uvs2, other.transform])); if (isAsync) { yield; } } if (root.uvs3) { this.uvs3 = _a._MergeElement(VertexBuffer.UV3Kind, root.uvs3, transform, vertexDatas.map((other) => [other.vertexData.uvs3, other.transform])); if (isAsync) { yield; } } if (root.uvs4) { this.uvs4 = _a._MergeElement(VertexBuffer.UV4Kind, root.uvs4, transform, vertexDatas.map((other) => [other.vertexData.uvs4, other.transform])); if (isAsync) { yield; } } if (root.uvs5) { this.uvs5 = _a._MergeElement(VertexBuffer.UV5Kind, root.uvs5, transform, vertexDatas.map((other) => [other.vertexData.uvs5, other.transform])); if (isAsync) { yield; } } if (root.uvs6) { this.uvs6 = _a._MergeElement(VertexBuffer.UV6Kind, root.uvs6, transform, vertexDatas.map((other) => [other.vertexData.uvs6, other.transform])); if (isAsync) { yield; } } if (root.colors) { this.colors = _a._MergeElement(VertexBuffer.ColorKind, root.colors, transform, vertexDatas.map((other) => [other.vertexData.colors, other.transform])); if (root.hasVertexAlpha !== undefined || vertexDatas.some((other) => other.vertexData.hasVertexAlpha !== undefined)) { this.hasVertexAlpha = root.hasVertexAlpha || vertexDatas.some((other) => other.vertexData.hasVertexAlpha); } if (isAsync) { yield; } } if (root.matricesIndices) { this.matricesIndices = _a._MergeElement(VertexBuffer.MatricesIndicesKind, root.matricesIndices, transform, vertexDatas.map((other) => [other.vertexData.matricesIndices, other.transform])); if (isAsync) { yield; } } if (root.matricesWeights) { this.matricesWeights = _a._MergeElement(VertexBuffer.MatricesWeightsKind, root.matricesWeights, transform, vertexDatas.map((other) => [other.vertexData.matricesWeights, other.transform])); if (isAsync) { yield; } } if (root.matricesIndicesExtra) { this.matricesIndicesExtra = _a._MergeElement(VertexBuffer.MatricesIndicesExtraKind, root.matricesIndicesExtra, transform, vertexDatas.map((other) => [other.vertexData.matricesIndicesExtra, other.transform])); if (isAsync) { yield; } } if (root.matricesWeightsExtra) { this.matricesWeightsExtra = _a._MergeElement(VertexBuffer.MatricesWeightsExtraKind, root.matricesWeightsExtra, transform, vertexDatas.map((other) => [other.vertexData.matricesWeightsExtra, other.transform])); } return this; } static _MergeElement(kind, source, transform, others) { const nonNullOthers = others.filter((other) => other[0] !== null && other[0] !== undefined); // If there is no source to copy and no other non-null sources then skip this element. if (!source && nonNullOthers.length == 0) { return source; } if (!source) { return this._MergeElement(kind, nonNullOthers[0][0], nonNullOthers[0][1], nonNullOthers.slice(1)); } const len = nonNullOthers.reduce((sumLen, elements) => sumLen + elements[0].length, source.length); const transformRange = kind === VertexBuffer.PositionKind ? _a._TransformVector3Coordinates : kind === VertexBuffer.NormalKind ? _a._TransformVector3Normals : kind === VertexBuffer.TangentKind ? _a._TransformVector4Normals : () => { }; if (source instanceof Float32Array) { // use non-loop method when the source is Float32Array const ret32 = new Float32Array(len); ret32.set(source); transform && transformRange(ret32, transform, 0, source.length); let offset = source.length; for (const [vertexData, transform] of nonNullOthers) { ret32.set(vertexData, offset); transform && transformRange(ret32, transform, offset, vertexData.length); offset += vertexData.length; } return ret32; } else { // don't use concat as it is super slow, just loop for other cases const ret = new Array(len); for (let i = 0; i < source.length; i++) { ret[i] = source[i]; } transform && transformRange(ret, transform, 0, source.length); let offset = source.length; for (const [vertexData, transform] of nonNullOthers) { for (let i = 0; i < vertexData.length; i++) { ret[offset + i] = vertexData[i]; } transform && transformRange(ret, transform, offset, vertexData.length); offset += vertexData.length; } return ret; } } _validate() { if (!this.positions) { throw new RuntimeError("Positions are required", ErrorCodes.MeshInvalidPositionsError); } const getElementCount = (kind, values) => { const stride = VertexBufferDeduceStride(kind); if (values.length % stride !== 0) { throw new Error("The " + kind + "s array count must be a multiple of " + stride); } return values.length / stride; }; const positionsElementCount = getElementCount(VertexBuffer.PositionKind, this.positions); const validateElementCount = (kind, values) => { const elementCount = getElementCount(kind, values); if (elementCount !== positionsElementCount) { throw new Error("The " + kind + "s element count (" + elementCount + ") does not match the positions count (" + positionsElementCount + ")"); } }; if (this.normals) { validateElementCount(VertexBuffer.NormalKind, this.normals); } if (this.tangents) { validateElementCount(VertexBuffer.TangentKind, this.tangents); } if (this.uvs) { validateElementCount(VertexBuffer.UVKind, this.uvs); } if (this.uvs2) { validateElementCount(VertexBuffer.UV2Kind, this.uvs2); } if (this.uvs3) { validateElementCount(VertexBuffer.UV3Kind, this.uvs3); } if (this.uvs4) { validateElementCount(VertexBuffer.UV4Kind, this.uvs4); } if (this.uvs5) { validateElementCount(VertexBuffer.UV5Kind, this.uvs5); } if (this.uvs6) { validateElementCount(VertexBuffer.UV6Kind, this.uvs6); } if (this.colors) { validateElementCount(VertexBuffer.ColorKind, this.colors); } if (this.matricesIndices) { validateElementCount(VertexBuffer.MatricesIndicesKind, this.matricesIndices); } if (this.matricesWeights) { validateElementCount(VertexBuffer.MatricesWeightsKind, this.matricesWeights); } if (this.matricesIndicesExtra) { validateElementCount(VertexBuffer.MatricesIndicesExtraKind, this.matricesIndicesExtra); } if (this.matricesWeightsExtra) { validateElementCount(VertexBuffer.MatricesWeightsExtraKind, this.matricesWeightsExtra); } } /** * Clone the current vertex data * @returns a copy of the current data */ clone() { const serializationObject = this.serialize(); return _a.Parse(serializationObject); } /** * Serializes the VertexData * @returns a serialized object */ serialize() { const serializationObject = {}; if (this.positions) { serializationObject.positions = Array.from(this.positions); } if (this.normals) { serializationObject.normals = Array.from(this.normals); } if (this.tangents) { serializationObject.tangents = Array.from(this.tangents); } if (this.uvs) { serializationObject.uvs = Array.from(this.uvs); } if (this.uvs2) { serializationObject.uvs2 = Array.from(this.uvs2); } if (this.uvs3) { serializationObject.uvs3 = Array.from(this.uvs3); } if (this.uvs4) { serializationObject.uvs4 = Array.from(this.uvs4); } if (this.uvs5) { serializationObject.uvs5 = Array.from(this.uvs5); } if (this.uvs6) { serializationObject.uvs6 = Array.from(this.uvs6); } if (this.colors) { serializationObject.colors = Array.from(this.colors); serializationObject.hasVertexAlpha = this.hasVertexAlpha; } if (this.matricesIndices) { serializationObject.matricesIndices = Array.from(this.matricesIndices); serializationObject.matricesIndicesExpanded = true; } if (this.matricesWeights) { serializationObject.matricesWeights = Array.from(this.matricesWeights); } if (this.matricesIndicesExtra) { serializationObject.matricesIndicesExtra = Array.from(this.matricesIndicesExtra); serializationObject.matricesIndicesExtraExpanded = true; } if (this.matricesWeightsExtra) { serializationObject.matricesWeightsExtra = Array.from(this.matricesWeightsExtra); } serializationObject.indices = this.indices ? Array.from(this.indices) : []; if (this.materialInfos) { serializationObject.materialInfos = []; for (const materialInfo of this.materialInfos) { const materialInfoSerializationObject = { indexStart: materialInfo.indexStart, indexCount: materialInfo.indexCount, materialIndex: materialInfo.materialIndex, verticesStart: materialInfo.verticesStart, verticesCount: materialInfo.verticesCount, }; serializationObject.materialInfos.push(materialInfoSerializationObject); } } return serializationObject; } // Statics /** * Extracts the vertexData from a mesh * @param mesh the mesh from which to extract the VertexData * @param copyWhenShared defines if the VertexData must be cloned when shared between multiple meshes, optional, default false