Use multiple materials for merged geometries in Three.js

After a long research I discovered that I was missing an extra parameter for the method 'merge' from the Geometry object, the last parameter is the index of the material that the mesh must have from the materials array, ex: 0 -> first material in 'materials' array... and so on.

So, my final piece of code looks like:

pine_geometry.merge(pine_1.geometry, pine_1.matrix, 0);

var pine_texture_2 = THREE.ImageUtils.loadTexture('./res/textures/5.jpg');
var pine_geometry_2 = new THREE.CylinderGeometry(0, 70, 250, 8);
var pine_material_2 = new THREE.MeshBasicMaterial({
  map : pine_texture_2
});

var pine_2 = new THREE.Mesh(pine_geometry_2);
pine_2.position.x = x;
pine_2.position.y = y + 175;
pine_2.position.z = z;

pine_2.updateMatrix();
pine_geometry.merge(pine_2.geometry, pine_2.matrix, 1);

(Note the last numbers I add to each merge).

However, I want to clarify that this practice only works when we are dealing with various geometries that are from the same type, in this case, we're merging two CylinderGeometry, but if we wanted to merge for example a Cylinder with a Box AND add the MeshFaceMaterial, it wouldn't be recognized properly and the console will throw us 'Cannot read property map/attributes from undefined', nevertheless we can still merge both geometries but not providing multiple materials (that's a terrible mistake I made).

Hope this helps to anyone.


Here's a general function to merge meshes with materials, You can also specify if you want it to return it as a buffer geometry.

function _mergeMeshes(meshes, toBufferGeometry) {

    var finalGeometry,
        materials = [],
        mergedGeometry = new THREE.Geometry(),
        mergeMaterial,
        mergedMesh;

    meshes.forEach(function(mesh, index) {
        mesh.updateMatrix();
        mesh.geometry.faces.forEach(function(face) {face.materialIndex = 0;});
        mergedGeometry.merge(mesh.geometry, mesh.matrix, index);
        materials.push(mesh.material);
    });

    mergedGeometry.groupsNeedUpdate = true;
    mergeMaterial = new THREE.MeshFaceMaterial(materials);

    if (toBufferGeometry) {
        finalGeometry = new THREE.BufferGeometry().fromGeometry(mergedGeometry);
    } else {
        finalGeometry = mergedGeometry;
    }

    mergedMesh = new THREE.Mesh(finalGeometry, mergeMaterial);
    mergedMesh.geometry.computeFaceNormals();
    mergedMesh.geometry.computeVertexNormals();

    return mergedMesh;

}

var mergedMesh = _mergeMeshes([trunkMesh, treeTopMesh], true);