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.

1,046 lines 90.2 kB
import { __decorate } from "../tslib.es6.js"; import { Vector3, Vector4, TmpVectors } from "../Maths/math.vector.js"; import { VertexBuffer } from "../Buffers/buffer.js"; import { _WarnImport } from "../Misc/devTools.js"; import { Color4 } from "../Maths/math.color.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 */ export 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 = VertexData._UniqueIdGenerator; VertexData._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) { VertexData._TransformVector3Coordinates(this.positions, matrix); } if (this.normals) { VertexData._TransformVector3Normals(this.normals, matrix); } if (this.tangents) { VertexData._TransformVector4Normals(this.tangents, matrix); } if (flip && this.indices) { VertexData._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 VertexData(); 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 = 0; 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) { VertexData._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) { VertexData._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 = VertexData._MergeElement(VertexBuffer.PositionKind, root.positions, transform, vertexDatas.map((other) => [other.vertexData.positions, other.transform])); if (isAsync) { yield; } if (root.normals) { this.normals = VertexData._MergeElement(VertexBuffer.NormalKind, root.normals, transform, vertexDatas.map((other) => [other.vertexData.normals, other.transform])); if (isAsync) { yield; } } if (root.tangents) { this.tangents = VertexData._MergeElement(VertexBuffer.TangentKind, root.tangents, transform, vertexDatas.map((other) => [other.vertexData.tangents, other.transform])); if (isAsync) { yield; } } if (root.uvs) { this.uvs = VertexData._MergeElement(VertexBuffer.UVKind, root.uvs, transform, vertexDatas.map((other) => [other.vertexData.uvs, other.transform])); if (isAsync) { yield; } } if (root.uvs2) { this.uvs2 = VertexData._MergeElement(VertexBuffer.UV2Kind, root.uvs2, transform, vertexDatas.map((other) => [other.vertexData.uvs2, other.transform])); if (isAsync) { yield; } } if (root.uvs3) { this.uvs3 = VertexData._MergeElement(VertexBuffer.UV3Kind, root.uvs3, transform, vertexDatas.map((other) => [other.vertexData.uvs3, other.transform])); if (isAsync) { yield; } } if (root.uvs4) { this.uvs4 = VertexData._MergeElement(VertexBuffer.UV4Kind, root.uvs4, transform, vertexDatas.map((other) => [other.vertexData.uvs4, other.transform])); if (isAsync) { yield; } } if (root.uvs5) { this.uvs5 = VertexData._MergeElement(VertexBuffer.UV5Kind, root.uvs5, transform, vertexDatas.map((other) => [other.vertexData.uvs5, other.transform])); if (isAsync) { yield; } } if (root.uvs6) { this.uvs6 = VertexData._MergeElement(VertexBuffer.UV6Kind, root.uvs6, transform, vertexDatas.map((other) => [other.vertexData.uvs6, other.transform])); if (isAsync) { yield; } } if (root.colors) { this.colors = VertexData._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 = VertexData._MergeElement(VertexBuffer.MatricesIndicesKind, root.matricesIndices, transform, vertexDatas.map((other) => [other.vertexData.matricesIndices, other.transform])); if (isAsync) { yield; } } if (root.matricesWeights) { this.matricesWeights = VertexData._MergeElement(VertexBuffer.MatricesWeightsKind, root.matricesWeights, transform, vertexDatas.map((other) => [other.vertexData.matricesWeights, other.transform])); if (isAsync) { yield; } } if (root.matricesIndicesExtra) { this.matricesIndicesExtra = VertexData._MergeElement(VertexBuffer.MatricesIndicesExtraKind, root.matricesIndicesExtra, transform, vertexDatas.map((other) => [other.vertexData.matricesIndicesExtra, other.transform])); if (isAsync) { yield; } } if (root.matricesWeightsExtra) { this.matricesWeightsExtra = VertexData._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 ? VertexData._TransformVector3Coordinates : kind === VertexBuffer.NormalKind ? VertexData._TransformVector3Normals : kind === VertexBuffer.TangentKind ? VertexData._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 = VertexBuffer.DeduceStride(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 VertexData.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 * @param forceCopy indicating that the VertexData must be cloned, optional, default false * @returns the object VertexData associated to the passed mesh */ static ExtractFromMesh(mesh, copyWhenShared, forceCopy) { return VertexData._ExtractFrom(mesh, copyWhenShared, forceCopy); } /** * Extracts the vertexData from the geometry * @param geometry the geometry from which to extract the VertexData * @param copyWhenShared defines if the VertexData must be cloned when the geometry is shared between multiple meshes, optional, default false * @param forceCopy indicating that the VertexData must be cloned, optional, default false * @returns the object VertexData associated to the passed mesh */ static ExtractFromGeometry(geometry, copyWhenShared, forceCopy) { return VertexData._ExtractFrom(geometry, copyWhenShared, forceCopy); } static _ExtractFrom(meshOrGeometry, copyWhenShared, forceCopy) { const result = new VertexData(); if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.PositionKind)) { result.positions = meshOrGeometry.getVerticesData(VertexBuffer.PositionKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.NormalKind)) { result.normals = meshOrGeometry.getVerticesData(VertexBuffer.NormalKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.TangentKind)) { result.tangents = meshOrGeometry.getVerticesData(VertexBuffer.TangentKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UVKind)) { result.uvs = meshOrGeometry.getVerticesData(VertexBuffer.UVKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UV2Kind)) { result.uvs2 = meshOrGeometry.getVerticesData(VertexBuffer.UV2Kind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UV3Kind)) { result.uvs3 = meshOrGeometry.getVerticesData(VertexBuffer.UV3Kind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UV4Kind)) { result.uvs4 = meshOrGeometry.getVerticesData(VertexBuffer.UV4Kind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UV5Kind)) { result.uvs5 = meshOrGeometry.getVerticesData(VertexBuffer.UV5Kind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UV6Kind)) { result.uvs6 = meshOrGeometry.getVerticesData(VertexBuffer.UV6Kind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.ColorKind)) { const geometry = meshOrGeometry.geometry || meshOrGeometry; const vertexBuffer = geometry.getVertexBuffer(VertexBuffer.ColorKind); const colors = geometry.getVerticesData(VertexBuffer.ColorKind, copyWhenShared, forceCopy); if (vertexBuffer.getSize() === 3) { const newColors = new Float32Array((colors.length * 4) / 3); for (let i = 0, j = 0; i < colors.length; i += 3, j += 4) { newColors[j] = colors[i]; newColors[j + 1] = colors[i + 1]; newColors[j + 2] = colors[i + 2]; newColors[j + 3] = 1; } result.colors = newColors; } else if (vertexBuffer.getSize() === 4) { result.colors = colors; } else { throw new Error(`Unexpected number of color components: ${vertexBuffer.getSize()}`); } } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesIndicesKind)) { result.matricesIndices = meshOrGeometry.getVerticesData(VertexBuffer.MatricesIndicesKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesWeightsKind)) { result.matricesWeights = meshOrGeometry.getVerticesData(VertexBuffer.MatricesWeightsKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesIndicesExtraKind)) { result.matricesIndicesExtra = meshOrGeometry.getVerticesData(VertexBuffer.MatricesIndicesExtraKind, copyWhenShared, forceCopy); } if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.MatricesWeightsExtraKind)) { result.matricesWeightsExtra = meshOrGeometry.getVerticesData(VertexBuffer.MatricesWeightsExtraKind, copyWhenShared, forceCopy); } result.indices = meshOrGeometry.getIndices(copyWhenShared, forceCopy); return result; } /** * Creates the VertexData for a Ribbon * @param options an object used to set the following optional parameters for the ribbon, required but can be empty * * pathArray array of paths, each of which an array of successive Vector3 * * closeArray creates a seam between the first and the last paths of the pathArray, optional, default false * * closePath creates a seam between the first and the last points of each path of the path array, optional, default false * * offset a positive integer, only used when pathArray contains a single path (offset = 10 means the point 1 is joined to the point 11), default rounded half size of the pathArray length * * sideOrientation optional and takes the values : Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE * * frontUvs only usable when you create a double-sided mesh, used to choose what parts of the texture image to crop and apply on the front side, optional, default vector4 (0, 0, 1, 1) * * backUVs only usable when you create a double-sided mesh, used to choose what parts of the texture image to crop and apply on the back side, optional, default vector4 (0, 0, 1, 1) * * invertUV swaps in the U and V coordinates when applying a texture, optional, default false * * uvs a linear array, of length 2 * number of vertices, of custom UV values, optional * * colors a linear array, of length 4 * number of vertices, of custom color values, optional * @returns the VertexData of the ribbon * @deprecated use CreateRibbonVertexData instead */ static CreateRibbon(options) { throw _WarnImport("ribbonBuilder"); } /** * Creates the VertexData for a box * @param options an object used to set the following optional parameters for the box, required but can be empty * * size sets the width, height and depth of the box to the value of size, optional default 1 * * width sets the width (x direction) of the box, overwrites the width set by size, optional, default size * * height sets the height (y direction) of the box, overwrites the height set by size, optional, default size * * depth sets the depth (z direction) of the box, overwrites the depth set by size, optional, default size * * faceUV an array of 6 Vector4 elements used to set different images to each box side * * faceColors an array of 6 Color3 elements used to set different colors to each box side * * sideOrientation optional and takes the values : Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE * * frontUvs only usable wh