Select Individual faces from an imported mesh or custom made mesh by blender

Hey there guys, just another babylonjs newbie here
So this is the thing here, i have been messing around with babylonjs while learning but i just hit a stumbling block that is really making me go crazy
My main goal is to be able to change a face material when a pick event happens to collide with the target mesh
So i created a simple Cube using MeshBuilder like below,

      const sampleBox = BABYLON.MeshBuilder.CreateBox('sampleBox', {
      size: 10,
     updatable: true
    }, scene, true);
   sampleBox.isPickable = true;
    sampleBox.position = new Vector3(0, 0, 0);
    let verticesCount = sampleBox.getTotalVertices();

Then i applied maltimaterial to 4 of the box faces like below

let material0 = new StandardMaterial('material0', scene);
    material0.diffuseTexture = new Texture('/@kga_brawlhalla.jpg', scene);
    material0.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material0.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material0.specularColor = new Color3.Black();

    let material1 = new StandardMaterial('material2', scene);
    material1.diffuseTexture = new Texture('/elina.jpg', scene);
    material1.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material1.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material1.specularColor = new Color3.Black();

    let material2 = new StandardMaterial('material2', scene);
    material2.diffuseTexture = new Texture('/@kga_pubg.jpg', scene);
    material2.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material2.diffuseTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material2.specularColor = new Color3.Black();

    let material3 = new StandardMaterial('material3', scene);
    material3.diffuseTexture = new Texture('/test-image.jpg', scene);
    material3.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material3.diffuseTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
    material3.specularColor = new Color3.Black();

    // Define the multimaterial
    let maltimat = new MultiMaterial('multi', scene);
    maltimat.subMaterials.push(material0);
    maltimat.subMaterials.push(material1);
    maltimat.subMaterials.push(material2);
    maltimat.subMaterials.push(material3);

    // Assign mesh material to the mesh
    sampleBox.material = maltimat;
    // Define the subMeshes
    sampleBox.subMeshes.push(new BABYLON.SubMesh(2, 0, verticesCount, 0, 6, sampleBox));
    sampleBox.subMeshes.push(new BABYLON.SubMesh(1, 0, verticesCount, 6, 6, sampleBox));
    sampleBox.subMeshes.push(new BABYLON.SubMesh(3, 0, verticesCount, 12, 6, sampleBox));
    sampleBox.subMeshes.push(new BABYLON.SubMesh(0, 0, verticesCount, 16, 6, sampleBox));

So when using the pick event i was able to change the material of a face that contained a submesh applied to it by accessing the submesh array and changing the materialIndex to one of the one contained in the maltimaterial the problems with this solution were as below

  • I tried using submeshId to identify the clicked face and change the material index but all it returns is zero

  • I used 4 submeshes for a reason such that i can check if a certain face contains a submesh material and i can just change the index else i can create a new BABYLON.submesh and apply to the selected face but how do i generate the startIndex automatically from the pick info or rather how do i do this

        scene.onPointerPick = (event, pickInfo) => {
              if (pickInfo.hit) {
                  let indices = pickInfo.pickedMesh.getIndices();
                  let faceId = pickInfo.faceId;
                  let index0 = indices[pickInfo.faceId * 3];
                  let index1 = indices[pickInfo.faceId * 3 + 1];
                  let index2 = indices[pickInfo.faceId * 3 + 2];
                  let positions = pickInfo.pickedMesh.getVerticesData(VertexBuffer.PositionKind);
               if (submeshId){
       sampleBox.subMeshes[pickInfo.subMeshId].materialIndex = 0;
      } else {
      new BABYLON.SubMesh(1,0,verticesCount, indexStart, count, sampleBox);
      }      
              }
          }
    

Another solution was to use vertexData but i could only manage to do that with colors by manipulating the colors array but not able to do this with images.

 let positions = [-5, 2, -3, -7, -2, -3, -3, -2, -3, 5, 2, 3, 7, -2, 3, 3, -2, 3];
     let indices = [0, 1, 2, 3, 4, 5];
     let colors = [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1];
    let vertexData = new BABYLON.VertexData();
    vertexData.positions = positions;
     vertexData.indices = indices;
    vertexData.colors = colors;
     vertexData.applyToMesh(sampleMesh);

