scripted/pyGeometryReplicator.py

scripted/pyGeometryReplicator.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(0x00080029)
26  drawDbClassification = "drawdb/geometry/geometryReplicator"
27  drawRegistrantId = "geometryReplicatorPlugin"
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 ##
65 ## geometryReplicatorShapeUI
66 ##
67 ## There's no need to draw and select this node in vp1.0, so this class
68 ## doesn't override draw(), select(), etc. But the creator() is needed for
69 ## plugin registration and avoid crash in cases (e.g., RB pop up menu of this node).
70 ##
71 ###############################################################################
72 class geometryReplicatorShapeUI(omui.MPxSurfaceShapeUI):
73  def __init__(self):
74  omui.MPxSurfaceShapeUI.__init__(self)
75 
76  @staticmethod
77  def creator():
78  return geometryReplicatorShapeUI()
79 
80 
81 ###############################################################################
82 ##
83 ## geometryReplicatorGeometryOverride
84 ##
85 ## Handles vertex data preparation for drawing the user defined shape in
86 ## Viewport 2.0.
87 ##
88 ###############################################################################
89 class geometryReplicatorGeometryOverride(omr.MPxGeometryOverride):
90  def __init__(self, obj):
91  omr.MPxGeometryOverride.__init__(self, obj)
92  self.fThisNode = om.MObject(obj)
93  self.fType = om.MFn.kInvalid # the type of the associated object.
94  self.fPath = om.MDagPath() # the associated object path.
95 
96  @staticmethod
97  def creator(obj):
98  return geometryReplicatorGeometryOverride(obj)
99 
100  def supportedDrawAPIs(self):
101  # this plugin supports both GL and DX
102  return (omr.MRenderer.kOpenGL | omr.MRenderer.kDirectX11)
103 
104  def isCPVShown(self):
105  res = False
106  plug = om.MPlug(self.fThisNode, geometryReplicator.aShowCPV)
107  if not plug.isNull:
108  res = plug.asBool()
109  return res
110 
111  def isBaseMesh(self):
112  res = False
113  plug = om.MPlug(self.fThisNode, geometryReplicator.aBaseMesh)
114  if not plug.isNull:
115  res = plug.asBool()
116  return res
117 
118  def requiresUnsharedGeometricStreams(self, requirements):
119  # Only when vertexid, faceid or localuvcoord is required, we need to extract
120  # unshared geometric streams. To test how these streams are extracted, the
121  # shape can be assigned with hwPhongShader after setting the env vars:
122  # - MAYA_HWPHONG_TEST_VERTEXID_AND_FACEID=1
123  # - MAYA_HWPHONG_TEST_LOCALUVCOORD=1
124  # - MAYA_HWPHONG_TEST_INDEXING=1
125  descList = requirements.vertexRequirements()
126  for desc in descList:
127  if desc.semantic == omr.MGeometry.kTexture and desc.semanticName in ["vertexid", "faceid", "localuvcoord"]:
128  return True
129  return False
130 
131  def updateDG(self):
132  if not self.fPath.isValid():
133  fnThisNode = om.MFnDependencyNode(self.fThisNode)
134 
135  messageAttr = fnThisNode.attribute("message")
136  messagePlug = om.MPlug(self.fThisNode, messageAttr)
137 
138  connections = messagePlug.connectedTo(False, True)
139  for plug in connections:
140  node = plug.node()
141  if node.hasFn(om.MFn.kMesh) or node.hasFn(om.MFn.kNurbsSurface) or node.hasFn(om.MFn.kNurbsCurve) or node.hasFn(om.MFn.kBezierCurve):
142  path = om.MDagPath.getAPathTo(node)
143  self.fPath = path
144  self.fType = path.apiType()
145  break
146 
147  def updateRenderItems(self, path, list):
148  if not self.fPath.isValid():
149  return
150  shaderManager = omr.MRenderer.getShaderManager()
151  if shaderManager is None:
152  return
153 
154  if self.fType == om.MFn.kNurbsCurve or self.fType == om.MFn.kBezierCurve:
155  ## add render items for drawing curve
156  curveItem = None
157  index = list.indexOf("geometryReplicatorCurve")
158  if index < 0:
159  curveItem = omr.MRenderItem.create("geometryReplicatorCurve", omr.MRenderItem.NonMaterialSceneItem, omr.MGeometry.kLines)
160  curveItem.setDrawMode(omr.MGeometry.kAll)
161  list.append(curveItem)
162 
163  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
164  if shader is not None:
165  theColor = [1.0, 0.0, 0.0, 1.0]
166  shader.setParameter("solidColor", theColor)
167 
168  curveItem.setShader(shader)
169  shaderManager.releaseShader(shader)
170 
171  else:
172  curveItem = list[index]
173 
174  if curveItem is not None:
175  curveItem.enable(True)
176 
177  elif self.fType == om.MFn.kMesh:
178  ## add render item for drawing wireframe on the mesh
179  wireframeItem = None
180  index = list.indexOf("geometryReplicatorWireframe")
181  if index < 0:
182  wireframeItem = omr.MRenderItem.create("geometryReplicatorWireframe", omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
183  wireframeItem.setDrawMode(omr.MGeometry.kWireframe)
184  wireframeItem.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
185  list.append(wireframeItem)
186 
187  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
188  if shader is not None:
189  theColor = [1.0, 0.0, 0.0, 1.0]
190  shader.setParameter("solidColor", theColor)
191 
192  wireframeItem.setShader(shader)
193  shaderManager.releaseShader(shader)
194 
195  else:
196  wireframeItem = list[index]
197 
198  if wireframeItem is not None:
199  wireframeItem.enable(True)
200 
201  ## disable StandardShadedItem if CPV is shown.
202  showCPV = self.isCPVShown()
203  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kShaded)
204  if index >= 0:
205  shadedItem = list[index]
206  if shadedItem is not None:
207  shadedItem.enable(not showCPV)
208 
209  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kTextured)
210  if index >= 0:
211  shadedItem = list[index]
212  if shadedItem is not None:
213  shadedItem.enable(not showCPV)
214 
215  ## add item for CPV.
216  index = list.indexOf("geometryReplicatorCPV")
217  if index >= 0:
218  cpvItem = list[index]
219  if cpvItem is not None:
220  cpvItem.enable(showCPV)
221 
222  else:
223  ## if no cpv item and showCPV is true, created the cpv item.
224  if showCPV:
225  cpvItem = omr.MRenderItem.create("geometryReplicatorCPV", omr.MRenderItem.MaterialSceneItem, omr.MGeometry.kTriangles)
226  cpvItem.setDrawMode(omr.MGeometry.kShaded | omr.MGeometry.kTextured)
227  list.append(cpvItem)
228 
229  shader = shaderManager.getStockShader(omr.MShaderManager.k3dCPVSolidShader)
230  if shader is not None:
231  cpvItem.setShader(shader)
232  cpvItem.enable(True)
233  shaderManager.releaseShader(shader)
234 
235  def populateGeometry(self, requirements, renderItems, data):
236  if not self.fPath.isValid():
237  return
238 
239  ## here, fPath is the path of the linked object instead of the plugin node; it
240  ## is used to determine the right type of the geometry shape, e.g., polygon
241  ## or NURBS surface.
242  ## The sharing flag is just for the polygon shape.
243  options = omr.MGeometryExtractor.kPolyGeom_Normal
244  if self.isBaseMesh():
245  options = options | omr.MGeometryExtractor.kPolyGeom_BaseMesh
246  if self.requiresUnsharedGeometricStreams(requirements):
247  options = options | omr.MGeometryExtractor.kPolyGeom_NotSharing
248 
249  extractor = omr.MGeometryExtractor(requirements, self.fPath, options)
250  if extractor is None:
251  return
252 
253  ## fill vertex buffer
254  descList = requirements.vertexRequirements()
255  for desc in descList:
256 
257  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:
258  vertexBuffer = data.createVertexBuffer(desc)
259  if vertexBuffer is not None:
260  ## MGeometryExtractor.vertexCount and MGeometryExtractor.populateVertexBuffer.
261  ## since the plugin node has the same vertex data as its linked scene object,
262  ## call vertexCount to allocate vertex buffer of the same size, and then call
263  ## populateVertexBuffer to copy the data.
264  vertexCount = extractor.vertexCount()
265  vertices = vertexBuffer.acquire(vertexCount, True) ## writeOnly - we don't need the current buffer values
266  if vertices is not None:
267  extractor.populateVertexBuffer(vertices, vertexCount, desc)
268  vertexBuffer.commit(vertices)
269 
270  ## fill index buffer
271  for item in renderItems:
272  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
273  if indexBuffer is None:
274  continue
275 
276  ## MGeometryExtractor.primitiveCount and MGeometryExtractor.populateIndexBuffer.
277  ## since the plugin node has the same index data as its linked scene object,
278  ## call primitiveCount to allocate index buffer of the same size, and then call
279  ## populateIndexBuffer to copy the data.
280  if item.primitive() == omr.MGeometry.kTriangles:
281  triangleDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kTriangle, "", omr.MGeometry.kTriangles, 3)
282  numTriangles = extractor.primitiveCount(triangleDesc)
283 
284  indices = indexBuffer.acquire(3 * numTriangles, True) ## writeOnly - we don't need the current buffer values
285  extractor.populateIndexBuffer(indices, numTriangles, triangleDesc)
286  indexBuffer.commit(indices)
287 
288  elif item.primitive() == omr.MGeometry.kLines:
289  edgeDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kEdgeLine, "", omr.MGeometry.kLines, 2)
290  numEdges = extractor.primitiveCount(edgeDesc)
291 
292  indices = indexBuffer.acquire(2 * numEdges, True) ## writeOnly - we don't need the current buffer values
293  extractor.populateIndexBuffer(indices, numEdges, edgeDesc)
294  indexBuffer.commit(indices)
295 
296  item.associateWithIndexBuffer(indexBuffer)
297 
298  def cleanUp(self):
299  # no-op
300  pass
301 
302 
303 ##---------------------------------------------------------------------------
304 ##---------------------------------------------------------------------------
305 ## Plugin Registration
306 ##---------------------------------------------------------------------------
307 ##---------------------------------------------------------------------------
308 
309 def initializePlugin(obj):
310  plugin = om.MFnPlugin(obj, "Autodesk", "3.0", "Any")
311  try:
312  plugin.registerShape("geometryReplicator", geometryReplicator.id, geometryReplicator.creator, geometryReplicator.initialize, geometryReplicatorShapeUI.creator, geometryReplicator.drawDbClassification)
313  except:
314  sys.stderr.write("Failed to register shape\n")
315  raise
316 
317  try:
318  omr.MDrawRegistry.registerGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId, geometryReplicatorGeometryOverride.creator)
319  except:
320  sys.stderr.write("Failed to register override\n")
321  raise
322 
323 def uninitializePlugin(obj):
324  plugin = om.MFnPlugin(obj)
325  try:
326  omr.MDrawRegistry.deregisterGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId)
327  except:
328  sys.stderr.write("Failed to deregister override\n")
329  raise
330 
331  try:
332  plugin.deregisterNode(geometryReplicator.id)
333  except:
334  sys.stderr.write("Failed to deregister shape\n")
335  raise
336