Merging Meshes

Native Babylon.js function

To easily merge a number of meshes to a single mesh use the static MergeMeshes of the Mesh class:

var newMesh = BABYLON.Mesh.MergeMeshes(arrayOfMeshes, disposeSource, allow32BitsIndices, meshSubclass, subdivideWithSubMeshes, multiMultiMaterials);
variabledescription
variable
arrayOfMeshes
description
An array of Meshes. They should all be of the same material.
variable
disposeSource (optional)
description
When true (default), the source meshes will be disposed upon completion.
variable
allow32BitsIndices (optional)
description
When the sum of the vertices > 64k, this must be set to true.
variable
meshSubclass (optional)
description
When set, vertices inserted into this Mesh. Meshes can then be merged into a Mesh sub-class.
variable
subdivideWithSubMeshes (optional)
description
When true (false default), subdivide mesh to his subMesh array with meshes source.
variable
multiMultiMaterials (optional)
description
When true (false default), subdivide mesh and accept multiple multi materials, ignores subdivideWithSubMeshes.

Since multiMultiMaterials defaults to false, the resulting merged mesh will have only one material applied to it (taken from the first mesh):

Merged Meshes Example

Compare with the following example which sets multiMultiMaterials to true:

Merging Meshes With Multiple Materials

See this page for more details on usage of merged meshes.

Use your own merge function

If you want to merge meshes into a new one using a self implemented function, you can use the following code as basis and improve it to your needs:

Note: Careful, when you merge cloned mesh, you need to update the world matrix of the mesh with computeWorldMatrix before calling the function.

Note: This article covers the internal merging process. You can also use BABYLON.VertexData object and its merge() function for a simpler solution.

var mergeMeshes = function (meshName, arrayObj, scene) {
var arrayPos = [];
var arrayNormal = [];
var arrayUv = [];
var arrayUv2 = [];
var arrayColor = [];
var arrayMatricesIndices = [];
var arrayMatricesWeights = [];
var arrayIndice = [];
var savedPosition = [];
var savedNormal = [];
var newMesh = new BABYLON.Mesh(meshName, scene);
var UVKind = true;
var UV2Kind = true;
var ColorKind = true;
var MatricesIndicesKind = true;
var MatricesWeightsKind = true;
for (var i = 0; i != arrayObj.length; i++) {
if (!arrayObj[i].isVerticesDataPresent([BABYLON.VertexBuffer.UVKind])) UVKind = false;
if (!arrayObj[i].isVerticesDataPresent([BABYLON.VertexBuffer.UV2Kind])) UV2Kind = false;
if (!arrayObj[i].isVerticesDataPresent([BABYLON.VertexBuffer.ColorKind])) ColorKind = false;
if (!arrayObj[i].isVerticesDataPresent([BABYLON.VertexBuffer.MatricesIndicesKind])) MatricesIndicesKind = false;
if (!arrayObj[i].isVerticesDataPresent([BABYLON.VertexBuffer.MatricesWeightsKind])) MatricesWeightsKind = false;
}
for (i = 0; i != arrayObj.length; i++) {
var ite = 0;
var iter = 0;
arrayPos[i] = arrayObj[i].getVerticesData(BABYLON.VertexBuffer.PositionKind);
arrayNormal[i] = arrayObj[i].getVerticesData(BABYLON.VertexBuffer.NormalKind);
if (UVKind) arrayUv = arrayUv.concat(arrayObj[i].getVerticesData(BABYLON.VertexBuffer.UVKind));
if (UV2Kind) arrayUv2 = arrayUv2.concat(arrayObj[i].getVerticesData(BABYLON.VertexBuffer.UV2Kind));
if (ColorKind) arrayColor = arrayColor.concat(arrayObj[i].getVerticesData(BABYLON.VertexBuffer.ColorKind));
if (MatricesIndicesKind) arrayMatricesIndices = arrayMatricesIndices.concat(arrayObj[i].getVerticesData(BABYLON.VertexBuffer.MatricesIndicesKind));
if (MatricesWeightsKind) arrayMatricesWeights = arrayMatricesWeights.concat(arrayObj[i].getVerticesData(BABYLON.VertexBuffer.MatricesWeightsKind));
var maxValue = savedPosition.length / 3;
arrayObj[i].computeWorldMatrix(true);
var worldMatrix = arrayObj[i].getWorldMatrix();
for (var ite = 0; ite != arrayPos[i].length; ite += 3) {
var vertex = BABYLON.Vector3.TransformCoordinates(new BABYLON.Vector3(arrayPos[i][ite], arrayPos[i][ite + 1], arrayPos[i][ite + 2]), worldMatrix);
savedPosition.push(vertex.x);
savedPosition.push(vertex.y);
savedPosition.push(vertex.z);
}
for (var iter = 0; iter != arrayNormal[i].length; iter += 3) {
var vertex = BABYLON.Vector3.TransformNormal(new BABYLON.Vector3(arrayNormal[i][iter], arrayNormal[i][iter + 1], arrayNormal[i][iter + 2]), worldMatrix);
savedNormal.push(vertex.x);
savedNormal.push(vertex.y);
savedNormal.push(vertex.z);
}
var tmp = arrayObj[i].getIndices();
for (it = 0; it != tmp.length; it++) {
arrayIndice.push(tmp[it] + maxValue);
}
arrayIndice = arrayIndice.concat(tmp);
arrayObj[i].dispose(false);
}
newMesh.setVerticesData(BABYLON.VertexBuffer.PositionKind, savedPosition, false);
newMesh.setVerticesData(BABYLON.VertexBuffer.NormalKind, savedNormal, false);
if (arrayUv.length > 0) newMesh.setVerticesData(BABYLON.VertexBuffer.UVKind, arrayUv, false);
if (arrayUv2.length > 0) newMesh.setVerticesData(BABYLON.VertexBuffer.UV2Kind, arrayUv, false);
if (arrayColor.length > 0) newMesh.setVerticesData(BABYLON.VertexBuffer.ColorKind, arrayUv, false);
if (arrayMatricesIndices.length > 0) newMesh.setVerticesData(BABYLON.VertexBuffer.MatricesIndicesKind, arrayUv, false);
if (arrayMatricesWeights.length > 0) newMesh.setVerticesData(BABYLON.VertexBuffer.MatricesWeightsKind, arrayUv, false);
newMesh.setIndices(arrayIndice);
return newMesh;
};

