Python API 2.0 Reference
python/api2/py2GeometryReplicator.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 import sys
12 import maya.api.OpenMaya as om
13 import maya.api.OpenMayaUI as omui
14 import maya.api.OpenMayaRender as omr
15 
16 def maya_useNewAPI():
17  """
18  The presence of this function tells Maya that the plugin produces, and
19  expects to be passed, objects created using the Maya Python API 2.0.
20  """
21  pass
22 
23 
24 class geometryReplicator(om.MPxSurfaceShape):
25  id = om.MTypeId(0x00080069)
26  drawDbClassification = "drawdb/geometry/geometryReplicator_py"
27  drawRegistrantId = "geometryReplicatorPlugin_py"
28 
29  # attributes
30  aShowCPV = None
31  aBaseMesh = None
32 
33  def __init__(self):
34  om.MPxSurfaceShape.__init__(self)
35 
36  @staticmethod
37  def creator():
38  return geometryReplicator()
39 
40  @staticmethod
41  def initialize():
42  nAttr = om.MFnNumericAttribute()
43 
44  geometryReplicator.aShowCPV = nAttr.create("showCPV", "sc", om.MFnNumericData.kBoolean, 0)
45  om.MPxNode.addAttribute(geometryReplicator.aShowCPV)
46 
47  geometryReplicator.aBaseMesh = nAttr.create("isBaseMesh", "bm", om.MFnNumericData.kBoolean, 0)
48  om.MPxNode.addAttribute(geometryReplicator.aBaseMesh)
49 
50  def postConstructor(self):
51  self.isRenderable = True
52 
53  def isBounded(self):
54  return True
55 
56  def boundingBox(self):
57  corner1 = om.MPoint( -0.5, 0.0, -0.5 )
58  corner2 = om.MPoint( 0.5, 0.0, 0.5 )
59 
60  return om.MBoundingBox( corner1, corner2 )
61 
62 ###############################################################################
63 ##
64 ## geometryReplicatorGeometryOverride
65 ##
66 ## Handles vertex data preparation for drawing the user defined shape in
67 ## Viewport 2.0.
68 ##
69 ###############################################################################
70 class geometryReplicatorGeometryOverride(omr.MPxGeometryOverride):
71  def __init__(self, obj):
72  omr.MPxGeometryOverride.__init__(self, obj)
73  self.fThisNode = om.MObject(obj)
74  self.fType = om.MFn.kInvalid # the type of the associated object.
75  self.fPath = om.MDagPath() # the associated object path.
76 
77  @staticmethod
78  def creator(obj):
79  return geometryReplicatorGeometryOverride(obj)
80 
81  def supportedDrawAPIs(self):
82  # this plugin supports both GL and DX
83  return (omr.MRenderer.kOpenGL | omr.MRenderer.kDirectX11)
84 
85  def isCPVShown(self):
86  res = False
87  plug = om.MPlug(self.fThisNode, geometryReplicator.aShowCPV)
88  if not plug.isNull:
89  res = plug.asBool()
90  return res
91 
92  def isBaseMesh(self):
93  res = False
94  plug = om.MPlug(self.fThisNode, geometryReplicator.aBaseMesh)
95  if not plug.isNull:
96  res = plug.asBool()
97  return res
98 
99  def requiresUnsharedGeometricStreams(self, requirements):
100  # Only when vertexid, faceid or localuvcoord is required, we need to extract
101  # unshared geometric streams. To test how these streams are extracted, the
102  # shape can be assigned with hwPhongShader after setting the env vars:
103  # - MAYA_HWPHONG_TEST_VERTEXID_AND_FACEID=1
104  # - MAYA_HWPHONG_TEST_LOCALUVCOORD=1
105  # - MAYA_HWPHONG_TEST_INDEXING=1
106  descList = requirements.vertexRequirements()
107  for desc in descList:
108  if desc.semantic == omr.MGeometry.kTexture and desc.semanticName in ["vertexid", "faceid", "localuvcoord"]:
109  return True
110  return False
111 
112  def updateDG(self):
113  if not self.fPath.isValid():
114  fnThisNode = om.MFnDependencyNode(self.fThisNode)
115 
116  messageAttr = fnThisNode.attribute("message")
117  messagePlug = om.MPlug(self.fThisNode, messageAttr)
118 
119  connections = messagePlug.connectedTo(False, True)
120  for plug in connections:
121  node = plug.node()
122  if node.hasFn(om.MFn.kMesh) or node.hasFn(om.MFn.kNurbsSurface) or node.hasFn(om.MFn.kNurbsCurve) or node.hasFn(om.MFn.kBezierCurve):
123  path = om.MDagPath.getAPathTo(node)
124  self.fPath = path
125  self.fType = path.apiType()
126  break
127 
128  def updateRenderItems(self, path, list):
129  if not self.fPath.isValid():
130  return
131  shaderManager = omr.MRenderer.getShaderManager()
132  if shaderManager is None:
133  return
134 
135  if self.fType == om.MFn.kNurbsCurve or self.fType == om.MFn.kBezierCurve:
136  ## add render items for drawing curve
137  curveItem = None
138  index = list.indexOf("geometryReplicatorCurve")
139  if index < 0:
140  curveItem = omr.MRenderItem.create("geometryReplicatorCurve", omr.MRenderItem.NonMaterialSceneItem, omr.MGeometry.kLines)
141  curveItem.setDrawMode(omr.MGeometry.kAll)
142  list.append(curveItem)
143 
144  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
145  if shader is not None:
146  theColor = [1.0, 0.0, 0.0, 1.0]
147  shader.setParameter("solidColor", theColor)
148 
149  curveItem.setShader(shader)
150  shaderManager.releaseShader(shader)
151 
152  else:
153  curveItem = list[index]
154 
155  if curveItem is not None:
156  curveItem.enable(True)
157 
158  elif self.fType == om.MFn.kMesh:
159  ## add render item for drawing wireframe on the mesh
160  wireframeItem = None
161  index = list.indexOf("geometryReplicatorWireframe")
162  if index < 0:
163  wireframeItem = omr.MRenderItem.create("geometryReplicatorWireframe", omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
164  wireframeItem.setDrawMode(omr.MGeometry.kWireframe)
165  wireframeItem.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
166  list.append(wireframeItem)
167 
168  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
169  if shader is not None:
170  theColor = [1.0, 0.0, 0.0, 1.0]
171  shader.setParameter("solidColor", theColor)
172 
173  wireframeItem.setShader(shader)
174  shaderManager.releaseShader(shader)
175 
176  else:
177  wireframeItem = list[index]
178 
179  if wireframeItem is not None:
180  wireframeItem.enable(True)
181 
182  ## disable StandardShadedItem if CPV is shown.
183  showCPV = self.isCPVShown()
184  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kShaded)
185  if index >= 0:
186  shadedItem = list[index]
187  if shadedItem is not None:
188  shadedItem.enable(not showCPV)
189 
190  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kTextured)
191  if index >= 0:
192  shadedItem = list[index]
193  if shadedItem is not None:
194  shadedItem.enable(not showCPV)
195 
196  ## add item for CPV.
197  index = list.indexOf("geometryReplicatorCPV")
198  if index >= 0:
199  cpvItem = list[index]
200  if cpvItem is not None:
201  cpvItem.enable(showCPV)
202 
203  else:
204  ## if no cpv item and showCPV is true, created the cpv item.
205  if showCPV:
206  cpvItem = omr.MRenderItem.create("geometryReplicatorCPV", omr.MRenderItem.MaterialSceneItem, omr.MGeometry.kTriangles)
207  cpvItem.setDrawMode(omr.MGeometry.kShaded | omr.MGeometry.kTextured)
208  list.append(cpvItem)
209 
210  shader = shaderManager.getStockShader(omr.MShaderManager.k3dCPVSolidShader)
211  if shader is not None:
212  cpvItem.setShader(shader)
213  cpvItem.enable(True)
214  shaderManager.releaseShader(shader)
215 
216  def populateGeometry(self, requirements, renderItems, data):
217  if not self.fPath.isValid():
218  return
219 
220  ## here, fPath is the path of the linked object instead of the plugin node; it
221  ## is used to determine the right type of the geometry shape, e.g., polygon
222  ## or NURBS surface.
223  ## The sharing flag is just for the polygon shape.
224  options = omr.MGeometryExtractor.kPolyGeom_Normal
225  if self.isBaseMesh():
226  options = options | omr.MGeometryExtractor.kPolyGeom_BaseMesh
227  if self.requiresUnsharedGeometricStreams(requirements):
228  options = options | omr.MGeometryExtractor.kPolyGeom_NotSharing
229 
230  extractor = omr.MGeometryExtractor(requirements, self.fPath, options)
231  if extractor is None:
232  return
233 
234  ## fill vertex buffer
235  descList = requirements.vertexRequirements()
236  for desc in descList:
237 
238  if desc.semantic == omr.MGeometry.kPosition or desc.semantic == omr.MGeometry.kPosition or desc.semantic == omr.MGeometry.kNormal or desc.semantic == omr.MGeometry.kTexture or desc.semantic == omr.MGeometry.kTangent or desc.semantic == omr.MGeometry.kBitangent or desc.semantic == omr.MGeometry.kColor:
239  vertexBuffer = data.createVertexBuffer(desc)
240  if vertexBuffer is not None:
241  ## MGeometryExtractor.vertexCount and MGeometryExtractor.populateVertexBuffer.
242  ## since the plugin node has the same vertex data as its linked scene object,
243  ## call vertexCount to allocate vertex buffer of the same size, and then call
244  ## populateVertexBuffer to copy the data.
245  vertexCount = extractor.vertexCount()
246  vertices = vertexBuffer.acquire(vertexCount, True) ## writeOnly - we don't need the current buffer values
247  if vertices is not None:
248  extractor.populateVertexBuffer(vertices, vertexCount, desc)
249  vertexBuffer.commit(vertices)
250 
251  ## fill index buffer
252  for item in renderItems:
253  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
254  if indexBuffer is None:
255  continue
256 
257  ## MGeometryExtractor.primitiveCount and MGeometryExtractor.populateIndexBuffer.
258  ## since the plugin node has the same index data as its linked scene object,
259  ## call primitiveCount to allocate index buffer of the same size, and then call
260  ## populateIndexBuffer to copy the data.
261  if item.primitive() == omr.MGeometry.kTriangles:
262  triangleDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kTriangle, "", omr.MGeometry.kTriangles, 3)
263  numTriangles = extractor.primitiveCount(triangleDesc)
264 
265  indices = indexBuffer.acquire(3 * numTriangles, True) ## writeOnly - we don't need the current buffer values
266  extractor.populateIndexBuffer(indices, numTriangles, triangleDesc)
267  indexBuffer.commit(indices)
268 
269  elif item.primitive() == omr.MGeometry.kLines:
270  edgeDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kEdgeLine, "", omr.MGeometry.kLines, 2)
271  numEdges = extractor.primitiveCount(edgeDesc)
272 
273  indices = indexBuffer.acquire(2 * numEdges, True) ## writeOnly - we don't need the current buffer values
274  extractor.populateIndexBuffer(indices, numEdges, edgeDesc)
275  indexBuffer.commit(indices)
276 
277  item.associateWithIndexBuffer(indexBuffer)
278 
279  def cleanUp(self):
280  # no-op
281  pass
282 
283 
284 ##---------------------------------------------------------------------------
285 ##---------------------------------------------------------------------------
286 ## Plugin Registration
287 ##---------------------------------------------------------------------------
288 ##---------------------------------------------------------------------------
289 
290 def initializePlugin(obj):
291  plugin = om.MFnPlugin(obj, "Autodesk", "3.0", "Any")
292  try:
293  plugin.registerShape("geometryReplicator_py", geometryReplicator.id, geometryReplicator.creator, geometryReplicator.initialize, geometryReplicator.drawDbClassification)
294  except:
295  sys.stderr.write("Failed to register shape\n")
296  raise
297 
298  try:
299  omr.MDrawRegistry.registerGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId, geometryReplicatorGeometryOverride.creator)
300  except:
301  sys.stderr.write("Failed to register override\n")
302  raise
303 
304 def uninitializePlugin(obj):
305  plugin = om.MFnPlugin(obj)
306  try:
307  omr.MDrawRegistry.deregisterGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId)
308  except:
309  sys.stderr.write("Failed to deregister override\n")
310  raise
311 
312  try:
313  plugin.deregisterNode(geometryReplicator.id)
314  except:
315  sys.stderr.write("Failed to deregister shape\n")
316  raise
317