Python API 2.0 Reference
python/api1/py1SlopeShader.py
1 #-
2 # ==========================================================================
3 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
4 # rights reserved.
5 #
6 # The coded instructions, statements, computer programs, and/or related
7 # material (collectively the "Data") in these files contain unpublished
8 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
9 # licensors, which is protected by U.S. and Canadian federal copyright
10 # law and by international treaties.
11 #
12 # The Data is provided for use exclusively by You. You have the right
13 # to use, modify, and incorporate this Data into other products for
14 # purposes authorized by the Autodesk software license agreement,
15 # without fee.
16 #
17 # The copyright notices in the Software and this entire statement,
18 # including the above license grant, this restriction and the
19 # following disclaimer, must be included in all copies of the
20 # Software, in whole or in part, and all derivative works of
21 # the Software, unless such copies or derivative works are solely
22 # in the form of machine-executable object code generated by a
23 # source language processor.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
26 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
27 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
28 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
29 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
30 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
31 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
32 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
33 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
34 # OR PROBABILITY OF SUCH DAMAGES.
35 #
36 # ==========================================================================
37 #+
38 
39 ########################################################################
40 # DESCRIPTION:
41 #
42 # Produces the dependency graph node "spSlopeShader".
43 #
44 # This plug-in implements a Maya software shader in Python. An angle attribute
45 # is used to define the space on the object that is being shaded, which is both
46 # walkable and non-walkable. The walkable and non-walkable colors are set as
47 # attributes on the node. The shader will render both the walkable and non-walkable
48 # areas based on the color that has been set.
49 #
50 # To use:
51 #
52 # (1) Load the slopeShader.py plug-in into Maya.
53 #
54 # import maya
55 # maya.cmds.loadPlugin("slopeShader.py")
56 #
57 # (2) Create a polygon sphere.
58 # (3) Assign a shader to the sphere.
59 # (4) Open the Attribute Editor > Color Channel > Utilities > spSlopeShader to apply
60 # the texture on the sphere.
61 # (5) Render the scene to view the results.
62 #
63 # The angle attribute can be adjusted to see different results.
64 #
65 ########################################################################
66 
67 # imports
68 from builtins import range
69 import maya.OpenMaya as OpenMaya
70 import maya.OpenMayaUI as OpenMayaUI
71 import maya.OpenMayaMPx as OpenMayaMPx
72 import math, sys
73 
74 # consts
75 kSlopeShaderBehaviourName = "slopeShaderBehaviour"
76 kSlopeNodeName = "spSlopeShader"
77 kSlopeNodeClassify = "utility/color"
78 kSlopeNodeId = OpenMaya.MTypeId(0x00080049)
79 
80 # Node definition
81 class slopeShader(OpenMayaMPx.MPxNode):
82  # class variables
83  aAngle = OpenMaya.MObject()
84  aColor1 = OpenMaya.MObject()
85  aColor2 = OpenMaya.MObject()
86  aTriangleNormalCamera = OpenMaya.MObject()
87  aOutColor = OpenMaya.MObject()
88  aMatrixEyeToWorld = OpenMaya.MObject()
89  aDirtyShaderAttr = OpenMaya.MObject()
90 
91  def __init__(self):
92  OpenMayaMPx.MPxNode.__init__(self)
93 
94 
95  def compute(self, plug, dataBlock):
96  if plug == slopeShader.aOutColor or plug.parent() == slopeShader.aOutColor:
97 
98  resultColor = OpenMaya.MFloatVector(0.0,0.0,0.0)
99 
100  try:
101  dataHandle = dataBlock.inputValue( slopeShader.aColor1 )
102  except:
103  sys.stderr.write( "Failed to get inputValue aColor1" )
104  raise
105  walkable = dataHandle.asFloatVector()
106 
107  try:
108  dataHandle = dataBlock.inputValue( slopeShader.aColor2 )
109  except:
110  sys.stderr.write( "Failed to get inputValue aColor2" )
111  raise
112  nonWalkable = dataHandle.asFloatVector()
113 
114  try:
115  dataHandle = dataBlock.inputValue( slopeShader.aTriangleNormalCamera )
116  except:
117  sys.stderr.write( "Failed to get inputValue aTriangleNormalCamera" )
118  raise
119  surfaceNormal = dataHandle.asFloatVector()
120 
121  try:
122  dataHandle = dataBlock.inputValue( slopeShader.aMatrixEyeToWorld )
123  except:
124  sys.stderr.write( "Failed to get inputValue aMatrixEyeToWorld" )
125  raise
126  viewMatrix = dataHandle.asFloatMatrix()
127 
128  # Get the angle
129  try:
130  dataHandle = dataBlock.inputValue( slopeShader.aAngle )
131  except:
132  sys.stderr.write( "Failed to get inputValue aAngle" )
133  raise
134  angle = dataHandle.asFloat()
135 
136  yVector = OpenMaya.MFloatVector(0, 1, 0)
137 
138  # Normalize the view vector
139  #
140  surfaceNormal.normalize()
141  WSVector = surfaceNormal * viewMatrix
142 
143  # find dot product
144  #
145  scalarNormal = WSVector * yVector
146 
147  # take the absolute value
148  #
149  if scalarNormal < 0.0:
150  scalarNormal *= -1.0
151 
152  radianAngle = math.radians(angle)
153  cosOfAngle = math.cos(radianAngle)
154  if cosOfAngle < scalarNormal:
155  resultColor = walkable
156  else:
157  resultColor = nonWalkable
158 
159  # set output color attribute
160  #
161  try:
162  outColorHandle = dataBlock.outputValue( slopeShader.aOutColor )
163  except:
164  sys.stderr.write( "Failed to get outputValue aOutColor" )
165  raise
166 
167  outColorHandle.setMFloatVector(resultColor)
168  outColorHandle.setClean()
169 
170  else:
171  return OpenMaya.kUnknownParameter
172 
173 
174  def postConstructor(self):
175  print("In slopeShader.postConstructor")
176  # OpenMayaMPx.MPxNode.setMPSafe(self, 1)
177 
178 
179 class slopeShaderBehavior(OpenMayaMPx.MPxDragAndDropBehavior):
180  def __init__(self):
181  OpenMayaMPx.MPxDragAndDropBehavior.__init__(self)
182 
183 
184  def shouldBeUsedFor(self, sourceNode, destinationNode, sourcePlug, destinationPlug):
185  """
186  Overloaded function from MPxDragAndDropBehavior
187  this method will return True if it is going to handle the connection
188  between the two nodes given.
189  """
190  result = False
191 
192  if sourceNode.hasFn(OpenMaya.MFn.kLambert):
193  #if the source node was a lambert
194  #than we will check the downstream connections to see
195  #if a slope shader is assigned to it.
196  #
197  shaderNode = None
198  src = OpenMaya.MFnDependencyNode(sourceNode)
199  connections = OpenMaya.MPlugArray()
200  src.getConnections(connections)
201 
202  for i in range(connections.length()):
203  #check the incoming connections to this plug
204  #
205  connectedPlugs = OpenMaya.MPlugArray()
206  connections[i].connectedTo(connectedPlugs, True, False)
207  for j in range(connectedPlugs.length()):
208  #if the incoming node is a slope shader than
209  #set shaderNode equal to it and break out of the inner
210  #loop
211  #
212  testNode = OpenMaya.MFnDependencyNode(connectedPlugs[j].node())
213  if testNode.typeName() == kSlopeNodeName:
214  shaderNode = connectedPlugs[j].node()
215  break
216 
217  #if the shaderNode is not None
218  #than we have found a slopeShader
219  #
220  if shaderNode is not None:
221  #if the destination node is a mesh than we will take
222  #care of this connection so set the result to True
223  #and break out of the outer loop
224  #
225  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
226  result = True
227  break
228 
229  node = OpenMaya.MFnDependencyNode(sourceNode)
230  if node.typeName() == kSlopeNodeName:
231  #if the sourceNode is a slope shader than check what we
232  #are dropping on to
233  #
234  if destinationNode.hasFn(OpenMaya.MFn.kLambert):
235  result = True
236  elif destinationNode.hasFn(OpenMaya.MFn.kMesh):
237  result = True
238 
239  return result
240 
241 
242  def connectNodeToNode(self, sourceNode, destinationNode, force):
243  """
244  Overloaded function from MPxDragAndDropBehavior
245  this method will handle the connection between the slopeShader and the shader it is
246  assigned to as well as any meshes that it is assigned to.
247  """
248  src = OpenMaya.MFnDependencyNode(sourceNode)
249 
250  #if we are dragging from a lambert
251  #we want to check what we are dragging
252  #onto.
253  if sourceNode.hasFn(OpenMaya.MFn.kLambert):
254  shaderNode = OpenMaya.MObject()
255  connections = OpenMaya.MPlugArray()
256  shaderNodes = OpenMaya.MObjectArray()
257  shaderNodes.clear()
258 
259  #if the source node was a lambert
260  #than we will check the downstream connections to see
261  #if a slope shader is assigned to it.
262  #
263  src.getConnections(connections)
264  for i in range(connections.length()):
265  #check the incoming connections to this plug
266  #
267  connectedPlugs = OpenMaya.MPlugArray()
268  connections[i].connectedTo(connectedPlugs, True, False)
269  for j in range(connectedPlugs.length()):
270  #if the incoming node is a slope shader than
271  #append the node to the shaderNodes array
272  #
273  currentnode = connectedPlugs[j].node()
274  if OpenMaya.MFnDependencyNode(currentnode).typeName() == kSlopeNodeName:
275  shaderNodes.append(currentnode)
276 
277  #if we found a shading node
278  #than check the destination node
279  #type to see if it is a mesh
280  #
281  if shaderNodes.length() > 0:
282  dest = OpenMaya.MFnDependencyNode(destinationNode)
283  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
284  #if the node is a mesh than for each slopeShader
285  #connect the worldMesh attribute to the dirtyShaderPlug
286  #attribute to force an evaluation of the node when the mesh
287  #changes
288  #
289  for i in range(shaderNodes.length()):
290  srcPlug = dest.findPlug("worldMesh")
291  destPlug = OpenMaya.MFnDependencyNode(shaderNodes[i]).findPlug("dirtyShaderPlug")
292 
293  if (not srcPlug.isNull()) and (not destPlug.isNull()):
294  cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
295  OpenMaya.MGlobal.executeCommand(cmd)
296 
297  #get the shading engine so we can assign the shader
298  #to the mesh after doing the connection
299  #
300  shadingEngine = self._findShadingEngine(sourceNode)
301 
302  #if there is a valid shading engine than make
303  #the connection
304  #
305  if not shadingEngine.isNull():
306  cmd = "sets -edit -forceElement %s %s" % (
307  OpenMaya.MFnDependencyNode(shadingEngine).name(),
308  OpenMaya.MFnDagNode(destinationNode).partialPathName()
309  )
310  OpenMaya.MGlobal.executeCommand(cmd)
311 
312  elif src.typeName() == kSlopeNodeName:
313  #if we are dragging from a slope shader
314  #than we want to see what we are dragging onto
315  #
316  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
317  #if the user is dragging onto a mesh
318  #than make the connection from the worldMesh
319  #to the dirtyShader plug on the slopeShader
320  #
321  dest = OpenMaya.MFnDependencyNode(destinationNode)
322  srcPlug = dest.findPlug("worldMesh")
323  destPlug = src.findPlug("dirtyShaderPlug")
324  if (not srcPlug.isNull()) and (not destPlug.isNull()):
325  cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
326  OpenMaya.MGlobal.executeCommand(cmd)
327 
328  elif destinationNode.hasFn(OpenMaya.MFn.kLambert):
329  dest = OpenMaya.MFnDependencyNode(destinationNode)
330  srcPlug = src.findPlug("outColor")
331  destPlug = dest.findPlug("color")
332  if (not srcPlug.isNull()) and (not destPlug.isNull()):
333  if force:
334  cmd = "connectAttr -f %s %s" % (srcPlug.name(), destPlug.name())
335  else:
336  cmd = "connectAttr %s %s" % (srcPlug.name(), destPlug.name())
337  OpenMaya.MGlobal.executeCommand(cmd)
338 
339 
340  def connectNodeToAttr(self, sourceNode, destinationPlug, force):
341  """
342  Overloaded function from MPxDragAndDropBehavior
343  this method will assign the correct output from the slope shader
344  onto the given attribute.
345  """
346  src = OpenMaya.MFnDependencyNode(sourceNode)
347 
348  #if we are dragging from a slopeShader
349  #to a shader than connect the outColor
350  #plug to the plug being passed in
351  #
352  if destinationPlug.node().hasFn(OpenMaya.MFn.kLambert):
353  if src.typeName() == kSlopeNodeName:
354  srcPlug = src.findPlug("outColor")
355  if (not srcPlug.isNull()) and (not destinationPlug.isNull()):
356  cmd = "connectAttr %s %s" % (srcPlug.name(), destinationPlug.name())
357  OpenMaya.MGlobal.executeCommand(cmd)
358  else:
359  #in all of the other cases we do not need the plug just the node
360  #that it is on
361  #
362  destinationNode = destinationPlug.node()
363  self.connectNodeToNode(sourceNode, destinationNode, force)
364 
365 
366  def _findShadingEngine(self, node):
367  """
368  Given the material MObject this method will
369  return the shading group that it is assigned to.
370  if there is no shading group associated with
371  the material than a null MObject is passed back.
372  """
373  nodeFn = OpenMaya.MFnDependencyNode (node)
374  srcPlug = nodeFn.findPlug("outColor")
375  nodeConnections = OpenMaya.MPlugArray()
376  srcPlug.connectedTo(nodeConnections, False, True)
377  #loop through the connections
378  #and find the shading engine node that
379  #it is connected to
380  #
381  for i in range(nodeConnections.length()):
382  theNode = nodeConnections[i].node()
383  if theNode.hasFn(OpenMaya.MFn.kShadingEngine):
384  return theNode
385 
386  #no shading engine associated so return a
387  #null MObject
388  #
389  return OpenMaya.MObject()
390 
391 
392 ##################################################################
393 
394 
395 def nodeCreator():
396  return OpenMayaMPx.asMPxPtr(slopeShader())
397 
398 
399 def behaviourCreator():
400  return OpenMayaMPx.asMPxPtr(slopeShaderBehavior())
401 
402 
403 def nodeInitializer():
405  nMAttr = OpenMaya.MFnMatrixAttribute()
406  nTAttr = OpenMaya.MFnTypedAttribute()
408  # input
409  slopeShader.aAngle = nAttr.create( "angle", "ang", OpenMaya.MFnNumericData.kFloat )
410  nAttr.setDefault(30.0)
411  nAttr.setMin(0.0)
412  nAttr.setMax(100.0)
413  nAttr.setKeyable(1)
414  nAttr.setStorable(1)
415  nAttr.setReadable(1)
416  nAttr.setWritable(1)
417 
418  slopeShader.aColor1 = nAttr.createColor("walkableColor", "w")
419  nAttr.setDefault(0.0, 1.0, 0.0)
420  nAttr.setKeyable(1)
421  nAttr.setStorable(1)
422  nAttr.setUsedAsColor(1)
423  nAttr.setReadable(1)
424  nAttr.setWritable(1)
425 
426  slopeShader.aColor2 = nAttr.createColor( "nonWalkableColor", "nw" )
427  nAttr.setDefault(1.0, 0.0, 0.0)
428  nAttr.setKeyable(1)
429  nAttr.setStorable(1)
430  nAttr.setUsedAsColor(1)
431  nAttr.setReadable(1)
432  nAttr.setWritable(1)
433 
434  # Surface Normal supplied by the render sampler
435  #
436  slopeShader.aTriangleNormalCamera = nAttr.createPoint( "triangleNormalCamera", "n" )
437  nAttr.setStorable(0)
438  nAttr.setHidden(1)
439  nAttr.setReadable(1)
440  nAttr.setWritable(1)
441 
442  # View matrix from the camera into world space
443  #
444  slopeShader.aMatrixEyeToWorld = nMAttr.create( "matrixEyeToWorld", "mew", OpenMaya.MFnMatrixAttribute.kFloat )
445  nAttr.setHidden(1)
446  nMAttr.setWritable(1)
447 
448  # Output Attributes
449  #
450  slopeShader.aOutColor = nAttr.createColor( "outColor", "oc" )
451  nAttr.setStorable(0)
452  nAttr.setHidden(0)
453  nAttr.setReadable(1)
454  nAttr.setWritable(0)
455 
456  meshTypeId = OpenMaya.MTypeId(OpenMaya.MFnData.kMesh)
457 
458  # dummy plug for forcing evaluation
459  #
460  slopeShader.aDirtyShaderAttr = nGAttr.create( "dirtyShaderPlug", "dsp")
461  nGAttr.setArray(1)
462  nGAttr.setHidden(0)
463  nGAttr.setUsesArrayDataBuilder(1)
464  nGAttr.setReadable(0)
465  nGAttr.setStorable(1)
466  nGAttr.setIndexMatters(0)
467  # nGAttr.addAccept(meshTypeId)
468 
469  # Add attribues
470  #
471  slopeShader.addAttribute(slopeShader.aAngle)
472  slopeShader.addAttribute(slopeShader.aColor1)
473  slopeShader.addAttribute(slopeShader.aColor2)
474  slopeShader.addAttribute(slopeShader.aTriangleNormalCamera)
475  slopeShader.addAttribute(slopeShader.aOutColor)
476  slopeShader.addAttribute(slopeShader.aMatrixEyeToWorld)
477  slopeShader.addAttribute(slopeShader.aDirtyShaderAttr)
478 
479  slopeShader.attributeAffects (slopeShader.aAngle, slopeShader.aOutColor)
480  slopeShader.attributeAffects (slopeShader.aColor1, slopeShader.aOutColor)
481  slopeShader.attributeAffects (slopeShader.aColor2, slopeShader.aOutColor)
482  slopeShader.attributeAffects (slopeShader.aTriangleNormalCamera, slopeShader.aOutColor)
483  slopeShader.attributeAffects (slopeShader.aDirtyShaderAttr, slopeShader.aOutColor)
484 
485 
486 # initialize the script plug-in
487 def initializePlugin(mobject):
488  mplugin = OpenMayaMPx.MFnPlugin(mobject)
489 
490  # register node
491  try:
492  mplugin.registerNode(kSlopeNodeName, kSlopeNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kSlopeNodeClassify)
493  except:
494  sys.stderr.write("Failed to register node: %s" % kSlopeNodeName )
495  raise
496 
497  # register behaviors
498  try:
499  mplugin.registerDragAndDropBehavior(kSlopeShaderBehaviourName, behaviourCreator)
500  except:
501  sys.stderr.write("Failed to register behaviour: %s" % kSlopeShaderBehaviourName)
502 
503  postCmd = "if( `window -exists createRenderNodeWindow` ) {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
504  OpenMaya.MGlobal.executeCommand(postCmd)
505 
506 
507 # uninitialize the script plug-in
508 def uninitializePlugin(mobject):
509  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
510  try:
511  mplugin.deregisterNode(kSlopeNodeId)
512  except:
513  sys.stderr.write( "Failed to unregister node: %s" % kSlopeNodeName )
514  raise
515 
516  try:
517  plugin.deregisterDragAndDropBehavior(kSlopeShaderBehaviourName)
518  except:
519  sys.stderr.write("Failed to deregister behaviour: %s" % kSlopeShaderBehaviourName)
520 
521  postCmd = "if( `window -exists createRenderNodeWindow` ) {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
522  OpenMaya.MGlobal.executeCommand(postCmd)