@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.
218 lines • 12.7 kB
JavaScript
import { Matrix, Vector4 } from "../../Maths/math.vector.js";
import { Color4 } from "../../Maths/math.color.js";
import { Mesh } from "../mesh.js";
import { VertexData } from "../mesh.vertexData.js";
import { useOpenGLOrientationForUV } from "../../Compat/compatibilityOptions.js";
import { CreateGroundVertexData } from "./groundBuilder.js";
/**
* 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 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)
* @returns the VertexData of the box
*/
export function CreateBoxVertexData(options) {
const nbFaces = 6;
let indices = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23];
const normals = [
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
];
const uvs = [];
let positions = [];
const width = options.width || options.size || 1;
const height = options.height || options.size || 1;
const depth = options.depth || options.size || 1;
const wrap = options.wrap || false;
let topBaseAt = options.topBaseAt === void 0 ? 1 : options.topBaseAt;
let bottomBaseAt = options.bottomBaseAt === void 0 ? 0 : options.bottomBaseAt;
topBaseAt = (topBaseAt + 4) % 4; // places values as 0 to 3
bottomBaseAt = (bottomBaseAt + 4) % 4; // places values as 0 to 3
const topOrder = [2, 0, 3, 1];
const bottomOrder = [2, 0, 1, 3];
let topIndex = topOrder[topBaseAt];
let bottomIndex = bottomOrder[bottomBaseAt];
let basePositions = [
1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1,
1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1,
];
if (wrap) {
indices = [2, 3, 0, 2, 0, 1, 4, 5, 6, 4, 6, 7, 9, 10, 11, 9, 11, 8, 12, 14, 15, 12, 13, 14];
basePositions = [
-1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1,
];
let topFaceBase = [
[1, 1, 1],
[-1, 1, 1],
[-1, 1, -1],
[1, 1, -1],
];
let bottomFaceBase = [
[-1, -1, 1],
[1, -1, 1],
[1, -1, -1],
[-1, -1, -1],
];
const topFaceOrder = [17, 18, 19, 16];
const bottomFaceOrder = [22, 23, 20, 21];
while (topIndex > 0) {
topFaceBase.unshift(topFaceBase.pop());
topFaceOrder.unshift(topFaceOrder.pop());
topIndex--;
}
while (bottomIndex > 0) {
bottomFaceBase.unshift(bottomFaceBase.pop());
bottomFaceOrder.unshift(bottomFaceOrder.pop());
bottomIndex--;
}
topFaceBase = topFaceBase.flat();
bottomFaceBase = bottomFaceBase.flat();
basePositions = basePositions.concat(topFaceBase).concat(bottomFaceBase);
indices.push(topFaceOrder[0], topFaceOrder[2], topFaceOrder[3], topFaceOrder[0], topFaceOrder[1], topFaceOrder[2]);
indices.push(bottomFaceOrder[0], bottomFaceOrder[2], bottomFaceOrder[3], bottomFaceOrder[0], bottomFaceOrder[1], bottomFaceOrder[2]);
}
const scaleArray = [width / 2, height / 2, depth / 2];
positions = basePositions.reduce((accumulator, currentValue, currentIndex) => accumulator.concat(currentValue * scaleArray[currentIndex % 3]), []);
const sideOrientation = options.sideOrientation === 0 ? 0 : options.sideOrientation || VertexData.DEFAULTSIDE;
const faceUV = options.faceUV || new Array(6);
const faceColors = options.faceColors;
const colors = [];
// default face colors and UV if undefined
for (let f = 0; f < 6; f++) {
if (faceUV[f] === undefined) {
faceUV[f] = new Vector4(0, 0, 1, 1);
}
if (faceColors && faceColors[f] === undefined) {
faceColors[f] = new Color4(1, 1, 1, 1);
}
}
// Create each face in turn.
for (let index = 0; index < nbFaces; index++) {
uvs.push(faceUV[index].z, useOpenGLOrientationForUV ? 1.0 - faceUV[index].w : faceUV[index].w);
uvs.push(faceUV[index].x, useOpenGLOrientationForUV ? 1.0 - faceUV[index].w : faceUV[index].w);
uvs.push(faceUV[index].x, useOpenGLOrientationForUV ? 1.0 - faceUV[index].y : faceUV[index].y);
uvs.push(faceUV[index].z, useOpenGLOrientationForUV ? 1.0 - faceUV[index].y : faceUV[index].y);
if (faceColors) {
for (let c = 0; c < 4; c++) {
colors.push(faceColors[index].r, faceColors[index].g, faceColors[index].b, faceColors[index].a);
}
}
}
// sides
VertexData._ComputeSides(sideOrientation, positions, indices, normals, uvs, options.frontUVs, options.backUVs);
// Result
const vertexData = new VertexData();
vertexData.indices = indices;
vertexData.positions = positions;
vertexData.normals = normals;
vertexData.uvs = uvs;
if (faceColors) {
const totalColors = sideOrientation === VertexData.DOUBLESIDE ? colors.concat(colors) : colors;
vertexData.colors = totalColors;
}
return vertexData;
}
/**
* Creates the VertexData for a segmented 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
* * segments sets the number of segments on the all axis (1 by default)
* * widthSegments sets the number of segments on the x axis (1 by default)
* * heightSegments sets the number of segments on the y axis (1 by default)
* * depthSegments sets the number of segments on the z axis (1 by default)
* @returns the VertexData of the box
*/
export function CreateSegmentedBoxVertexData(options) {
const width = options.width || options.size || 1;
const height = options.height || options.size || 1;
const depth = options.depth || options.size || 1;
const widthSegments = (options.widthSegments || options.segments || 1) | 0;
const heightSegments = (options.heightSegments || options.segments || 1) | 0;
const depthSegments = (options.depthSegments || options.segments || 1) | 0;
const rotationMatrix = new Matrix();
const translationMatrix = new Matrix();
const transformMatrix = new Matrix();
const bottomPlane = CreateGroundVertexData({ width: width, height: depth, subdivisionsX: widthSegments, subdivisionsY: depthSegments });
Matrix.TranslationToRef(0, -height / 2, 0, translationMatrix);
Matrix.RotationZToRef(Math.PI, rotationMatrix);
rotationMatrix.multiplyToRef(translationMatrix, transformMatrix);
bottomPlane.transform(transformMatrix);
const topPlane = CreateGroundVertexData({ width: width, height: depth, subdivisionsX: widthSegments, subdivisionsY: depthSegments });
Matrix.TranslationToRef(0, height / 2, 0, transformMatrix);
topPlane.transform(transformMatrix);
const negXPlane = CreateGroundVertexData({ width: height, height: depth, subdivisionsX: heightSegments, subdivisionsY: depthSegments });
Matrix.TranslationToRef(-width / 2, 0, 0, translationMatrix);
Matrix.RotationZToRef(Math.PI / 2, rotationMatrix);
rotationMatrix.multiplyToRef(translationMatrix, transformMatrix);
negXPlane.transform(transformMatrix);
const posXPlane = CreateGroundVertexData({ width: height, height: depth, subdivisionsX: heightSegments, subdivisionsY: depthSegments });
Matrix.TranslationToRef(width / 2, 0, 0, translationMatrix);
Matrix.RotationZToRef(-Math.PI / 2, rotationMatrix);
rotationMatrix.multiplyToRef(translationMatrix, transformMatrix);
posXPlane.transform(transformMatrix);
const negZPlane = CreateGroundVertexData({ width: width, height: height, subdivisionsX: widthSegments, subdivisionsY: heightSegments });
Matrix.TranslationToRef(0, 0, -depth / 2, translationMatrix);
Matrix.RotationXToRef(-Math.PI / 2, rotationMatrix);
rotationMatrix.multiplyToRef(translationMatrix, transformMatrix);
negZPlane.transform(transformMatrix);
const posZPlane = CreateGroundVertexData({ width: width, height: height, subdivisionsX: widthSegments, subdivisionsY: heightSegments });
Matrix.TranslationToRef(0, 0, depth / 2, translationMatrix);
Matrix.RotationXToRef(Math.PI / 2, rotationMatrix);
rotationMatrix.multiplyToRef(translationMatrix, transformMatrix);
posZPlane.transform(transformMatrix);
// Result
bottomPlane.merge([topPlane, posXPlane, negXPlane, negZPlane, posZPlane], true);
return bottomPlane;
}
/**
* Creates a box mesh
* * The parameter `size` sets the size (float) of each box side (default 1)
* * You can set some different box dimensions by using the parameters `width`, `height` and `depth` (all by default have the same value of `size`)
* * You can set different colors and different images to each box side by using the parameters `faceColors` (an array of 6 Color3 elements) and `faceUV` (an array of 6 Vector4 elements)
* * Please read this tutorial : https://doc.babylonjs.com/features/featuresDeepDive/materials/using/texturePerBoxFace
* * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
* * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4). Detail here : https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set#side-orientation
* * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
* @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set#box
* @param name defines the name of the mesh
* @param options defines the options used to create the mesh
* @param scene defines the hosting scene
* @returns the box mesh
*/
export function CreateBox(name, options = {}, scene = null) {
const box = new Mesh(name, scene);
options.sideOrientation = Mesh._GetDefaultSideOrientation(options.sideOrientation);
box._originalBuilderSideOrientation = options.sideOrientation;
const vertexData = CreateBoxVertexData(options);
vertexData.applyToMesh(box, options.updatable);
return box;
}
/**
* Class containing static functions to help procedurally build meshes
* @deprecated please use CreateBox directly
*/
export const BoxBuilder = {
// eslint-disable-next-line @typescript-eslint/naming-convention
CreateBox,
};
// Side effects
VertexData.CreateBox = CreateBoxVertexData;
Mesh.CreateBox = (name, size, scene = null, updatable, sideOrientation) => {
const options = {
size,
sideOrientation,
updatable,
};
return CreateBox(name, options, scene);
};
//# sourceMappingURL=boxBuilder.js.map