Join thousands of single meshes by layer

I’m having trouble with a script that helps me to simplify some sketchup files that we get from time to time in the office.
Hundreds of thousands of individual meshes that I want to join by layer.
I saw the script above which should be faster than the normal join command and I would like it to automatically join all the meshes for each layer.

this is the script snippet I am starting from:

def JoinByLayer() :
    layer_list = rs.LayerIds()
    rs.EnableRedraw(False)
    for layer_id in layer_list:
        rs.UnselectAllObjects()
        rs.ObjectsByLayer(layer_id,True)
        objs=rs.SelectedObjects()
        if objs and len(objs)>1: rs.Command("_Join")

What I would like is a way to join a large number of single meshes in the fastest way possible, hypothetically even 1 million single meshes by layer.
Up to a few thousand meshes everything seems to work fine with the command line join, but if we are talking about hundreds of thousands of meshes, then it hangs forever…
I am using rhino 7 latest sr.
Can anyone help me with this?

Thanks in advance!

do not use rhinoscript syntax but rhinocommon directly:

https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.mesh/append

but maybe check the export (sketchup) and import (rhino) options.

1 Like

Hi @Pitti, you might try if below script runs any faster:

JoinMeshesByLayer.py (2.1 KB)

Note: it ignores hidden or locked layers or hidden or locked meshes…

_
c.

1 Like
        # get meshes from rhino objects
        # meshes = List[Rhino.Geometry.Mesh]([rs.coercemesh(obj.Id) for obj in rc])
        meshes = List[Rhino.Geometry.Mesh]([obj.Geometry for obj in rc])

it should be possible to get the geometry directly from the obj.

1 Like

Yes, you’re right. I’ve changed it in above upload.

thanks,
clement

I was using this method, thanks to @Helvetosaur
but yours it’s even a bit faster.

def JoinMeshesByLayer():
    rs.EnableRedraw(False)
    layers=rs.LayerNames()
    for layer in layers:
        layer_meshes=[obj for obj in rs.ObjectsByLayer(layer) if rs.IsMesh(obj)]
        print "Found {} meshes on layer {}".format(len(layer_meshes),layer)
        if layer_meshes and len(layer_meshes)>1:
            joined=rs.JoinMeshes(layer_meshes,False)
            if joined:
                rs.ObjectLayer(joined,layer)
                try:
                    rs.DeleteObjects(layer_meshes)
                except:
                    pass
JoinMeshesByLayer()

Before the Join I am running this to create layers by material names and put the meshes in the same layer by material:

for guid in guids:
    obj = rs.coercerhinoobject(guid)
    material_index = obj.Attributes.MaterialIndex
    rhino_material = sc.doc.Materials[material_index]
    material_color = rhino_material.DiffuseColor
    material_name = rhino_material.Name
    #print rhino_material.Name
    layer_index = sc.doc.Layers.Find(material_name, True)
    if layer_index >= 0:
        obj.Attributes.LayerIndex = layer_index
        obj.CommitChanges()
    else:
        new_layer_index = sc.doc.Layers.Add(material_name, material_color)
        obj.Attributes.LayerIndex = new_layer_index
        obj.CommitChanges()

Now I just need to auto assign the original materials to the joined meshes because right now they get lost , do you have a suggestion also for this?

Hi @Pitti,

wouldn’t it be better to assign the material to the layer in your last script ? By default objects without materials use their layer’s material if one is assigned to the layer. You could do it once after the new layer is created using:

new_layer_index = sc.doc.Layers.Add(material_name, material_color)
layer = sc.doc.Layers[new_layer_index]
rs.LayerMaterialIndex(layer, material_index)
...

if this is not practical, post a small example so i can see what type of materials (or render materials) you need to be assigned. Which version of Rhino do you have ?

_
c.

what i am doing is importing a large sketchup file with hundreds of thousands if not millions of individual meshes and make it usable;

the key steps in short are these:

  • Create a layer for each material in the scene, with the name of the material itself. (DONE)

  • Put the meshes in each corresponding layer.(DONE)

  • Join the meshes by layer so that you go from a million individual meshes to a few hundred (I end up with just one object per material/layer) (DONE)

Assign the corresponding material to the objects/layers (MISSING)

Export the result in Obj format.(DONE)

Your script gives me this error:

Message: expected str, got Layer

Hi @Pitti, ok try changing this line:

rs.LayerMaterialIndex(layer, material_index)

to this:

rs.LayerMaterialIndex(layer.FullPath, material_index)

If above works, you should have the material assigned to the layer. The question is if you then get obj files with the correct material assignment once exported. Therefore i’ve asked for a small example file.

_
c.

Working like a charm ,Thanks for the help!

after testing several files I got this error on one of them:

this line:
rs.LayerMaterialIndex(layer.FullPath, material_index)

shows this:
“blablabla does not exist in layer table”

Hi @Pitti,

i cannot repeat this if i do not have an example file and no exact error description. In which script and which line do you get that error ?

You might add some check to the script after this line

new_layer_index = sc.doc.Layers.Add(material_name, material_color)

if new_layer_index is smaller than zero then it could be that adding the new layer failed because it already exists. Do you have multiple materials with the same name in the materials Panel ?

_
c.

1 Like

All of the skp file I am using are under nda so I can’t share, I am sorry, I have no timeat the moment to prepare a sample file.
For now I have solved by putting the material code line in the middle of a try except, and skipping that error, it is not crucial if a material is missing eventually.