scripted/pyFootPrintNode_GeometryOverride.py

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