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 Maya handle this attribute
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  def postConstructor(self):
192  self.setMPSafe(True)
193 
194 ##
195 ## Override declaration
196 #######################################################
197 class brickTextureNodeOverride(omr.MPxShadingNodeOverride):
198  @staticmethod
199  def creator(obj):
200  return brickTextureNodeOverride(obj)
201 
202  def __init__(self, obj):
203  omr.MPxShadingNodeOverride.__init__(self, obj)
204 
205  # Register fragments with the manager if needed
206  fragmentMgr = omr.MRenderer.getFragmentManager()
207  if fragmentMgr != None:
208  if not fragmentMgr.hasFragment("brickTextureNodePluginFragment"):
209  fragmentBody = "<fragment uiName=\"brickTextureNodePluginFragment\" name=\"brickTextureNodePluginFragment\" type=\"plumbing\" class=\"ShadeFragment\" version=\"1.0\">"
210  fragmentBody += " <description><![CDATA[Brick procedural texture fragment]]></description>"
211  fragmentBody += " <properties>"
212  fragmentBody += " <float3 name=\"brickColor\" />"
213  fragmentBody += " <float3 name=\"jointColor\" />"
214  fragmentBody += " <float name=\"blurFactor\" />"
215  fragmentBody += " <float2 name=\"uvCoord\" semantic=\"mayaUvCoordSemantic\" flags=\"varyingInputParam\" />"
216  fragmentBody += " <float2 name=\"uvFilterSize\" />"
217  fragmentBody += " </properties>"
218  fragmentBody += " <values>"
219  fragmentBody += " <float3 name=\"brickColor\" value=\"0.75,0.3,0.1\" />"
220  fragmentBody += " <float3 name=\"jointColor\" value=\"0.75,0.75,0.75\" />"
221  fragmentBody += " </values>"
222  fragmentBody += " <outputs>"
223  fragmentBody += " <float3 name=\"outColor\" />"
224  fragmentBody += " </outputs>"
225  fragmentBody += " <implementation>"
226  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"Cg\" lang_version=\"2.1\">"
227  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
228  fragmentBody += " <source><![CDATA["
229  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
230  fragmentBody += "{ \n"
231  fragmentBody += " if (t < a) return 0.0f; \n"
232  fragmentBody += " if (t > b) return 1.0f; \n"
233  fragmentBody += " return (t - a)/(b - a); \n"
234  fragmentBody += "} \n"
235  fragmentBody += "float3 brickTextureNodePluginFragment(float3 color1, float3 color2, float blur, float2 uv, float2 fs) \n"
236  fragmentBody += "{ \n"
237  fragmentBody += " uv -= floor(uv); \n"
238  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
239  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
240  fragmentBody += " float du = blur*fs.x/2.0f; \n"
241  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
242  fragmentBody += " float t = max( \n"
243  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
244  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
245  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
246  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
247  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
248  fragmentBody += "} \n]]>"
249  fragmentBody += " </source>"
250  fragmentBody += " </implementation>"
251  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"HLSL\" lang_version=\"11.0\">"
252  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
253  fragmentBody += " <source><![CDATA["
254  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
255  fragmentBody += "{ \n"
256  fragmentBody += " if (t < a) return 0.0f; \n"
257  fragmentBody += " if (t > b) return 1.0f; \n"
258  fragmentBody += " return (t - a)/(b - a); \n"
259  fragmentBody += "} \n"
260  fragmentBody += "float3 brickTextureNodePluginFragment(float3 color1, float3 color2, float blur, float2 uv, float2 fs) \n"
261  fragmentBody += "{ \n"
262  fragmentBody += " uv -= floor(uv); \n"
263  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
264  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
265  fragmentBody += " float du = blur*fs.x/2.0f; \n"
266  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
267  fragmentBody += " float t = max( \n"
268  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
269  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
270  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
271  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
272  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
273  fragmentBody += "} \n]]>"
274  fragmentBody += " </source>"
275  fragmentBody += " </implementation>"
276  fragmentBody += " <implementation render=\"OGSRenderer\" language=\"GLSL\" lang_version=\"3.0\">"
277  fragmentBody += " <function_name val=\"brickTextureNodePluginFragment\" />"
278  fragmentBody += " <source><![CDATA["
279  fragmentBody += "float btnplinearstep(float t, float a, float b) \n"
280  fragmentBody += "{ \n"
281  fragmentBody += " if (t < a) return 0.0f; \n"
282  fragmentBody += " if (t > b) return 1.0f; \n"
283  fragmentBody += " return (t - a)/(b - a); \n"
284  fragmentBody += "} \n"
285  fragmentBody += "vec3 brickTextureNodePluginFragment(vec3 color1, vec3 color2, float blur, vec2 uv, vec2 fs) \n"
286  fragmentBody += "{ \n"
287  fragmentBody += " uv -= floor(uv); \n"
288  fragmentBody += " float v1 = 0.05f; float v2 = 0.45f; float v3 = 0.55f; float v4 = 0.95f; \n"
289  fragmentBody += " float u1 = 0.05f; float u2 = 0.45f; float u3 = 0.55f; float u4 = 0.95f; \n"
290  fragmentBody += " float du = blur*fs.x/2.0f; \n"
291  fragmentBody += " float dv = blur*fs.y/2.0f; \n"
292  fragmentBody += " float t = max( \n"
293  fragmentBody += " min(btnplinearstep(uv.y, v1 - dv, v1 + dv) - btnplinearstep(uv.y, v2 - dv, v2 + dv), \n"
294  fragmentBody += " max(btnplinearstep(uv.x, u3 - du, u3 + du), 1.0f - btnplinearstep(uv.x, u2 - du, u2 + du))), \n"
295  fragmentBody += " min(btnplinearstep(uv.y, v3 - dv, v3 + dv) - btnplinearstep(uv.y, v4 - dv, v4 + dv), \n"
296  fragmentBody += " btnplinearstep(uv.x, u1 - du, u1 + du) - btnplinearstep(uv.x, u4 - du, u4 + du))); \n"
297  fragmentBody += " return t*color1 + (1.0f - t)*color2; \n"
298  fragmentBody += "} \n]]>"
299  fragmentBody += " </source>"
300  fragmentBody += " </implementation>"
301  fragmentBody += " </implementation>"
302  fragmentBody += "</fragment>"
303 
304  fragmentMgr.addShadeFragmentFromBuffer(fragmentBody, False)
305 
306  def supportedDrawAPIs(self):
307  return omr.MRenderer.kOpenGL | omr.MRenderer.kOpenGLCoreProfile | omr.MRenderer.kDirectX11
308 
309  def fragmentName(self):
310  return "brickTextureNodePluginFragment"
311 
312 
313 ##
314 ## Plugin setup
315 #######################################################
316 sRegistrantId = "brickTexturePlugin"
317 
318 def initializePlugin(obj):
319  plugin = om.MFnPlugin(obj, "Autodesk", "4.5", "Any")
320  try:
321  userClassify = "texture/2d:drawdb/shader/texture/2d/brickTexture"
322  plugin.registerNode("brickTexture", brickTextureNode.id, brickTextureNode.creator, brickTextureNode.initialize, om.MPxNode.kDependNode, userClassify)
323  except:
324  sys.stderr.write("Failed to register node\n")
325  raise
326 
327  try:
328  global sRegistrantId
329  omr.MDrawRegistry.registerShadingNodeOverrideCreator("drawdb/shader/texture/2d/brickTexture", sRegistrantId, brickTextureNodeOverride.creator)
330  except:
331  sys.stderr.write("Failed to register override\n")
332  raise
333 
334 def uninitializePlugin(obj):
335  plugin = om.MFnPlugin(obj)
336  try:
337  plugin.deregisterNode(brickTextureNode.id)
338  except:
339  sys.stderr.write("Failed to deregister node\n")
340  raise
341 
342  try:
343  global sRegistrantId
344  omr.MDrawRegistry.deregisterShadingNodeOverrideCreator("drawdb/shader/texture/2d/brickTexture", sRegistrantId)
345  except:
346  sys.stderr.write("Failed to deregister override\n")
347  raise
348