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 updateDG(self):
119  if not self.fPath.isValid():
120  fnThisNode = om.MFnDependencyNode(self.fThisNode)
121 
122  messageAttr = fnThisNode.attribute("message")
123  messagePlug = om.MPlug(self.fThisNode, messageAttr)
124 
125  connections = messagePlug.connectedTo(False, True)
126  for plug in connections:
127  node = plug.node()
128  if node.hasFn(om.MFn.kMesh) or node.hasFn(om.MFn.kNurbsSurface) or node.hasFn(om.MFn.kNurbsCurve) or node.hasFn(om.MFn.kBezierCurve):
129  path = om.MDagPath.getAPathTo(node)
130  self.fPath = path
131  self.fType = path.apiType()
132  break
133 
134  def updateRenderItems(self, path, list):
135  if not self.fPath.isValid():
136  return
137  shaderManager = omr.MRenderer.getShaderManager()
138  if shaderManager is None:
139  return
140 
141  if self.fType == om.MFn.kNurbsCurve or self.fType == om.MFn.kBezierCurve:
142  ## add render items for drawing curve
143  curveItem = None
144  index = list.indexOf("geometryReplicatorCurve")
145  if index < 0:
146  curveItem = omr.MRenderItem.create("geometryReplicatorCurve", omr.MRenderItem.NonMaterialSceneItem, omr.MGeometry.kLines)
147  curveItem.setDrawMode(omr.MGeometry.kAll)
148  list.append(curveItem)
149 
150  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
151  if shader is not None:
152  theColor = [1.0, 0.0, 0.0, 1.0]
153  shader.setParameter("solidColor", theColor)
154 
155  curveItem.setShader(shader)
156  shaderManager.releaseShader(shader)
157 
158  else:
159  curveItem = list[index]
160 
161  if curveItem is not None:
162  curveItem.enable(True)
163 
164  elif self.fType == om.MFn.kMesh:
165  ## add render item for drawing wireframe on the mesh
166  wireframeItem = None
167  index = list.indexOf("geometryReplicatorWireframe")
168  if index < 0:
169  wireframeItem = omr.MRenderItem.create("geometryReplicatorWireframe", omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
170  wireframeItem.setDrawMode(omr.MGeometry.kWireframe)
171  wireframeItem.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
172  list.append(wireframeItem)
173 
174  shader = shaderManager.getStockShader(omr.MShaderManager.k3dSolidShader)
175  if shader is not None:
176  theColor = [1.0, 0.0, 0.0, 1.0]
177  shader.setParameter("solidColor", theColor)
178 
179  wireframeItem.setShader(shader)
180  shaderManager.releaseShader(shader)
181 
182  else:
183  wireframeItem = list[index]
184 
185  if wireframeItem is not None:
186  wireframeItem.enable(True)
187 
188  ## disable StandardShadedItem if CPV is shown.
189  showCPV = self.isCPVShown()
190  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kShaded)
191  if index >= 0:
192  shadedItem = list[index]
193  if shadedItem is not None:
194  shadedItem.enable(not showCPV)
195 
196  index = list.indexOf("StandardShadedItem", omr.MGeometry.kTriangles, omr.MGeometry.kTextured)
197  if index >= 0:
198  shadedItem = list[index]
199  if shadedItem is not None:
200  shadedItem.enable(not showCPV)
201 
202  ## add item for CPV.
203  index = list.indexOf("geometryReplicatorCPV")
204  if index >= 0:
205  cpvItem = list[index]
206  if cpvItem is not None:
207  cpvItem.enable(showCPV)
208 
209  else:
210  ## if no cpv item and showCPV is true, created the cpv item.
211  if showCPV:
212  cpvItem = omr.MRenderItem.create("geometryReplicatorCPV", omr.MRenderItem.MaterialSceneItem, omr.MGeometry.kTriangles)
213  cpvItem.setDrawMode(omr.MGeometry.kShaded | omr.MGeometry.kTextured)
214  list.append(cpvItem)
215 
216  shader = shaderManager.getStockShader(omr.MShaderManager.k3dCPVSolidShader)
217  if shader is not None:
218  cpvItem.setShader(shader)
219  cpvItem.enable(True)
220  shaderManager.releaseShader(shader)
221 
222  def populateGeometry(self, requirements, renderItems, data):
223  if not self.fPath.isValid():
224  return
225 
226  ## here, fPath is the path of the linked object instead of the plugin node; it
227  ## is used to determine the right type of the geometry shape, e.g., polygon
228  ## or NURBS surface.
229  ## The sharing flag (true here) is just for the polygon shape.
230  options = omr.MGeometryExtractor.kPolyGeom_Normal
231  if self.isBaseMesh():
232  options = options | omr.MGeometryExtractor.kPolyGeom_BaseMesh
233 
234  extractor = omr.MGeometryExtractor(requirements, self.fPath, options)
235  if extractor is None:
236  return
237 
238  ## fill vertex buffer
239  descList = requirements.vertexRequirements()
240  for desc in descList:
241 
242  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:
243  vertexBuffer = data.createVertexBuffer(desc)
244  if vertexBuffer is not None:
245  ## MGeometryExtractor.vertexCount and MGeometryExtractor.populateVertexBuffer.
246  ## since the plugin node has the same vertex data as its linked scene object,
247  ## call vertexCount to allocate vertex buffer of the same size, and then call
248  ## populateVertexBuffer to copy the data.
249  vertexCount = extractor.vertexCount()
250  vertices = vertexBuffer.acquire(vertexCount, True) ## writeOnly - we don't need the current buffer values
251  if vertices is not None:
252  extractor.populateVertexBuffer(vertices, vertexCount, desc)
253  vertexBuffer.commit(vertices)
254 
255  ## fill index buffer
256  for item in renderItems:
257  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
258  if indexBuffer is None:
259  continue
260 
261  ## MGeometryExtractor.primitiveCount and MGeometryExtractor.populateIndexBuffer.
262  ## since the plugin node has the same index data as its linked scene object,
263  ## call primitiveCount to allocate index buffer of the same size, and then call
264  ## populateIndexBuffer to copy the data.
265  if item.primitive() == omr.MGeometry.kTriangles:
266  triangleDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kTriangle, "", omr.MGeometry.kTriangles, 3)
267  numTriangles = extractor.primitiveCount(triangleDesc)
268 
269  indices = indexBuffer.acquire(3 * numTriangles, True) ## writeOnly - we don't need the current buffer values
270  extractor.populateIndexBuffer(indices, numTriangles, triangleDesc)
271  indexBuffer.commit(indices)
272 
273  elif item.primitive() == omr.MGeometry.kLines:
274  edgeDesc = omr.MIndexBufferDescriptor(omr.MIndexBufferDescriptor.kEdgeLine, "", omr.MGeometry.kLines, 2)
275  numEdges = extractor.primitiveCount(edgeDesc)
276 
277  indices = indexBuffer.acquire(2 * numEdges, True) ## writeOnly - we don't need the current buffer values
278  extractor.populateIndexBuffer(indices, numEdges, edgeDesc)
279  indexBuffer.commit(indices)
280 
281  item.associateWithIndexBuffer(indexBuffer)
282 
283  def cleanUp(self):
284  # no-op
285  pass
286 
287 
288 ##---------------------------------------------------------------------------
289 ##---------------------------------------------------------------------------
290 ## Plugin Registration
291 ##---------------------------------------------------------------------------
292 ##---------------------------------------------------------------------------
293 
294 def initializePlugin(obj):
295  plugin = om.MFnPlugin(obj, "Autodesk", "3.0", "Any")
296  try:
297  plugin.registerShape("geometryReplicator", geometryReplicator.id, geometryReplicator.creator, geometryReplicator.initialize, geometryReplicatorShapeUI.creator, geometryReplicator.drawDbClassification)
298  except:
299  sys.stderr.write("Failed to register shape\n")
300  raise
301 
302  try:
303  omr.MDrawRegistry.registerGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId, geometryReplicatorGeometryOverride.creator)
304  except:
305  sys.stderr.write("Failed to register override\n")
306  raise
307 
308 def uninitializePlugin(obj):
309  plugin = om.MFnPlugin(obj)
310  try:
311  omr.MDrawRegistry.deregisterGeometryOverrideCreator(geometryReplicator.drawDbClassification, geometryReplicator.drawRegistrantId)
312  except:
313  sys.stderr.write("Failed to deregister override\n")
314  raise
315 
316  try:
317  plugin.deregisterNode(geometryReplicator.id)
318  except:
319  sys.stderr.write("Failed to deregister shape\n")
320  raise
321