Python API 2.0 Reference
python/api2/py2FootPrintNode_GeometryOverride_AnimatedMaterial.py
1 
2 ########################################################################
3 # DESCRIPTION:
4 #
5 # This plug-in demonstrates how to draw a simple mesh like foot Print in an efficient way.
6 #
7 # This efficient way is supported in Viewport 2.0.
8 #
9 # For comparison, you can also reference a devkit sample named footPrintNode. (See: pyFootPrintNode.py)
10 # In that sample, we draw the footPrint using the MUIDrawManager primitives in footPrintDrawOverride::addUIDrawables()
11 #
12 # For comparison, you can also reference another devkit sample named rawfootPrintNode.
13 # In that sample, we draw the footPrint with OpenGL\DX in method rawFootPrintDrawOverride::draw().
14 #
15 # Note that the method:
16 # footPrint.draw()
17 # is only called in legacy default viewport to draw the foot print.
18 # while the methods:
19 # footPrintGeometryOverrideAnimatedMaterial.updateDG()
20 # footPrintGeometryOverrideAnimatedMaterial.updateRenderItems()
21 # footPrintGeometryOverrideAnimatedMaterial.populateGeometry()
22 # are only called in Viewport 2.0 to prepare and draw the foot print.
23 #
24 # Example usage is to load the plug-in and create the node using
25 # the createNode command:
26 #
27 # import maya.cmds as cmds
28 # cmds.loadPlugin('footPrintNode_GeometryOverride_AnimatedMaterial')
29 # cmds.createNode('footPrint_GeometryOverride_AnimatedMaterial')
30 #
31 #########################################################################
32 from builtins import range
33 import sys
34 import ctypes
35 import maya.api.OpenMaya as om
36 import maya.api.OpenMayaUI as omui
37 import maya.api.OpenMayaAnim as oma
38 import maya.api.OpenMayaRender as omr
39 import maya.mel as mel
40 
41 def maya_useNewAPI():
42  """
43  The presence of this function tells Maya that the plugin produces, and
44  expects to be passed, objects created using the Maya Python API 2.0.
45  """
46  pass
47 
48 def matrixAsArray(matrix):
49  array = []
50  for i in range(16):
51  array.append(matrix[i])
52  return array
53 
54 ## Foot Data
55 ##
56 sole = [ [ 0.00, 0.0, -0.70 ],
57  [ 0.04, 0.0, -0.69 ],
58  [ 0.09, 0.0, -0.65 ],
59  [ 0.13, 0.0, -0.61 ],
60  [ 0.16, 0.0, -0.54 ],
61  [ 0.17, 0.0, -0.46 ],
62  [ 0.17, 0.0, -0.35 ],
63  [ 0.16, 0.0, -0.25 ],
64  [ 0.15, 0.0, -0.14 ],
65  [ 0.13, 0.0, 0.00 ],
66  [ 0.00, 0.0, 0.00 ],
67  [ -0.13, 0.0, 0.00 ],
68  [ -0.15, 0.0, -0.14 ],
69  [ -0.16, 0.0, -0.25 ],
70  [ -0.17, 0.0, -0.35 ],
71  [ -0.17, 0.0, -0.46 ],
72  [ -0.16, 0.0, -0.54 ],
73  [ -0.13, 0.0, -0.61 ],
74  [ -0.09, 0.0, -0.65 ],
75  [ -0.04, 0.0, -0.69 ],
76  [ -0.00, 0.0, -0.70 ] ]
77 heel = [ [ 0.00, 0.0, 0.06 ],
78  [ 0.13, 0.0, 0.06 ],
79  [ 0.14, 0.0, 0.15 ],
80  [ 0.14, 0.0, 0.21 ],
81  [ 0.13, 0.0, 0.25 ],
82  [ 0.11, 0.0, 0.28 ],
83  [ 0.09, 0.0, 0.29 ],
84  [ 0.04, 0.0, 0.30 ],
85  [ 0.00, 0.0, 0.30 ],
86  [ -0.04, 0.0, 0.30 ],
87  [ -0.09, 0.0, 0.29 ],
88  [ -0.11, 0.0, 0.28 ],
89  [ -0.13, 0.0, 0.25 ],
90  [ -0.14, 0.0, 0.21 ],
91  [ -0.14, 0.0, 0.15 ],
92  [ -0.13, 0.0, 0.06 ],
93  [ -0.00, 0.0, 0.06 ] ]
94 soleCount = 21
95 heelCount = 17
96 
97 ## Maintain a mini cache for 3d solid shaders in order to reuse the shader
98 ## instance whenever possible. This can allow Viewport 2.0 optimization e.g.
99 ## the GPU instancing system and the consolidation system to be leveraged.
100 ##
101 the3dSolidShaders = {}
102 
103 def get3dSolidShader(color):
104  # Return the shader instance if exists.
105  key = str(color)
106  if key in the3dSolidShaders:
107  return the3dSolidShaders[key]
108 
109  shader = None
110 
111  shaderMgr = omr.MRenderer.getShaderManager()
112  if shaderMgr:
113  shader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
114 
115  if shader:
116  shader.setParameter(footPrintGeometryOverrideAnimatedMaterial.colorParameterName_, color)
117  the3dSolidShaders[key] = shader
118 
119  return shader
120 
121 def releaseShaders():
122  shaderMgr = omr.MRenderer.getShaderManager()
123  if shaderMgr:
124  for key, shader in list(the3dSolidShaders.items()):
125  shaderMgr.releaseShader(shader)
126 
127  the3dSolidShaders.clear()
128 
129 
130 #############################################################################
131 ##
132 ## Node implementation with standard viewport draw
133 ##
134 #############################################################################
135 class footPrint(omui.MPxLocatorNode):
136  id = om.MTypeId( 0x0008006A )
137  drawDbClassification = "drawdb/geometry/footPrint_GeometryOverride_AnimatedMaterial_py"
138  drawRegistrantId = "footPrintNode_GeometryOverride_AnimatedMaterialPlugin_py"
139 
140  size = None ## The size of the foot
141 
142  @staticmethod
143  def creator():
144  return footPrint()
145 
146  @staticmethod
147  def initialize():
148  unitFn = om.MFnUnitAttribute()
149 
150  footPrint.size = unitFn.create( "size", "sz", om.MFnUnitAttribute.kDistance )
151  unitFn.default = om.MDistance(1.0)
152 
153  om.MPxNode.addAttribute( footPrint.size )
154 
155  def __init__(self):
156  omui.MPxLocatorNode.__init__(self)
157 
158  def compute(self, plug, data):
159  return None
160 
161  def draw(self, view, path, style, status):
162  ## Get the size
163  ##
164  thisNode = self.thisMObject()
165  plug = om.MPlug( thisNode, footPrint.size )
166  sizeVal = plug.asMDistance()
167  multiplier = sizeVal.asCentimeters()
168 
169  global sole, soleCount
170  global heel, heelCount
171 
172  view.beginGL()
173 
174  ## drawing in VP1 views will be done using V1 Python APIs:
175  import maya.OpenMayaRender as v1omr
176  glRenderer = v1omr.MHardwareRenderer.theRenderer()
177  glFT = glRenderer.glFunctionTable()
178 
179  if ( style == omui.M3dView.kFlatShaded ) or ( style == omui.M3dView.kGouraudShaded ):
180  ## Push the color settings
181  ##
182  glFT.glPushAttrib( v1omr.MGL_CURRENT_BIT )
183 
184  # Show both faces
185  glFT.glDisable( v1omr.MGL_CULL_FACE )
186 
187  if status == omui.M3dView.kActive:
188  view.setDrawColor( 13, omui.M3dView.kActiveColors )
189  else:
190  view.setDrawColor( 13, omui.M3dView.kDormantColors )
191 
192  glFT.glBegin( v1omr.MGL_TRIANGLE_FAN )
193  for i in range(soleCount-1):
194  glFT.glVertex3f( sole[i][0] * multiplier, sole[i][1] * multiplier, sole[i][2] * multiplier )
195  glFT.glEnd()
196 
197  glFT.glBegin( v1omr.MGL_TRIANGLE_FAN )
198  for i in range(heelCount-1):
199  glFT.glVertex3f( heel[i][0] * multiplier, heel[i][1] * multiplier, heel[i][2] * multiplier )
200  glFT.glEnd()
201 
202  glFT.glPopAttrib()
203 
204  ## Draw the outline of the foot
205  ##
206  glFT.glBegin( v1omr.MGL_LINES )
207  for i in range(soleCount-1):
208  glFT.glVertex3f( sole[i][0] * multiplier, sole[i][1] * multiplier, sole[i][2] * multiplier )
209  glFT.glVertex3f( sole[i+1][0] * multiplier, sole[i+1][1] * multiplier, sole[i+1][2] * multiplier )
210 
211  for i in range(heelCount-1):
212  glFT.glVertex3f( heel[i][0] * multiplier, heel[i][1] * multiplier, heel[i][2] * multiplier )
213  glFT.glVertex3f( heel[i+1][0] * multiplier, heel[i+1][1] * multiplier, heel[i+1][2] * multiplier )
214  glFT.glEnd()
215 
216  view.endGL()
217 
218  def isBounded(self):
219  return True
220 
221  def boundingBox(self):
222  ## Get the size
223  ##
224  thisNode = self.thisMObject()
225  plug = om.MPlug( thisNode, footPrint.size )
226  sizeVal = plug.asMDistance()
227  multiplier = sizeVal.asCentimeters()
228 
229  corner1 = om.MPoint( -0.17, 0.0, -0.7 )
230  corner2 = om.MPoint( 0.17, 0.0, 0.3 )
231 
232  corner1 *= multiplier
233  corner2 *= multiplier
234 
235  return om.MBoundingBox( corner1, corner2 )
236 
237  def getShapeSelectionMask(self):
238  selType = om.MSelectionMask("footPrintSelection")
239  return om.MSelectionMask( selType )
240 
241 #############################################################################
242 ##
243 ## Viewport 2.0 override implementation
244 ##
245 #############################################################################
246 
247 class footPrintGeometryOverrideAnimatedMaterial(omr.MPxGeometryOverride):
248  colorParameterName_ = "solidColor"
249  wireframeItemName_ = "footPrintLocatorWires"
250  shadedItemName_ = "footPrintLocatorTriangles"
251 
252  @staticmethod
253  def creator(obj):
254  return footPrintGeometryOverrideAnimatedMaterial(obj)
255 
256  def __init__(self, obj):
257  omr.MPxGeometryOverride.__init__(self, obj)
258  self.mLocatorNode = obj
259  self.mMultiplier = 0.0
260  self.mMultiplierChanged = True
261 
262  def __del__(self):
263  pass
264 
265  def supportedDrawAPIs(self):
266  # this plugin supports all modes
267  return omr.MRenderer.kOpenGL | omr.MRenderer.kOpenGLCoreProfile | omr.MRenderer.kDirectX11
268 
269  def hasUIDrawables(self):
270  return False
271 
272  def updateDG(self):
273  plug = om.MPlug(self.mLocatorNode, footPrint.size)
274  newScale = 1.0
275  if not plug.isNull:
276  sizeVal = plug.asMDistance()
277  newScale = sizeVal.asCentimeters()
278 
279  if newScale != self.mMultiplier:
280  self.mMultiplier = newScale
281  self.mMultiplierChanged = True
282 
283  def cleanUp(self):
284  pass
285 
286  def isIndexingDirty(self, item):
287  return False
288 
289  def isStreamDirty(self, desc):
290  return self.mMultiplierChanged
291 
292  def updateRenderItems(self, dagPath, renderList ):
293  shader = get3dSolidShader(omr.MGeometryUtilities.wireframeColor(dagPath))
294  if not shader:
295  return
296 
297  fullItemList = (
298  (footPrintGeometryOverrideAnimatedMaterial.wireframeItemName_, omr.MGeometry.kLines, omr.MGeometry.kWireframe),
299  (footPrintGeometryOverrideAnimatedMaterial.shadedItemName_, omr.MGeometry.kTriangles, omr.MGeometry.kShaded)
300  )
301 
302  for itemName, geometryType, drawMode in fullItemList:
303  renderItem = None
304  index = renderList.indexOf(itemName)
305  if index < 0:
306  renderItem = omr.MRenderItem.create(
307  itemName,
308  omr.MRenderItem.DecorationItem,
309  geometryType)
310  renderItem.setDrawMode(drawMode)
311  renderItem.setDepthPriority(5)
312 
313  renderList.append(renderItem)
314  else:
315  renderItem = renderList[index]
316 
317  if renderItem:
318  renderItem.setShader(shader)
319  renderItem.enable(True)
320 
321  def populateGeometry(self, requirements, renderItems, data):
322  vertexBufferDescriptorList = requirements.vertexRequirements()
323 
324  for vertexBufferDescriptor in vertexBufferDescriptorList:
325  if vertexBufferDescriptor.semantic == omr.MGeometry.kPosition:
326  verticesCount = soleCount+heelCount
327  verticesBuffer = data.createVertexBuffer(vertexBufferDescriptor)
328  verticesPositionDataAddress = verticesBuffer.acquire(verticesCount, True)
329  verticesPositionData = ((ctypes.c_float * 3)*verticesCount).from_address(verticesPositionDataAddress)
330 
331  verticesPointerOffset = 0
332 
333  # We concatenate the heel and sole positions into a single vertex buffer.
334  # The index buffers will decide which positions will be selected for each render items.
335  for vtxList in (heel, sole):
336  for vtx in vtxList:
337  verticesPositionData[verticesPointerOffset][0] = vtx[0] * self.mMultiplier
338  verticesPositionData[verticesPointerOffset][1] = vtx[1] * self.mMultiplier
339  verticesPositionData[verticesPointerOffset][2] = vtx[2] * self.mMultiplier
340  verticesPointerOffset += 1
341 
342  verticesBuffer.commit(verticesPositionDataAddress)
343 
344  break
345 
346  for item in renderItems:
347  if not item:
348  continue
349 
350  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
351 
352  if item.name() == footPrintGeometryOverrideAnimatedMaterial.wireframeItemName_:
353 
354  primitiveIndex = 0
355  startIndex = 0
356  numPrimitive = heelCount + soleCount - 2
357  numIndex = numPrimitive * 2
358 
359  indicesAddress = indexBuffer.acquire(numIndex, True)
360  indices = (ctypes.c_uint * numIndex).from_address(indicesAddress)
361 
362  i = 0
363 
364  while i < numIndex:
365 
366  if i < (heelCount - 1) * 2:
367  startIndex = 0
368  primitiveIndex = i // 2
369  else:
370  startIndex = heelCount
371  primitiveIndex = i // 2 - heelCount + 1
372 
373  indices[i] = startIndex + primitiveIndex
374  indices[i+1] = startIndex + primitiveIndex + 1
375 
376  i += 2
377 
378  indexBuffer.commit(indicesAddress)
379 
380  elif item.name() == footPrintGeometryOverrideAnimatedMaterial.shadedItemName_:
381  primitiveIndex = 0
382  startIndex = 0
383  numPrimitive = heelCount + soleCount - 4
384  numIndex = numPrimitive * 3
385 
386  indicesAddress = indexBuffer.acquire(numIndex, True)
387  indices = (ctypes.c_uint * numIndex).from_address(indicesAddress)
388 
389  i = 0
390 
391  while i < numIndex:
392 
393  if i < (heelCount - 2) * 3:
394  startIndex = 0
395  primitiveIndex = i // 3
396  else:
397  startIndex = heelCount
398  primitiveIndex = i // 3 - heelCount + 2
399 
400  indices[i] = startIndex
401  indices[i+1] = startIndex + primitiveIndex + 1
402  indices[i+2] = startIndex + primitiveIndex + 2
403 
404  i += 3
405 
406  indexBuffer.commit(indicesAddress)
407 
408  item.associateWithIndexBuffer(indexBuffer)
409 
410  mMultiplierChanged = False
411 
412 def initializePlugin(obj):
413  plugin = om.MFnPlugin(obj, "Autodesk", "3.0", "Any")
414 
415  try:
416  plugin.registerNode("py2FootPrint_GeometryOverride_AnimatedMaterial", footPrint.id, footPrint.creator, footPrint.initialize, om.MPxNode.kLocatorNode, footPrint.drawDbClassification)
417  except:
418  sys.stderr.write("Failed to register node\n")
419  raise
420 
421  try:
422  omr.MDrawRegistry.registerGeometryOverrideCreator(footPrint.drawDbClassification, footPrint.drawRegistrantId, footPrintGeometryOverrideAnimatedMaterial.creator)
423  except:
424  sys.stderr.write("Failed to register override\n")
425  raise
426 
427  try:
428  om.MSelectionMask.registerSelectionType("footPrintSelection")
429  mel.eval("selectType -byName \"footPrintSelection\" 1")
430  except:
431  sys.stderr.write("Failed to register selection mask\n")
432  raise
433 
434 def uninitializePlugin(obj):
435  plugin = om.MFnPlugin(obj)
436 
437  try:
438  plugin.deregisterNode(footPrint.id)
439  except:
440  sys.stderr.write("Failed to deregister node\n")
441  pass
442 
443  try:
444  omr.MDrawRegistry.deregisterGeometryOverrideCreator(footPrint.drawDbClassification, footPrint.drawRegistrantId)
445  except:
446  sys.stderr.write("Failed to deregister override\n")
447  pass
448 
449  try:
450  om.MSelectionMask.deregisterSelectionType("footPrintSelection")
451  except:
452  sys.stderr.write("Failed to deregister selection mask\n")
453  pass
454 
455  try:
456  releaseShaders()
457  except:
458  sys.stderr.write("Failed to release shaders\n")
459  pass
460 
461 # =======================================================================
462 # Copyright 2020 Autodesk, Inc. All rights reserved.
463 #
464 # This computer source code and related instructions and comments are the
465 # unpublished confidential and proprietary information of Autodesk, Inc.
466 # and are protected under applicable copyright and trade secret law. They
467 # may not be disclosed to, copied or used by any third party without the
468 # prior written consent of Autodesk, Inc.
469 # =======================================================================