@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