@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.
843 lines (842 loc) • 32 kB
JavaScript
import { Quaternion, Matrix, Vector3, Vector2 } from "../Maths/math.vector.js";
import { VertexBuffer } from "../Buffers/buffer.js";
import { SubMesh } from "../Meshes/subMesh.js";
import { Mesh } from "../Meshes/mesh.js";
import { Color4 } from "../Maths/math.color.js";
import { VertexData } from "./mesh.vertexData.js";
/**
* Unique ID when we import meshes from Babylon to CSG
*/
let CurrentCSGMeshId = 0;
/**
* Represents a vertex of a polygon. Use your own vertex class instead of this
* one to provide additional features like texture coordinates and vertex
* colors. Custom vertex classes need to provide a `pos` property and `clone()`,
* `flip()`, and `interpolate()` methods that behave analogous to the ones
* defined by `BABYLON.CSG.Vertex`. This class provides `normal` so convenience
* functions like `BABYLON.CSG.sphere()` can return a smooth vertex normal, but `normal`
* is not used anywhere else.
* Same goes for uv, it allows to keep the original vertex uv coordinates of the 2 meshes
*/
class Vertex {
/**
* Initializes the vertex
* @param pos The position of the vertex
* @param normal The normal of the vertex
* @param uv The texture coordinate of the vertex
* @param vertColor The RGBA color of the vertex
*/
constructor(
/**
* The position of the vertex
*/
pos,
/**
* The normal of the vertex
*/
normal,
/**
* The texture coordinate of the vertex
*/
uv,
/**
* The texture coordinate of the vertex
*/
vertColor) {
this.pos = pos;
this.normal = normal;
this.uv = uv;
this.vertColor = vertColor;
}
/**
* Make a clone, or deep copy, of the vertex
* @returns A new Vertex
*/
clone() {
return new Vertex(this.pos.clone(), this.normal.clone(), this.uv?.clone(), this.vertColor?.clone());
}
/**
* Invert all orientation-specific data (e.g. vertex normal). Called when the
* orientation of a polygon is flipped.
*/
flip() {
this.normal = this.normal.scale(-1);
}
/**
* Create a new vertex between this vertex and `other` by linearly
* interpolating all properties using a parameter of `t`. Subclasses should
* override this to interpolate additional properties.
* @param other the vertex to interpolate against
* @param t The factor used to linearly interpolate between the vertices
* @returns The new interpolated vertex
*/
interpolate(other, t) {
return new Vertex(Vector3.Lerp(this.pos, other.pos, t), Vector3.Lerp(this.normal, other.normal, t), this.uv && other.uv ? Vector2.Lerp(this.uv, other.uv, t) : undefined, this.vertColor && other.vertColor ? Color4.Lerp(this.vertColor, other.vertColor, t) : undefined);
}
}
/**
* Represents a plane in 3D space.
*/
class CSGPlane {
/**
* Initializes the plane
* @param normal The normal for the plane
* @param w
*/
constructor(normal, w) {
this.normal = normal;
this.w = w;
}
/**
* Construct a plane from three points
* @param a Point a
* @param b Point b
* @param c Point c
* @returns A new plane
*/
static FromPoints(a, b, c) {
const v0 = c.subtract(a);
const v1 = b.subtract(a);
if (v0.lengthSquared() === 0 || v1.lengthSquared() === 0) {
return null;
}
const n = Vector3.Normalize(Vector3.Cross(v0, v1));
return new CSGPlane(n, Vector3.Dot(n, a));
}
/**
* Clone, or make a deep copy of the plane
* @returns a new Plane
*/
clone() {
return new CSGPlane(this.normal.clone(), this.w);
}
/**
* Flip the face of the plane
*/
flip() {
this.normal.scaleInPlace(-1);
this.w = -this.w;
}
/**
* Split `polygon` by this plane if needed, then put the polygon or polygon
* fragments in the appropriate lists. Coplanar polygons go into either
`* coplanarFront` or `coplanarBack` depending on their orientation with
* respect to this plane. Polygons in front or in back of this plane go into
* either `front` or `back`
* @param polygon The polygon to be split
* @param coplanarFront Will contain polygons coplanar with the plane that are oriented to the front of the plane
* @param coplanarBack Will contain polygons coplanar with the plane that are oriented to the back of the plane
* @param front Will contain the polygons in front of the plane
* @param back Will contain the polygons begind the plane
*/
splitPolygon(polygon, coplanarFront, coplanarBack, front, back) {
const coplanarConst = 0;
const frontConst = 1;
const backConst = 2;
const spanningConst = 3;
// Classify each point as well as the entire polygon into one of the above
// four classes.
let polygonType = 0;
const types = [];
let i;
let t;
for (i = 0; i < polygon.vertices.length; i++) {
t = Vector3.Dot(this.normal, polygon.vertices[i].pos) - this.w;
const type = t < -CSGPlane.EPSILON ? backConst : t > CSGPlane.EPSILON ? frontConst : coplanarConst;
polygonType |= type;
types.push(type);
}
// Put the polygon in the correct list, splitting it when necessary
switch (polygonType) {
case coplanarConst:
(Vector3.Dot(this.normal, polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).push(polygon);
break;
case frontConst:
front.push(polygon);
break;
case backConst:
back.push(polygon);
break;
case spanningConst: {
const f = [], b = [];
for (i = 0; i < polygon.vertices.length; i++) {
const j = (i + 1) % polygon.vertices.length;
const ti = types[i], tj = types[j];
const vi = polygon.vertices[i], vj = polygon.vertices[j];
if (ti !== backConst) {
f.push(vi);
}
if (ti !== frontConst) {
b.push(ti !== backConst ? vi.clone() : vi);
}
if ((ti | tj) === spanningConst) {
t = (this.w - Vector3.Dot(this.normal, vi.pos)) / Vector3.Dot(this.normal, vj.pos.subtract(vi.pos));
const v = vi.interpolate(vj, t);
f.push(v);
b.push(v.clone());
}
}
let poly;
if (f.length >= 3) {
poly = new CSGPolygon(f, polygon.shared);
if (poly.plane) {
front.push(poly);
}
}
if (b.length >= 3) {
poly = new CSGPolygon(b, polygon.shared);
if (poly.plane) {
back.push(poly);
}
}
break;
}
}
}
}
/**
* `CSG.Plane.EPSILON` is the tolerance used by `splitPolygon()` to decide if a
* point is on the plane
*/
CSGPlane.EPSILON = 1e-5;
/**
* Represents a convex polygon. The vertices used to initialize a polygon must
* be coplanar and form a convex loop.
*
* Each convex polygon has a `shared` property, which is shared between all
* polygons that are clones of each other or were split from the same polygon.
* This can be used to define per-polygon properties (such as surface color)
*/
class CSGPolygon {
/**
* Initializes the polygon
* @param vertices The vertices of the polygon
* @param shared The properties shared across all polygons
*/
constructor(vertices, shared) {
this.vertices = vertices;
this.shared = shared;
this.plane = CSGPlane.FromPoints(vertices[0].pos, vertices[1].pos, vertices[2].pos);
}
/**
* Clones, or makes a deep copy, or the polygon
* @returns A new CSGPolygon
*/
clone() {
const vertices = this.vertices.map((v) => v.clone());
return new CSGPolygon(vertices, this.shared);
}
/**
* Flips the faces of the polygon
*/
flip() {
this.vertices.reverse().map((v) => {
v.flip();
});
this.plane.flip();
}
}
/**
* Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
* by picking a polygon to split along. That polygon (and all other coplanar
* polygons) are added directly to that node and the other polygons are added to
* the front and/or back subtrees. This is not a leafy BSP tree since there is
* no distinction between internal and leaf nodes
*/
class Node {
/**
* Initializes the node
* @param polygons A collection of polygons held in the node
*/
constructor(polygons) {
this._plane = null;
this._front = null;
this._back = null;
this._polygons = new Array();
if (polygons) {
this.build(polygons);
}
}
/**
* Clones, or makes a deep copy, of the node
* @returns The cloned node
*/
clone() {
const node = new Node();
node._plane = this._plane && this._plane.clone();
node._front = this._front && this._front.clone();
node._back = this._back && this._back.clone();
node._polygons = this._polygons.map((p) => p.clone());
return node;
}
/**
* Convert solid space to empty space and empty space to solid space
*/
invert() {
for (let i = 0; i < this._polygons.length; i++) {
this._polygons[i].flip();
}
if (this._plane) {
this._plane.flip();
}
if (this._front) {
this._front.invert();
}
if (this._back) {
this._back.invert();
}
const temp = this._front;
this._front = this._back;
this._back = temp;
}
/**
* Recursively remove all polygons in `polygons` that are inside this BSP
* tree.
* @param polygons Polygons to remove from the BSP
* @returns Polygons clipped from the BSP
*/
clipPolygons(polygons) {
if (!this._plane) {
return polygons.slice();
}
let front = [], back = [];
for (let i = 0; i < polygons.length; i++) {
this._plane.splitPolygon(polygons[i], front, back, front, back);
}
if (this._front) {
front = this._front.clipPolygons(front);
}
if (this._back) {
back = this._back.clipPolygons(back);
}
else {
back = [];
}
return front.concat(back);
}
/**
* Remove all polygons in this BSP tree that are inside the other BSP tree
* `bsp`.
* @param bsp BSP containing polygons to remove from this BSP
*/
clipTo(bsp) {
this._polygons = bsp.clipPolygons(this._polygons);
if (this._front) {
this._front.clipTo(bsp);
}
if (this._back) {
this._back.clipTo(bsp);
}
}
/**
* Return a list of all polygons in this BSP tree
* @returns List of all polygons in this BSP tree
*/
allPolygons() {
let polygons = this._polygons.slice();
if (this._front) {
polygons = polygons.concat(this._front.allPolygons());
}
if (this._back) {
polygons = polygons.concat(this._back.allPolygons());
}
return polygons;
}
/**
* Build a BSP tree out of `polygons`. When called on an existing tree, the
* new polygons are filtered down to the bottom of the tree and become new
* nodes there. Each set of polygons is partitioned using the first polygon
* (no heuristic is used to pick a good split)
* @param polygons Polygons used to construct the BSP tree
*/
build(polygons) {
if (!polygons.length) {
return;
}
if (!this._plane) {
this._plane = polygons[0].plane.clone();
}
const front = [], back = [];
for (let i = 0; i < polygons.length; i++) {
this._plane.splitPolygon(polygons[i], this._polygons, this._polygons, front, back);
}
if (front.length) {
if (!this._front) {
this._front = new Node();
}
this._front.build(front);
}
if (back.length) {
if (!this._back) {
this._back = new Node();
}
this._back.build(back);
}
}
}
/**
* Class for building Constructive Solid Geometry
* @deprecated Please use CSG2 instead
*/
export class CSG {
constructor() {
this._polygons = new Array();
}
/**
* Convert a VertexData to CSG
* @param data defines the VertexData to convert to CSG
* @returns the new CSG
*/
static FromVertexData(data) {
let vertex, polygon, vertices;
const polygons = [];
const indices = data.indices;
const positions = data.positions;
const normals = data.normals;
const uvs = data.uvs;
const vertColors = data.colors;
if (!indices || !positions) {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: VertexData must at least contain positions and indices";
}
for (let i = 0; i < indices.length; i += 3) {
vertices = [];
for (let j = 0; j < 3; j++) {
const indexIndices = i + j;
const offset = indices[indexIndices];
const normal = normals ? Vector3.FromArray(normals, offset * 3) : Vector3.Zero();
const uv = uvs ? Vector2.FromArray(uvs, offset * 2) : undefined;
const vertColor = vertColors ? Color4.FromArray(vertColors, offset * 4) : undefined;
const position = Vector3.FromArray(positions, offset * 3);
vertex = new Vertex(position, normal, uv, vertColor);
vertices.push(vertex);
}
polygon = new CSGPolygon(vertices, { subMeshId: 0, meshId: CurrentCSGMeshId, materialIndex: 0 });
// To handle the case of degenerated triangle
// polygon.plane == null <=> the polygon does not represent 1 single plane <=> the triangle is degenerated
if (polygon.plane) {
polygons.push(polygon);
}
}
const csg = CSG._FromPolygons(polygons);
csg.matrix = Matrix.Identity();
csg.position = Vector3.Zero();
csg.rotation = Vector3.Zero();
csg.scaling = Vector3.One();
csg.rotationQuaternion = Quaternion.Identity();
CurrentCSGMeshId++;
return csg;
}
/**
* Convert the Mesh to CSG
* @param mesh The Mesh to convert to CSG
* @param absolute If true, the final (local) matrix transformation is set to the identity and not to that of `mesh`. It can help when dealing with right-handed meshes (default: false)
* @returns A new CSG from the Mesh
*/
static FromMesh(mesh, absolute = false) {
let vertex, normal, uv = undefined, position, vertColor = undefined, polygon, vertices;
const polygons = [];
let matrix, meshPosition, meshRotation, meshRotationQuaternion = null, meshScaling;
let invertWinding = false;
if (mesh instanceof Mesh) {
mesh.computeWorldMatrix(true);
matrix = mesh.getWorldMatrix();
meshPosition = mesh.position.clone();
meshRotation = mesh.rotation.clone();
if (mesh.rotationQuaternion) {
meshRotationQuaternion = mesh.rotationQuaternion.clone();
}
meshScaling = mesh.scaling.clone();
if (mesh.material && absolute) {
invertWinding = mesh.material.sideOrientation === 0;
}
}
else {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: Wrong Mesh type, must be BABYLON.Mesh";
}
const indices = mesh.getIndices(), positions = mesh.getVerticesData(VertexBuffer.PositionKind), normals = mesh.getVerticesData(VertexBuffer.NormalKind), uvs = mesh.getVerticesData(VertexBuffer.UVKind), vertColors = mesh.getVerticesData(VertexBuffer.ColorKind);
if (indices === null) {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: Mesh has no indices";
}
if (positions === null) {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: Mesh has no positions";
}
if (normals === null) {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: Mesh has no normals";
}
const subMeshes = mesh.subMeshes;
if (!subMeshes) {
// eslint-disable-next-line no-throw-literal
throw "BABYLON.CSG: Mesh has no submeshes";
}
for (let sm = 0, sml = subMeshes.length; sm < sml; sm++) {
for (let i = subMeshes[sm].indexStart, il = subMeshes[sm].indexCount + subMeshes[sm].indexStart; i < il; i += 3) {
vertices = [];
for (let j = 0; j < 3; j++) {
const indexIndices = j === 0 ? i + j : invertWinding ? i + 3 - j : i + j;
const sourceNormal = new Vector3(normals[indices[indexIndices] * 3], normals[indices[indexIndices] * 3 + 1], normals[indices[indexIndices] * 3 + 2]);
if (uvs) {
uv = new Vector2(uvs[indices[indexIndices] * 2], uvs[indices[indexIndices] * 2 + 1]);
}
if (vertColors) {
vertColor = new Color4(vertColors[indices[indexIndices] * 4], vertColors[indices[indexIndices] * 4 + 1], vertColors[indices[indexIndices] * 4 + 2], vertColors[indices[indexIndices] * 4 + 3]);
}
const sourcePosition = new Vector3(positions[indices[indexIndices] * 3], positions[indices[indexIndices] * 3 + 1], positions[indices[indexIndices] * 3 + 2]);
position = Vector3.TransformCoordinates(sourcePosition, matrix);
normal = Vector3.TransformNormal(sourceNormal, matrix);
vertex = new Vertex(position, normal, uv, vertColor);
vertices.push(vertex);
}
polygon = new CSGPolygon(vertices, { subMeshId: sm, meshId: CurrentCSGMeshId, materialIndex: subMeshes[sm].materialIndex });
// To handle the case of degenerated triangle
// polygon.plane == null <=> the polygon does not represent 1 single plane <=> the triangle is degenerated
if (polygon.plane) {
polygons.push(polygon);
}
}
}
const csg = CSG._FromPolygons(polygons);
csg.matrix = absolute ? Matrix.Identity() : matrix;
csg.position = absolute ? Vector3.Zero() : meshPosition;
csg.rotation = absolute ? Vector3.Zero() : meshRotation;
csg.scaling = absolute ? Vector3.One() : meshScaling;
csg.rotationQuaternion = absolute && meshRotationQuaternion ? Quaternion.Identity() : meshRotationQuaternion;
CurrentCSGMeshId++;
return csg;
}
/**
* Construct a CSG solid from a list of `CSG.Polygon` instances.
* @param polygons Polygons used to construct a CSG solid
* @returns A new CSG solid
*/
static _FromPolygons(polygons) {
const csg = new CSG();
csg._polygons = polygons;
return csg;
}
/**
* Clones, or makes a deep copy, of the CSG
* @returns A new CSG
*/
clone() {
const csg = new CSG();
csg._polygons = this._polygons.map((p) => p.clone());
csg.copyTransformAttributes(this);
return csg;
}
/**
* Unions this CSG with another CSG
* @param csg The CSG to union against this CSG
* @returns The unioned CSG
*/
union(csg) {
const a = new Node(this.clone()._polygons);
const b = new Node(csg.clone()._polygons);
a.clipTo(b);
b.clipTo(a);
b.invert();
b.clipTo(a);
b.invert();
a.build(b.allPolygons());
return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
}
/**
* Unions this CSG with another CSG in place
* @param csg The CSG to union against this CSG
*/
unionInPlace(csg) {
const a = new Node(this._polygons);
const b = new Node(csg._polygons);
a.clipTo(b);
b.clipTo(a);
b.invert();
b.clipTo(a);
b.invert();
a.build(b.allPolygons());
this._polygons = a.allPolygons();
}
/**
* Subtracts this CSG with another CSG
* @param csg The CSG to subtract against this CSG
* @returns A new CSG
*/
subtract(csg) {
const a = new Node(this.clone()._polygons);
const b = new Node(csg.clone()._polygons);
a.invert();
a.clipTo(b);
b.clipTo(a);
b.invert();
b.clipTo(a);
b.invert();
a.build(b.allPolygons());
a.invert();
return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
}
/**
* Subtracts this CSG with another CSG in place
* @param csg The CSG to subtract against this CSG
*/
subtractInPlace(csg) {
const a = new Node(this._polygons);
const b = new Node(csg._polygons);
a.invert();
a.clipTo(b);
b.clipTo(a);
b.invert();
b.clipTo(a);
b.invert();
a.build(b.allPolygons());
a.invert();
this._polygons = a.allPolygons();
}
/**
* Intersect this CSG with another CSG
* @param csg The CSG to intersect against this CSG
* @returns A new CSG
*/
intersect(csg) {
const a = new Node(this.clone()._polygons);
const b = new Node(csg.clone()._polygons);
a.invert();
b.clipTo(a);
b.invert();
a.clipTo(b);
b.clipTo(a);
a.build(b.allPolygons());
a.invert();
return CSG._FromPolygons(a.allPolygons()).copyTransformAttributes(this);
}
/**
* Intersects this CSG with another CSG in place
* @param csg The CSG to intersect against this CSG
*/
intersectInPlace(csg) {
const a = new Node(this._polygons);
const b = new Node(csg._polygons);
a.invert();
b.clipTo(a);
b.invert();
a.clipTo(b);
b.clipTo(a);
a.build(b.allPolygons());
a.invert();
this._polygons = a.allPolygons();
}
/**
* Return a new CSG solid with solid and empty space switched. This solid is
* not modified.
* @returns A new CSG solid with solid and empty space switched
*/
inverse() {
const csg = this.clone();
csg.inverseInPlace();
return csg;
}
/**
* Inverses the CSG in place
*/
inverseInPlace() {
this._polygons.map((p) => {
p.flip();
});
}
/**
* This is used to keep meshes transformations so they can be restored
* when we build back a Babylon Mesh
* NB : All CSG operations are performed in world coordinates
* @param csg The CSG to copy the transform attributes from
* @returns This CSG
*/
copyTransformAttributes(csg) {
this.matrix = csg.matrix;
this.position = csg.position;
this.rotation = csg.rotation;
this.scaling = csg.scaling;
this.rotationQuaternion = csg.rotationQuaternion;
return this;
}
/**
* Build vertex data from CSG
* Coordinates here are in world space
* @param onBeforePolygonProcessing called before each polygon is being processed
* @param onAfterPolygonProcessing called after each polygon has been processed
* @returns the final vertex data
*/
toVertexData(onBeforePolygonProcessing = null, onAfterPolygonProcessing = null) {
const matrix = this.matrix.clone();
matrix.invert();
const polygons = this._polygons;
const vertices = [];
const indices = [];
const normals = [];
let uvs = null;
let vertColors = null;
const vertex = Vector3.Zero();
const normal = Vector3.Zero();
const uv = Vector2.Zero();
const vertColor = new Color4(0, 0, 0, 0);
const polygonIndices = [0, 0, 0];
const verticeDict = {};
let vertexIdx;
for (let i = 0, il = polygons.length; i < il; i++) {
const polygon = polygons[i];
if (onBeforePolygonProcessing) {
onBeforePolygonProcessing(polygon);
}
for (let j = 2, jl = polygon.vertices.length; j < jl; j++) {
polygonIndices[0] = 0;
polygonIndices[1] = j - 1;
polygonIndices[2] = j;
for (let k = 0; k < 3; k++) {
vertex.copyFrom(polygon.vertices[polygonIndices[k]].pos);
normal.copyFrom(polygon.vertices[polygonIndices[k]].normal);
if (polygon.vertices[polygonIndices[k]].uv) {
if (!uvs) {
uvs = [];
}
uv.copyFrom(polygon.vertices[polygonIndices[k]].uv);
}
if (polygon.vertices[polygonIndices[k]].vertColor) {
if (!vertColors) {
vertColors = [];
}
vertColor.copyFrom(polygon.vertices[polygonIndices[k]].vertColor);
}
const localVertex = Vector3.TransformCoordinates(vertex, matrix);
const localNormal = Vector3.TransformNormal(normal, matrix);
vertexIdx = verticeDict[localVertex.x + "," + localVertex.y + "," + localVertex.z];
let areUvsDifferent = false;
if (uvs && !(uvs[vertexIdx * 2] === uv.x || uvs[vertexIdx * 2 + 1] === uv.y)) {
areUvsDifferent = true;
}
let areColorsDifferent = false;
if (vertColors &&
!(vertColors[vertexIdx * 4] === vertColor.r ||
vertColors[vertexIdx * 4 + 1] === vertColor.g ||
vertColors[vertexIdx * 4 + 2] === vertColor.b ||
vertColors[vertexIdx * 4 + 3] === vertColor.a)) {
areColorsDifferent = true;
}
// Check if 2 points can be merged
if (!(typeof vertexIdx !== "undefined" &&
normals[vertexIdx * 3] === localNormal.x &&
normals[vertexIdx * 3 + 1] === localNormal.y &&
normals[vertexIdx * 3 + 2] === localNormal.z) ||
areUvsDifferent ||
areColorsDifferent) {
vertices.push(localVertex.x, localVertex.y, localVertex.z);
if (uvs) {
uvs.push(uv.x, uv.y);
}
normals.push(normal.x, normal.y, normal.z);
if (vertColors) {
vertColors.push(vertColor.r, vertColor.g, vertColor.b, vertColor.a);
}
vertexIdx = verticeDict[localVertex.x + "," + localVertex.y + "," + localVertex.z] = vertices.length / 3 - 1;
}
indices.push(vertexIdx);
if (onAfterPolygonProcessing) {
onAfterPolygonProcessing();
}
}
}
}
const result = new VertexData();
result.positions = vertices;
result.normals = normals;
if (uvs) {
result.uvs = uvs;
}
if (vertColors) {
result.colors = vertColors;
}
result.indices = indices;
return result;
}
/**
* Build Raw mesh from CSG
* Coordinates here are in world space
* @param name The name of the mesh geometry
* @param scene The Scene
* @param keepSubMeshes Specifies if the submeshes should be kept
* @returns A new Mesh
*/
buildMeshGeometry(name, scene, keepSubMeshes) {
const mesh = new Mesh(name, scene);
const polygons = this._polygons;
let currentIndex = 0;
const subMeshDict = {};
let subMeshObj;
if (keepSubMeshes) {
// Sort Polygons, since subMeshes are indices range
polygons.sort((a, b) => {
if (a.shared.meshId === b.shared.meshId) {
return a.shared.subMeshId - b.shared.subMeshId;
}
else {
return a.shared.meshId - b.shared.meshId;
}
});
}
const vertexData = this.toVertexData((polygon) => {
// Building SubMeshes
if (!subMeshDict[polygon.shared.meshId]) {
subMeshDict[polygon.shared.meshId] = {};
}
if (!subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId]) {
subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId] = {
indexStart: +Infinity,
indexEnd: -Infinity,
materialIndex: polygon.shared.materialIndex,
};
}
subMeshObj = subMeshDict[polygon.shared.meshId][polygon.shared.subMeshId];
}, () => {
subMeshObj.indexStart = Math.min(currentIndex, subMeshObj.indexStart);
subMeshObj.indexEnd = Math.max(currentIndex, subMeshObj.indexEnd);
currentIndex++;
});
vertexData.applyToMesh(mesh);
if (keepSubMeshes) {
// We offset the materialIndex by the previous number of materials in the CSG mixed meshes
let materialIndexOffset = 0, materialMaxIndex;
mesh.subMeshes = [];
for (const m in subMeshDict) {
materialMaxIndex = -1;
for (const sm in subMeshDict[m]) {
subMeshObj = subMeshDict[m][sm];
SubMesh.CreateFromIndices(subMeshObj.materialIndex + materialIndexOffset, subMeshObj.indexStart, subMeshObj.indexEnd - subMeshObj.indexStart + 1, mesh);
materialMaxIndex = Math.max(subMeshObj.materialIndex, materialMaxIndex);
}
materialIndexOffset += ++materialMaxIndex;
}
}
return mesh;
}
/**
* Build Mesh from CSG taking material and transforms into account
* @param name The name of the Mesh
* @param material The material of the Mesh
* @param scene The Scene
* @param keepSubMeshes Specifies if submeshes should be kept
* @returns The new Mesh
*/
toMesh(name, material = null, scene, keepSubMeshes) {
const mesh = this.buildMeshGeometry(name, scene, keepSubMeshes);
mesh.material = material;
mesh.position.copyFrom(this.position);
mesh.rotation.copyFrom(this.rotation);
if (this.rotationQuaternion) {
mesh.rotationQuaternion = this.rotationQuaternion.clone();
}
mesh.scaling.copyFrom(this.scaling);
mesh.computeWorldMatrix(true);
return mesh;
}
}
//# sourceMappingURL=csg.js.map