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