Convert different layers to materials

been working with Keyshot for years and for that it requires all same materials on same layer before export.
(assign material by layer) Now working with Lumion and it requires same materials setup as objects (assign material by object) before export into lumion.

so to convert my old models I have been selecting all objects in a layer and then assigning them to a material.
and so on…

is there an easy way to convert each seperate layer to an individual material object?

sorry if niot too clear, not easy to explain!

I’m not totally sure I understood. You need every object to have a different, unique material?

If it’s hard to explain in words you can always just post a file that shows what you have now and what you need to have later as a valid setup for lumion export

for lumion each object with the same material, i.e metal chrome, needs to have a unique material assigned.

For keyshot each object with same material needs to be assigned to the same layer.

Would a different name for the material be enough? For example create copies of the layer material “metal_chrome” for each object on it and name them “metal_chrome_001”, “metal_chrome_002”, etc

So… assign each object on a layer the material that the layer has… ?

-Pascal

yes correct

here is a movie to show what I am doing…

I have been working with keyshot - this requires all the same material to be on same layer.

Now I am testing lumion and it requires all same material to be assigned via object…

is there an easy way to convert my rhino model?

From the link you posted:

" * If you assign the material to a layer, all objects on the layer will then be identified by that material name when imported in Lumion "

Anyways here is a script that creates one new, uniquely named material per layer and then assigns this material to all objects on this layer


import Rhino
import scriptcontext as sc

def assignNewMaterialByLayer():
    
    # disable redraw, because depending on the number of objects this could get heavy
    sc.doc.Views.RedrawEnabled = False
    
    # create a dict to keep track of material <-> layer relation
    layerMaterialDict = {}
    
    # iterate over all layers in the doc
    for layer in sc.doc.Layers:
        
        # create a new material
        newMaterial = Rhino.DocObjects.Material()
        # name it like the layer fullpath, because fullpath is ALWAYS unique
        newMaterial.Name = layer.FullPath
        # add it to the doc, so we can assign it to objects
        index = sc.doc.Materials.Add(newMaterial)
        # finally store in the dict together with its layer
        layerMaterialDict[layer.Index] = index
        
    # iterate over all objects
    for object in sc.doc.Objects:
        
        # assign a material by layerindex, easy to do with our dict
        object.Attributes.MaterialIndex = layerMaterialDict[object.Attributes.LayerIndex]
        # tell the object to use its assigned material as its materialsource
        object.Attributes.MaterialSource = Rhino.DocObjects.ObjectMaterialSource.MaterialFromObject
        # commit the changes we made
        object.CommitChanges()
        
    # re-enable redraw
    sc.doc.Views.RedrawEnabled = True
    # and redraw all views so we can see what we did
    sc.doc.Views.Redraw()
            
if __name__ == "__main__":
    assignNewMaterialByLayer()

Hello Lando
that works great, many thanks… I did not expect someone to create code, I thought there might be a standard way to do this in rhino…

The Lumion documention is mis leading! your script works just how I want it to work.

many thanks
rich

I have noticed that if the material already has a texture applied to it, this is removed when I run the script… is it possible to keep the texture ?

Yes this is possible the script would need to check for actual materials applied to layers and prefer them if existing.

I might get to that today, time is a bit tight though

Here is an updated version of the script:
if a layer already has a material assigned, the material will be assigned to all objects on it, otherwise a new material will be created and assigned to all objects on that layer.
Hope it helps!

import Rhino
import scriptcontext as sc

