scripted/slopeShader.py

scripted/slopeShader.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 # import maya
40 # maya.cmds.loadPlugin("slopeShader.py")
41 #
42 # Create a sphere and assign a shader such as lambert to it.
43 # Select the color channel and link it to Utilities/Sp SlopeShader
44 # Render the scene
45 
46 # imports
47 import maya.OpenMaya as OpenMaya
48 import maya.OpenMayaUI as OpenMayaUI
49 import maya.OpenMayaMPx as OpenMayaMPx
50 import math, sys
51 
52 # consts
53 kSlopeShaderBehaviourName = "slopeShaderBehaviour"
54 kSlopeNodeName = "spSlopeShader"
55 kSlopeNodeClassify = "utility/color"
56 kSlopeNodeId = OpenMaya.MTypeId(0x87001)
57 
58 # Node definition
59 class slopeShader(OpenMayaMPx.MPxNode):
60  # class variables
61  aAngle = OpenMaya.MObject()
62  aColor1 = OpenMaya.MObject()
63  aColor2 = OpenMaya.MObject()
64  aTriangleNormalCamera = OpenMaya.MObject()
65  aOutColor = OpenMaya.MObject()
66  aMatrixEyeToWorld = OpenMaya.MObject()
67  aDirtyShaderAttr = OpenMaya.MObject()
68 
69  def __init__(self):
70  OpenMayaMPx.MPxNode.__init__(self)
71 
72 
73  def compute(self, plug, dataBlock):
74  if plug == slopeShader.aOutColor or plug.parent() == slopeShader.aOutColor:
75 
76  resultColor = OpenMaya.MFloatVector(0.0,0.0,0.0)
77 
78  try:
79  dataHandle = dataBlock.inputValue( slopeShader.aColor1 )
80  except:
81  sys.stderr.write( "Failed to get inputValue aColor1" )
82  raise
83  walkable = dataHandle.asFloatVector()
84 
85  try:
86  dataHandle = dataBlock.inputValue( slopeShader.aColor2 )
87  except:
88  sys.stderr.write( "Failed to get inputValue aColor2" )
89  raise
90  nonWalkable = dataHandle.asFloatVector()
91 
92  try:
93  dataHandle = dataBlock.inputValue( slopeShader.aTriangleNormalCamera )
94  except:
95  sys.stderr.write( "Failed to get inputValue aTriangleNormalCamera" )
96  raise
97  surfaceNormal = dataHandle.asFloatVector()
98 
99  try:
100  dataHandle = dataBlock.inputValue( slopeShader.aMatrixEyeToWorld )
101  except:
102  sys.stderr.write( "Failed to get inputValue aMatrixEyeToWorld" )
103  raise
104  viewMatrix = dataHandle.asFloatMatrix()
105 
106  # Get the angle
107  try:
108  dataHandle = dataBlock.inputValue( slopeShader.aAngle )
109  except:
110  sys.stderr.write( "Failed to get inputValue aAngle" )
111  raise
112  angle = dataHandle.asFloat()
113 
114  yVector = OpenMaya.MFloatVector(0, 1, 0)
115 
116  # Normalize the view vector
117  #
118  surfaceNormal.normalize()
119  WSVector = surfaceNormal * viewMatrix
120 
121  # find dot product
122  #
123  scalarNormal = WSVector * yVector
124 
125  # take the absolute value
126  #
127  if scalarNormal < 0.0:
128  scalarNormal *= -1.0
129 
130  radianAngle = math.radians(angle)
131  cosOfAngle = math.cos(radianAngle)
132  if cosOfAngle < scalarNormal:
133  resultColor = walkable
134  else:
135  resultColor = nonWalkable
136 
137  # set output color attribute
138  #
139  try:
140  outColorHandle = dataBlock.outputValue( slopeShader.aOutColor )
141  except:
142  sys.stderr.write( "Failed to get outputValue aOutColor" )
143  raise
144 
145  outColorHandle.setMFloatVector(resultColor)
146  outColorHandle.setClean()
147 
148  else:
149  return OpenMaya.kUnknownParameter
150 
151 
152  def postConstructor(self):
153  print "In slopeShader.postConstructor"
154  # OpenMayaMPx.MPxNode.setMPSafe(self, 1)
155 
156 
157 class slopeShaderBehavior(OpenMayaMPx.MPxDragAndDropBehavior):
158  def __init__(self):
159  OpenMayaMPx.MPxDragAndDropBehavior.__init__(self)
160 
161 
162  def shouldBeUsedFor(self, sourceNode, destinationNode, sourcePlug, destinationPlug):
163  """
164  Overloaded function from MPxDragAndDropBehavior
165  this method will return True if it is going to handle the connection
166  between the two nodes given.
167  """
168  result = False
169 
170  if sourceNode.hasFn(OpenMaya.MFn.kLambert):
171  #if the source node was a lambert
172  #than we will check the downstream connections to see
173  #if a slope shader is assigned to it.
174  #
175  shaderNode = None
176  src = OpenMaya.MFnDependencyNode(sourceNode)
177  connections = OpenMaya.MPlugArray()
178  src.getConnections(connections)
179 
180  for i in range(connections.length()):
181  #check the incoming connections to this plug
182  #
183  connectedPlugs = OpenMaya.MPlugArray()
184  connections[i].connectedTo(connectedPlugs, True, False)
185  for j in range(connectedPlugs.length()):
186  #if the incoming node is a slope shader than
187  #set shaderNode equal to it and break out of the inner
188  #loop
189  #
190  testNode = OpenMaya.MFnDependencyNode(connectedPlugs[j].node())
191  if testNode.typeName() == kSlopeNodeName:
192  shaderNode = connectedPlugs[j].node()
193  break
194 
195  #if the shaderNode is not None
196  #than we have found a slopeShader
197  #
198  if shaderNode is not None:
199  #if the destination node is a mesh than we will take
200  #care of this connection so set the result to True
201  #and break out of the outer loop
202  #
203  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
204  result = True
205  break
206 
207  node = OpenMaya.MFnDependencyNode(sourceNode)
208  if node.typeName() == kSlopeNodeName:
209  #if the sourceNode is a slope shader than check what we
210  #are dropping on to
211  #
212  if destinationNode.hasFn(OpenMaya.MFn.kLambert):
213  result = True
214  elif destinationNode.hasFn(OpenMaya.MFn.kMesh):
215  result = True
216 
217  return result
218 
219 
220  def connectNodeToNode(self, sourceNode, destinationNode, force):
221  """
222  Overloaded function from MPxDragAndDropBehavior
223  this method will handle the connection between the slopeShader and the shader it is
224  assigned to as well as any meshes that it is assigned to.
225  """
226  src = OpenMaya.MFnDependencyNode(sourceNode)
227 
228  #if we are dragging from a lambert
229  #we want to check what we are dragging
230  #onto.
231  if sourceNode.hasFn(OpenMaya.MFn.kLambert):
232  shaderNode = OpenMaya.MObject()
233  connections = OpenMaya.MPlugArray()
234  shaderNodes = OpenMaya.MObjectArray()
235  shaderNodes.clear()
236 
237  #if the source node was a lambert
238  #than we will check the downstream connections to see
239  #if a slope shader is assigned to it.
240  #
241  src.getConnections(connections)
242  for i in range(connections.length()):
243  #check the incoming connections to this plug
244  #
245  connectedPlugs = OpenMaya.MPlugArray()
246  connections[i].connectedTo(connectedPlugs, True, False)
247  for j in range(connectedPlugs.length()):
248  #if the incoming node is a slope shader than
249  #append the node to the shaderNodes array
250  #
251  currentnode = connectedPlugs[j].node()
252  if OpenMaya.MFnDependencyNode(currentnode).typeName() == kSlopeNodeName:
253  shaderNodes.append(currentnode)
254 
255  #if we found a shading node
256  #than check the destination node
257  #type to see if it is a mesh
258  #
259  if shaderNodes.length() > 0:
260  dest = OpenMaya.MFnDependencyNode(destinationNode)
261  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
262  #if the node is a mesh than for each slopeShader
263  #connect the worldMesh attribute to the dirtyShaderPlug
264  #attribute to force an evaluation of the node when the mesh
265  #changes
266  #
267  for i in range(shaderNodes.length()):
268  srcPlug = dest.findPlug("worldMesh")
269  destPlug = OpenMaya.MFnDependencyNode(shaderNodes[i]).findPlug("dirtyShaderPlug")
270 
271  if (not srcPlug.isNull()) and (not destPlug.isNull()):
272  cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
273  OpenMaya.MGlobal.executeCommand(cmd)
274 
275  #get the shading engine so we can assign the shader
276  #to the mesh after doing the connection
277  #
278  shadingEngine = self._findShadingEngine(sourceNode)
279 
280  #if there is a valid shading engine than make
281  #the connection
282  #
283  if not shadingEngine.isNull():
284  cmd = "sets -edit -forceElement %s %s" % (
285  OpenMaya.MFnDependencyNode(shadingEngine).name(),
286  OpenMaya.MFnDagNode(destinationNode).partialPathName()
287  )
288  OpenMaya.MGlobal.executeCommand(cmd)
289 
290  elif src.typeName() == kSlopeNodeName:
291  #if we are dragging from a slope shader
292  #than we want to see what we are dragging onto
293  #
294  if destinationNode.hasFn(OpenMaya.MFn.kMesh):
295  #if the user is dragging onto a mesh
296  #than make the connection from the worldMesh
297  #to the dirtyShader plug on the slopeShader
298  #
299  dest = OpenMaya.MFnDependencyNode(destinationNode)
300  srcPlug = dest.findPlug("worldMesh")
301  destPlug = src.findPlug("dirtyShaderPlug")
302  if (not srcPlug.isNull()) and (not destPlug.isNull()):
303  cmd = "connectAttr -na %s %s" % (srcPlug.name(), destPlug.name())
304  OpenMaya.MGlobal.executeCommand(cmd)
305 
306  elif destinationNode.hasFn(OpenMaya.MFn.kLambert):
307  dest = OpenMaya.MFnDependencyNode(destinationNode)
308  srcPlug = src.findPlug("outColor")
309  destPlug = dest.findPlug("color")
310  if (not srcPlug.isNull()) and (not destPlug.isNull()):
311  if force:
312  cmd = "connectAttr -f %s %s" % (srcPlug.name(), destPlug.name())
313  else:
314  cmd = "connectAttr %s %s" % (srcPlug.name(), destPlug.name())
315  OpenMaya.MGlobal.executeCommand(cmd)
316 
317 
318  def connectNodeToAttr(self, sourceNode, destinationPlug, force):
319  """
320  Overloaded function from MPxDragAndDropBehavior
321  this method will assign the correct output from the slope shader
322  onto the given attribute.
323  """
324  src = OpenMaya.MFnDependencyNode(sourceNode)
325 
326  #if we are dragging from a slopeShader
327  #to a shader than connect the outColor
328  #plug to the plug being passed in
329  #
330  if destinationPlug.node().hasFn(OpenMaya.MFn.kLambert):
331  if src.typeName() == kSlopeNodeName:
332  srcPlug = src.findPlug("outColor")
333  if (not srcPlug.isNull()) and (not destinationPlug.isNull()):
334  cmd = "connectAttr %s %s" % (srcPlug.name(), destinationPlug.name())
335  OpenMaya.MGlobal.executeCommand(cmd)
336  else:
337  #in all of the other cases we do not need the plug just the node
338  #that it is on
339  #
340  destinationNode = destinationPlug.node()
341  self.connectNodeToNode(sourceNode, destinationNode, force)
342 
343 
344  def _findShadingEngine(self, node):
345  """
346  Given the material MObject this method will
347  return the shading group that it is assigned to.
348  if there is no shading group associated with
349  the material than a null MObject is passed back.
350  """
351  nodeFn = OpenMaya.MFnDependencyNode (node)
352  srcPlug = nodeFn.findPlug("outColor")
353  nodeConnections = OpenMaya.MPlugArray()
354  srcPlug.connectedTo(nodeConnections, False, True)
355  #loop through the connections
356  #and find the shading engine node that
357  #it is connected to
358  #
359  for i in range(nodeConnections.length()):
360  theNode = nodeConnections[i].node()
361  if theNode.hasFn(OpenMaya.MFn.kShadingEngine):
362  return theNode
363 
364  #no shading engine associated so return a
365  #null MObject
366  #
367  return OpenMaya.MObject()
368 
369 
370 ##################################################################
371 
372 
373 def nodeCreator():
374  return OpenMayaMPx.asMPxPtr(slopeShader())
375 
376 
377 def behaviourCreator():
378  return OpenMayaMPx.asMPxPtr(slopeShaderBehavior())
379 
380 
381 def nodeInitializer():
382  nAttr = OpenMaya.MFnNumericAttribute()
383  nMAttr = OpenMaya.MFnMatrixAttribute()
384  nTAttr = OpenMaya.MFnTypedAttribute()
385  nGAttr = OpenMaya.MFnGenericAttribute()
386  # input
387  slopeShader.aAngle = nAttr.create( "angle", "ang", OpenMaya.MFnNumericData.kFloat )
388  nAttr.setDefault(30.0)
389  nAttr.setMin(0.0)
390  nAttr.setMax(100.0)
391  nAttr.setKeyable(1)
392  nAttr.setStorable(1)
393  nAttr.setReadable(1)
394  nAttr.setWritable(1)
395 
396  slopeShader.aColor1 = nAttr.createColor("walkableColor", "w")
397  nAttr.setDefault(0.0, 1.0, 0.0)
398  nAttr.setKeyable(1)
399  nAttr.setStorable(1)
400  nAttr.setUsedAsColor(1)
401  nAttr.setReadable(1)
402  nAttr.setWritable(1)
403 
404  slopeShader.aColor2 = nAttr.createColor( "nonWalkableColor", "nw" )
405  nAttr.setDefault(1.0, 0.0, 0.0)
406  nAttr.setKeyable(1)
407  nAttr.setStorable(1)
408  nAttr.setUsedAsColor(1)
409  nAttr.setReadable(1)
410  nAttr.setWritable(1)
411 
412  # Surface Normal supplied by the render sampler
413  #
414  slopeShader.aTriangleNormalCamera = nAttr.createPoint( "triangleNormalCamera", "n" )
415  nAttr.setStorable(0)
416  nAttr.setHidden(1)
417  nAttr.setReadable(1)
418  nAttr.setWritable(1)
419 
420  # View matrix from the camera into world space
421  #
422  slopeShader.aMatrixEyeToWorld = nMAttr.create( "matrixEyeToWorld", "mew", OpenMaya.MFnMatrixAttribute.kFloat )
423  nAttr.setHidden(1)
424  nMAttr.setWritable(1)
425 
426  # Output Attributes
427  #
428  slopeShader.aOutColor = nAttr.createColor( "outColor", "oc" )
429  nAttr.setStorable(0)
430  nAttr.setHidden(0)
431  nAttr.setReadable(1)
432  nAttr.setWritable(0)
433 
434  meshTypeId = OpenMaya.MTypeId(OpenMaya.MFnData.kMesh)
435 
436  # dummy plug for forcing evaluation
437  #
438  slopeShader.aDirtyShaderAttr = nGAttr.create( "dirtyShaderPlug", "dsp")
439  nGAttr.setArray(1)
440  nGAttr.setHidden(0)
441  nGAttr.setUsesArrayDataBuilder(1)
442  nGAttr.setReadable(0)
443  nGAttr.setStorable(1)
444  nGAttr.setIndexMatters(0)
445  # nGAttr.addAccept(meshTypeId)
446 
447  # Add attribues
448  #
449  slopeShader.addAttribute(slopeShader.aAngle)
450  slopeShader.addAttribute(slopeShader.aColor1)
451  slopeShader.addAttribute(slopeShader.aColor2)
452  slopeShader.addAttribute(slopeShader.aTriangleNormalCamera)
453  slopeShader.addAttribute(slopeShader.aOutColor)
454  slopeShader.addAttribute(slopeShader.aMatrixEyeToWorld)
455  slopeShader.addAttribute(slopeShader.aDirtyShaderAttr)
456 
457  slopeShader.attributeAffects (slopeShader.aAngle, slopeShader.aOutColor)
458  slopeShader.attributeAffects (slopeShader.aColor1, slopeShader.aOutColor)
459  slopeShader.attributeAffects (slopeShader.aColor2, slopeShader.aOutColor)
460  slopeShader.attributeAffects (slopeShader.aTriangleNormalCamera, slopeShader.aOutColor)
461  slopeShader.attributeAffects (slopeShader.aDirtyShaderAttr, slopeShader.aOutColor)
462 
463 
464 # initialize the script plug-in
465 def initializePlugin(mobject):
466  mplugin = OpenMayaMPx.MFnPlugin(mobject)
467 
468  # register node
469  try:
470  mplugin.registerNode(kSlopeNodeName, kSlopeNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kDependNode, kSlopeNodeClassify)
471  except:
472  sys.stderr.write("Failed to register node: %s" % kSlopeNodeName )
473  raise
474 
475  # register behaviors
476  try:
477  mplugin.registerDragAndDropBehavior(kSlopeShaderBehaviourName, behaviourCreator)
478  except:
479  sys.stderr.write("Failed to register behaviour: %s" % kSlopeShaderBehaviourName)
480 
481  postCmd = "if( `window -exists createRenderNodeWindow` ) {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
482  OpenMaya.MGlobal.executeCommand(postCmd)
483 
484 
485 # uninitialize the script plug-in
486 def uninitializePlugin(mobject):
487  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
488  try:
489  mplugin.deregisterNode(kSlopeNodeId)
490  except:
491  sys.stderr.write( "Failed to unregister node: %s" % kSlopeNodeName )
492  raise
493 
494  try:
495  plugin.deregisterDragAndDropBehavior(kSlopeShaderBehaviourName)
496  except:
497  sys.stderr.write("Failed to deregister behaviour: %s" % kSlopeShaderBehaviourName)
498 
499  postCmd = "if( `window -exists createRenderNodeWindow` ) {refreshCreateRenderNodeWindow(\"%s\");}\n" % kSlopeNodeClassify
500  OpenMaya.MGlobal.executeCommand(postCmd)