Importing GLB file - every material is single mesh

Hello,

I am trying to import GLB file into a scene (using THREE.GLTFLoader) and the issue I am facing with is the following:
The GLB file itself has a object with 4 different materials, and during the export I am getting 4 different meshes out of it. This makes things difficult because I want to have everything as a single mesh.

on the picture you may see the structure of GLB file in Blender.

GLTFLoader does not create multi-material meshes. In the screenshot above it will create four meshes as children of a single parent Group. You’ll need to either work with that Group instead, or merge them after loading using BufferGeometryUtils.

2 Likes

Thanks for your answer. I was able to merge all those different meshes into one with the following function:

function createMesh(insertedMeshes) {

var materials = [],
geometries = [],
mergedGeometry = new THREE.BufferGeometry(),
meshMaterial,
mergedMesh;

insertedMeshes.forEach(function(mesh, index) {
  mesh.updateMatrix();
  geometries.push(mesh.geometry);
  meshMaterial = new THREE.MeshStandardMaterial(mesh.material);
  materials.push(meshMaterial);
});

mergedGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries(geometries, true);
mergedGeometry.groupsNeedUpdate = true;

mergedMesh = new THREE.Mesh(mergedGeometry, materials);

return mergedMesh;

}

I hope it will help somebody.

Mirko

3 Likes

Hi @donmccurdy … Is it ok group multiple meshes to a group to reduce the draw calls?

If you mean putting multiple THREE.Mesh instances as children of a shared THREE.Group, then there’s nothing wrong with that but it will not reduce draw calls. One Mesh/Material pair is one draw call. InstancedMesh will help if you’re drawing many of the same Mesh/Material pair.

1 Like

as you said in case of multimaterial it creates new group and adds meshes formed by separating materials to it.
i need to identify this group later. is there any way to identify this ? or could you provide the part of code in the gltf loader where this process is done so that i can add one property in the userData for that group.

The relevant code is here:

I believe you can assume that if you see several THREE.Mesh instances as direct children of a THREE.Group in the output of GLTFLoader, they were originally a multi-primitive mesh. There is no precise match to the concept of “multi-material mesh” in glTF.

1 Like

Thanks for the answer, by checking the response of the GLTF loader, I could find nodes and meshes in parser.json.
Using this I identified nodes with multiple primitives, names of these nodes matches with the names of groups in the scene whose children are separated by materials.

here’s the code snippet

const loader = new GLTFLoader();

      // Load the GLB scene
      loader.load(
        url,
        (gltf: any) => {
         
          const scene = gltf.scene;
          processJson(gltf.parser.json);
          processScene(scene);
        },
        (xhr: ProgressEvent<EventTarget>) => {
          console.log((xhr.loaded / xhr.total) * 100);
        },
        (error) => {
         console.log(error);
        }
      );
    })



processJson(json: any) {
    const {  meshes, nodes } = json;

    //   identify groups with multi mat separated children
    if (meshes) {
      for (const node of nodes){
        const {mesh: meshIndex, name} = node;
        const mesh = meshes[meshIndex];
        let primitivesWithMatCount = 0;
        const { primitives } = mesh;

        for (const primitive of primitives) {
          if ("material" in primitive) {
            primitivesWithMatCount++;
          }
        }

        if (primitivesWithMatCount > 1) {
          combinedMeshesParentNames.push(name);
        }
      }
    }
  }

combine meshes into one



    const areChildrenSeparatedByMat= (child: any) => {
      if (!child.isGroup) return false;

      const { name } = child;

      return combinedMeshesParentNames.includes(name);
    };

   const combineMeshes = (meshes: any[]) => {
      var materials = [],
        geometries = [],
        mergedGeometry: any = new THREE.BufferGeometry(),
        meshMaterial,
        mergedMesh;

      for (const mesh of meshes) {
        mesh.updateMatrix();
        geometries.push(mesh.geometry);
        meshMaterial = new THREE.MeshStandardMaterial(mesh.material as any);
        materials.push(meshMaterial);
      }

      mergedGeometry = mergeBufferGeometries(geometries, true);
      mergedGeometry.groupsNeedUpdate = true;

      mergedMesh = new THREE.Mesh(mergedGeometry, materials);

      return mergedMesh;
    };

    const combineChildren = (group: THREE.Group) => {
      const children = group.children;
      const mergedMesh = combineMeshes(children);
      mergedMesh.name = group.name;
      mergedMesh.userData = group.userData;
      if (group.parent) {
        group.parent.add(mergedMesh);
        group.parent.remove(group);
      } else {
        // todo : need to handle this
      }
    };


 

  processScene(scene: THREE.Scene) =>{

      if (combinedMeshesParentNames.length == 0) return  
   
     scene.traverse((child: any) => {
    

        if (areChildrenSeparatedByMat(child)) {
          combineChildren(child);
        }
   

      });
   }

can help someone struggling with the same problem