scripted/pyHwColorPerVertexShader.py

scripted/pyHwColorPerVertexShader.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 try:
13  from OpenGL.GL import *
14 except:
15  pass
16 import maya.api.OpenMaya as om
17 import maya.api.OpenMayaUI as omui
18 import maya.api.OpenMayaRender as omr
19 
20 def maya_useNewAPI():
21  """
22  The presence of this function tells Maya that the plugin produces, and
23  expects to be passed, objects created using the Maya Python API 2.0.
24  """
25  pass
26 
27 
28 def matrixAsArray(matrix):
29  array = []
30  for i in range(16):
31  array.append(matrix[i])
32  return array
33 
34 def drawBoundingBox(box, color):
35  min = om.MFloatPoint( box.min )
36  max = om.MFloatPoint( box.max )
37 
38  glPushAttrib(GL_ALL_ATTRIB_BITS)
39 
40  glDisable( GL_LIGHTING )
41  glColor3f( color.r, color.g, color.b)
42  glBegin( GL_LINE_STRIP )
43  glVertex3f( min.x, min.y, min.z )
44  glVertex3f( max.x, min.y, min.z )
45  glVertex3f( max.x, max.y, min.z )
46  glVertex3f( min.x, max.y, min.z )
47  glVertex3f( min.x, min.y, min.z )
48  glVertex3f( min.x, min.y, max.z )
49  glVertex3f( min.x, max.y, max.z )
50  glVertex3f( min.x, max.y, min.z )
51  glVertex3f( max.x, max.y, min.z )
52  glVertex3f( max.x, max.y, max.z )
53  glVertex3f( max.x, min.y, max.z )
54  glVertex3f( max.x, min.y, min.z )
55  glVertex3f( max.x, min.y, max.z )
56  glVertex3f( min.x, min.y, max.z )
57  glVertex3f( min.x, max.y, max.z )
58  glVertex3f( max.x, max.y, max.z )
59  glEnd()
60 
61  glPopAttrib()
62 
63 class hwColorPerVertexShader(omui.MPxHwShaderNode):
64  id = om.MTypeId(0x00105450)
65 
66  # Attributes
67  aColorGain = None
68  aColorBias = None
69  aTranspGain = None
70  aTranspBias = None
71  aNormalsPerVertex = None
72  aColorsPerVertex = None
73  aColorSetName = None
74  aTexRotateX = None
75  aTexRotateY = None
76  aTexRotateZ = None
77  aDrawBoundingBox = None
78 
79  def __init__(self):
80  omui.MPxHwShaderNode.__init__(self)
81 
82  # Cached internal values
83  self.mColorGain = [1.0, 0.0, 0.0]
84  self.mColorBias = [0.0, 0.0, 0.0]
85  self.mTranspGain = 1.0
86  self.mTranspBias = 0.0
87 
88  self.mNormalsPerVertex = 0
89  self.mColorsPerVertex = 0
90  self.mColorSetName = ""
91  self.mTexRotateX = 0.0
92  self.mTexRotateY = 0.0
93  self.mTexRotateZ = 0.0
94 
95  self.mDrawBoundingBox = False
96 
97  self.mAttributesChanged = False
98 
99  self.mSwatchShader = None
100 
101  def __del__(self):
102  if self.mSwatchShader is not None:
103  shaderMgr = omr.MRenderer.getShaderManager()
104  if shaderMgr is not None:
105  shaderMgr.releaseShader(self.mSwatchShader)
106  self.mSwatchShader = None
107 
108  ## The creator method creates an instance of the
109  ## hwColorPerVertexShader class and is the first method called by Maya
110  ## when a hwColorPerVertexShader needs to be created.
111  ##
112  @staticmethod
113  def creator():
114  return hwColorPerVertexShader()
115 
116  ## The initialize routine is called after the node has been created.
117  ## It sets up the input and output attributes and adds them to the node.
118  ## Finally the dependencies are arranged so that when the inputs
119  ## change Maya knowns to call compute to recalculate the output values.
120  ##
121  @staticmethod
122  def initialize():
123  nAttr = om.MFnNumericAttribute()
124  tAttr = om.MFnTypedAttribute()
125 
126  ## Create input attributes.
127  ## All attributes are cached internal
128  ##
129  hwColorPerVertexShader.aColorGain = nAttr.createColor( "colorGain", "cg")
130  nAttr.storable = True
131  nAttr.keyable = True
132  nAttr.default = (1.0, 1.0, 1.0)
133  nAttr.cached = True
134  nAttr.internal = True
135  nAttr.affectsAppearance = True
136 
137  hwColorPerVertexShader.aTranspGain = nAttr.create("transparencyGain", "tg", om.MFnNumericData.kFloat, 1.0)
138  nAttr.storable = True
139  nAttr.keyable = True
140  nAttr.default = 1.0
141  nAttr.setSoftMin(0.0)
142  nAttr.setSoftMax(2.0)
143  nAttr.cached = True
144  nAttr.internal = True
145  nAttr.affectsAppearance = True
146 
147  hwColorPerVertexShader.aColorBias = nAttr.createColor( "colorBias", "cb")
148  nAttr.storable = True
149  nAttr.keyable = True
150  nAttr.default = (0.0, 0.0, 0.0)
151  nAttr.cached = True
152  nAttr.internal = True
153  nAttr.affectsAppearance = True
154 
155  hwColorPerVertexShader.aTranspBias = nAttr.create( "transparencyBias", "tb", om.MFnNumericData.kFloat, 0.0)
156  nAttr.storable = True
157  nAttr.keyable = True
158  nAttr.default = 0.0
159  nAttr.setSoftMin(-1.0)
160  nAttr.setSoftMax(1.0)
161  nAttr.cached = True
162  nAttr.internal = True
163  nAttr.affectsAppearance = True
164 
165  hwColorPerVertexShader.aNormalsPerVertex = nAttr.create("normalsPerVertex", "nv", om.MFnNumericData.kInt, 0)
166  nAttr.storable = True
167  nAttr.keyable = False
168  nAttr.default = 0
169  nAttr.setSoftMin(0)
170  nAttr.setSoftMax(3)
171  nAttr.cached = True
172  nAttr.internal = True
173  nAttr.affectsAppearance = True
174 
175  hwColorPerVertexShader.aColorsPerVertex = nAttr.create("colorsPerVertex", "cv", om.MFnNumericData.kInt, 0)
176  nAttr.storable = True
177  nAttr.keyable = False
178  nAttr.default = 0
179  nAttr.setSoftMin(0)
180  nAttr.setSoftMax(5)
181  nAttr.cached = True
182  nAttr.internal = True
183  nAttr.affectsAppearance = True
184 
185  hwColorPerVertexShader.aColorSetName = tAttr.create("colorSetName", "cs", om.MFnData.kString, om.MObject.kNullObj)
186  tAttr.storable = True
187  tAttr.keyable = False
188  tAttr.cached = True
189  tAttr.internal = True
190  tAttr.affectsAppearance = True
191 
192  hwColorPerVertexShader.aTexRotateX = nAttr.create( "texRotateX", "tx", om.MFnNumericData.kFloat, 0.0)
193  nAttr.storable = True
194  nAttr.keyable = True
195  nAttr.default = 0.0
196  nAttr.cached = True
197  nAttr.internal = True
198  nAttr.affectsAppearance = True
199 
200  hwColorPerVertexShader.aTexRotateY = nAttr.create( "texRotateY", "ty", om.MFnNumericData.kFloat, 0.0)
201  nAttr.storable = True
202  nAttr.keyable = True
203  nAttr.default = 0.0
204  nAttr.cached = True
205  nAttr.internal = True
206  nAttr.affectsAppearance = True
207 
208  hwColorPerVertexShader.aTexRotateZ = nAttr.create( "texRotateZ", "tz", om.MFnNumericData.kFloat, 0.0)
209  nAttr.storable = True
210  nAttr.keyable = True
211  nAttr.default = 0.0
212  nAttr.cached = True
213  nAttr.internal = True
214  nAttr.affectsAppearance = True
215 
216  hwColorPerVertexShader.aDrawBoundingBox = nAttr.create( "drawBoundingBox", "dbb", om.MFnNumericData.kBoolean, False)
217  nAttr.storable = True
218  nAttr.keyable = True
219  nAttr.default = False
220  nAttr.cached = True
221  nAttr.internal = True
222  nAttr.affectsAppearance = True
223 
224  ## create output attributes here
225  ## outColor is the only output attribute and it is inherited
226  ## so we do not need to create or add it.
227  ##
228 
229  ## Add the attributes here
230 
231  om.MPxNode.addAttribute(hwColorPerVertexShader.aColorGain)
232  om.MPxNode.addAttribute(hwColorPerVertexShader.aTranspGain)
233  om.MPxNode.addAttribute(hwColorPerVertexShader.aColorBias)
234  om.MPxNode.addAttribute(hwColorPerVertexShader.aTranspBias)
235  om.MPxNode.addAttribute(hwColorPerVertexShader.aNormalsPerVertex)
236  om.MPxNode.addAttribute(hwColorPerVertexShader.aColorsPerVertex)
237  om.MPxNode.addAttribute(hwColorPerVertexShader.aColorSetName)
238  om.MPxNode.addAttribute(hwColorPerVertexShader.aTexRotateX)
239  om.MPxNode.addAttribute(hwColorPerVertexShader.aTexRotateY)
240  om.MPxNode.addAttribute(hwColorPerVertexShader.aTexRotateZ)
241  om.MPxNode.addAttribute(hwColorPerVertexShader.aDrawBoundingBox)
242 
243  om.MPxNode.attributeAffects (hwColorPerVertexShader.aColorGain, omui.MPxHwShaderNode.outColor)
244  om.MPxNode.attributeAffects (hwColorPerVertexShader.aTranspGain, omui.MPxHwShaderNode.outColor)
245  om.MPxNode.attributeAffects (hwColorPerVertexShader.aColorBias, omui.MPxHwShaderNode.outColor)
246  om.MPxNode.attributeAffects (hwColorPerVertexShader.aTranspBias, omui.MPxHwShaderNode.outColor)
247  om.MPxNode.attributeAffects (hwColorPerVertexShader.aNormalsPerVertex, omui.MPxHwShaderNode.outColor)
248  om.MPxNode.attributeAffects (hwColorPerVertexShader.aColorsPerVertex, omui.MPxHwShaderNode.outColor)
249  om.MPxNode.attributeAffects (hwColorPerVertexShader.aColorSetName, omui.MPxHwShaderNode.outColor)
250  om.MPxNode.attributeAffects (hwColorPerVertexShader.aTexRotateX, omui.MPxHwShaderNode.outColor)
251  om.MPxNode.attributeAffects (hwColorPerVertexShader.aTexRotateY, omui.MPxHwShaderNode.outColor)
252  om.MPxNode.attributeAffects (hwColorPerVertexShader.aTexRotateZ, omui.MPxHwShaderNode.outColor)
253  om.MPxNode.attributeAffects (hwColorPerVertexShader.aDrawBoundingBox, omui.MPxHwShaderNode.outColor)
254 
255  ## The compute method is called by Maya when the input values
256  ## change and the output values need to be recomputed.
257  ##
258  ## In this case this is only used for software shading, when the to
259  ## compute the rendering swatches.
260  def compute(self, plug, data):
261  ## Check that the requested recompute is one of the output values
262  ##
263  if (plug != omui.MPxHwShaderNode.outColor) and (plug.parent() != omui.MPxHwShaderNode.outColor):
264  return None
265 
266  inputData = data.inputValue(hwColorPerVertexShader.aColorGain)
267  color = inputData.asFloatVector()
268 
269  outColorHandle = data.outputValue(hwColorPerVertexShader.outColor)
270  outColorHandle.setMFloatVector(color)
271 
272  data.setClean(plug)
273 
274  def postConstructor(self):
275  self.setMPSafe(False)
276 
277  ## Internally cached attribute handling routines
278  def getInternalValueInContext(self, plug, handle, ctx):
279  handledAttribute = False
280 
281  if (plug == hwColorPerVertexShader.aColorGain):
282  handledAttribute = True
283  handle.set3Float( self.mColorGain[0], self.mColorGain[1], self.mColorGain[2] )
284 
285  elif (plug == hwColorPerVertexShader.aColorBias):
286  handledAttribute = True
287  handle.set3Float( self.mColorBias[0], self.mColorBias[1], self.mColorBias[2] )
288 
289  elif (plug == hwColorPerVertexShader.aTranspGain):
290  handledAttribute = True
291  handle.setFloat( self.mTranspGain )
292 
293  elif (plug == hwColorPerVertexShader.aTranspBias):
294  handledAttribute = True
295  handle.setFloat( self.mTranspBias )
296 
297  elif (plug == hwColorPerVertexShader.aNormalsPerVertex):
298  handledAttribute = True
299  handle.setInt( self.mNormalsPerVertex )
300 
301  elif (plug == hwColorPerVertexShader.aColorsPerVertex):
302  handledAttribute = True
303  handle.setInt( self.mColorsPerVertex )
304 
305  elif (plug == hwColorPerVertexShader.aColorSetName):
306  handledAttribute = True
307  handle.setString( self.mColorSetName )
308 
309  elif (plug == hwColorPerVertexShader.aTexRotateX):
310  handledAttribute = True
311  handle.setFloat( self.mTexRotateX )
312 
313  elif (plug == hwColorPerVertexShader.aTexRotateY):
314  handledAttribute = True
315  handle.setFloat( self.mTexRotateY )
316 
317  elif (plug == hwColorPerVertexShader.aTexRotateZ):
318  handledAttribute = True
319  handle.setFloat( self.mTexRotateZ )
320 
321  elif (plug == hwColorPerVertexShader.aDrawBoundingBox):
322  handledAttribute = True
323  handle.setBool( self.mDrawBoundingBox )
324 
325  return handledAttribute
326 
327  def setInternalValueInContext(self, plug, handle, ctx):
328  handledAttribute = False
329 
330  if (plug == hwColorPerVertexShader.aNormalsPerVertex):
331  handledAttribute = True
332  self.mNormalsPerVertex = handle.asInt()
333 
334  elif (plug == hwColorPerVertexShader.aColorsPerVertex):
335  handledAttribute = True
336  self.mColorsPerVertex = handle.asInt()
337 
338  elif (plug == hwColorPerVertexShader.aColorSetName):
339  handledAttribute = True
340  self.mColorSetName = handle.asString()
341 
342  elif (plug == hwColorPerVertexShader.aTexRotateX):
343  handledAttribute = True
344  self.mTexRotateX = handle.asFloat()
345 
346  elif (plug == hwColorPerVertexShader.aTexRotateY):
347  handledAttribute = True
348  self.mTexRotateY = handle.asFloat()
349 
350  elif (plug == hwColorPerVertexShader.aTexRotateZ):
351  handledAttribute = True
352  self.mTexRotateZ = handle.asFloat()
353 
354  elif (plug == hwColorPerVertexShader.aColorGain):
355  handledAttribute = True
356  val = handle.asFloat3()
357  if val != self.mColorGain:
358  self.mColorGain = val
359  self.mAttributesChanged = True
360 
361  elif (plug == hwColorPerVertexShader.aColorBias):
362  handledAttribute = True
363  val = handle.asFloat3()
364  if val != self.mColorBias:
365  self.mColorBias = val
366  self.mAttributesChanged = True
367 
368  elif (plug == hwColorPerVertexShader.aTranspGain):
369  handledAttribute = True
370  val = handle.asFloat()
371  if val != self.mTranspGain:
372  self.mTranspGain = val
373  self.mAttributesChanged = True
374 
375  elif (plug == hwColorPerVertexShader.aTranspBias):
376  handledAttribute = True
377  val = handle.asFloat()
378  if val != self.mTranspBias:
379  self.mTranspBias = val
380  self.mAttributesChanged = True
381 
382  elif (plug == hwColorPerVertexShader.aDrawBoundingBox):
383  handledAttribute = True
384  val = handle.asBool()
385  if val != self.mDrawBoundingBox:
386  self.mDrawBoundingBox = val
387  self.mAttributesChanged = True
388 
389  return handledAttribute
390 
391  def bind(self, request, view):
392  return
393 
394  def unbind(self, request, view):
395  return
396 
397  def geometry(self, request, view, prim, writable, indexCount, indexArray, vertexCount, vertexIDs, vertexArray, normalCount, normalArrays, colorCount, colorArrays, texCoordCount, texCoordArrays):
398 
399  if self.mDrawBoundingBox:
400  points = ((ctypes.c_float * 3)*vertexCount).from_buffer(vertexArray)
401 
402  # Compute the bounding box.
403  box = om.MBoundingBox()
404  for i in range(vertexCount):
405  point = points[i]
406  box.expand( om.MPoint(point[0], point[1], point[2]) )
407 
408  # Draw the bounding box.
409  wireColor = om.MColor( (0.1, 0.15, 0.35) )
410  drawBoundingBox( box, wireColor )
411 
412  ## If we've received a color, that takes priority
413  ##
414  if colorCount > 0 and colorArrays[ colorCount - 1] is not None:
415  indexArray = (ctypes.c_uint).from_buffer(indexArray)
416  vertexArray = (ctypes.c_float * 3).from_buffer(vertexArray)
417  colorArray = (ctypes.c_float * 4).from_buffer(colorArrays[colorCount - 1])
418 
419  glPushAttrib(GL_ALL_ATTRIB_BITS)
420  glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
421  glDisable(GL_LIGHTING)
422 
423  glEnableClientState(GL_COLOR_ARRAY)
424  glColorPointer( 4, GL_FLOAT, 0, colorArray )
425 
426  glEnableClientState(GL_VERTEX_ARRAY)
427  glVertexPointer ( 3, GL_FLOAT, 0, vertexArray )
428  glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray )
429 
430  glDisableClientState(GL_COLOR_ARRAY)
431 
432  glPopClientAttrib()
433  glPopAttrib()
434  return
435 
436  ## If this attribute is enabled, normals, tangents,
437  ## and binormals can be visualized using false coloring.
438  ## Negative values will clamp to black however.
439  if normalCount > self.mNormalsPerVertex:
440  normalCount = self.mNormalsPerVertex
441 
442  if normalCount > 0:
443  indexArray = (ctypes.c_uint).from_buffer(indexArray)
444  vertexArray = (ctypes.c_float * 3).from_buffer(vertexArray)
445  normalArray = (ctypes.c_float * 3).from_buffer(normalArrays[normalCount - 1])
446 
447  glPushAttrib(GL_ALL_ATTRIB_BITS)
448  glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
449  glDisable(GL_LIGHTING)
450 
451  glEnableClientState(GL_COLOR_ARRAY)
452  glColorPointer(3, GL_FLOAT, 0, normalArray )
453 
454  glEnableClientState(GL_VERTEX_ARRAY)
455  glVertexPointer ( 3, GL_FLOAT, 0, vertexArray )
456  glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray )
457 
458  glDisableClientState(GL_COLOR_ARRAY)
459 
460  glPopClientAttrib()
461  glPopAttrib()
462  return
463 
464  self.draw( prim, writable, indexCount, indexArray, vertexCount, vertexArray, colorCount, colorArrays )
465 
466  ## Batch overrides
467  def glBind(self, shapePath):
468  return
469 
470  def glUnbind(self, shapePath):
471  return
472 
473  def glGeometry(self, path, prim, writable, indexCount, indexArray, vertexCount, vertexIDs, vertexArray, normalCount, normalArrays, colorCount, colorArrays, texCoordCount, texCoordArrays):
474  ## If this attribute is enabled, normals, tangents,
475  ## and binormals can be visualized using false coloring.
476  ## Negative values will clamp to black however.
477  if normalCount > self.mNormalsPerVertex:
478  normalCount = self.mNormalsPerVertex
479 
480  if normalCount > 0:
481  indexArray = (ctypes.c_uint).from_buffer(indexArray)
482  vertexArray = (ctypes.c_float * 3).from_buffer(vertexArray)
483  normalArray = (ctypes.c_float * 3).from_buffer(normalArrays[normalCount - 1])
484 
485  glPushAttrib(GL_ALL_ATTRIB_BITS)
486  glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
487  glDisable(GL_LIGHTING)
488  glDisable(GL_BLEND)
489 
490  glEnableClientState(GL_COLOR_ARRAY)
491  glColorPointer(3, GL_FLOAT, 0, normalArray )
492 
493  glEnableClientState(GL_VERTEX_ARRAY)
494  glVertexPointer ( 3, GL_FLOAT, 0, vertexArray )
495  glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray )
496 
497  glDisableClientState(GL_COLOR_ARRAY)
498 
499  glPopClientAttrib()
500  glPopAttrib()
501  return
502 
503  self.draw( prim, writable, indexCount, indexArray, vertexCount, vertexArray, colorCount, colorArrays )
504 
505  ## Overridden to draw an image for swatch rendering.
506  ##
507  def renderSwatchImage(self, outImage):
508  return omr.MRenderUtilities.renderMaterialViewerGeometry("meshSphere",
509  self.thisMObject(),
510  outImage,
511  lightRig=omr.MRenderUtilities.kSwatchLight )
512 
513  def draw(self, prim, writable, indexCount, indexArray, vertexCount, vertexArray, colorCount, colorArrays):
514  ## Should check this value to allow caching
515  ## of color values.
516  self.mAttributesChanged = False
517 
518  ## We assume triangles here.
519  ##
520  if (vertexCount == 0) or not ((prim == GL_TRIANGLES) or (prim == GL_TRIANGLE_STRIP)):
521  raise ValueError("kFailure")
522 
523  indexArray = (ctypes.c_uint).from_buffer(indexArray)
524  vertexArray = (ctypes.c_float * 3).from_buffer(vertexArray)
525 
526  glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT )
527  glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT)
528  glDisable(GL_LIGHTING)
529 
530  glEnableClientState(GL_VERTEX_ARRAY)
531  glVertexPointer ( 3, GL_FLOAT, 0, vertexArray )
532 
533  ## Do "cheesy" multi-pass here for more than one color set
534  ##
535  if colorCount <= 1:
536  glDisable(GL_BLEND)
537  if colorCount > 0 and colorArrays[0] is not None:
538  colorArray = (ctypes.c_float * 4).from_buffer(colorArrays[0])
539  glEnableClientState(GL_COLOR_ARRAY)
540  glColorPointer(4, GL_FLOAT, 0, colorArray)
541  else:
542  glColor4f(1.0, 0.5, 1.0, 1.0)
543  glDrawElements( prim, indexCount, GL_UNSIGNED_INT, indexArray )
544 
545  else:
546  ## Do a 1:1 Blend if we have more than on color set available.
547  glEnable(GL_BLEND)
548  glBlendFunc(GL_ONE, GL_ONE)
549  glEnableClientState(GL_COLOR_ARRAY)
550 
551  for i in range(colorCount):
552  if colorArrays[i] is not None:
553  ## Apply gain and bias
554  ##
555  colorArray = (ctypes.c_float * 4).from_buffer(colorArrays[i])
556 
557  if self.mColorGain != [1.0, 1.0, 1.0] or self.mColorBias != [0.0, 0.0, 0.0] or self.mTranspGain != 1.0 or self.mTranspBias != 0.0:
558  ## This sample code is a CPU bottlneck. It could be
559  ## replaced with a vertex program or color matrix
560  ## operator.
561  ##
562 
563  ## We really want to scale 1-transp.
564  ## T = 1 - ((1-T)*gain + bias) =
565  ## = T * gain + 1 - gain - bias
566  biasT = 1 - self.mTranspGain - self.mTranspBias
567 
568  ## Either make a copy or read directly colors.
569  ##
570  if not (writable & omui.MPxHwShaderNode.kWriteColorArrays):
571  colorArray = []
572  origColors = ((ctypes.c_float * 4)*vertexCount).from_buffer(colorArrays[i])
573  for ii in range(vertexCount):
574  origColor = origColors[ii]
575  colorArray.append( (origColor[0] * self.mColorGain[0]) + self.mColorBias[0] )
576  colorArray.append( (origColor[1] * self.mColorGain[1]) + self.mColorBias[1] )
577  colorArray.append( (origColor[2] * self.mColorGain[2]) + self.mColorBias[2] )
578  colorArray.append( (origColor[3] * self.mTranspGain) + biasT )
579  else:
580  origColors = ((ctypes.c_float * 4)*vertexCount).from_buffer(colorArrays[i])
581  for ii in range(vertexCount):
582  origColor = origColors[ii]
583  origColor[0] = (origColor[0] * self.mColorGain[0]) + self.mColorBias[0]
584  origColor[1] = (origColor[1] * self.mColorGain[1]) + self.mColorBias[1]
585  origColor[2] = (origColor[2] * self.mColorGain[2]) + self.mColorBias[2]
586  origColor[3] = (origColor[3] * self.mTranspGain) + biasT
587 
588  glDisable(GL_BLEND)
589  glEnableClientState(GL_COLOR_ARRAY)
590  glColorPointer(4, GL_FLOAT, 0, colorArray)
591 
592  else:
593  glDisable(GL_BLEND)
594  glDisableClientState(GL_COLOR_ARRAY)
595  glColor4f(1.0, 1.0, 1.0, 1.0)
596 
597  glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray )
598 
599  glDisable(GL_BLEND)
600  glPopClientAttrib()
601  glPopAttrib()
602 
603 
604  ## Tells maya that color per vertex will be needed.
605  def colorsPerVertex(self):
606  ## Going to be displaying false coloring,
607  ## so skip getting internal colors.
608  if self.mNormalsPerVertex:
609  return 0
610 
611  numColors = 0
612 
613  path = self.currentPath()
614  if path.hasFn( om.MFn.kMesh ):
615  fnMesh = om.MFnMesh( path.node() )
616  numColorSets = fnMesh.numColorSets
617  if numColorSets < 2:
618  numColors = numColorSets
619  else:
620  numColors = 2
621 
622  return numColors
623 
624  ## Tells maya that color per vertex will be needed.
625  def hasTransparency(self):
626  return True
627 
628  ## Tells maya that color per vertex will be needed.
629  def normalsPerVertex(self):
630  numNormals = self.mNormalsPerVertex
631 
632  path = self.currentPath()
633  if path.hasFn( om.MFn.kMesh ):
634  fnMesh = om.MFnMesh( path.node() )
635  ## Check the # of uvsets. If no uvsets
636  ## then can't return tangent or binormals
637  ##
638  if fnMesh.numUVSets == 0:
639  ## Put out a warnnig if we're asking for too many uvsets.
640  dispWarn = "Asking for more uvsets then available for shape: "
641  dispWarn += path.fullPathName()
642  om.MGlobal.displayWarning( dispWarn )
643 
644  if self.mNormalsPerVertex:
645  numNormals = 1
646  else:
647  numNormals = 0
648 
649  return numNormals
650 
651  ## Tells maya that texCoords per vertex will be needed.
652  def texCoordsPerVertex(self):
653  return 0
654 
655  def colorSetName(self):
656  return self.mColorSetName
657 
658  def wantDrawBoundingBox(self):
659  return self.mDrawBoundingBox
660 
661 
662 class hwCPVShaderOverride(omr.MPxShaderOverride):
663  ## Current hwColorPerVertexShader node associated with the shader override.
664  ## Updated during doDG() time.
665  fShaderNode = None
666 
667  ## Blend state will be used in the draw
668  fBlendState = None
669 
670  def __init__(self, obj):
671  omr.MPxShaderOverride.__init__(self, obj)
672 
673  def __del__(self):
674  self.fShaderNode = None
675  self.fBlendState = None
676 
677  @staticmethod
678  def creator(obj):
679  return hwCPVShaderOverride(obj)
680 
681  # 1) Initialize phase
682  def initialize(self, shader):
683  # Retrieve and cache the actual node pointer
684  if not shader.isNull():
685  self.fShaderNode = omui.MPxHwShaderNode.getHwShaderNode(shader)
686 
687  # Set position requirement
688  reqName = ""
689  self.addGeometryRequirement( omr.MVertexBufferDescriptor(reqName, omr.MGeometry.kPosition, omr.MGeometry.kFloat, 3) )
690 
691  # Set correct color requirement
692  if self.fShaderNode is not None:
693  reqName = self.fShaderNode.colorSetName()
694  self.addGeometryRequirement( omr.MVertexBufferDescriptor(reqName, omr.MGeometry.kColor, omr.MGeometry.kFloat, 4) )
695 
696  return "Autodesk Maya hwColorPerVertexShader"
697 
698  # 2) Update phase -- Not implemented, nothing to do since we explicitly
699  # rebuild on every update (thus triggering the
700  # initialize method each time).
701 
702  # 3) Draw phase
703  def draw(self, context, renderItemList):
704  ##--------------------------
705  ## Matrix set up
706  ##--------------------------
707  ## Set world view matrix
708  glMatrixMode(GL_MODELVIEW)
709  glPushMatrix()
710  transform = context.getMatrix(omr.MFrameContext.kWorldViewMtx)
711  glLoadMatrixd( matrixAsArray(transform) )
712 
713  ## Set projection matrix
714  glMatrixMode(GL_PROJECTION)
715  glPushMatrix()
716  projection = context.getMatrix(omr.MFrameContext.kProjectionMtx)
717  glLoadMatrixd( matrixAsArray(projection) )
718 
719  ##--------------------------
720  ## State set up
721  ##--------------------------
722  stateMgr = context.getStateManager()
723 
724  ## Blending
725  ## Save current blend status
726  curBlendState = stateMgr.getBlendState()
727  if curBlendState is None:
728  return False
729 
730  if self.fBlendState is None:
731  ## If we haven't got a blending state, then acquire a new one.
732  ## Since it would be expensive to acquire the state, we would
733  ## store it.
734  ## Create a new blend status and acquire blend state from the context
735  blendDesc = omr.MBlendStateDesc()
736  for i in range(omr.MBlendState.kMaxTargets):
737  blendDesc.targetBlends[i].blendEnable = True
738  blendDesc.targetBlends[i].sourceBlend = omr.MBlendState.kSourceAlpha
739  blendDesc.targetBlends[i].destinationBlend = omr.MBlendState.kInvSourceAlpha
740  blendDesc.targetBlends[i].blendOperation = omr.MBlendState.kAdd
741  blendDesc.targetBlends[i].alphaSourceBlend = omr.MBlendState.kOne
742  blendDesc.targetBlends[i].alphaDestinationBlend = omr.MBlendState.kInvSourceAlpha
743  blendDesc.targetBlends[i].alphaBlendOperation = omr.MBlendState.kAdd
744 
745  blendDesc.blendFactor = (1.0, 1.0, 1.0, 1.0)
746 
747  self.fBlendState = stateMgr.acquireBlendState(blendDesc)
748  if self.fBlendState is None:
749  return False
750 
751  ## Activate the blend on the device
752  stateMgr.setBlendState(self.fBlendState)
753 
754  ## Bounding box draw
755  if self.fShaderNode.wantDrawBoundingBox():
756  for renderItem in renderItemList:
757  if renderItem is None:
758  continue
759 
760  ## Modelview matrix is already set so just use the object
761  ## space bbox.
762  box = renderItem.boundingBox(om.MSpace.kObject)
763 
764  ## Draw the bounding box.
765  wireColor = om.MColor( (0.1, 0.15, 0.35) )
766  drawBoundingBox( box, wireColor )
767 
768  ##--------------------------
769  ## geometry draw
770  ##--------------------------
771  useCustomDraw = False
772  if useCustomDraw:
773  ## Custom draw
774  ## Does not set state, matrix or material
775  self.customDraw(context, renderItemList)
776 
777  else:
778  ## Internal standard draw
779  ## Does not set state, matrix or material
780  self.drawGeometry(context)
781 
782 
783  ##--------------------------
784  ## Matrix restore
785  ##--------------------------
786  glPopMatrix()
787  glMatrixMode(GL_MODELVIEW)
788  glPopMatrix()
789 
790 
791  ##--------------------------
792  ## State restore
793  ##--------------------------
794  ## Restore blending
795  stateMgr.setBlendState(curBlendState)
796 
797  return True
798 
799  # draw helper method
800  def customDraw(self, context, renderItemList):
801  glPushClientAttrib ( GL_CLIENT_ALL_ATTRIB_BITS )
802 
803  for renderItem in renderItemList:
804  if renderItem is None:
805  continue
806 
807  geometry = renderItem.geometry()
808  if geometry is not None:
809  hwCPVShaderOverride.customDrawGeometry(context, geometry, renderItem.primitive())
810  continue
811 
812  glPopClientAttrib()
813 
814  return True
815 
816  # draw helper method
817  @staticmethod
818  def customDrawGeometry(context, geometry, indexPrimType):
819  ## Dump out vertex field information for each field
820  ##
821  bufferCount = geometry.vertexBufferCount()
822 
823  boundData = True
824  for i in range(bufferCount):
825  buffer = geometry.vertexBuffer(i)
826  if buffer is None:
827  boundData = False
828  break
829 
830  desc = buffer.descriptor()
831  dataHandle = buffer.resourceHandle()
832  if dataHandle == 0:
833  boundData = False
834  break
835 
836  dataBufferId = (ctypes.c_uint).from_address(dataHandle)
837  fieldOffset = ctypes.c_void_p(desc.offset)
838  fieldStride = desc.stride
839 
840  ## Bind each data buffer
841  glBindBuffer(GL_ARRAY_BUFFER, dataBufferId)
842  currentError = glGetError()
843  if currentError != GL_NO_ERROR:
844  boundData = False
845 
846  if boundData:
847  ## Set the data pointers
848  if desc.semantic == omr.MGeometry.kPosition:
849  glEnableClientState(GL_VERTEX_ARRAY)
850  glVertexPointer(3, GL_FLOAT, fieldStride*4, fieldOffset)
851  currentError = glGetError()
852  if currentError != GL_NO_ERROR:
853  boundData = False
854  elif desc.semantic == omr.MGeometry.kColor:
855  glEnableClientState(GL_COLOR_ARRAY)
856  glColorPointer(4, GL_FLOAT, fieldStride*4, fieldOffset)
857  currentError = glGetError()
858  if currentError != GL_NO_ERROR:
859  boundData = False
860 
861  if not boundData:
862  break
863 
864  if boundData and geometry.indexBufferCount() > 0:
865  ## Dump out indexing information
866  ##
867  buffer = geometry.indexBuffer(0)
868  indexHandle = buffer.resourceHandle()
869  indexBufferCount = 0
870  indexBufferId = 0
871  if indexHandle != 0:
872  indexBufferId = (ctypes.c_uint).from_address(indexHandle)
873  indexBufferCount = ctypes.c_int(buffer.size())
874 
875  ## Bind the index buffer
876  if indexBufferId and indexBufferId > 0:
877  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId)
878  currentError = glGetError()
879  if currentError == GL_NO_ERROR:
880  indexPrimTypeGL = GL_TRIANGLES
881  if indexPrimType == omr.MGeometry.kPoints:
882  indexPrimTypeGL = GL_POINTS
883  elif indexPrimType == omr.MGeometry.kLines:
884  indexPrimTypeGL = GL_LINES
885  elif indexPrimType == omr.MGeometry.kLineStrip:
886  indexPrimTypeGL = GL_LINE_STRIP
887  elif indexPrimType == omr.MGeometry.kTriangles:
888  indexPrimTypeGL = GL_TRIANGLES
889  elif indexPrimType == omr.MGeometry.kTriangleStrip:
890  indexPrimTypeGL = GL_TRIANGLE_STRIP
891  else:
892  boundData = false
893 
894  if boundData:
895  ## Draw the geometry
896  indexType = GL_UNSIGNED_SHORT
897  if buffer.dataType() == omr.MGeometry.kUnsignedInt32:
898  indexType = GL_UNSIGNED_INT
899  glDrawElements(indexPrimTypeGL, indexBufferCount, indexType, ctypes.c_void_p(0))
900 
901 
902  def rebuildAlways(self):
903  # Since color set name changing needs to add a new
904  # named requirement to the geometry, so we should
905  # return True here to trigger the geometry rebuild.
906  return True
907 
908  def supportedDrawAPIs(self):
909  return omr.MRenderer.kOpenGL
910 
911  def isTransparent(self):
912  return True
913 
914 
915 ## The initializePlugin method is called by Maya when the
916 ## plugin is loaded. It registers the hwColorPerVertexShader node
917 ## which provides Maya with the creator and initialize methods to be
918 ## called when a hwColorPerVertexShader is created.
919 ##
920 sHWCPVShaderRegistrantId = "HWCPVShaderRegistrantId"
921 
922 def initializePlugin(obj):
923  plugin = om.MFnPlugin(obj, "Autodesk", "4.5", "Any")
924  try:
925  swatchName = omui.MHWShaderSwatchGenerator.initialize()
926  userClassify = "shader/surface/utility/:drawdb/shader/surface/hwColorPerVertexShader:swatch/" + swatchName
927  plugin.registerNode("hwColorPerVertexShader", hwColorPerVertexShader.id, hwColorPerVertexShader.creator, hwColorPerVertexShader.initialize, om.MPxNode.kHwShaderNode, userClassify)
928  except:
929  sys.stderr.write("Failed to register node\n")
930  raise
931 
932  try:
933  global sHWCPVShaderRegistrantId
934  omr.MDrawRegistry.registerShaderOverrideCreator("drawdb/shader/surface/hwColorPerVertexShader", sHWCPVShaderRegistrantId, hwCPVShaderOverride.creator)
935  except:
936  sys.stderr.write("Failed to register override\n")
937  raise
938 
939 
940 ## The unitializePlugin is called when Maya needs to unload the plugin.
941 ## It basically does the opposite of initialize by calling
942 ## the deregisterNode to remove it.
943 ##
944 def uninitializePlugin(obj):
945  plugin = om.MFnPlugin(obj)
946  try:
947  plugin.deregisterNode(hwColorPerVertexShader.id)
948  except:
949  sys.stderr.write("Failed to deregister node\n")
950  raise
951 
952  try:
953  global sHWCPVShaderRegistrantId
954  omr.MDrawRegistry.deregisterShaderOverrideCreator("drawdb/shader/surface/hwColorPerVertexShader", sHWCPVShaderRegistrantId)
955  except:
956  sys.stderr.write("Failed to deregister override\n")
957  raise
958