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