Merging Meshes with Constructive Solid Geometry

You can also construct complex meshes by using subtract, inverse, union, and intersect methods of the CSG class.

For example, let's say you want to create a "pipe" shape with an inner and outer diameter (i.e., not just a "tube" mesh, which is a curved plane with no "thickness"). This can be constructed by first creating a "cylinder" mesh, and then subtracting a "tube" mesh from the inside of it.

function createPipe(diamInner: number, diamOuter: number, length: number, scene: BABYLON.Scene): BABYLON.Mesh {
// Create the outer wall using a Cylinder mesh
const mOuter = BABYLON.MeshBuilder.CreateCylinder(
"mOuter",
{
diameter: diamOuter,
height: length,
},
scene,
);
// Create the inner wall using a Tube mesh
const mInner = BABYLON.MeshBuilder.CreateTube(
"mInner",
{
path: [new BABYLON.Vector3(0, -length / 2, 0), new BABYLON.Vector3(0, length / 2, 0)],
radius: diamInner / 2,
sideOrientation: BABYLON.Mesh.DOUBLESIDE,
},
scene,
);
// Create CSG objects from each mesh
const outerCSG = BABYLON.CSG.FromMesh(mOuter);
const innerCSG = BABYLON.CSG.FromMesh(mInner);
// Create a new CSG object by subtracting the inner tube from the outer cylinder
const pipeCSG = outerCSG.subtract(innerCSG);
// Create the resulting mesh from the new CSG object
const mPipe = pipeCSG.toMesh("mPipe", null, scene);
// Dispose of the meshes, no longer needed
mInner.dispose();
mOuter.dispose();
scene.removeMesh(mInner);
scene.removeMesh(mOuter);
// Return the result
return mPipe;
}

Playground example:

Pipe CSG Example

Subtract example:

CSG Subtract Example