Help in colouring object loaded from ObjLoader

Hi there.

I am trying to build upon a speckle idea from the last hackatron. the Mass On Speed.
and am i trying to build a webapp that loads a speckle model and then overlays an obj over the model.

i am using typescript for overlaying the obj with the ObjLoader from speckle.

I am using the following code for this:

async function overlayObj(objData: string, id: string) {
  if (!viewer) {
    console.error("Viewer is not initialized");
    return;
  }
  if (lastLoaded) {
    viewer.unloadObject(id);
    console.log("Disposed last loaded object");
  }
  /** Create a loader for the .obj data */
  const loader = new ObjLoader(viewer.getWorldTree(), id, objData);
  /** Load the obj data */
  await viewer.loadObject(loader, false);
  // this seems to be sepckle stuff
  const tree = viewer.getWorldTree();
  // get node from loader.tree.nodeMap with id and set the colour of the object to red
  const nodes = viewer.getWorldTree().findId(id);
  // Set the color of the object to red
  if (nodes && Array.isArray(nodes) && nodes.length > 0) {
    for (const node of nodes) {
      for (const child of node.children) {
        const obj = child.model.raw;
        if (obj.isMesh ) {
          obj.material.color.set(0xff0000);
          console.log(`Node ${node.id} color set to red`);
        } else {
          console.error(`Node.model is not a THREE.Mesh: ${child.model}`);
        }
      }
    }
  } else {
    console.error("Node array is empty");
  }
  // Refresh the viewer
  viewer.requestRender();
  lastLoaded = loader;
  console.log("OBJ file loaded successfully");
}

but this code loads the obj propperly but does not colour it red.
any help on how i can colour the loaded OBJ in red would be appreciated.
i am not getting any errors in the console and it seems that the script changes the material colour, but nothing happens.

i really am a beginner in undestranding programming and how speckle and three work together.

Thank you.

Hi @Ionut_Anton

You’re approach is mostly correct! Just one or two details missing:

If you want to color specific objects based on their id it’s easier if you do it like this

const materialData = {
  id: 'test',
  color: 0xff0000,
  emissive: 0x0,
  opacity: 1,
  roughness: 1,
  metalness: 0,
  vertexColors: false
}
const renderViews= viewer.getWorldTree().getRenderTree().getRenderViewsForNodeId(id)
if(renderViews) 
  viewer.getRenderer().setMaterial(renderViews, materialData)

So basically you don’t necessarily need to go over node children manually, there are helper functions in RenderTree that can help.

Materials need to be applied via the setMaterial API call in order for them to 100% benefit from the viewer’s extra features like RTE or batching.

Let us know if you need more help

Cheers

Hi there,

thank you for the swift reply.
i get it now that i have to build a new material and asign to the objects.

unfortunately the code does not do anything.
as far as i undestand the renderviews it gets dont carry any material.
image

const materialData = {
id: “objred”,
color: 0xff0000,
emissive: 0x0,
opacity: 1,
roughness: 1,
metalness: 0,
vertexColors: false,
};
const renderViews = viewer
.getWorldTree()
.getRenderTree()
.getRenderViewsForNodeId(id);
if (renderViews) {
const existingMaterial = viewer.getRenderer().getMaterial(renderViews[0]);
viewer.getRenderer().setMaterial(renderViews, materialData);
const newMaterial = viewer.getRenderer().getMaterial(renderViews[0]);
const pause = 1;
}

i did try this but exisntingMaterial and newMaterial are both empty.

Cheers,

You are trying to apply a material to a BlockInstance which in the context of the OBJ content would be the equivalent of a group that is not displayable in any way.

You are getting the render views, but only applying the material to the first one, renderViews[0] which is a BlockInstance and does not have nor take materials. What you probably want is to apply the material to all the renderViews returned by the call to getRenderViewsForNodeId. Also note that setMaterial takes an array of renderviews as it’s first argument.

If you have a look at the live example I’ve prepared for you, you will see that it’s functioning properly

Cheers