Assign Existing Custom Material to Layer(s) within a script

I’m trying to assign existing (non-default) materials (by name or index) after the AddLayer portion of my script.

Seems like it should be very simple, but I’ve tried my darndest to figure this out, including all the script-bits that seem like they should do it, including AddMaterialToLayer; and LayerMaterialIndex (which states: Returns or changes the material index of a layer, but I can’t figure out how to enact the “change” ability.) I also looked into LayerId, etc.

Searched this forum, Developer site, sample scripts, and GitHub, which had a breakdown of the Material scripts, yet no mention of how to actually assign an existing material.

Here’s my script so far, which creates a set of subLayers beneath a Layer, and assigns properties to them. (Ultimately I’m planning on having options for selected/different/additional subLayers.)

# Creates a specified template layer tree for a new element
# v04 by Alan Farkas 230327

import rhinoscriptsyntax as rs
from System.Drawing import Color
#from System.Drawing import Color

new_element = rs.GetString("New Element?")
new_element = new_element[0].upper()+new_element[1:]

rs.AddLayer(new_element)
rs.AddLayer("crv"+new_element, Color.Red, parent=new_element)
rs.AddLayer("bbox"+new_element, Color.FromArgb(20, 0, 0, 0), parent=new_element)
rs.AddLayer("light"+new_element, Color.Aqua, parent=new_element)
rs.AddLayer("glass"+new_element, Color.FromArgb(100, 105, 105, 105), parent=new_element)
rs.AddLayer("sol"+new_element, Color.Green, parent=new_element)
rs.AddLayer("ref"+new_element, Color.Black, parent=new_element)
rs.AddLayer("SM_"+new_element, Color.White, parent=new_element)

layers = rs.LayerNames(new_element)
if layers:
    for layer in layers:
        black = rs.CreateColor((0,0,0))
        if rs.LayerPrintColor(layer)!=black:
            rs.LayerPrintColor(layer, black)

#Set LayerPrintWidths
#This works in v3A

bbox_layer_name = rs.AddLayer("bbox"+new_element, Color.FromArgb(20, 0, 0, 0), parent=new_element)
rs.LayerPrintWidth(bbox_layer_name, -1)

crv_layer_name = rs.AddLayer("crv"+new_element, Color.Red, parent=new_element)
rs.LayerPrintWidth(crv_layer_name, -1)

#Assign Existing Custom Materials to the appropriate above subLayers


Hi @Alan_Farkas,

below example script assigns an existing RenderMaterial named “Wood” to a layer named “Layer 02” which is a sub-layer of a layer named “Layer 01”. This is the function which i’ve been using in Rhino 7:

import Rhino
import scriptcontext

def AssignRenderMaterialToLayer(layer_path, render_material_name):
    
    # find the layer by it's full path
    index = scriptcontext.doc.Layers.FindByFullPath(layer_path, -1)
    if index < 0: 
        print "Layer '{}' not found".format(layer_path)
        return False
    
    # find any rendermaterials by render_material_name
    data = scriptcontext.doc.RenderMaterials.GetEnumerator()
    mats = filter(lambda rm: rm.Name == render_material_name, data)
    if not mats:
        print "Material '{}' not found".format(render_material_name) 
        return False
    
    # assign first found rendermaterial to layer
    layer = scriptcontext.doc.Layers[index]
    layer.RenderMaterial = mats[0]
    return True

To call the function from your script, you can use:

rc = AssignRenderMaterialToLayer("Layer 01::Layer 02", "Wood")

The function returns True if the assignment worked, otherwise it returns False and prints the error to the commandline. Note that when you have multiple render materials with the name “Wood”, it will assign the first.

_
c.

Thanks so much for this. I’m new to scripting, so maybe a silly question:

This seems to be in RhinoScript — can I use it in my current script, which uses RhinoPython?

Hi @Alan_Farkas,

it is Python which uses RhinoCommon methods. All RhinoScriptSyntax (rs) methods do this so you can just paste the function into your code and use it.

_
c.

Great, thanks so much.

Side note: You would think we could easily create a list of Material Names with their assigned Index numbers, and then a chart that compiles the Layer Names with their assigned Material Indices. Then a simple script for AssignMaterialToLayer(MaterialIndex, LayerName) could be used, no? Or has this already been mentioned ad nauseam?

If @pascal and co are reading this, would be interested in your take on this.

Hi @Alan_Farkas,

this should all be possible with a single script. To build this you can of course split this into multiple tasks at the beginning and then make multiple functions which are accessed in your main script.

When creating materials i would rely on RenderMaterial and access them either via their name (as in my example above) or their id instead of using on material indices.

_
c.

Thanks for all the help.

And for teaching me how to spell “Indices.” :nerd_face:

1 Like