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