During cooperation of large projects, I always encounter the situation like below:
Got a final large project model with numerous render materials from different designers, many blocks in it, and the goal is to make some rendering for presentation or simply add my part of model in it. There will be ten different glass materials, five grey colored materials, etc.
I always want to purge and rearrange the materials for simplication and better control.
There is an extension in Sketchup called material replacer that meet demand( The Fastest Way to REPLACE MATERIALS in SketchUp!). While in rhino, the way to do this is to:
- right click the material in material panel and hit âselect objectsâ.
- right click another material for replacement and hit âassign to objectsâ.
The above action cannot affect objects in blocks or blocks of block which is very common in large projects. I also found topics like this, and they seems not solved yet:
https://discourse.mcneel.com/t/real-global-material-replacement-please-please-fix/11414/9
https://discourse.mcneel.com/t/how-to-replace-material-in-rhino-similar-as-the-use-as-replacement-function-in-vray/79921/11
So I wrote a script for this purpose, and the usage is like that of sketchup.
#!encoding:utf-8
# A simple script for global material replacement
# It can replace all materials(including objects in blocks) of a model with another.
# Since I havn't found out how to get the RenderMaterial of a subobject in (nested)blocks,
# you can only pick objects outside blocks for the script to know the two material for replacement.
import scriptcontext as sc
import Rhino
import rhinoscriptsyntax as rs
def SelectObjs(prompt, preselect=True):
go = Rhino.Input.Custom.GetObject()
go.SetCommandPrompt(prompt)
go.GeometryFilter = Rhino.DocObjects.ObjectType.Brep|Rhino.DocObjects.ObjectType.Mesh
go.SubObjectSelect = False
if not preselect:
go.EnablePreSelect(False, False);
go.DeselectAllBeforePostSelect = True;
go.Get()
if (go.CommandResult() == Rhino.Commands.Result.Success):
return go.Object(0)
return None
def ChangeMat(Objects,RenderMat1,RenderMat2):
for object in Objects:
if type(object)!=Rhino.DocObjects.InstanceObject:
if object.RenderMaterial!=None:
if object.RenderMaterial.Name==RenderMat1.Name:
object.RenderMaterial=RenderMat2
object.CommitChanges()
else:
idef=object.InstanceDefinition
idefIndex=idef.Index
RefObjects=idef.GetObjects()
for obj in RefObjects:
if type(obj)==Rhino.DocObjects.InstanceObject:
ChangeMat([obj],RenderMat1,RenderMat2)
else:
if obj.RenderMaterial!=None:
if obj.RenderMaterial.Name == RenderMat1.Name:
obj.RenderMaterial=RenderMat2
obj.CommitChanges()
newGeometry = []
newAttributes = []
for object in RefObjects:
newGeometry.append(rs.coercegeometry(object))
ref = Rhino.DocObjects.ObjRef(object)
attr = ref.Object().Attributes
newAttributes.append(attr)
InstanceDefinitionTable = sc.doc.ActiveDoc.InstanceDefinitions
p=InstanceDefinitionTable.ModifyGeometry(idefIndex, newGeometry, newAttributes)
def ReplaceMaterials():
RenderMat1=None
while RenderMat1==None:
object1 = SelectObjs('Select Material to replace',False).Object()
RenderMat1 = object1.RenderMaterial
RenderMat2=None
while RenderMat2==None or RenderMat2.Name==RenderMat1.Name:
object2 = SelectObjs('Select Material to replace with',False).Object()
RenderMat2 = object2.RenderMaterial
ObjectAll=sc.doc.Objects
ChangeMat(ObjectAll,RenderMat1,RenderMat2)
ReplaceMaterials()
I created an aliase for this script. Almost done.
But I havnât found out how to get the RenderMaterial of a subobject in (nested)blocks. If it can get materials of objects in blocks, one will not have to make new material assigned objects outside blocks for the script to refer to.
So does anyone know how to refer to the subobject materials in blocks? Iâve tried set the âgo.SubObjectSelect = Trueâ in my script and
a=SelectObjs('***',True)
CompIndex=a.Object().GetSelectedSubObjects()
to get the Index of the subobject, but still canât reach the rendermaterial of the selected subobject. And the nested blocks will make it more complicated.