scripted/pyBrickShader.py

scripted/pyBrickShader.py
1 #-
2 # ===========================================================================
3 # Copyright 2015 Autodesk, Inc. All rights reserved.
4 #
5 # Use of this software is subject to the terms of the Autodesk license
6 # agreement provided at the time of installation or download, or which
7 # otherwise accompanies this software in either electronic or hard copy form.
8 # ===========================================================================
9 #+
10 
11 import sys
12 import math
13 import maya.api.OpenMaya as om
14 import maya.api.OpenMayaRender as omr
15 
16 # Produces dependency graph node brickTexture
17 # This node is an example of a brick 2d texture.
18 # The output attribute of the BrickTexture node is called "outColor". To use this shader, create a BrickTexture
19 # and connect its output to an input of a surface/shader node such as Color.
20 
21 
22 def maya_useNewAPI():
23  """
24  The presence of this function tells Maya that the plugin produces, and
25  expects to be passed, objects created using the Maya Python API 2.0.
26  """
27  pass
28 
29 def step(t, c):
30  if t < c:
31  return 0.0
32  return 1.0
33 
34 def smoothstep(t, a, b):
35  if t < a:
36  return 0.0
37  if t > b:
38  return 1.0
39  t = (t - a)/(b - a)
40  return t*t*(3.0 - 2.0*t)
41 
42 def linearstep(t, a, b):
43  if t < a:
44  return 0.0
45  if t > b:
46  return 1.0
47  return (t - a)/(b - a)
48 
49 ##
50 ## Node declaration
51 #######################################################
52 class brickTextureNode(om.MPxNode):
53  # Id tag for use with binary file format
54  id = om.MTypeId( 0x8100d )
55 
56  # Input attributes
57  aColor1 = None
58  aColor2 = None
59  aBlurFactor = None
60  aUVCoord = None
61  aFilterSize = None
62 
63  # Output attributes
64  aOutColor = None
65 
66  @staticmethod
67  def creator():
68  return brickTextureNode()
69 
70  @staticmethod
71  def initialize():
72  nAttr = om.MFnNumericAttribute()
73 
74  # Input attributes
75 
76  brickTextureNode.aColor1 = nAttr.createColor("brickColor", "bc")
77  nAttr.keyable = True
78  nAttr.storable = True
79  nAttr.readable = True
80  nAttr.writable = True
81  nAttr.default = (.75, .3, .1) # Brown
82 
83  brickTextureNode.aColor2 = nAttr.createColor("jointColor", "jc")
84  nAttr.keyable = True
85  nAttr.storable = True
86  nAttr.readable = True
87  nAttr.writable = True
88  nAttr.default = (.75, .75, .75) # Grey
89 
90  brickTextureNode.aBlurFactor = nAttr.create( "blurFactor", "bf", om.MFnNumericData.kFloat)
91  nAttr.keyable = True
92  nAttr.storable = True
93  nAttr.readable = True
94  nAttr.writable = True
95 
96  # Implicit shading network attributes
97 
98  child1 = nAttr.create( "uCoord", "u", om.MFnNumericData.kFloat)
99  child2 = nAttr.create( "vCoord", "v", om.MFnNumericData.kFloat)
100  brickTextureNode.aUVCoord = nAttr.create( "uvCoord", "uv", child1, child2)
101  nAttr.keyable = True
102  nAttr.storable = True
103  nAttr.readable = True
104  nAttr.writable = True
105  nAttr.hidden = True
106 
107  child1 = nAttr.create( "uvFilterSizeX", "fsx", om.MFnNumericData.kFloat)
108  child2 = nAttr.create( "uvFilterSizeY", "fsy", om.MFnNumericData.kFloat)
109  brickTextureNode.aFilterSize = nAttr.create("uvFilterSize", "fs", child1, child2)
110  nAttr.keyable = True
111  nAttr.storable = True
112  nAttr.readable = True
113  nAttr.writable = True
114  nAttr.hidden = True
115 
116  # Output attributes
117  brickTextureNode.aOutColor = nAttr.createColor("outColor", "oc")
118  nAttr.keyable = False
119  nAttr.storable = False
120  nAttr.readable = True
121  nAttr.writable = False
122 
123  # Add attributes to the node database.
124  om.MPxNode.addAttribute(brickTextureNode.aColor1)
125  om.MPxNode.addAttribute(brickTextureNode.aColor2)
126  om.MPxNode.addAttribute(brickTextureNode.aBlurFactor)
127  om.MPxNode.addAttribute(brickTextureNode.aFilterSize)
128  om.MPxNode.addAttribute(brickTextureNode.aUVCoord)
129 
130  om.MPxNode.addAttribute(brickTextureNode.aOutColor)
131 
132  # All input affect the output color
133  om.MPxNode.attributeAffects(brickTextureNode.aColor1, brickTextureNode.aOutColor)
134  om.MPxNode.attributeAffects(brickTextureNode.aColor2, brickTextureNode.aOutColor)
135  om.MPxNode.attributeAffects(brickTextureNode.aBlurFactor, brickTextureNode.aOutColor)
136  om.MPxNode.attributeAffects(brickTextureNode.aFilterSize, brickTextureNode.aOutColor)
137  om.MPxNode.attributeAffects(brickTextureNode.aUVCoord, brickTextureNode.aOutColor)
138 
139  def __init__(self):
140  om.MPxNode.__init__(self)
141 
142  #######################################################
143  ## DESCRIPTION:
144  ## This function gets called by Maya to evaluate the texture.
145  ##
146  ## Get color1 and color2 from the input block.
147  ## Get UV coordinates from the input block.
148  ## Compute the color of our brick for a given UV coordinate.
149  ## Put the result into the output plug.
150  #######################################################
151  def compute(self, plug, block):
152  # outColor or individial R, G, B channel
153  if (plug != brickTextureNode.aOutColor) and (plug.parent() != brickTextureNode.aOutColor):
154  return None # Let the Maya parent class compute the plug
155 
156  uv = block.inputValue( brickTextureNode.aUVCoord ).asFloat2()
157  surfaceColor1 = block.inputValue( brickTextureNode.aColor1 ).asFloatVector()
158  surfaceColor2 = block.inputValue( brickTextureNode.aColor2 ).asFloatVector()
159 
160  # normalize the UV coords
161  uv[0] -= math.floor(uv[0])
162  uv[1] -= math.floor(uv[1])
163 
164  borderWidth = 0.1
165  brickHeight = 0.4
166  brickWidth = 0.9
167  blur = block.inputValue( brickTextureNode.aBlurFactor ).asFloat()
168 
169  v1 = borderWidth/2
170  v2 = v1 + brickHeight
171  v3 = v2 + borderWidth
172  v4 = v3 + brickHeight
173  u1 = borderWidth/2
174  u2 = brickWidth/2
175  u3 = u2 + borderWidth
176  u4 = u1 + brickWidth
177 
178  fs = block.inputValue( brickTextureNode.aFilterSize ).asFloat2()
179  du = blur*fs[0]/2.0
180  dv = blur*fs[1]/2.0
181 
182  t = max(min(linearstep(uv[1], v1 - dv, v1 + dv) - linearstep(uv[1], v2 - dv, v2 + dv), max(linearstep(uv[0], u3 - du, u3 + du), 1 - linearstep(uv[0], u2 - du, u2 + du))), min(linearstep(uv[1], v3 - dv, v3 + dv) - linearstep(uv[1], v4 - dv, v4 + dv), linearstep(uv[0], u1 - du, u1 + du) - linearstep(uv[0], u4 - du, u4 + du)))
183 
184  resultColor = t*surfaceColor1 + (1.0 - t)*surfaceColor2
185 
186  # set ouput color attribute
187  outColorHandle = block.outputValue( brickTextureNode.aOutColor )
188  outColorHandle.setMFloatVector( resultColor )
189  outColorHandle.setClean()
190 
191  # The plug has been successfully computed
192  return self
193 
194  def postConstructor(self):
195  self.setMPSafe(True)
196 
197 ##
198 ## Override declaration
199 #######################################################
200 class brickTextureNodeOverride(omr.MPxShadingNodeOverride):
201  @staticmethod
202  def creator(obj):
203  return brickTextureNodeOverride(obj)
204 
205  def __init__(self, obj):
206  omr.MPxShadingNodeOverride.__init__(self, obj)
207 
208  # Register fragments with the manager if needed
209  fragmentMgr = omr.MRenderer.getFragmentManager()
210  if fragmentMgr != None:
211  if not fragmentMgr.hasFragment("brickTextureNodePluginFragment"):
212  fragmentBody = "<fragment uiName=\"brickTextureNodePluginFragment\" name=\"brickTextureNodePluginFragment\" type=\"plumbing\" class=\"ShadeFragment\" version=\"1.0\">"
213  fragmentBody += " <description><![CDATA[Brick procedural texture fragment]]></description>"
214  fragmentBody += " <properties>"
215  fragmentBody += " <float3 name=\"brickColor\" />"
216  fragmentBody += " <float3 name=\"jointColor\" />"
217  fragmentBody += " <float name=\"blurFactor\" />"
218  fragmentBody += " <float2 name=\"uvCoord\" semantic=\"mayaUvCoordSemantic\" flags=\"varyingInputParam\" />"
219  fragmentBody += " <float2 name=\"uvFilterSize\" />"
220  fragmentBody += " </properties>"
221  fragmentBody += " <values>"
222  fragmentBody += " <float3 name=\"brickColor\" value=\"0.75,0.3,0.1\" />"
223  fragmentBody += " <float3 name=\"jointColor\" value=\"0.75,0.75,0.75\" />"
224  fragmentBody += " </values>"
225  fragmentBody += " <outputs>"
226  fragmentBody += " <float3 name=\"outColor\" />"
227  fragmentBody += " </outputs>"
228  fragmentBody += " <implementation>"
229  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"Cg\" lang_version=\"2.1\">"
230  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
231  fragmentBody += " <source><![CDATA["
232  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
233  fragmentBody += "{ \n"
234  fragmentBody += " if (t < a) return 0.0f; \n"
235  fragmentBody += " if (t > b) return 1.0f; \n"
236  fragmentBody += " return (t - a)/(b - a); \n"
237  fragmentBody += "} \n"
238  fragmentBody += "float3 brickTextureNodePluginFragment(float3 color1, float3 color2, float blur, float2 uv, float2 fs) \n"
239  fragmentBody += "{ \n"
240  fragmentBody += " uv -= floor(uv); \n"
241  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
242  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
243  fragmentBody += " float du = blur*fs.x/2.0f; \n"
244  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
245  fragmentBody += " float t = max( \n"
246  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
247  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
248  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
249  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
250  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
251  fragmentBody += "} \n]]>"
252  fragmentBody += " </source>"
253  fragmentBody += " </implementation>"
254  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"HLSL\" lang_version=\"11.0\">"
255  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
256  fragmentBody += " <source><![CDATA["
257  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
258  fragmentBody += "{ \n"
259  fragmentBody += " if (t < a) return 0.0f; \n"
260  fragmentBody += " if (t > b) return 1.0f; \n"
261  fragmentBody += " return (t - a)/(b - a); \n"
262  fragmentBody += "} \n"
263  fragmentBody += "float3 brickTextureNodePluginFragment(float3 color1, float3 color2, float blur, float2 uv, float2 fs) \n"
264  fragmentBody += "{ \n"
265  fragmentBody += " uv -= floor(uv); \n"
266  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
267  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
268  fragmentBody += " float du = blur*fs.x/2.0f; \n"
269  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
270  fragmentBody += " float t = max( \n"
271  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
272  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
273  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
274  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
275  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
276  fragmentBody += "} \n]]>"
277  fragmentBody += " </source>"
278  fragmentBody += " </implementation>"
279  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"GLSL\" lang_version=\"3.0\">"
280  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
281  fragmentBody += " <source><![CDATA["
282  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
283  fragmentBody += "{ \n"
284  fragmentBody += " if (t < a) return 0.0f; \n"
285  fragmentBody += " if (t > b) return 1.0f; \n"
286  fragmentBody += " return (t - a)/(b - a); \n"
287  fragmentBody += "} \n"
288  fragmentBody += "vec3 brickTextureNodePluginFragment(vec3 color1, vec3 color2, float blur, vec2 uv, vec2 fs) \n"
289  fragmentBody += "{ \n"
290  fragmentBody += " uv -= floor(uv); \n"
291  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
292  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
293  fragmentBody += " float du = blur*fs.x/2.0f; \n"
294  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
295  fragmentBody += " float t = max( \n"
296  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
297  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
298  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
299  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
300  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
301  fragmentBody += "} \n]]>"
302  fragmentBody += " </source>"
303  fragmentBody += " </implementation>"
304  fragmentBody += " </implementation>"
305  fragmentBody += "</fragment>"
306 
307  fragmentMgr.addShadeFragmentFromBuffer(fragmentBody, False)
308 
309  def supportedDrawAPIs(self):
310  return omr.MRenderer.kOpenGL | omr.MRenderer.kOpenGLCoreProfile | omr.MRenderer.kDirectX11
311 
312  def fragmentName(self):
313  return "brickTextureNodePluginFragment"
314 
315 
316 ##
317 ## Plugin setup
318 #######################################################
319 sRegistrantId = "brickTexturePlugin"
320 
321 def initializePlugin(obj):
322  plugin = om.MFnPlugin(obj, "Autodesk", "4.5", "Any")
323  try:
324  userClassify = "texture/2d:drawdb/shader/texture/2d/brickTexture"
325  plugin.registerNode("brickTexture", brickTextureNode.id, brickTextureNode.creator, brickTextureNode.initialize, om.MPxNode.kDependNode, userClassify)
326  except:
327  sys.stderr.write("Failed to register node\n")
328  raise
329 
330  try:
331  global sRegistrantId
332  omr.MDrawRegistry.registerShadingNodeOverrideCreator("drawdb/shader/texture/2d/brickTexture", sRegistrantId, brickTextureNodeOverride.creator)
333  except:
334  sys.stderr.write("Failed to register override\n")
335  raise
336 
337 def uninitializePlugin(obj):
338  plugin = om.MFnPlugin(obj)
339  try:
340  plugin.deregisterNode(brickTextureNode.id)
341  except:
342  sys.stderr.write("Failed to deregister node\n")
343  raise
344 
345  try:
346  global sRegistrantId
347  omr.MDrawRegistry.deregisterShadingNodeOverrideCreator("drawdb/shader/texture/2d/brickTexture", sRegistrantId)
348  except:
349  sys.stderr.write("Failed to deregister override\n")
350  raise
351