@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
JavaScript
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