Scripting API
Materials
from ArtisanPlugin.Scripting import RealtimeRenderApi as rtr
The viewer carries its own material catalogue - metals, gems, pearls, enamels, ceramics - and applies them to parts, where a part is one mesh of the exported model. Because the exporter joins each Rhino layer into one part named after the layer, the natural way to restyle a piece is by layer name, and SetMaterialByLayer is the call to reach for first.
Viewer-only, all of it: the Rhino document is never modified and no material table entry changes. Assignments made here are persisted the same way a click in the viewer is, so they survive UpdateRender’s model reload and reopening the window. To change the document use Design/Render materials or the Studio scene.
The catalogue names
materialName is a viewer catalogue id, upper-cased for you. The families:
- Metals:
YELLOW_GOLD,WHITE_GOLD,ROSE_GOLD(each with_LIGHT/_DARKvariants),GREEN_GOLD,GOLD_2N…GOLD_6N,SILVER,PLATINUM,TITANIUM- and every one takes a finish suffix:_BRUSHED,_FLORENTINE,_HAMMERED,_ROUGH,_SANDBLAST. - Gems:
DIAMOND(plus_BLACK,_CHAMPAGNE,_COGNAC_01, fancy colors…),RUBY,SAPPHIRE(many colors),EMERALD,MORGANITE,AQUAMARINE,TOPAZ_*,TOURMALINE_*,TANZANITE,AMETHYST,CITRINE,PERIDOT, garnets,ALEXANDRITEand more. - Others:
PEARL(12 variants),ENAMEL_*_TRANSLUCENT/ENAMEL_*_OPAQUE,CERAMIC_*,LEATHER_*.
An unknown name is not an error - the page falls back silently, so a misspelled material just looks grey. Spell from this list.
Restyle a whole layer
count = rtr.SetMaterialByLayer("Metal 01", "ROSE_GOLD") # -> parts changed
| Parameter | Default | Meaning |
|---|---|---|
layerName | required | The Rhino layer, matched against the viewer’s parts |
materialName | required | A catalogue name from the list above |
The layer matches case-insensitively and treats spaces and underscores as equal, so "METAL_01", "metal 01" and "Metal 01" all hit the same layer. Returns how many parts changed.
This is the one material call that waits and validates: when nothing matches it throws “No viewer part matches layer ‘<name>’. Available: …” listing every layer the viewer actually has - so a script (or an AI agent) can read the error and correct itself. It gives the page 5 seconds and then throws “The viewer did not respond in time.”
List the viewer’s parts
import json
raw = rtr.GetViewerItems() # -> str, never None
items = json.loads(json.loads(raw)) # -> [{"uuid": ..., "layer": ..., "material": ..., ...}]
Returns the viewer’s parts as JSON: an array of objects carrying at least a uuid, a layer and a material. You only need it when addressing single parts - SetMaterialByLayer looks the layer up for you.
Two things about the string. It is JSON produced by JSON.stringify inside the page and then JSON-encoded again on the way out of WebView2, so in Python it takes two json.loads passes to reach the array. And it never returns None: when the page has not finished loading you get the literal string "[]". Waits up to 5 seconds, then throws “The viewer did not respond in time.”
Set a single part’s material
rtr.SetViewerMaterial(part_uuid, "YELLOW_GOLD") # -> None
| Parameter | Default | Meaning |
|---|---|---|
partUuid | required | A uuid from GetViewerItems() |
materialName | required | A catalogue name |
The by-uuid variant, for when two parts share a layer look you want to break up. Fire and forget: an unknown uuid or material name simply does nothing, with no error. An empty partUuid throws “Part uuid is required (see GetViewerItems()).”
Fine-tune a material live
rtr.EditViewerMaterial("YELLOW_GOLD", color="#f5c518", roughness=0.35) # -> None
rtr.EditViewerMaterial("DIAMOND", ior=2.42, opacity=0.95)
| Parameter | Default | Meaning |
|---|---|---|
materialName | required | The catalogue material to edit |
color | None | CSS color ("#b76e79", "white") |
roughness | -1 (untouched) | 0–1, metals |
ior | -1 (untouched) | index of refraction, gems, ~1.4–2.4 |
opacity | -1 (untouched) | 0–1 |
Edits the material everywhere it is currently applied - it mutates the shared catalogue entry, not one part. Pass only what you want changed; passing nothing throws “Pass at least one property to edit (color, roughness, ior, opacity).”
The edit is ephemeral: re-applying the material from the catalogue (including via SetMaterialByLayer) resets it to stock. Order matters - assign materials first, fine-tune second.