I would appreciate any help and even buy a soda and give a hug if i get some help and also if you were near me:hugs:

Hi @Peanut_Butter and welcome to the forum. Might be worth a read How to change face color of a box dynamically?

Hi/ you can select faces grouped in submeshes by material id of multiMaterial.

please read about multi materials/
https://doc.babylonjs.com/how_to/multi_materials

in your code you create materials but dont create multimaterial
var multimat = new BABYLON.MultiMaterial(“multi”, scene);
and dont assign submaterials
multimat.subMaterials.push(material0);

  • dont assign face groups to submaterials
    sphere.subMeshes = [];
    var verticesCount = sphere.getTotalVertices();
    new BABYLON.SubMesh(0, 0, verticesCount, 0, 900, sphere);

However!!! easiest way is assign multimaterial in the program in which you work and export the object with babylon exporter. import in babylon engine and you can select submesh by submaterial id

*on screenshot i make in 3ds max multimaterial with one base materal for ambient not active submeshes, one for logo decal and 5 submaterials for active submesh but assign only #2 to active submesh(chair back) and now i can swap submaterials #2, #3, #4, #5, #6 when clicking on the material thumbnail sphere
2

now you can select yourSubMesh from an array pickInfo.pickedMesh.subMeshes by subMaterial id = yourSubMeshID

var subMaterialID = yourSubMeshID;
var yourSubMesh = pickInfo.pickedMesh.subMeshes[subMaterialID]

now you can change subMesh material index and this change material on submesh
activeMesh.subMeshes[2].materialIndex = targetMatID

but if you need animate material you need clone submesh and play with opacity or use shader material or maybe node material.
sorry for bad english/ I hope I clearly explained what can be done?

1 Like

I actually use blender to make my models so i will find a way to replicate 3ds max as what you have said, Your explanation is solid but what about the submeshid when i print it out to the console it only comes out as zero here is a sample of mine from the playGround

SubmeshId only returning 0

And here is an example from babylon and which when clicked on the different parts prints out a different number for each material/submesh. Any logical explanation for where i am going wrong

SubmeshId working in babylon example

yes sorry i dont scroll your code/ you a really create submaterial.
i think something went wrong when we add submeshes and now we have 9 submeses not 4
http://screenshot.su/show.php?img=adfb6afbee8c198a9b09548a40944959.jpg
and in original PG
http://screenshot.su/show.php?img=78608fd0e9c3c7dd5e6a02ab82c33326.jpg
when we click on mid of sphere we get id 2 but need get id1 and when click on bottom of sphere we get id 4 but we add only 3 submeshes where 0,1,2 and i dont understand why we get id 2 on not id 1 and this looks strange
http://screenshot.su/show.php?img=c65f0a21b9271d1b9c719dc16b383492.jpg
i dont understand what are hapen and why we get more submeshes than we push in array)))

and mesh look wrong if you decrease opacity you can see this
http://screenshot.su/show.php?img=45692245550fd62d4d90139e7bb0b482.jpg

at the moment nothing else comes to mind since I did not understand the creation of subobjects at the program level.

So i was able to solve this ginormous puzzle and read the babylonjs documentation on multimaterials and found out where i was going wrong. Here is a link to the working kind and solution.
Submeshes and submeshId the correct way on a cube
When creating the multimaterials using submeshes it is wise to declare an empty array like so on the corresponding mesh before defining or assigning materials on different parts of the mesh
sampleBox.subMeshes = [];

If you intend to use the submesh Ids of the submeshes declared
THANKS FOR THE HELP EVERYONE :ok_hand:

1 Like

@kvasss i realised that the wierd opacity stuff happens if you do not declare a submesh material to all the parts of the mesh but this only happens in the playground for some reason so i did my homework and walla it works. So i don’t think imported meshes should experience this wierdness unless backfaceCulling is set to true or other submeshes on the object do not have a material assigned to them
Working kind on playground

1 Like

Hi @Peanut_Butter I understood. When a mesh is created, it immediately creates the subMeshes property with one element in the array - the mesh itself that was created. That’s why we always got 0 when click on the mesh. By writing sampleBox.subMeshes = ; you just clear the subMeshes array === original mesh. and recreate this mesh with new submeshes

1 Like