forked from jesterKing/rhipy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
create_doublesided_rendermaterial.html
192 lines (178 loc) · 10.3 KB
/
create_doublesided_rendermaterial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<html>
<head>
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<h1>Creating a double-sided material</h1>
<p><em>written by Nathan 'jesterKing' Letwory</em></p>
<h2>Introduction</h2>
<p>To create a double-sided material type we are going to use <code>RenderMaterial</code> from
the <code>Rhino.Render</code> namespace.</p>
<p>A GUID is needed to create a material of a specific type. One can find many GUIDs
in the <a href="https://developer.rhino3d.com/api/RhinoCommon/html/Properties_T_Rhino_Render_RenderMaterial.htm"><code>RenderMaterial</code>
properties</a>,
but unfortunately the double-sided material GUID is not exposed through the API
yet.</p>
<p>To figure out what the GUID of the double-sided material type is I create one
manually in Rhino, then save the material as a file. The resulting <code>.rmtl</code> file
can be opened in a text editor. In the <code><material></code> tag you'll find the
<code>type-id</code> attribute that holds the GUID
we need.</p>
<p>Following these steps give us the information we need: a double-sided material
type has the GUID <code>E6CD1973-B739-496E-AB69-32957FA48492</code>.</p>
<p>A new instance we create with one of the static method <a href="https://developer.rhino3d.com/api/RhinoCommon/html/Overload_Rhino_Render_RenderContent_Create.htm"><code>RenderContent.Create()</code>
overloads</a>.
Using one of these overloads will result in the material being created <em>and</em>
added to the persistent content list. In other words it will show up in the
material editor.</p>
<p>A double-sided material references two other materials. This means we need to
create these materials as well. Such materials will be set as child content to
the double-sided material.</p>
<p>To create render materials as the child content for the double-sided material in
the front and back slots we'll be using <a href="https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Render_RenderContent_Create.htm"><code>RenderContent.Create(Guid type, RenderContent parent, string childSlotName, RenderContent.ShowContentChooserFlags flags, RhinoDoc doc)</code></a>.</p>
<h2>Create and setup the material</h2>
<p>We'll create and assign the material only when there is an object selection.</p>
<div class="codefragment">
<div class="fragmentname"><<if objects are selected create material and assign>>=</div>
<div class="code">
<pre><code><span class="hljs-comment"># only if we have a selection do the work</span>
<span class="hljs-keyword">if</span> object_selection:
<<create a double-sided material>>
<<assign material to <span class="hljs-built_in">object</span> selection>>
</code></pre>
</div>
</div><p>To create the double-sided parent material we use <code>RenderContent.Create</code>. We
don't need much else besides the render content GUID and the document.</p>
<p>Once we have a material we need to bracket any changes we want to make between
<code>BeginChange()</code> and <code>EndChange()</code> calls. Otherwise Rhino will ignore any
programmatical changes to the render content because it is already in the
document after creation.</p>
<p>We set a name suffixed with a new GUID each time this material is created by the
script. This so we don't end up with materials that have the same name. While it
is possible to do so with a script names should really be unique.</p>
<p>Note that the <code>front_material</code> and <code>back_material</code> instances are created within
the <code>BeginChange()</code> and <code>EndChange()</code> bracket of <code>render_material</code>. The creation
of these materials is done using the
<a href="https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Render_RenderContent_Create.htm"><code>RenderContent.Create()</code></a>
function that creates the material as child to a parent content using the given
child slot name.</p>
<div class="codefragment">
<div class="fragmentname"><<create a double-sided material>>=</div>
<div class="code">
<pre><code><span class="hljs-comment"># create material</span>
render_material = Rhino.Render.RenderContent.Create(
System.Guid(<span class="hljs-string">"E6CD1973-B739-496E-AB69-32957FA48492"</span>),
Rhino.Render.RenderContent.ShowContentChooserFlags.<span class="hljs-literal">None</span>,
scriptcontext.doc)
render_material.BeginChange(Rhino.Render.RenderContent.ChangeContexts.Program)
render_material.Name = <span class="hljs-string">"Double-Sided Material "</span> + System.Guid.NewGuid().ToString()
front_material = Rhino.Render.RenderContent.Create(
Rhino.Render.RenderMaterial.MetalMaterialGuid,
render_material,
<span class="hljs-string">"front"</span>,
Rhino.Render.RenderContent.ShowContentChooserFlags.<span class="hljs-literal">None</span>,
scriptcontext.doc)
back_material = Rhino.Render.RenderContent.Create(
Rhino.Render.RenderMaterial.PlasterMaterialGuid,
render_material,
<span class="hljs-string">"back"</span>,
Rhino.Render.RenderContent.ShowContentChooserFlags.<span class="hljs-literal">None</span>,
scriptcontext.doc)
random_color1 = Rhino.Display.Color4f(
random.random(),
random.random(),
random.random(),
<span class="hljs-number">1.0</span>)
random_color2 = Rhino.Display.Color4f(
random.random(),
random.random(),
random.random(),
<span class="hljs-number">1.0</span>)
<span class="hljs-comment"># set color for front material</span>
front_material.BeginChange(Rhino.Render.RenderContent.ChangeContexts.Program)
front_material.SetParameter(<span class="hljs-string">"color"</span>, random_color1)
<span class="hljs-comment"># set color for back material</span>
back_material.BeginChange(Rhino.Render.RenderContent.ChangeContexts.Program)
back_material.SetParameter(<span class="hljs-string">"color"</span>, random_color2)
render_material.EndChange()
</code></pre>
</div>
</div><h2>Assign material to objects</h2>
<p>Now that we have a material we can assign it to each object in our selection.
For Rhino to notice changes to objects in the document we need to do something
similar like the method above for changing render content that is already in the
document. We have to <code>CommitChanges()</code> on the object for the changes to stick.</p>
<div class="codefragment">
<div class="fragmentname"><<assign material to object selection>>=</div>
<div class="code">
<pre><code><span class="hljs-comment"># assign</span>
<span class="hljs-keyword">for</span> ob <span class="hljs-keyword">in</span> object_selection:
<span class="hljs-built_in">print</span>(<span class="hljs-string">"Adding material"</span>, render_material.Name, <span class="hljs-string">"to"</span>, ob)
ob.RenderMaterial = render_material
ob.CommitChanges()
</code></pre>
</div>
</div><h2>The imports</h2>
<p>We need access to the <code>Rhino.Render</code> namespace for the material and render
content related classes. Further we want to access the document. For that we are
going to use <code>scriptcontext</code>. We'll import just the namespaces or modules
meaning we'll have to include these when using classes and functionality they
provide. We're doing that for this script to make it clear where the used bits
and pieces come from.</p>
<div class="codefragment">
<div class="fragmentname"><<import libraries>>=</div>
<div class="code">
<pre><code><span class="hljs-keyword">import</span> System
<span class="hljs-keyword">import</span> Rhino.Display
<span class="hljs-keyword">import</span> Rhino.Render
</code></pre>
</div>
</div><p>To create a random color we'll need the <code>random</code> module along with access to
<code>Color4f</code> from the <code>Rhino.Display</code> namespace.</p>
<div class="codefragment">
<div class="fragmentname"><<import libraries>>=+</div>
<div class="code">
<pre><code><span class="hljs-keyword">import</span> scriptcontext
<span class="hljs-keyword">import</span> random
</code></pre>
</div>
</div><h2>Main structure of the script</h2>
<p>The script will be implemented as a script, not a command. It will import the
necessary modules and namespaces. It'll take note of the currently selected
objects. Then it will create a double-sided material as we already explained.
Finally the new material will be assigned to each of the selected objects.</p>
<div class="codefragment">
<div class="fragmentname"><<Double-Sided Material Creation Script.*>>=</div>
<div class="code">
<pre><code><<<span class="hljs-keyword">import</span> libraries>>
<<determine selected objects>>
<<<span class="hljs-keyword">if</span> objects are selected create material <span class="hljs-keyword">and</span> assign>>
</code></pre>
</div>
</div><h2>Retrieve object selection</h2>
<p>We're not going to ask the user to select anything. Instead the script relies on
the selection already being made. We still create a material, but without a
selection no assignment will be made.</p>
<div class="codefragment">
<div class="fragmentname"><<determine selected objects>>=</div>
<div class="code">
<pre><code><span class="hljs-comment"># get object selection</span>
object_selection = [ob <span class="hljs-keyword">for</span> ob <span class="hljs-keyword">in</span> scriptcontext.doc.Objects <span class="hljs-keyword">if</span> ob.IsSelected(<span class="hljs-literal">False</span>)]
</code></pre>
</div>
</div><h2>Conclusion</h2>
<p>It is relatively straightforward to programmatically create new render content
in Rhino. There are a few mechanisms the programmer needs to be mindful of:
bracketing of changes to render content, and explicitely committing changes made
to document objects.</p>
<p>Note that this particular implementation does not really check whether it is
useful to even have a <code>RenderMaterial</code> assigned. It would be good to add that
extra security.</p>
<p>Furthermore the script could be improved with a piece of code that actually
queries the user for object selection if none has been made.</p>
<p>These improvements are left to the reader to implement, as the main goal here
has been achieved: show how to create and assign a new material of a specific
type.</p>
<p>The generated script is in the repository <a href="https://github.com/jesterKing/rhipy/blob/master/create_doublesided_rendermaterial.py">here</a></p>
</body>
</html>