def assignNewMaterialByLayer():
    
    # disable redraw, because depending on the number of objects this could get heavy
    sc.doc.Views.RedrawEnabled = False
    
    # create a dict to keep track of material <-> layer relation
    layerMaterialDict = {}
    
    # iterate over all layers in the doc
    for layer in sc.doc.Layers:
        
        # if there is already a layermaterial, we get to be lazy :)
        index = layer.RenderMaterialIndex
        if index == -1:
        
            # create a new material
            newMaterial = Rhino.DocObjects.Material()
            # name it like the layer fullpath, because fullpath is ALWAYS unique
            newMaterial.Name = layer.FullPath
            # add it to the doc, so we can assign it to objects
            index = sc.doc.Materials.Add(newMaterial)
            
        # finally store in the dict together with its layer
        layerMaterialDict[layer.Index] = index
        
    # iterate over all objects
    for object in sc.doc.Objects:
        
        # assign a material by layerindex, easy to do with our dict
        object.Attributes.MaterialIndex = layerMaterialDict[object.Attributes.LayerIndex]
        # tell the object to use its assigned material as its materialsource
        object.Attributes.MaterialSource = Rhino.DocObjects.ObjectMaterialSource.MaterialFromObject
        # commit the changes we made
        object.CommitChanges()
        
    # re-enable redraw
    sc.doc.Views.RedrawEnabled = True
    # and redraw all views so we can see what we did
    sc.doc.Views.Redraw()
            
if __name__ == "__main__":
    assignNewMaterialByLayer()

thank you very much, I will give it a try…

hello, testing i get this error…

is it a simple fix?

many thanks

Well, the way of finding the layer index is the same between the two scripts. Does the first script still work for you? Did you make any changes?

I can test what is going on there later

Also are you on rhino 5 or 6? Sometimes that changes things

ok just tested in rhino 6 and that does NOT give error.
(I was getting error in rhino 5)
So I will only use rhino 6.

But still getting issues with textures. they are removed when I run script.

thanks for your time and help

That’s unexpected.
Can you upload a small example file which gets its textures removed? I could make sure that it does not happen with an example

test.3dm (4.9 MB)

thank you

Ok i misunderstood, i thought you would have materials assigned to layers but in your file they are all asigned to objects. I updated the script to also check if any object on a layer already has a material assigned and then use that.

Also when you save a file with textures for somebody else there is an option “save textures” which has to be checked or the textures will be lost in translation

image

The new version should work, at least it worked with the textures i assigned… let me know how it works :slight_smile:

import Rhino
import scriptcontext as sc

def assignNewMaterialByLayer():
    
    # disable redraw, because depending on the number of objects this could get heavy
    sc.doc.Views.RedrawEnabled = False
    
    # create a dict to keep track of material <-> layer relation
    layerMaterialDict = {}
    
    # iterate over all layers in the doc
    for layer in sc.doc.Layers:
        
        # if there is already a layermaterial, we get to be lazy :)
        index = layer.RenderMaterialIndex
        if index == -1:
            
            # lets see if we can find an object with a material on this layer
            objMaterialIndex = None
            objRefArr = sc.doc.Objects.FindByLayer(layer.Name)
            for obj in objRefArr:
                if obj.Attributes.MaterialIndex != -1:
                    objMaterialIndex = obj.Attributes.MaterialIndex
                    break
            if objMaterialIndex:
                index = objMaterialIndex
        
            else:
                # create a new material
                newMaterial = Rhino.DocObjects.Material()
                # name it like the layer fullpath, because fullpath is ALWAYS unique
                newMaterial.Name = layer.FullPath
                # add it to the doc, so we can assign it to objects
                index = sc.doc.Materials.Add(newMaterial)
            
        # finally store in the dict together with its layer
        layerMaterialDict[layer.Index] = index
        
    # iterate over all objects
    for object in sc.doc.Objects:
        
        # assign a material by layerindex, easy to do with our dict
        object.Attributes.MaterialIndex = layerMaterialDict[object.Attributes.LayerIndex]
        # tell the object to use its assigned material as its materialsource
        object.Attributes.MaterialSource = Rhino.DocObjects.ObjectMaterialSource.MaterialFromObject
        # commit the changes we made
        object.CommitChanges()
        
    # re-enable redraw
    sc.doc.Views.RedrawEnabled = True
    # and redraw all views so we can see what we did
    sc.doc.Views.Redraw()
            
if __name__ == "__main__":
    assignNewMaterialByLayer()

hello and thank you.
I just tested and it still removed the texture. so i have created a simple test file.

can you test for me to make sure I am not going mad!

test2.3dm (1.3 MB)