scripted/pyApiMeshShape.py

scripted/pyApiMeshShape.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, math, ctypes, collections
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 ## helper function
24 def useSelectHighlight(selectedList, path):
25 
26  displayStatus = omr.MGeometryUtilities.displayStatus(path)
27  if displayStatus == omr.MGeometryUtilities.kHilite or displayStatus == omr.MGeometryUtilities.kActiveComponent:
28  return True
29 
30  pathCopy = om.MDagPath(path)
31 
32  while pathCopy.length() > 0:
33  if selectedList.hasItem(pathCopy):
34  return True
35  pathCopy.pop()
36 
37  return False
38 
39 def floatApproxEqual(left, right):
40  return abs(left - right) < 0.0001
41 
42 ################################################################################
43 ##
44 ## This class holds the underlying geometry for the shape or data.
45 ## This is where geometry specific data and methods should go.
46 ##
47 ################################################################################
48 class apiMeshGeomUV:
49  def __init__(self, other=None):
50  if other:
51  self.copy(other)
52  else:
53  self.reset()
54 
55  def uvId(self, fvi):
56  return self.faceVertexIndex[fvi]
57 
58  def getUV(self, uvId):
59  return [ self.ucoord[uvId], self.vcoord[uvId] ]
60 
61  def u(self, uvId):
62  return self.ucoord[uvId]
63 
64  def v(self, uvId):
65  return self.vcoord[uvId]
66 
67  def uvcount(self):
68  return len(self.ucoord)
69 
70  def append_uv(self, u, v):
71  self.ucoord.append( u )
72  self.vcoord.append( v )
73 
74  def reset(self):
75  self.ucoord = om.MFloatArray()
76  self.vcoord = om.MFloatArray()
77  self.faceVertexIndex = om.MIntArray()
78 
79  def copy(self, other):
80  self.ucoord = om.MFloatArray(other.ucoord)
81  self.vcoord = om.MFloatArray(other.vcoord)
82  self.faceVertexIndex = om.MIntArray(other.faceVertexIndex)
83 
84 class apiMeshGeom:
85  def __init__(self):
86  self.vertices = om.MPointArray()
87  self.face_counts = om.MIntArray()
88  self.face_connects = om.MIntArray()
89  self.normals = om.MVectorArray()
90  self.uvcoords = apiMeshGeomUV()
91  self.faceCount = 0
92 
93  def copy(self, other):
94  self.vertices = om.MPointArray(other.vertices)
95  self.face_counts = om.MIntArray(other.face_counts)
96  self.face_connects = om.MIntArray(other.face_connects)
97  self.normals = om.MVectorArray(other.normals)
98  self.uvcoords = apiMeshGeomUV(other.uvcoords)
99  self.faceCount = other.faceCount
100 
101 ################################################################################
102 ##
103 ## Provides a data type for some arbitrary user geometry.
104 ##
105 ## A users geometry class can exist in the DAG by creating an
106 ## MPxSurfaceShape (and UI) class for it and can also be passed through
107 ## DG connections by creating an MPxGeometryData class for it.
108 ##
109 ## MPxGeometryData is the same as MPxData except it provides
110 ## additional methods to modify the geometry data via an iterator.
111 ##
112 ################################################################################
113 ## Ascii file IO defines
114 ##
115 kDblQteChar = "\""
116 kSpaceChar = " "
117 kWrapString = "\n\t\t"
118 kVertexKeyword = "v"
119 kNormalKeyword = "vn"
120 kTextureKeyword = "vt"
121 kFaceKeyword = "face"
122 kUVKeyword = "uv"
123 
124 class apiMeshGeomIterator(om.MPxGeometryIterator):
125  def __init__(self, userGeometry, components):
126  om.MPxGeometryIterator.__init__(self, userGeometry, components)
127  self.geometry = userGeometry
128  self.reset()
129 
130  def reset(self):
131  ## Resets the iterator to the start of the components so that another
132  ## pass over them may be made.
133  ##
134  om.MPxGeometryIterator.reset(self)
135  self.currentPoint = 0
136  if self.geometry:
137  maxVertex = len(self.geometry.vertices)
138  self.maxPoints = maxVertex
139 
140  def point(self):
141  ## Returns the point for the current element in the iteration.
142  ## This is used by the transform tools for positioning the
143  ## manipulator in component mode. It is also used by deformers.
144  ##
145  pnt = om.MPoint()
146  if self.geometry:
147  pnt = self.geometry.vertices[ self.index() ]
148  return pnt
149 
150  def setPoint(self, pnt):
151  ## Set the point for the current element in the iteration.
152  ## This is used by deformers.
153  ##
154  if self.geometry:
155  self.geometry.vertices[ self.index() ] = pnt
156 
157  def iteratorCount(self):
158  ## Return the number of vertices in the iteration.
159  ## This is used by deformers such as smooth skinning
160  ##
161  if self.geometry:
162  return len(self.geometry.vertices)
163  return 0
164 
165  def hasPoints(self):
166  ## Returns true since the shape data has points.
167  ##
168  return True
169 
170 class apiMeshData(om.MPxGeometryData):
171  typeName = "apiMeshData"
172  id = om.MTypeId(0x80777)
173 
174  @staticmethod
175  def creator():
176  return apiMeshData()
177 
178  def __init__(self):
179  om.MPxGeometryData.__init__(self)
180  self.fGeometry = apiMeshGeom()
181 
182  def __del__(self):
183  self.fGeometry = None
184 
185  def readASCII(self, argList, idx):
186  idx = self.readVerticesASCII(argList, idx)
187  idx = self.readNormalsASCII(argList, idx)
188  idx = self.readFacesASCII(argList, idx)
189  idx = self.readUVASCII(argList, idx)
190  return idx
191 
192  def readBinary(self, inputData, length):
193  ## not implemented
194  return 0
195 
196  def writeASCII(self):
197  data = self.writeVerticesASCII()
198  data += self.writeNormalsASCII()
199  data += self.writeFacesASCII()
200  data += writeUVASCII()
201  return data
202 
203  def writeBinary(self):
204  ## not implemented
205  return bytearray()
206 
207  def copy(self, src):
208  self.fGeometry.copy(src.fGeometry)
209 
210  def typeId(self):
211  return apiMeshData.id
212 
213  def name(self):
214  return apiMeshData.typeName
215 
216  ##################################################################
217  ##
218  ## Overrides from MPxGeometryData
219  ##
220  ##################################################################
221 
222  def iterator(self, componentList, component, useComponents, world=None):
223  if useComponents:
224  return apiMeshGeomIterator(self.fGeometry, componentList)
225 
226  return apiMeshGeomIterator(self.fGeometry, component)
227 
228  ##################################################################
229  ##
230  ## Helper methods
231  ##
232  ##################################################################
233 
234  def readVerticesASCII(self, argList, idx):
235  geomStr = ""
236  try:
237  geomStr = argList.asString(idx)
238  except:
239  geomStr = ""
240  pass
241 
242  if geomStr == kVertexKeyword:
243  idx = argList.lastArgUsed()+1
244  vertexCount = argList.asInt(idx)
245  idx = argList.lastArgUsed()+1
246  for i in xrange(vertexCount):
247  vertex = argList.asPoint(idx)
248  idx = argList.lastArgUsed()+1
249  self.fGeometry.vertices.append(vertex)
250 
251  return idx
252 
253  def readNormalsASCII(self, argList, idx):
254  geomStr = ""
255  try:
256  geomStr = argList.asString(idx)
257  except:
258  geomStr = ""
259  pass
260 
261  if geomStr == kNormalKeyword:
262  idx = argList.lastArgUsed()+1
263  normalCount = argList.asInt(idx)
264  idx = argList.lastArgUsed()+1
265  for i in xrange(normalCount):
266  normal = argList.asVector(idx)
267  idx = argList.lastArgUsed()+1
268  self.fGeometry.normals.append(normal)
269 
270  return idx
271 
272  def readFacesASCII(self, argList, idx):
273  geomStr = ""
274  try:
275  geomStr = argList.asString(idx)
276  except:
277  geomStr = ""
278  pass
279 
280  while geomStr == kFaceKeyword:
281  idx = argList.lastArgUsed()+1
282  faceCount = argList.asInt(idx)
283  idx = argList.lastArgUsed()+1
284  self.fGeometry.face_counts.append(faceCount)
285  for i in xrange(faceCount):
286  vid = argList.asInt(idx)
287  idx = argList.lastArgUsed()+1
288  self.fGeometry.face_connects.append(vid)
289 
290  try:
291  geomStr = argList.asString(idx)
292  except:
293  geomStr = ""
294  pass
295 
296  self.fGeometry.faceCount = len(self.fGeometry.face_counts)
297  return idx
298 
299  def readUVASCII(self, argList, idx):
300  self.fGeometry.uvcoords.reset()
301 
302  geomStr = ""
303  try:
304  geomStr = argList.asString(idx)
305  except:
306  geomStr = ""
307  pass
308 
309  if geomStr == kUVKeyword:
310  idx = argList.lastArgUsed()+1
311  uvCount = argList.asInt(idx)
312  idx = argList.lastArgUsed()+1
313  faceVertexListCount = argList.asInt(idx)
314  idx = argList.lastArgUsed()+1
315  for i in xrange(uvCount):
316  u = argList.asDouble(idx)
317  idx = argList.lastArgUsed()+1
318  v = argList.asDouble(idx)
319  idx = argList.lastArgUsed()+1
320  self.fGeometry.uvcoords.append_uv(u, v)
321 
322  for i in xrange(faceVertexListCount):
323  fvi = argList.asInt(idx)
324  idx = argList.lastArgUsed()+1
325  self.fGeometry.uvcoords.faceVertexIndex.append( fvi )
326 
327  return idx
328 
329  def writeVerticesASCII(self):
330  vertexCount = len(self.fGeometry.vertices)
331 
332  data = "\n"
333  data += kWrapString
334  data += kDblQteChar + kVertexKeyword + kDblQteChar + kSpaceChar + str(vertexCount)
335 
336  for i in xrange(vertexCount):
337  vertex = self.fGeometry.vertices[i]
338 
339  data += kWrapString
340  data += str(vertex[0]) + kSpaceChar + str(vertex[1]) + kSpaceChar + str(vertex[2])
341 
342  return data
343 
344  def writeNormalsASCII(self):
345  normalCount = len(self.fGeometry.normals)
346 
347  data = "\n"
348  data += kWrapString
349  data += kDblQteChar + kNormalKeyword + kDblQteChar + kSpaceChar + str(normalCount)
350 
351  for i in xrange(normalCount):
352  normal = self.fGeometry.normals[i]
353 
354  data += kWrapString
355  data += str(normal[0]) + kSpaceChar + str(normal[1]) + kSpaceChar + str(normal[2])
356 
357  return data
358 
359  def writeFacesASCII(self):
360  numFaces = len(self.fGeometry.face_counts)
361 
362  data = ""
363  vid = 0
364 
365  for i in xrange(numFaces):
366  faceVertexCount = self.fGeometry.face_counts[i]
367 
368  data += "\n"
369  data += kWrapString
370  data += kDblQteChar + kFaceKeyword + kDblQteChar + kSpaceChar + str(faceVertexCount)
371 
372  data += kWrapString
373 
374  for v in xrange(faceVertexCount):
375  value = self.fGeometry.face_connects[vid]
376 
377  data += str(value) + kSpaceChar
378  vid += 1
379 
380  return data
381 
382  def writeUVASCII(self):
383  uvCount = self.fGeometry.uvcoords.uvcount()
384  faceVertexCount = len(self.fGeometry.uvcoords.faceVertexIndex)
385 
386  data = ""
387 
388  if uvCount > 0:
389  data = "\n"
390  data += kWrapString
391  data += kDblQteChar + kUVKeyword + kDblQteChar + kSpaceChar + str(uvCount) + kSpaceChar + str(faceVertexCount)
392 
393  for i in xrange(uvCount):
394  uv = self.fGeometry.uvcoords.getUV(i)
395 
396  data += kWrapString
397  data += uv[0] + kSpaceChar + uv[1] + kSpaceChar
398 
399  for i in xrange(faceVertexCount):
400  value = self.fGeometry.uvcoords.faceVertexIndex[i]
401 
402  data += kWrapString
403  data += value + kSpaceChar
404 
405  return data
406 
407 
408 ################################################################################
409 ##
410 ## apiMeshShape
411 ##
412 ## Implements a new type of shape node in maya called apiMesh.
413 ##
414 ## INPUTS
415 ## inputSurface - input apiMeshData
416 ## outputSurface - output apiMeshData
417 ## worldSurface - array of world space apiMeshData, each element
418 ## represents an istance of the shape
419 ## OUTPUTS
420 ## mControlPoints - inherited control vertices for the mesh. These values
421 ## are tweaks (offsets) that will be applied to the
422 ## vertices of the input shape.
423 ## bboxCorner1 - bounding box upper left corner
424 ## bboxCorner2 - bounding box lower right corner
425 ##
426 ################################################################################
427 class apiMesh(om.MPxSurfaceShape):
428  id = om.MTypeId(0x80099)
429 
430  ##########################################################
431  ##
432  ## Attributes
433  ##
434  ##########################################################
435  inputSurface = None
436  outputSurface = None
437  worldSurface = None
438 
439  useWeightedTransformUsingFunction = None
440  useWeightedTweakUsingFunction = None
441 
442  ## used to support tweaking of points, the inputSurface attribute data is
443  ## transferred into the cached surface when it is dirty. The control points
444  ## tweaks are added into it there.
445  ##
446  cachedSurface = None
447 
448  bboxCorner1 = None
449  bboxCorner2 = None
450 
451  @staticmethod
452  def creator():
453  return apiMesh()
454 
455  @staticmethod
456  def initialize():
457  typedAttr = om.MFnTypedAttribute()
458  numericAttr = om.MFnNumericAttribute()
459 
460  ## ----------------------- INPUTS --------------------------
461  apiMesh.inputSurface = typedAttr.create( "inputSurface", "is", apiMeshData.id, om.MObject.kNullObj )
462  typedAttr.storable = False
463  om.MPxNode.addAttribute( apiMesh.inputSurface )
464 
465  apiMesh.useWeightedTransformUsingFunction = numericAttr.create( "useWeightedTransformUsingFunction", "utru", om.MFnNumericData.kBoolean, True )
466  numericAttr.keyable = True
467  om.MPxNode.addAttribute( apiMesh.useWeightedTransformUsingFunction )
468 
469  apiMesh.useWeightedTweakUsingFunction = numericAttr.create( "useWeightedTweakUsingFunction", "utwu", om.MFnNumericData.kBoolean, True )
470  numericAttr.keyable = True
471  om.MPxNode.addAttribute( apiMesh.useWeightedTweakUsingFunction )
472 
473  ## ----------------------- OUTPUTS -------------------------
474 
475  ## bbox attributes
476  ##
477  apiMesh.bboxCorner1 = numericAttr.create( "bboxCorner1", "bb1", om.MFnNumericData.k3Double, 0 )
478  numericAttr.array = False
479  numericAttr.usesArrayDataBuilder = False
480  numericAttr.hidden = False
481  numericAttr.keyable = False
482  om.MPxNode.addAttribute( apiMesh.bboxCorner1 )
483 
484  apiMesh.bboxCorner2 = numericAttr.create( "bboxCorner2", "bb2", om.MFnNumericData.k3Double, 0 )
485  numericAttr.array = False
486  numericAttr.usesArrayDataBuilder = False
487  numericAttr.hidden = False
488  numericAttr.keyable = False
489  om.MPxNode.addAttribute( apiMesh.bboxCorner2 )
490 
491  ## local/world output surface attributes
492  ##
493  apiMesh.outputSurface = typedAttr.create( "outputSurface", "os", apiMeshData.id, om.MObject.kNullObj )
494  typedAttr.writable = False
495  om.MPxNode.addAttribute( apiMesh.outputSurface )
496 
497  apiMesh.worldSurface = typedAttr.create( "worldSurface", "ws", apiMeshData.id, om.MObject.kNullObj )
498  typedAttr.cached = False
499  typedAttr.writable = False
500  typedAttr.array = True
501  typedAttr.usesArrayDataBuilder = True
502  typedAttr.disconnectBehavior = om.MFnAttribute.kDelete
503  typedAttr.worldSpace = True
504  om.MPxNode.addAttribute( apiMesh.worldSurface )
505 
506  ## Cached surface used for file IO
507  ##
508  apiMesh.cachedSurface = typedAttr.create( "cachedSurface", "cs", apiMeshData.id, om.MObject.kNullObj )
509  typedAttr.readable = True
510  typedAttr.writable = True
511  typedAttr.storable = True
512  om.MPxNode.addAttribute( apiMesh.cachedSurface )
513 
514  ## ---------- Specify what inputs affect the outputs ----------
515  ##
516  om.MPxNode.attributeAffects( apiMesh.inputSurface, apiMesh.outputSurface )
517  om.MPxNode.attributeAffects( apiMesh.inputSurface, apiMesh.worldSurface )
518  om.MPxNode.attributeAffects( apiMesh.outputSurface, apiMesh.worldSurface )
519  om.MPxNode.attributeAffects( apiMesh.inputSurface, apiMesh.bboxCorner1 )
520  om.MPxNode.attributeAffects( apiMesh.inputSurface, apiMesh.bboxCorner2 )
521  om.MPxNode.attributeAffects( apiMesh.cachedSurface, apiMesh.outputSurface )
522  om.MPxNode.attributeAffects( apiMesh.cachedSurface, apiMesh.worldSurface )
523 
524  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlPoints, apiMesh.outputSurface )
525  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueX, apiMesh.outputSurface )
526  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueY, apiMesh.outputSurface )
527  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueZ, apiMesh.outputSurface )
528  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlPoints, apiMesh.cachedSurface )
529  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueX, apiMesh.cachedSurface )
530  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueY, apiMesh.cachedSurface )
531  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueZ, apiMesh.cachedSurface )
532  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlPoints, apiMesh.worldSurface )
533  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueX, apiMesh.worldSurface )
534  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueY, apiMesh.worldSurface )
535  om.MPxNode.attributeAffects( om.MPxSurfaceShape.mControlValueZ, apiMesh.worldSurface )
536 
537  def __init__(self):
538  om.MPxSurfaceShape.__init__(self)
539 
540  ##########################################################
541  ##
542  ## Overrides
543  ##
544  ##########################################################
545 
546  ## From MPxNode
547  ##
548  def postConstructor(self):
549  ##
550  ## Description
551  ##
552  ## When instances of this node are created internally, the MObject associated
553  ## with the instance is not created until after the constructor of this class
554  ## is called. This means that no member functions of MPxSurfaceShape can
555  ## be called in the constructor.
556  ## The postConstructor solves this problem. Maya will call this function
557  ## after the internal object has been created.
558  ## As a general rule do all of your initialization in the postConstructor.
559  ##
560 
561  ## This call allows the shape to have shading groups assigned
562  ##
563  self.isRenderable = True
564 
565  ## Is there input history to this node
566  ##
567  self.fHasHistoryOnCreate = False
568 
569  ## Is the shape dirty? Used by VP2.0 sub-scene evaluator
570  ##
571  self.fShapeDirty = True
572  self.fMaterialDirty = True
573 
574  def compute(self, plug, datablock):
575  ##
576  ## Description
577  ##
578  ## When input attributes are dirty this method will be called to
579  ## recompute the output attributes.
580  ##
581  ## Arguments
582  ##
583  ## plug - the attribute that triggered the compute
584  ## datablock - the nodes data
585  ##
586 
587  if plug == apiMesh.outputSurface:
588  return self.computeOutputSurface( plug, datablock )
589 
590  elif plug == apiMesh.cachedSurface:
591  return self.computeOutputSurface( plug, datablock )
592 
593  elif plug == apiMesh.worldSurface:
594  return self.computeWorldSurface( plug, datablock )
595 
596  else:
597  return None
598 
599  def preEvaluation(self, context, evaluationNode):
600  ##
601  ## Description
602  ##
603  ## Pre evaluate will mark the shape as dirty
604  ## PreEvaluate will be called before each evaluation of this node
605  ##
606  ## Arguments
607  ##
608  ## context - Evaluation context in which the compute happen
609  ## evaluationNode - contains information about the dirtyness of plugs
610  ##
611  if context.isNormal():
612  if evaluationNode.dirtyPlugExists(apiMesh.inputSurface) or evaluationNode.dirtyPlugExists(apiMesh.mControlPoints):
613  self.setShapeDirty()
614 
615  def postEvaluation(self, context, evaluationNode, evalType):
616  ##
617  ## Description
618  ##
619  ## Post evaluate will signal viewport dirty so that renderer can pick it up
620  ## PostEvaluate will be called before each evaluation of this node
621  ##
622  ## Arguments
623  ##
624  ## context - Evaluation context in which the compute happen
625  ## evaluationNode - contains information about the dirtyness of plugs
626  ## evalType - type of evaluation that was performed
627  ##
628  if context.isNormal():
629  if evaluationNode.dirtyPlugExists(apiMesh.inputSurface) or evaluationNode.dirtyPlugExists(apiMesh.mControlPoints):
630  self.notifyViewport()
631 
632  def setDependentsDirty(self, plug, plugArray):
633  ##
634  ## Description
635  ##
636  ## Horribly abuse the purpose of this method to notify the Viewport 2.0
637  ## renderer that something about this shape has changed and that it should
638  ## be retranslated.
639  ##
640 
641  ## if the dirty attribute is the output mesh then we need to signal the
642  ## the renderer that it needs to update the object
643 
644  if plug == apiMesh.inputSurface or plug == om.MPxSurfaceShape.mControlPoints or plug == om.MPxSurfaceShape.mControlValueX or plug == om.MPxSurfaceShape.mControlValueY or plug == om.MPxSurfaceShape.mControlValueZ:
645  self.signalDirtyToViewport()
646 
647  def getInternalValue(self, plug, handle):
648  ##
649  ## Description
650  ##
651  ## Handle internal attributes.
652  ##
653  ## Attributes that require special storage, bounds checking,
654  ## or other non-standard behavior can be marked as "Internal" by
655  ## using the "MFnAttribute.setInternal" method.
656  ##
657  ## The get/setInternalValue methods will get called for internal
658  ## attributes whenever the attribute values are stored or retrieved
659  ## using getAttr/setAttr or MPlug getValue/setValue.
660  ##
661  ## The inherited attribute mControlPoints is internal and we want
662  ## its values to get stored only if there is input history. Otherwise
663  ## any changes to the vertices are stored in the cachedMesh and outputMesh
664  ## directly.
665  ##
666  ## If values are retrieved then we want the controlPoints value
667  ## returned if there is history, this will be the offset or tweak.
668  ## In the case of no history, the vertex position of the cached mesh
669  ## is returned.
670  ##
671  isOk = True
672 
673  if plug == om.MPxSurfaceShape.mControlPoints or plug == om.MPxSurfaceShape.mControlValueX or plug == om.MPxSurfaceShape.mControlValueY or plug == om.MPxSurfaceShape.mControlValueZ:
674  ## If there is input history then the control point value is
675  ## directly returned. This is the tweak or offset that
676  ## was applied to the vertex.
677  ##
678  ## If there is no input history then return the actual vertex
679  ## position and ignore the controlPoints attribute.
680  ##
681  if self.hasHistory():
682  return om.MPxNode.getInternalValue(self, plug, handle)
683 
684  else:
685  if plug == om.MPxSurfaceShape.mControlPoints and not plug.isArray():
686  index = plug.logicalIndex()
687  pnt = self.getPointValue(index)
688  handle.set3Double( pnt[0], pnt[1], pnt[2] )
689 
690  elif plug == om.MPxSurfaceShape.mControlValueX:
691  parentPlug = plug.parent()
692  index = parentPlug.logicalIndex()
693  val = self.getChannelValue( index, 0 )
694  handle.setDouble( val )
695 
696  elif plug == om.MPxSurfaceShape.mControlValueY:
697  parentPlug = plug.parent()
698  index = parentPlug.logicalIndex()
699  val = self.getChannelValue( index, 1 )
700  handle.setDouble( val )
701 
702  elif plug == om.MPxSurfaceShape.mControlValueZ:
703  parentPlug = plug.parent()
704  index = parentPlug.logicalIndex()
705  val = self.getChannelValue( index, 2 )
706  handle.setDouble( val )
707 
708  ## This inherited attribute is used to specify whether or
709  ## not this shape has history. During a file read, the shape
710  ## is created before any input history can get connected.
711  ## This attribute, also called "tweaks", provides a way to
712  ## for the shape to determine if there is input history
713  ## during file reads.
714  ##
715  elif plug == om.MPxSurfaceShape.mHasHistoryOnCreate:
716  handle.setBool( self.fHasHistoryOnCreate )
717 
718  else:
719  isOk = om.MPxSurfaceShape.getInternalValue(self, plug, handle)
720 
721  return isOk
722 
723  def setInternalValue(self, plug, handle):
724  ##
725  ## Description
726  ##
727  ## Handle internal attributes.
728  ##
729  ## Attributes that require special storage, bounds checking,
730  ## or other non-standard behavior can be marked as "Internal" by
731  ## using the "MFnAttribute.setInternal" method.
732  ##
733  ## The get/setInternalValue methods will get called for internal
734  ## attributes whenever the attribute values are stored or retrieved
735  ## using getAttr/setAttr or MPlug getValue/setValue.
736  ##
737  ## The inherited attribute mControlPoints is internal and we want
738  ## its values to get stored only if there is input history. Otherwise
739  ## any changes to the vertices are stored in the cachedMesh and outputMesh
740  ## directly.
741  ##
742  ## If values are retrieved then we want the controlPoints value
743  ## returned if there is history, this will be the offset or tweak.
744  ## In the case of no history, the vertex position of the cached mesh
745  ## is returned.
746  ##
747  isOk = True
748 
749  if plug == om.MPxSurfaceShape.mControlPoints or plug == om.MPxSurfaceShape.mControlValueX or plug == om.MPxSurfaceShape.mControlValueY or plug == om.MPxSurfaceShape.mControlValueZ:
750  ## If there is input history then set the control points value
751  ## using the normal mechanism. In this case we are setting
752  ## the tweak or offset that will get applied to the input
753  ## history.
754  ##
755  ## If there is no input history then ignore the controlPoints
756  ## attribute and set the vertex position directly in the
757  ## cachedMesh.
758  ##
759  if self.hasHistory():
760  self.verticesUpdated()
761  return om.MPxNode.setInternalValue(self, plug, handle)
762 
763  else:
764  if plug == om.MPxSurfaceShape.mControlPoints and not plug.isArray():
765  index = plug.logicalIndex()
766  self.setPointValue( index, handle.asDouble3() )
767 
768  elif plug == om.MPxSurfaceShape.mControlValueX:
769  parentPlug = plug.parent()
770  index = parentPlug.logicalIndex()
771  self.setChannelValue( index, 0, handle.asDouble() )
772 
773  elif plug == om.MPxSurfaceShape.mControlValueY:
774  parentPlug = plug.parent()
775  index = parentPlug.logicalIndex()
776  self.setChannelValue( index, 1, handle.asDouble() )
777 
778  elif plug == om.MPxSurfaceShape.mControlValueZ:
779  parentPlug = plug.parent()
780  index = parentPlug.logicalIndex()
781  self.setChannelValue( index, 2, handle.asDouble() )
782 
783  ## This inherited attribute is used to specify whether or
784  ## not this shape has history. During a file read, the shape
785  ## is created before any input history can get connected.
786  ## This attribute, also called "tweaks", provides a way to
787  ## for the shape to determine if there is input history
788  ## during file reads.
789  ##
790  elif plug == om.MPxSurfaceShape.mHasHistoryOnCreate:
791  self.fHasHistoryOnCreate = handle.asBool()
792 
793  else:
794  isOk = om.MPxSurfaceShape.setInternalValue(self, plug, handle)
795 
796  return isOk
797 
798  def connectionMade(self, plug, otherPlug, asSrc):
799  ##
800  ## Description
801  ##
802  ## Whenever a connection is made to this node, this method
803  ## will get called.
804  ##
805 
806  if plug == apiMesh.inputSurface:
807  thisObj = self.thisMObject()
808  historyPlug = om.MPlug( thisObj, om.MPxSurfaceShape.mHasHistoryOnCreate )
809  historyPlug.setBool( True )
810  else:
811  thisObj = self.thisMObject()
812  dgNode = om.MFnDependencyNode( thisObj )
813  instObjGroups = dgNode.findPlug("instObjGroups", True)
814  if plug == instObjGroups:
815  self.setMaterialDirty(True)
816 
817  return om.MPxNode.connectionMade(self, plug, otherPlug, asSrc )
818 
819  def connectionBroken(self, plug, otherPlug, asSrc):
820  ##
821  ## Description
822  ##
823  ## Whenever a connection to this node is broken, this method
824  ## will get called.
825  ##
826 
827  if plug == apiMesh.inputSurface:
828  thisObj = self.thisMObject()
829  historyPlug = om.MPlug( thisObj, om.MPxSurfaceShape.mHasHistoryOnCreate )
830  historyPlug.setBool( False )
831  else:
832  thisObj = self.thisMObject()
833  dgNode = om.MFnDependencyNode( thisObj )
834  instObjGroups = dgNode.findPlug("instObjGroups", True)
835  if plug == instObjGroups:
836  self.setMaterialDirty(True)
837 
838  return om.MPxNode.connectionBroken(self, plug, otherPlug, asSrc )
839 
840  def shouldSave(self, plug):
841  ##
842  ## Description
843  ##
844  ## During file save this method is called to determine which
845  ## attributes of this node should get written. The default behavior
846  ## is to only save attributes whose values differ from the default.
847  ##
848 
849  result = True
850 
851  if plug == om.MPxSurfaceShape.mControlPoints or plug == om.MPxSurfaceShape.mControlValueX or plug == om.MPxSurfaceShape.mControlValueY or plug == om.MPxSurfaceShape.mControlValueZ:
852  if self.hasHistory():
853  ## Calling this will only write tweaks if they are
854  ## different than the default value.
855  ##
856  result = om.MPxNode.shouldSave(self, plug)
857 
858  else:
859  result = False
860 
861  elif plug == apiMesh.cachedSurface:
862  if self.hasHistory():
863  result = False
864 
865  else:
866  data = plug.asMObject()
867  result = not data.isNull()
868 
869  else:
870  result = om.MPxNode.shouldSave(self, plug)
871 
872  return result
873 
874  ## Attribute to component (components)
875  ##
876  def componentToPlugs(self, component, list):
877  ##
878  ## Description
879  ##
880  ## Converts the given component values into a selection list of plugs.
881  ## This method is used to map components to attributes.
882  ##
883  ## Arguments
884  ##
885  ## component - the component to be translated to a plug/attribute
886  ## list - a list of plugs representing the passed in component
887  ##
888 
889  if component.hasFn(om.MFn.kSingleIndexedComponent):
890  fnVtxComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(component) )
891  thisNode = self.thisMObject()
892  plug = om.MPlug( thisNode, om.MPxSurfaceShape.mControlPoints )
893  ## If this node is connected to a tweak node, reset the
894  ## plug to point at the tweak node.
895  ##
896  self.convertToTweakNodePlug(plug)
897 
898  for i in xrange(fnVtxComp.elementCount):
899  plug.selectAncestorLogicalIndex(fnVtxComp.element(i), plug.attribute())
900  list.add(plug)
901 
902  def matchComponent(self, item, spec, list):
903  ##
904  ## Description:
905  ##
906  ## Component/attribute matching method.
907  ## This method validates component names and indices which are
908  ## specified as a string and adds the corresponding component
909  ## to the passed in selection list.
910  ##
911  ## For instance, select commands such as "select shape1.vtx[0:7]"
912  ## are validated with this method and the corresponding component
913  ## is added to the selection list.
914  ##
915  ## Arguments
916  ##
917  ## item - DAG selection item for the object being matched
918  ## spec - attribute specification object
919  ## list - list to add components to
920  ##
921  ## Returns
922  ##
923  ## the result of the match
924  ##
925 
926  result = om.MPxSurfaceShape.kMatchOk
927  attrSpec = spec[0]
928  dim = attrSpec.dimensions
929 
930  ## Look for attributes specifications of the form :
931  ## vtx[ index ]
932  ## vtx[ lower:upper ]
933  ##
934  if len(spec) == 1 and dim > 0 and attrSpec.name == "vtx":
935  numVertices = len(self.meshGeom().vertices)
936  attrIndex = attrSpec[0]
937 
938  upper = 0
939  lower = 0
940  if attrIndex.hasLowerBound():
941  lower = attrIndex.getLower()
942  if attrIndex.hasUpperBound():
943  upper = attrIndex.getUpper()
944 
945  ## Check the attribute index xrange is valid
946  ##
947  if lower > upper or upper >= numVertices:
948  result = om.MPxSurfaceShape.kMatchInvalidAttributeRange
949 
950  else:
951  path = item.getDagPath(0)
952  fnVtxComp = om.MFnSingleIndexedComponent()
953  vtxComp = fnVtxComp.create( om.MFn.kMeshVertComponent )
954 
955  for i in xrange(lower, upper+1):
956  fnVtxComp.addElement( i )
957 
958  list.add( path, vtxComp )
959 
960  else:
961  ## Pass this to the parent class
962  result = om.MPxSurfaceShape.matchComponent(self, item, spec, list )
963 
964  return result
965 
966  def match(self, mask, componentList):
967  ##
968  ## Description:
969  ##
970  ## Check for matches between selection type / component list, and
971  ## the type of this shape / or it's components
972  ##
973  ## This is used by sets and deformers to make sure that the selected
974  ## components fall into the "vertex only" category.
975  ##
976  ## Arguments
977  ##
978  ## mask - selection type mask
979  ## componentList - possible component list
980  ##
981  ## Returns
982  ## True if matched any
983  ##
984 
985  result = False
986 
987  if len(componentList) == 0:
988  result = mask.intersects( om.MSelectionMask.kSelectMeshes )
989 
990  else:
991  for comp in componentList:
992  if comp.apiType() == om.MFn.kMeshVertComponent and mask.intersects(om.MSelectionMask.kSelectMeshVerts):
993  result = True
994  break
995 
996  return result
997 
998  ## Support deformers (components)
999  ##
1000  def createFullVertexGroup(self):
1001  ##
1002  ## Description
1003  ## This method is used by maya when it needs to create a component
1004  ## containing every vertex (or control point) in the shape.
1005  ## This will get called if you apply some deformer to the whole
1006  ## shape, i.e. select the shape in object mode and add a deformer to it.
1007  ##
1008  ## Returns
1009  ##
1010  ## A "complete" component representing all vertices in the shape.
1011  ##
1012 
1013  ## Create a vertex component
1014  ##
1015  fnComponent = om.MFnSingleIndexedComponent()
1016  fullComponent = fnComponent.create( om.MFn.kMeshVertComponent )
1017 
1018  ## Set the component to be complete, i.e. the elements in
1019  ## the component will be [0:numVertices-1]
1020  ##
1021  numVertices = len(self.meshGeom().vertices)
1022  fnComponent.setCompleteData( numVertices )
1023 
1024  return fullComponent
1025 
1026  def getShapeSelectionMask(self):
1027  ##
1028  ## Description
1029  ## This method is overriden to support interactive object selection in Viewport 2.0
1030  ##
1031  ## Returns
1032  ##
1033  ## The selection mask of the shape
1034  ##
1035 
1036  selType = om.MSelectionMask.kSelectMeshes
1037  return om.MSelectionMask( selType )
1038 
1039  def getComponentSelectionMask(self):
1040  ##
1041  ## Description
1042  ## This method is overriden to support interactive component selection in Viewport 2.0
1043  ##
1044  ## Returns
1045  ##
1046  ## The mask of the selectable components of the shape
1047  ##
1048 
1049  selMask = om.MSelectionMask(om.MSelectionMask.kSelectMeshVerts)
1050  selMask.addMask(om.MSelectionMask.kSelectMeshEdges)
1051  selMask.addMask(om.MSelectionMask.kSelectMeshFaces)
1052  return selMask
1053 
1054  def localShapeInAttr(self):
1055  ##
1056  ## Description
1057  ##
1058  ## Returns the input attribute of the shape. This is used by
1059  ## maya to establish input connections for deformers etc.
1060  ## This attribute must be data of type kGeometryData.
1061  ##
1062  ## Returns
1063  ##
1064  ## input attribute for the shape
1065  ##
1066 
1067  return apiMesh.inputSurface
1068 
1069  def localShapeOutAttr(self):
1070  ##
1071  ## Description
1072  ##
1073  ## Returns the output attribute of the shape. This is used by
1074  ## maya to establish out connections for deformers etc.
1075  ## This attribute must be data of type kGeometryData.
1076  ##
1077  ## Returns
1078  ##
1079  ## output attribute for the shape
1080  ##
1081  ##
1082 
1083  return apiMesh.outputSurface
1084 
1085  def worldShapeOutAttr(self):
1086  ##
1087  ## Description
1088  ##
1089  ## Returns the output attribute of the shape. This is used by
1090  ## maya to establish out connections for deformers etc.
1091  ## This attribute must be data of type kGeometryData.
1092  ##
1093  ## Returns
1094  ##
1095  ## output attribute for the shape
1096  ##
1097  ##
1098 
1099  return apiMesh.outputSurface
1100 
1101  def cachedShapeAttr(self):
1102  ##
1103  ## Description
1104  ##
1105  ## Returns the cached shape attribute of the shape.
1106  ## This attribute must be data of type kGeometryData.
1107  ##
1108  ## Returns
1109  ##
1110  ## cached shape attribute
1111  ##
1112 
1113  return apiMesh.cachedSurface
1114 
1115 
1116  def geometryData(self):
1117  ##
1118  ## Description
1119  ##
1120  ## Returns the data object for the surface. This gets
1121  ## called internally for grouping (set) information.
1122  ##
1123 
1124  datablock = self.forceCache()
1125  handle = datablock.inputValue( apiMesh.inputSurface )
1126  return handle.data()
1127 
1128  def closestPoint(self, toThisPoint, theClosestPoint, tolerance):
1129  ##
1130  ## Description
1131  ##
1132  ## Returns the closest point to the given point in space.
1133  ## Used for rigid bind of skin. Currently returns wrong results;
1134  ## override it by implementing a closest point calculation.
1135 
1136  ## Iterate through the geometry to find the closest point within
1137  ## the given tolerance.
1138  ##
1139  geometry = self.meshGeom()
1140  numVertices = len(geometry.vertices)
1141  for i in xrange(numVertices):
1142  tryThisOne = geometry.vertices[i]
1143 
1144  ## Set the output point to the result (hardcode for debug just now)
1145  ##
1146  theClosestPoint = geometry.vertices[0]
1147 
1148  ## Support the translate/rotate/scale tool (components)
1149  ##
1150  def transformUsing(self, mat, componentList, cachingMode=om.MPxSurfaceShape.kNoPointCaching, pointCache=None):
1151  ##
1152  ## Description
1153  ##
1154  ## Transforms the given components. This method is used by
1155  ## the move, rotate, and scale tools in component mode.
1156  ## The bounding box has to be updated here, so do the normals and
1157  ## any other attributes that depend on vertex positions.
1158  ##
1159  ## Arguments
1160  ## mat - matrix to tranform the components by
1161  ## componentList - list of components to be transformed,
1162  ## or an empty list to indicate the whole surface
1163  ## cachingMode - how to use the supplied pointCache (kSavePoints, kRestorePoints)
1164  ## pointCache - if non-None, save or restore points from this list base
1165  ## on the cachingMode
1166  ##
1167 
1168  geometry = self.meshGeom()
1169 
1170  ## Create cachingMode boolean values for clearer reading of conditional code below
1171  ##
1172  savePoints = (cachingMode == om.MPxSurfaceShape.kSavePoints and pointCache is not None)
1173  restorePoints = (cachingMode == om.MPxSurfaceShape.kRestorePoints and pointCache is not None)
1174 
1175  cacheIndex = 0
1176  cacheLen = 0
1177  if pointCache:
1178  cacheLen = len(pointCache)
1179 
1180  if restorePoints:
1181  ## restore the points based on the data provided in the pointCache attribute
1182  ##
1183  if len(componentList) > 0:
1184  ## traverse the component list
1185  ##
1186  for comp in componentList:
1187  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1188  elemCount = fnComp.elementCount
1189 
1190  for idx in xrange(elementCount):
1191  elemIndex = fnComp.element(idx)
1192  geometry.vertices[elemIndex] = pointCache[cacheIndex]
1193  cacheIndex += 1
1194  if cacheIndex >= cacheLen:
1195  break
1196 
1197  if cacheIndex >= cacheLen:
1198  break
1199 
1200  else:
1201  ## if the component list is of zero-length, it indicates that we
1202  ## should transform the entire surface
1203  ##
1204  vertLen = len(geometry.vertices)
1205  for idx in xrange(vertLen):
1206  geometry.vertices[idx] = pointCache[cacheIndex]
1207  cacheIndex += 1
1208  if cacheIndex >= cacheLen:
1209  break
1210 
1211  else:
1212  ## Transform the surface vertices with the matrix.
1213  ## If savePoints is True, save the points to the pointCache.
1214  ##
1215  if len(componentList) > 0:
1216  ## Traverse the componentList
1217  ##
1218  setSizeIncrement = True
1219  for comp in componentList:
1220  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1221  elemCount = fnComp.elementCount
1222 
1223  if savePoints and setSizeIncrement:
1224  pointCache.sizeIncrement = elemCount
1225  setSizeIncrement = False
1226 
1227  for idx in xrange(elemCount):
1228  elemIndex = fnComp.element(idx)
1229  if savePoints:
1230  pointCache.append(geometry.vertices[elemIndex])
1231 
1232  geometry.vertices[elemIndex] *= mat
1233  geometry.normals[idx] = geometry.normals[idx].transformAsNormal( mat )
1234 
1235  else:
1236  ## If the component list is of zero-length, it indicates that we
1237  ## should transform the entire surface
1238  ##
1239  vertLen = len(geometry.vertices)
1240  if savePoints:
1241  pointCache.sizeIncrement = vertLen
1242 
1243  for idx in xrange(vertLen):
1244  if savePoints:
1245  pointCache.append(geometry.vertices[idx])
1246 
1247  geometry.vertices[idx] *= mat
1248  geometry.normals[idx] = geometry.normals[idx].transformAsNormal( mat )
1249 
1250  ## Update the surface
1251  self.updateCachedSurface( geometry, componentList )
1252 
1253  def tweakUsing(self, mat, componentList, cachingMode, pointCache, handle):
1254  ##
1255  ## Description
1256  ##
1257  ## Transforms the given components. This method is used by
1258  ## the move, rotate, and scale tools in component mode when the
1259  ## tweaks for the shape are stored on a separate tweak node.
1260  ## The bounding box has to be updated here, so do the normals and
1261  ## any other attributes that depend on vertex positions.
1262  ##
1263  ## Arguments
1264  ## mat - matrix to tranform the components by
1265  ## componentList - list of components to be transformed,
1266  ## or an empty list to indicate the whole surface
1267  ## cachingMode - how to use the supplied pointCache (kSavePoints, kRestorePoints, kUpdatePoints)
1268  ## pointCache - if non-null, save or restore points from this list base
1269  ## on the cachingMode
1270  ## handle - handle to the attribute on the tweak node where the
1271  ## tweaks should be stored
1272  ##
1273 
1274  geometry = self.meshGeom()
1275 
1276  ## Create cachingMode boolean values for clearer reading of conditional code below
1277  ##
1278  savePoints = (cachingMode == om.MPxSurfaceShape.kSavePoints and pointCache is not None)
1279  updatePoints = (cachingMode == om.MPxSurfaceShape.kUpdatePoints and pointCache is not None)
1280  restorePoints = (cachingMode == om.MPxSurfaceShape.kRestorePoints and pointCache is not None)
1281 
1282  builder = handle.builder()
1283 
1284  cacheIndex = 0
1285  cacheLen = 0
1286  if pointCache:
1287  cacheLen = len(pointCache)
1288 
1289  if restorePoints:
1290  ## restore points from the pointCache
1291  ##
1292  if len(componentList) > 0:
1293  ## traverse the component list
1294  ##
1295  for comp in componentList:
1296  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1297  elemCount = fnComp.elementCount
1298 
1299  for idx in xrange(elementCount):
1300  elemIndex = fnComp.element(idx)
1301  cachePt = pointCache[cacheIndex]
1302  elem = builder.addElement( elemIndex )
1303  elem.set3Double(cachePt.x, cachePt.y, cachePt.z)
1304  cacheIndex += 1
1305  if cacheIndex >= cacheLen:
1306  break
1307 
1308  if cacheIndex >= cacheLen:
1309  break
1310 
1311  else:
1312  ## if the component list is of zero-length, it indicates that we
1313  ## should transform the entire surface
1314  ##
1315  vertLen = len(geometry.vertices)
1316  for idx in xrange(vertLen):
1317  cachePt = pointCache[cacheIndex]
1318  elem = builder.addElement( idx )
1319  elem.set3Double(cachePt.x, cachePt.y, cachePt.z)
1320  cacheIndex += 1
1321  if cacheIndex >= cacheLen:
1322  break
1323 
1324  else:
1325  ## Tweak the points. If savePoints is True, also save the tweaks in the
1326  ## pointCache. If updatePoints is True, add the new tweaks to the existing
1327  ## data in the pointCache.
1328  ##
1329  if len(componentList) > 0:
1330  setSizeIncrement = True
1331  for comp in componentList:
1332  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1333  elemCount = fnComp.elementCount
1334 
1335  if savePoints and setSizeIncrement:
1336  pointCache.sizeIncrement = elemCount
1337  setSizeIncrement = False
1338 
1339  for idx in xrange(elementCount):
1340  elemIndex = fnComp.element(idx)
1341  currPt = newPt = geometry.vertices[elemIndex]
1342  newPt *= mat
1343  delta = newPt - currPt
1344  elem = builder.addElement( elemIndex )
1345  elem.set3Double(delta.x, delta.y, delta.z)
1346 
1347  if savePoints:
1348  ## store the points in the pointCache for undo
1349  ##
1350  pointCache.append(delta*(-1.0))
1351 
1352  elif updatePoints and cacheIndex < cacheLen:
1353  pointCache[cacheIndex] = pointCache[cacheIndex] - delta
1354  cacheIndex += 1
1355 
1356  else:
1357  ## if the component list is of zero-length, it indicates that we
1358  ## should transform the entire surface
1359  ##
1360  vertLen = len(geometry.vertices)
1361  if savePoints:
1362  pointCache.sizeIncrement = vertLen
1363 
1364  for idx in xrange(vertLen):
1365  currPt = newPt = geometry.vertices[idx]
1366  newPt *= mat
1367  delta = newPt - currPt
1368  elem = builder.addElement( idx )
1369  elem.set3Double(delta.x, delta.y, delta.z)
1370 
1371  if savePoints:
1372  ## store the points in the pointCache for undo
1373  ##
1374  pointCache.append(delta*(-1.0))
1375 
1376  elif updatePoints and idx < cacheLen:
1377  pointCache[cacheIndex] = pointCache[cacheIndex] - delta
1378  cacheIndex += 1
1379 
1380  ## Set the builder into the handle.
1381  ##
1382  handle.set(builder)
1383 
1384  ## Tell maya the bounding box for this object has changed
1385  ## and thus "boundingBox()" needs to be called.
1386  ##
1387  self.childChanged( om.MPxSurfaceShape.kBoundingBoxChanged )
1388 
1389  ## Signal to the viewport that it needs to update the object
1390  self.signalDirtyToViewport()
1391 
1392  ## Support the soft-select translate/rotate/scale tool (components)
1393  ##
1394  def weightedTransformUsing(self, xform, space, componentList, cachingMode, pointCache, freezePlane):
1395  ##
1396  ## Description
1397  ##
1398  ## Transforms the given soft-selected components interpolated using the specified weights.
1399  ## This method is used by the move, rotate, and scale tools in component mode.
1400  ## The bounding box has to be updated here, so do the normals and
1401  ## any other attributes that depend on vertex positions.
1402  ## It is similar to the transformUsing() virtual function.
1403  ##
1404  ## Arguments
1405  ##
1406  ## xform the matrix representing the transformation that is to be applied to the components
1407  ## space the matrix representing the transformation space to perform the interpolated transformation.
1408  ## A value of None indicates it should be ignored.
1409  ## componentList a list of components to be transformed and their weights. This list will not be empty.
1410  ## cachingMode whether the points should be added/updated in the pointCache, or restored from
1411  ## the pointCache, or transform using the original values in the pointCache.
1412  ## pointCache used to store for undo and restore points during undo
1413  ## freezePlane used for symmetric transformation of components. A value of None indicates
1414  ## it is not used and there is no symmetric transformation.
1415  ##
1416 
1417  ## For example purposes only, use the default MPxSurfaceShape.weightedTransformUsing() if the
1418  ## useWeightedTransformUsingFunction is False
1419  ##
1420  plg_useWeightedTransformUsingFunction = om.MPlug( self.thisMObject(), apiMesh.useWeightedTransformUsingFunction )
1421  val_useWeightedTransformUsingFunction = plg_useWeightedTransformUsingFunction.asBool()
1422  if not val_useWeightedTransformUsingFunction:
1423  om.MPxSurfaceShape.weightedTransformUsing(self, xform, space, componentList, cachingMode, pointCache, freezePlane)
1424  self.signalDirtyToViewport()
1425  return
1426 
1427  ## Create cachingMode boolean values for clearer reading of conditional code below
1428  ##
1429  savePoints = (cachingMode == om.MPxSurfaceShape.kSavePoints and pointCache is not None)
1430  updatePoints = (cachingMode == om.MPxSurfaceShape.kUpdatePoints and pointCache is not None)
1431  restorePoints = (cachingMode == om.MPxSurfaceShape.kRestorePoints and pointCache is not None)
1432  transformOrigPoints = (cachingMode == om.MPxSurfaceShape.kTransformOriginalPoints and pointCache is not None)
1433 
1434  ## Pre-calculate parameters
1435  spaceInv = om.MMatrix()
1436  if space:
1437  spaceInv = space.inverse()
1438 
1439  ## Traverse the componentList and modify the control points
1440  ##
1441  geometry = self.meshGeom()
1442  almostZero = 1.0e-5 ## Hardcoded tolerance
1443  pointCacheIndex = 0
1444  setSizeIncrement = True
1445 
1446  for comp in componentList:
1447  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1448  elemCount = fnComp.elementCount
1449  hasWeights = fnComp.hasWeights
1450  hasSeam = (freezePlane is not None)
1451 
1452  if savePoints and setSizeIncrement:
1453  pointCache.sizeIncrement = elemCount
1454  setSizeIncrement = False
1455 
1456  for idx in xrange(elementCount):
1457  elemIndex = fnComp.element( idx )
1458  perc = 1.0
1459  if hasWeights:
1460  perc = fnComp.weight(idx).influence() ## get the weight for the component
1461 
1462  ## Only act upon points (store in pointCache, transform, etc) that have a non-zero weight
1463  if perc > almostZero: ## if the point has enough weight to be transformed
1464  if restorePoints:
1465  ## restore the original point from the point cache
1466  geometry.vertices[elemIndex] = om.MVector( pointCache[pointCacheIndex] )
1467  pointCacheIndex += 1
1468 
1469  else: ## perform point transformation
1470  ## Update the pointCache with the original value
1471  if savePoints:
1472  pointCache.append( geometry.vertices[elemIndex] )
1473 
1474  elif transformOrigPoints: ## start by reverting points back to their original values stored in the pointCache for the transformation
1475  geometry.vertices[elemIndex] = om.MVector( pointCache[pointCacheIndex] )
1476 
1477  elif updatePoints: ## update the pointCache with the current values
1478  pointCache[pointCacheIndex] = geometry.vertices[elemIndex]
1479 
1480  ## Compute interpolated transformation matrix
1481  mat = om.MMatrix()
1482  if perc == 1.0:
1483  mat = xform.asMatrix()
1484  elif space:
1485  mat = space * xform.asMatrix(perc) * spaceInv
1486  else:
1487  mat = xform.asMatrix(perc)
1488 
1489  ## transform to new position
1490  currPt = newPt = geometry.vertices[elemIndex]
1491  newPt *= mat
1492 
1493  ## handle symmetry and reflection
1494  if hasSeam and fnComp.weight(idx).seam() > 0.0:
1495  newPt += freezePlane.normal() * (fnComp.weight(idx).seam() * (freezePlane.directedDistance(currPt) - freezePlane.directedDistance(newPt)))
1496 
1497  ## Update the geometry with the new point
1498  geometry.vertices[elemIndex] = newPt
1499  pointCacheIndex += 1
1500 
1501  ## Update the surface
1502  self.updateCachedSurface( geometry, componentList )
1503 
1504  def weightedTweakUsing(self, xform, space, componentList, cachingMode, pointCache, freezePlane, handle):
1505  ##
1506  ## Description
1507  ##
1508  ## Transforms the given soft-selected components interpolated using the specified weights.
1509  ## This method is used by the move, rotate, and scale tools in component mode when the
1510  ## tweaks for the shape are stored on a separate tweak node.
1511  ## The bounding box has to be updated here, so do the normals and
1512  ## any other attributes that depend on vertex positions.
1513  ##
1514  ## It is similar to the tweakUsing() virtual function and is based on apiMesh.tweakUsing().
1515  ##
1516  ##
1517  ## Arguments
1518  ##
1519  ## xform the matrix representing the transformation that is to be applied to the components
1520  ## space the matrix representing the transformation space to perform the interpolated transformation.
1521  ## A value of None indicates it should be ignored.
1522  ## componentList a list of components to be transformed and their weights. This list will not be empty.
1523  ## cachingMode whether the points should be added/updated in the pointCache, or restored from
1524  ## the pointCache, or transform using use the original values in the pointCache.
1525  ## pointCache used to store for undo and restore points during undo
1526  ## freezePlane used for symmetric transformation of components. A value of None indicates
1527  ## it is not used and there is no symmetric transformation.
1528  ## handle - handle to the attribute on the tweak node where the
1529  ## tweaks should be stored
1530  ##
1531 
1532  ## For example purposes only, use the default MPxSurfaceShape.weightedTweakUsing() if the
1533  ## useWeightedTweakUsingFunction is False
1534  ##
1535  plg_useWeightedTweakUsingFunction = om.MPlug( self.thisMObject(), apiMesh.useWeightedTweakUsingFunction )
1536  val_useWeightedTweakUsingFunction = plg_useWeightedTweakUsingFunction.asBool()
1537  if not val_useWeightedTweakUsingFunction:
1538  om.MPxSurfaceShape.weightedTweakUsing(self, xform, space, componentList, cachingMode, pointCache, freezePlane, handle)
1539  return
1540 
1541  geometry = self.meshGeom()
1542 
1543  ## Create cachingMode boolean values for clearer reading of conditional code below
1544  ##
1545  savePoints = (cachingMode == om.MPxSurfaceShape.kSavePoints and pointCache is not None)
1546  updatePoints = (cachingMode == om.MPxSurfaceShape.kUpdatePoints and pointCache is not None)
1547  restorePoints = (cachingMode == om.MPxSurfaceShape.kRestorePoints and pointCache is not None)
1548  transformOrigPoints = (cachingMode == om.MPxSurfaceShape.kTransformOriginalPoints and pointCache is not None)
1549 
1550  builder = handle.builder()
1551 
1552  cacheIndex = 0
1553  cacheLen = 0
1554  if pointCache:
1555  cacheLen = len(pointCache)
1556 
1557  if restorePoints:
1558  ## restore points from the pointCache
1559  ##
1560  ## traverse the component list
1561  ##
1562  for comp in componentList:
1563  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1564  elemCount = fnComp.elementCount
1565 
1566  for idx in xrange(elementCount):
1567  elemIndex = fnComp.element( idx )
1568  cachePt = pointCache[cacheIndex]
1569  elem = builder.addElement( elemIndex )
1570  elem.set3Double(cachePt.x, cachePt.y, cachePt.z)
1571  cacheIndex += 1
1572  if cacheIndex >= cacheLen:
1573  break
1574 
1575  else:
1576  ## Tweak the points. If savePoints is True, also save the tweaks in the
1577  ## pointCache. If updatePoints is True, add the new tweaks to the existing
1578  ## data in the pointCache.
1579  ##
1580 
1581  ## Specify a few parameters (for weighted transformation)
1582  almostZero = 1.0e-5 ## Hardcoded tolerance
1583  setSizeIncrement = True
1584  spaceInv = om.MMatrix()
1585  if space:
1586  spaceInv = space.inverse()
1587 
1588  for comp in componentList:
1589  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
1590  elemCount = fnComp.elementCount
1591  hasWeights = fnComp.hasWeights ## (for weighted transformation)
1592  hasSeam = (freezePlane is not None) ## (for weighted transformation)
1593 
1594  if savePoints and setSizeIncrement:
1595  pointCache.sizeIncrement = elemCount
1596  setSizeIncrement = False
1597 
1598  for idx in xrange(elementCount):
1599  elemIndex = fnComp.element( idx )
1600  perc = 1.0
1601  if hasWeights:
1602  perc = fnComp.weight(idx).influence() ## get the weight for the component
1603 
1604  ## Only act upon points (store in pointCache, transform, etc) that have a non-zero weight
1605  if perc > almostZero: ## if the point has enough weight to be transformed (for weighted transformation)
1606 
1607  ## Compute interpolated transformation matrix (for weighted transformation)
1608  ##
1609  mat = om.MMatrix()
1610  if perc == 1.0:
1611  mat = xform.asMatrix()
1612  elif space:
1613  mat = space * xform.asMatrix(perc) * spaceInv
1614  else:
1615  mat = xform.asMatrix(perc)
1616 
1617  ## Start by reverting points back to their original values stored in
1618  ## the pointCache for the transformation
1619  ##
1620  if transformOrigPoints:
1621  geometry.vertices[elemIndex] = om.MVector( pointCache[cacheIndex] )
1622 
1623  ## Perform transformation of the point
1624  ##
1625  currPt = newPt = geometry.vertices[elemIndex]
1626  newPt *= mat
1627 
1628  ## Handle symmetry and reflection (for weighted transformation)
1629  ##
1630  if hasSeam and fnComp.weight(idx).seam() > 0.0:
1631  newPt += freezePlane.normal() * (fnComp.weight(idx).seam() * (freezePlane.directedDistance(currPt) - freezePlane.directedDistance(newPt)))
1632 
1633  ## Calculate deltas and final positions
1634  delta = newPt - currPt
1635 
1636  elem = builder.addElement( elemIndex )
1637  elem.set3Double(delta.x, delta.y, delta.z)
1638 
1639  if savePoints:
1640  ## store the points in the pointCache for undo
1641  ##
1642  pointCache.append(delta*(-1.0))
1643  elif updatePoints and cacheIndex < cacheLen:
1644  pointCache[cacheIndex] = pointCache[cacheIndex] - delta
1645  cacheIndex += 1
1646 
1647  ## Set the builder into the handle.
1648  ##
1649  handle.set(builder)
1650 
1651  ## Tell maya the bounding box for this object has changed
1652  ## and thus "boundingBox()" needs to be called.
1653  ##
1654  self.childChanged( om.MPxSurfaceShape.kBoundingBoxChanged )
1655 
1656  ## Support the move tools normal/u/v mode (components)
1657  ##
1658  def vertexOffsetDirection(self, component, direction, mode, normalize):
1659  ##
1660  ## Description
1661  ##
1662  ## Returns offsets for the given components to be used my the
1663  ## move tool in normal/u/v mode.
1664  ##
1665  ## Arguments
1666  ##
1667  ## component - components to calculate offsets for
1668  ## direction - array of offsets to be filled
1669  ## mode - the type of offset to be calculated
1670  ## normalize - specifies whether the offsets should be normalized
1671  ##
1672  ## Returns
1673  ##
1674  ## True if the offsets could be calculated, False otherwise
1675  ##
1676 
1677  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(component) )
1678  if component.apiType() != om.MFn.kMeshVertComponent:
1679  return False
1680 
1681  geometry = self.meshGeom()
1682  if not geometry:
1683  return False
1684 
1685  ## For each vertex add the appropriate offset
1686  ##
1687  for idx in fnComp:
1688  normal = geometry.normals[ idx ]
1689 
1690  if mode == om.MPxSurfaceShape.kNormal:
1691  if normalize:
1692  normal.normalize()
1693  direction.append( normal )
1694 
1695  else:
1696  ## Construct an orthonormal basis from the normal
1697  ## uAxis, and vAxis are the new vectors.
1698  ##
1699  normal.normalize()
1700 
1701  i = 0
1702  a = math.abs(normal[0])
1703 
1704  if a < math.abs(normal[1]):
1705  i = 1
1706  a = fabs(normal[1])
1707 
1708  if a < math.abs(normal[2]):
1709  i = 2
1710 
1711  j = (i+1)%3
1712  k = (j+1)%3
1713 
1714  a = math.sqrt(normal[i]*normal[i] + normal[j]*normal[j])
1715  uAxis[i] = -normal[j]/a
1716  uAxis[j] = normal[i]/a
1717  uAxis[k] = 0.0
1718  vAxis = normal^uAxis
1719 
1720  if mode == om.MPxSurfaceShape.kUTangent or mode == om.MPxSurfaceShape.kUVNTriad:
1721  if normalize:
1722  uAxis.normalize()
1723  direction.append( uAxis )
1724 
1725  if mode == om.MPxSurfaceShape.kVTangent or mode == om.MPxSurfaceShape.kUVNTriad:
1726  if normalize:
1727  vAxis.normalize()
1728  direction.append( vAxis )
1729 
1730  if mode == om.MPxSurfaceShape.kUVNTriad:
1731  if normalize:
1732  normal.normalize()
1733  direction.append( normal )
1734 
1735  return True
1736 
1737  ## Bounding box methods
1738  ##
1739  def isBounded(self):
1740  ##
1741  ## Description
1742  ##
1743  ## Specifies that this object has a boundingBox.
1744  ##
1745 
1746  return True
1747 
1748  def boundingBox(self):
1749  ##
1750  ## Description
1751  ##
1752  ## Returns the bounding box for this object.
1753  ## It is a good idea not to recompute here as this funcion is called often.
1754  ##
1755  if self.fShapeDirty:
1756  # Update:
1757  self.meshObject()
1758 
1759  thisNode = self.thisMObject()
1760  c1Plug = om.MPlug( thisNode, apiMesh.bboxCorner1 )
1761  c2Plug = om.MPlug( thisNode, apiMesh.bboxCorner2 )
1762  corner1Object = c1Plug.asMObject()
1763  corner2Object = c2Plug.asMObject()
1764 
1765  fnData = om.MFnNumericData()
1766  fnData.setObject( corner1Object )
1767  corner1 = fnData.getData()
1768 
1769  fnData.setObject( corner2Object )
1770  corner2 = fnData.getData()
1771 
1772  corner1Point = om.MPoint( corner1[0], corner1[1], corner1[2] )
1773  corner2Point = om.MPoint( corner2[0], corner2[1], corner2[2] )
1774 
1775  return om.MBoundingBox( corner1Point, corner2Point )
1776 
1777  ## Associates a user defined iterator with the shape (components)
1778  ##
1779  def geometryIteratorSetup(self, componentList, components, forReadOnly=False):
1780  ##
1781  ## Description
1782  ##
1783  ## Creates a geometry iterator compatible with his shape.
1784  ##
1785  ## Arguments
1786  ##
1787  ## componentList - list of components to be iterated
1788  ## components - component to be iterator
1789  ## forReadOnly -
1790  ##
1791  ## Returns
1792  ##
1793  ## An iterator for the components
1794  ##
1795 
1796  if components.isNull():
1797  vtxComponents = om.MObjectArray([self.convertToVertexComponent(c) for c in componentList])
1798  return apiMeshGeomIterator( self.meshGeom(), vtxComponents )
1799 
1800  return apiMeshGeomIterator( self.meshGeom(), self.convertToVertexComponent(components) )
1801 
1802  def acceptsGeometryIterator(self, arg0, arg1=None, arg2=None):
1803  ##
1804  ## Description
1805  ##
1806  ## Specifies that this shape can provide an iterator for getting/setting
1807  ## control point values.
1808  ##
1809  ## Arguments
1810  ##
1811  ## writable - maya asks for an iterator that can set points if this is True
1812  ##
1813  ## OR
1814  ##
1815  ## component - the component
1816  ## writable - maya asks for an iterator that can set points if this is True
1817  ## forReadOnly - maya asking for an iterator for querying only
1818  ##
1819 
1820  return True
1821 
1822  ##########################################################
1823  ##
1824  ## Helper methods
1825  ##
1826  ##########################################################
1827 
1828  def hasHistory(self):
1829  ##
1830  ## Description
1831  ##
1832  ## Returns True if the shape has input history, False otherwise.
1833  ##
1834  return self.fHasHistoryOnCreate
1835 
1836  def shapeDirty(self):
1837  ##
1838  ## Description
1839  ##
1840  ## Returns True if the input surface of the shape has been dirtied since
1841  ## the last reset of the flag
1842  ##
1843  return self.fShapeDirty
1844 
1845  def resetShapeDirty(self):
1846  ##
1847  ## Description
1848  ##
1849  ## Reset the shape dirty state of the node
1850  ##
1851  self.fShapeDirty = False
1852 
1853  def materialDirty(self):
1854  ##
1855  ## Description
1856  ##
1857  ## Returns true if the shading group of the shape has been changed since
1858  ## the last reset of the flag
1859  ##
1860  return self.fMaterialDirty
1861 
1862  def setMaterialDirty(self, dirty):
1863  ##
1864  ## Description
1865  ##
1866  ## Reset the material dirty state of the node
1867  ##
1868  self.fMaterialDirty = dirty
1869 
1870  def computeInputSurface(self, plug, datablock):
1871  ##
1872  ## Description
1873  ##
1874  ## If there is input history, evaluate the input attribute
1875  ##
1876 
1877  ## Get the input surface if there is history
1878  ##
1879  if self.hasHistory():
1880  inputHandle = datablock.inputValue( apiMesh.inputSurface )
1881 
1882  surf = inputHandle.asPluginData()
1883  if not isinstance(surf, apiMeshData):
1884  raise RuntimeError("computeInputSurface : invalid inputSurface data found")
1885 
1886  ## Create the cachedSurface and copy the input surface into it
1887  ##
1888  fnDataCreator = om.MFnPluginData()
1889  fnDataCreator.create( apiMeshData.id )
1890 
1891  newCachedData = fnDataCreator.data()
1892  if not isinstance(newCachedData, apiMeshData):
1893  raise RuntimeError("computeInputSurface : invalid proxy cached apiMeshData object")
1894 
1895  newCachedData.fGeometry.copy(surf.fGeometry)
1896 
1897  cachedHandle = datablock.outputValue( apiMesh.cachedSurface )
1898  if not isinstance(cachedHandle, om.MDataHandle):
1899  raise RuntimeError("computeInputSurface : invalid cachedSurface")
1900 
1901  cachedHandle.setMPxData( newCachedData )
1902 
1903  def computeOutputSurface(self, plug, datablock):
1904  ##
1905  ## Description
1906  ##
1907  ## Compute the outputSurface attribute.
1908  ##
1909  ## If there is no history, use cachedSurface as the
1910  ## input surface. All tweaks will get written directly
1911  ## to it. Output is just a copy of the cached surface
1912  ## that can be connected etc.
1913  ##
1914 
1915  ## Check for an input surface. The input surface, if it
1916  ## exists, is copied to the cached surface.
1917  ##
1918  self.computeInputSurface( plug, datablock )
1919 
1920  ## Get a handle to the cached data
1921  ##
1922  cachedHandle = datablock.outputValue( apiMesh.cachedSurface )
1923  if not isinstance(cachedHandle, om.MDataHandle):
1924  raise RuntimeError("computeOutputSurface : invalid cachedSurface")
1925 
1926  cached = cachedHandle.asPluginData()
1927  if not isinstance(cached, apiMeshData):
1928  raise RuntimeError("computeOutputSurface : invalid cachedSurface data found")
1929 
1930  datablock.setClean( plug )
1931 
1932  ## Apply any vertex offsets.
1933  ##
1934  if self.hasHistory():
1935  self.applyTweaks( datablock, cached.fGeometry )
1936 
1937  else:
1938  cpHandle = datablock.inputArrayValue( om.MPxSurfaceShape.mControlPoints )
1939  cpHandle.setAllClean()
1940 
1941  ## Create some output data
1942  ##
1943  fnDataCreator = om.MFnPluginData()
1944  fnDataCreator.create( apiMeshData.id )
1945 
1946  newData = fnDataCreator.data()
1947  if not isinstance(newData, apiMeshData):
1948  raise RuntimeError("computeOutputSurface : invalid proxy cached apiMeshData object")
1949 
1950  ## Copy the data
1951  ##
1952  newData.fGeometry.copy(cached.fGeometry)
1953 
1954  ## Assign the new data to the outputSurface handle
1955  ##
1956  outHandle = datablock.outputValue( apiMesh.outputSurface )
1957  outHandle.setMPxData( newData )
1958 
1959  ## Update the bounding box attributes
1960  ##
1961  self.computeBoundingBox( datablock )
1962 
1963  def computeWorldSurface(self, plug, datablock):
1964  ##
1965  ## Description
1966  ##
1967  ## Compute the worldSurface attribute.
1968  ##
1969 
1970  self.computeOutputSurface( plug, datablock )
1971  inHandle = datablock.outputValue( apiMesh.outputSurface )
1972  outSurf = inHandle.asPluginData()
1973  if not isinstance(outSurf, apiMeshData):
1974  raise RuntimeError("computeWorldSurface : invalid outSurf")
1975 
1976  ## Create some output data
1977  ##
1978  fnDataCreator = om.MFnPluginData()
1979  fnDataCreator.create( apiMeshData.id )
1980 
1981  newData = fnDataCreator.data()
1982  if not isinstance(newData, apiMeshData):
1983  raise RuntimeError("computeWorldSurface : invalid proxy cached apiMeshData object")
1984 
1985  ## Get worldMatrix from MPxSurfaceShape and set it to MPxGeometryData
1986  worldMat = self.getWorldMatrix(datablock, 0)
1987  newData.matrix = worldMat
1988 
1989  ## Copy the data
1990  ##
1991  newData.fGeometry.copy( outSurf.fGeometry )
1992 
1993  ## Assign the new data to the outputSurface handle
1994  ##
1995  arrayIndex = plug.logicalIndex()
1996 
1997  worldHandle = datablock.outputArrayValue( apiMesh.worldSurface )
1998  builder = worldHandle.builder()
1999  outHandle = builder.addElement( arrayIndex )
2000 
2001  outHandle.setMPxData( newData )
2002 
2003  def computeBoundingBox(self, datablock):
2004  ##
2005  ## Description
2006  ##
2007  ## Use the larges/smallest vertex positions to set the corners
2008  ## of the bounding box.
2009  ##
2010 
2011  ## Update bounding box
2012  ##
2013  lowerHandle = datablock.outputValue( apiMesh.bboxCorner1 )
2014  upperHandle = datablock.outputValue( apiMesh.bboxCorner2 )
2015 
2016  geometry = self.meshGeom()
2017  cnt = len(geometry.vertices)
2018  if cnt == 0:
2019  return
2020 
2021  ## This clears any old bbox values
2022  ##
2023  tmppnt = geometry.vertices[0]
2024  lower = [ tmppnt[0], tmppnt[1], tmppnt[2] ]
2025  upper = [ tmppnt[0], tmppnt[1], tmppnt[2] ]
2026 
2027  for i in xrange(cnt):
2028  pnt = geometry.vertices[i]
2029 
2030  if pnt[0] < lower[0]: lower[0] = pnt[0]
2031  if pnt[1] < lower[1]: lower[1] = pnt[1]
2032  if pnt[2] < lower[2]: lower[2] = pnt[2]
2033 
2034  if pnt[0] > upper[0]: upper[0] = pnt[0]
2035  if pnt[1] > upper[1]: upper[1] = pnt[1]
2036  if pnt[2] > upper[2]: upper[2] = pnt[2]
2037 
2038  lowerHandle.set3Double(lower[0], lower[1], lower[2])
2039  upperHandle.set3Double(upper[0], upper[1], upper[2])
2040 
2041  lowerHandle.setClean()
2042  upperHandle.setClean()
2043 
2044  ## Signal that the bounding box has changed.
2045  ##
2046  self.childChanged( om.MPxSurfaceShape.kBoundingBoxChanged )
2047 
2048  def convertToVertexComponent(self, components):
2049  """
2050  Converts edge and face components into vertex components. This
2051  allows applying transform offsets to the vertex when edge or faces
2052  are selected.
2053  """
2054  retVal = components
2055 
2056  try:
2057  srcComponent = om.MFnSingleIndexedComponent(components)
2058  srcComponentType = srcComponent.componentType
2059  except:
2060  return components
2061 
2062  if srcComponentType != om.MFn.kMeshVertComponent:
2063  srcIndices = set(srcComponent.getElements())
2064  retVal = srcComponent.create(om.MFn.kMeshVertComponent)
2065  vtxComponent = om.MFnSingleIndexedComponent(retVal)
2066 
2067  geomPtr = self.meshGeom()
2068 
2069  base = 0
2070  edgeId = 0
2071  for faceIdx in xrange(0, geomPtr.faceCount):
2072  # ignore degenerate faces
2073  numVerts = geomPtr.face_counts[faceIdx]
2074  if numVerts > 2:
2075  for v in xrange(0, numVerts):
2076  if srcComponentType == om.MFn.kMeshEdgeComponent:
2077  if edgeId in srcIndices:
2078  vindex1 = base + (v % numVerts)
2079  vindex2 = base + ((v+1) % numVerts)
2080 
2081  vertexId1 = geomPtr.face_connects[vindex1]
2082  vertexId2 = geomPtr.face_connects[vindex2]
2083 
2084  vtxComponent.addElement(vertexId1)
2085  vtxComponent.addElement(vertexId2)
2086 
2087  edgeId += 1
2088  else:
2089  # Face component:
2090  if faceIdx in srcIndices:
2091  vindex = base + (v % numVerts)
2092  vertexId = geomPtr.face_connects[vindex]
2093  vtxComponent.addElement(vertexId)
2094 
2095  base += numVerts;
2096 
2097  return retVal
2098 
2099  def applyTweaks(self, datablock, geometry):
2100  ##
2101  ## Description
2102  ##
2103  ## If the shape has history, apply any tweaks (offsets) made
2104  ## to the control points.
2105  ##
2106 
2107  cpHandle = datablock.inputArrayValue( om.MPxSurfaceShape.mControlPoints )
2108 
2109  ## Loop through the component list and transform each vertex.
2110  ##
2111  while not cpHandle.isDone():
2112  elemIndex = cpHandle.elementLogicalIndex()
2113  pntHandle = cpHandle.outputValue()
2114 
2115  offset = pntHandle.asDouble3()
2116 
2117  ## Apply the tweaks to the output surface
2118  ##
2119  geometry.vertices[elemIndex] += om.MPoint(offset[0],offset[1],offset[2])
2120 
2121  cpHandle.next()
2122 
2123 
2124  def updateCachedSurface(self, geometry, componentList):
2125  ##
2126  ## Description
2127  ##
2128  ## Update the cached surface attribute, handle the tweak history as appropriate,
2129  ## and trigger a bounding box change calculation.
2130  ##
2131  ## Arguments
2132  ## geometry - the modified geometry to apply to the cached surface attribute
2133  ##
2134 
2135  ## Retrieve the value of the cached surface attribute.
2136  ## We will set the new geometry data into the cached surface attribute
2137  ##
2138  ## Access the datablock directly. This code has to be efficient
2139  ## and so we bypass the compute mechanism completely.
2140  ## NOTE: In general we should always go though compute for getting
2141  ## and setting attributes.
2142  ##
2143  datablock = self.forceCache()
2144 
2145  cachedHandle = datablock.outputValue( apiMesh.cachedSurface )
2146  cached = cachedHandle.asPluginData()
2147 
2148  dHandle = datablock.outputValue( om.MPxSurfaceShape.mControlPoints )
2149 
2150  ## If there is history then calculate the tweaks necessary for
2151  ## setting the final positions of the vertices.
2152  ##
2153  if self.hasHistory() and cached:
2154  ## Since the shape has history, we need to store the tweaks (deltas)
2155  ## between the input shape and the tweaked shape in the control points
2156  ## attribute.
2157  ##
2158  self.buildControlPoints( datablock, len(geometry.vertices) )
2159 
2160  cpHandle = om.MArrayDataHandle( dHandle )
2161 
2162  ## Loop through the component list and transform each vertex.
2163  ##
2164  for comp in componentList:
2165  fnComp = om.MFnSingleIndexedComponent( self.convertToVertexComponent(comp) )
2166  for elemIndex in fnComp.getElements():
2167  cpHandle.jumpToLogicalElement( elemIndex )
2168  pntHandle = cpHandle.outputValue()
2169 
2170  pnt = pntHandle.asDouble3()
2171 
2172  oldPnt = cached.fGeometry.vertices[elemIndex]
2173  newPnt = geometry.vertices[elemIndex]
2174  offset = newPnt - oldPnt
2175 
2176  pnt[0] += offset[0]
2177  pnt[1] += offset[1]
2178  pnt[2] += offset[2]
2179 
2180  pntHandle.set3Double(pnt[0], pnt[1], pnt[2])
2181 
2182  ## Copy outputSurface to cachedSurface
2183  ##
2184  if cached:
2185  cached.fGeometry.copy(geometry)
2186 
2187  pCPs = om.MPlug( self.thisMObject(), om.MPxSurfaceShape.mControlPoints)
2188  pCPs.setMDataHandle(dHandle)
2189 
2190  ## Moving vertices will likely change the bounding box.
2191  ##
2192  self.computeBoundingBox( datablock )
2193 
2194  ## Tell maya the bounding box for this object has changed
2195  ## and thus "boundingBox()" needs to be called.
2196  ##
2197  self.childChanged( om.MPxSurfaceShape.kBoundingBoxChanged )
2198 
2199  ## Signal to the viewport that it needs to update the object
2200  self.signalDirtyToViewport()
2201 
2202  def getPointValue(self, pntInd):
2203  ##
2204  ## Description
2205  ##
2206  ## Helper function to return the value of a given vertex
2207  ## from the cachedMesh.
2208  ##
2209  geometry = self.cachedGeom()
2210  if geometry:
2211  return geometry.vertices[ pntInd ]
2212 
2213  return om.MPoint()
2214 
2215  def getChannelValue(self, pntInd, vlInd):
2216  ##
2217  ## Description
2218  ##
2219  ## Helper function to return the value of a given vertex
2220  ## from the cachedMesh.
2221  ##
2222  geometry = self.cachedGeom()
2223  if geometry:
2224  return geometry.vertices[ pntInd ][ vlInd ]
2225 
2226  return 0
2227 
2228  def setPointValue(self, pntInd, val):
2229  ##
2230  ## Description
2231  ##
2232  ## Helper function to set the value of a given vertex
2233  ## in the cachedMesh.
2234  ##
2235  geometry = self.cachedGeom()
2236  if geometry:
2237  geometry.vertices[ pntInd ] = om.MPoint(val)
2238 
2239  self.verticesUpdated()
2240 
2241  def setChannelValue(self, pntInd, vlInd, val):
2242  ##
2243  ## Description
2244  ##
2245  ## Helper function to set the value of a given vertex
2246  ## in the cachedMesh.
2247  ##
2248  geometry = self.cachedGeom()
2249  if geometry:
2250  geometry.vertices[ pntInd ][ vlInd ] = val
2251 
2252  self.verticesUpdated()
2253 
2254  def meshObject(self):
2255  ##
2256  ## Description
2257  ##
2258  ## Get a reference to the mesh data (outputSurface)
2259  ## from the datablock. If dirty then an evaluation is
2260  ## triggered.
2261  ##
2262 
2263  ## Get the datablock for this node
2264  ##
2265  datablock = self.forceCache()
2266 
2267  ## Calling inputValue will force a recompute if the
2268  ## connection is dirty. This means the most up-to-date
2269  ## mesh data will be returned by this method.
2270  ##
2271  handle = datablock.inputValue( apiMesh.outputSurface )
2272  return handle.data()
2273 
2274  def meshGeom(self):
2275  ##
2276  ## Description
2277  ##
2278  ## Returns the apiMeshGeom of the outputSurface.
2279  ##
2280 
2281  fnData = om.MFnPluginData( self.meshObject() )
2282  data = fnData.data()
2283  if not isinstance(data, apiMeshData):
2284  raise RuntimeError("meshGeom : failed to get apiMeshData")
2285 
2286  return data.fGeometry
2287 
2288  def cachedObject(self):
2289  ##
2290  ## Description
2291  ##
2292  ## Get a reference to the mesh data (cachedSurface)
2293  ## from the datablock. No evaluation is triggered.
2294  ##
2295 
2296  ## Get the datablock for this node
2297  ##
2298  datablock = self.forceCache()
2299  handle = datablock.outputValue( apiMesh.cachedSurface )
2300  return handle.data()
2301 
2302  def cachedGeom(self):
2303  ##
2304  ## Description
2305  ##
2306  ## Returns the apiMeshGeom of the cachedSurface.
2307  ##
2308 
2309  fnData = om.MFnPluginData( self.cachedObject() )
2310  data = fnData.data()
2311  if not isinstance(data, apiMeshData):
2312  raise RuntimeError("cachedGeom : failed to get apiMeshData")
2313 
2314  return data.fGeometry
2315 
2316  def buildControlPoints(self, datablock, count):
2317  ##
2318  ## Description
2319  ##
2320  ## Check the controlPoints array. If there is input history
2321  ## then we will use this array to store tweaks (vertex movements).
2322  ##
2323 
2324  cpH = datablock.outputArrayValue( om.MPxSurfaceShape.mControlPoints )
2325 
2326  oldBuilder = cpH.builder()
2327  if count != len(oldBuilder):
2328  ## Make and set the new builder based on the
2329  ## info from the old builder.
2330  builder = om.MArrayDataBuilder( oldBuilder )
2331 
2332  for vtx in xrange(count):
2333  builder.addElement( vtx )
2334 
2335  cpH.set( builder )
2336 
2337  cpH.setAllClean()
2338 
2339  def verticesUpdated(self):
2340  ##
2341  ## Description
2342  ##
2343  ## Helper function to tell maya that this shape's
2344  ## vertices have updated and that the bbox needs
2345  ## to be recalculated and the shape redrawn.
2346  ##
2347  self.childChanged( om.MPxSurfaceShape.kBoundingBoxChanged )
2348  self.childChanged( om.MPxSurfaceShape.kObjectChanged )
2349 
2350  def setShapeDirty(self):
2351  self.fShapeDirty = True
2352 
2353  def notifyViewport(self):
2354  omr.MRenderer.setGeometryDrawDirty(self.thisMObject())
2355 
2356  def signalDirtyToViewport(self):
2357  self.setShapeDirty()
2358  self.notifyViewport()
2359 
2360 ################################################################################
2361 ##
2362 ## apiMeshShapeUI
2363 ##
2364 ## Encapsulates the UI portion of a user defined shape. All of the
2365 ## drawing and selection code goes here.
2366 ##
2367 ################################################################################
2368 class apiMeshUI(omui.MPxSurfaceShapeUI):
2369 
2370  @staticmethod
2371  def creator():
2372  return apiMeshUI()
2373 
2374  def __init__(self):
2375  omui.MPxSurfaceShapeUI.__init__(self)
2376 
2377  #####################################################################
2378  ##
2379  ## Overrides
2380  ##
2381  #####################################################################
2382 
2383  ## Main draw routine for UV editor. This is called by maya when the
2384  ## shape is selected and the UV texture window is visible.
2385  ##
2386  def drawUV(self, view, info):
2387  ##
2388  ## Description:
2389  ## Main entry point for UV drawing. This method is called by the UV
2390  ## texture editor when the shape is 'active'.
2391  ##
2392  ## Input:
2393  ## A 3dView.
2394  ##
2395 
2396  meshNode = self.surfaceShape()
2397  geom = meshNode.meshGeom()
2398 
2399  if geom.uvcoords.uvcount() > 0:
2400  view.setDrawColor( om.MColor( (1.0, 0.0, 0.0) ) )
2401 
2402  if info.drawingFunction == omui.MTextureEditorDrawInfo.kDrawWireframe:
2403  self.drawUVWireframe( geom, view, info )
2404 
2405  elif info.drawingFunction == omui.MTextureEditorDrawInfo.kDrawEverything or info.drawingFunction == omui.MTextureEditorDrawInfo.kDrawUVForSelect:
2406  self.drawUVWireframe( geom, view, info )
2407  self.drawUVMapCoordNum( geom, view, info, True )
2408 
2409  else:
2410  self.drawUVWireframe( geom, view, info )
2411 
2412  def canDrawUV(self):
2413  ##
2414  ## Description:
2415  ## Tells Maya that this surface shape supports uv drawing.
2416  ##
2417 
2418  meshNode = self.surfaceShape()
2419  geom = meshNode.meshGeom()
2420  return (geom.uvcoords.uvcount() > 0)
2421 
2422  ## Main selection routine
2423  ##
2424  def select(self, selectInfo, selectionList, worldSpaceSelectPts):
2425  ##
2426  ## Description:
2427  ##
2428  ## Main selection routine
2429  ##
2430  ## Arguments:
2431  ##
2432  ## selectInfo - the selection state information
2433  ## selectionList - the list of selected items to add to
2434  ## worldSpaceSelectPts -
2435  ##
2436 
2437  selected = False
2438  componentSelected = False
2439  hilited = False
2440 
2441  hilited = (selectInfo.displayStatus() == omui.M3dView.kHilite)
2442  if hilited:
2443  componentSelected = self.selectVertices( selectInfo, selectionList, worldSpaceSelectPts )
2444  selected = selected or componentSelected
2445 
2446  if not selected:
2447  meshNode = self.surfaceShape()
2448 
2449  ## NOTE: If the geometry has an intersect routine it should
2450  ## be called here with the selection ray to determine if the
2451  ## the object was selected.
2452 
2453  selected = True
2454  priorityMask = om.MSelectionMask( om.MSelectionMask.kSelectNurbsSurfaces )
2455 
2456  item = om.MSelectionList()
2457  item.add( selectInfo.selectPath() )
2458 
2459  xformedPt = om.MPoint()
2460  if selectInfo.singleSelection():
2461  center = meshNode.boundingBox().center
2462  xformedPt = center
2463  xformedPt *= selectInfo.selectPath().inclusiveMatrix()
2464 
2465  selectInfo.addSelection( item, xformedPt, selectionList, worldSpaceSelectPts, priorityMask, False )
2466 
2467  return selected
2468 
2469  #####################################################################
2470  ##
2471  ## Helper routines
2472  ##
2473  #####################################################################
2474 
2475  def selectVertices(self, selectInfo, selectionList, worldSpaceSelectPts):
2476  ##
2477  ## Description:
2478  ##
2479  ## Vertex selection.
2480  ##
2481  ## Arguments:
2482  ##
2483  ## selectInfo - the selection state information
2484  ## selectionList - the list of selected items to add to
2485  ## worldSpaceSelectPts -
2486  ##
2487 
2488  selected = False
2489  view = selectInfo.view()
2490  path = selectInfo.multiPath()
2491  singleSelection = selectInfo.singleSelection()
2492 
2493  ## Create a component that will store the selected vertices
2494  ##
2495  fnComponent = om.MFnSingleIndexedComponent()
2496  surfaceComponent = fnComponent.create( om.MFn.kMeshVertComponent )
2497 
2498  ## if the user did a single mouse click and we find > 1 selection
2499  ## we will use the alignmentMatrix to find out which is the closest
2500  ##
2501  alignmentMatrix = om.MMatrix()
2502  if singleSelection:
2503  alignmentMatrix = selectInfo.getAlignmentMatrix()
2504 
2505  singlePoint = om.MPoint()
2506  selectionPoint = om.MPoint()
2507  closestPointVertexIndex = -1
2508  previousZ = 0
2509 
2510  ## Get the geometry information
2511  ##
2512  meshNode = self.surfaceShape()
2513  geom = meshNode.meshGeom()
2514 
2515  ## Loop through all vertices of the mesh and
2516  ## see if they lie withing the selection area
2517  ##
2518  for currentPoint in geom.vertices:
2519  ## Sets OpenGL's render mode to select and stores
2520  ## selected items in a pick buffer
2521  ##
2522  view.beginSelect()
2523 
2524  glBegin( GL_POINTS )
2525  glVertex3f( currentPoint[0], currentPoint[1], currentPoint[2] )
2526  glEnd()
2527 
2528  if view.endSelect() > 0: ## Hit count > 0
2529  selected = True
2530 
2531  if singleSelection:
2532  xformedPoint = currentPoint
2533  xformedPoint.homogenize()
2534  xformedPoint *= alignmentMatrix
2535  z = xformedPoint.z
2536  if closestPointVertexIndex < 0 or z > previousZ:
2537  closestPointVertexIndex = vertexIndex
2538  singlePoint = currentPoint
2539  previousZ = z
2540 
2541  else:
2542  ## multiple selection, store all elements
2543  ##
2544  fnComponent.addElement( vertexIndex )
2545 
2546  ## If single selection, insert the closest point into the array
2547  ##
2548  if selected and singleSelection:
2549  fnComponent.addElement(closestPointVertexIndex)
2550 
2551  ## need to get world space position for this vertex
2552  ##
2553  selectionPoint = singlePoint
2554  selectionPoint *= path.inclusiveMatrix()
2555 
2556  ## Add the selected component to the selection list
2557  ##
2558  if selected:
2559  selectionItem = om.MSelectionList()
2560  selectionItem.add( path, surfaceComponent )
2561 
2562  mask = om.MSelectionMask( om.MSelectionMask.kSelectComponentsMask )
2563  selectInfo.addSelection( selectionItem, selectionPoint, selectionList, worldSpaceSelectPts, mask, True )
2564 
2565  return selected
2566 
2567  def drawUVWireframe(self, geom, view, info):
2568  ##
2569  ## Description:
2570  ## Draws the UV layout in wireframe mode.
2571  ##
2572 
2573  view.beginGL()
2574 
2575  ## Draw the polygons
2576  ##
2577  vid = 0
2578  vid_start = vid
2579  for i in xrange(geom.faceCount):
2580  glBegin( GL_LINES )
2581 
2582  vid_start = vid
2583  for v in xrange(geom.face_counts[i]-1):
2584  uvId1 = geom.uvcoords.uvId(vid)
2585  uvId2 = geom.uvcoords.uvId(vid+1)
2586 
2587  uv1 = geom.uvcoords.getUV(uvId1)
2588  uv2 = geom.uvcoords.getUV(uvId2)
2589 
2590  glVertex3f( uv1[0], uv1[1], 0.0 )
2591  glVertex3f( uv2[0], uv2[1], 0.0 )
2592  vid += 1
2593 
2594  uvId1 = geom.uvcoords.uvId(vid)
2595  uvId2 = geom.uvcoords.uvId(vid_start)
2596 
2597  uv1 = geom.uvcoords.getUV(uvId1)
2598  uv2 = geom.uvcoords.getUV(uvId2)
2599 
2600  glVertex3f( uv1[0], uv1[1], 0.0 )
2601  glVertex3f( uv2[0], uv2[1], 0.0 )
2602  vid += 1
2603 
2604  glEnd()
2605 
2606  view.endGL()
2607 
2608  def drawUVMapCoord(self, view, uvId, uv, drawNumbers):
2609  ##
2610  ## Description:
2611  ## Draw the specified uv value into the port view. If drawNumbers is True
2612  ## It will also draw the UV id for the the UV.
2613  ##
2614 
2615  if drawNumbers:
2616  view.drawText( str(uvId), om.MPoint( uv[0], uv[1], 0 ), omui.M3dView.kCenter )
2617 
2618  glVertex3f( uv[0], uv[1], 0.0 )
2619 
2620  def drawUVMapCoordNum(self, geom, view, info, drawNumbers):
2621  ##
2622  ## Description:
2623  ## Draw the UV points for all uvs on this surface shape.
2624  ##
2625 
2626  view.beginGL()
2627 
2628  ptSize = glGetFloatv( GL_POINT_SIZE )
2629  glPointSize( 4.0 )
2630 
2631  for uvId in xrange(geom.uvcoords.uvcount()):
2632  uv = geom.uvcoords.getUV( uvId )
2633  self.drawUVMapCoord( view, uvId, uv, drawNumbers )
2634 
2635  glPointSize( ptSize )
2636 
2637  view.endGL()
2638 
2639 ################################################################################
2640 ##
2641 ## apiMeshCreator
2642 ##
2643 ## A DG node that takes a maya mesh as input and outputs apiMeshData.
2644 ## If there is no input then the node creates a cube or sphere
2645 ## depending on what the shapeType attribute is set to.
2646 ##
2647 ################################################################################
2648 class apiMeshCreator(om.MPxNode):
2649  id = om.MTypeId(0x80089)
2650 
2651  ##########################################################
2652  ##
2653  ## Attributes
2654  ##
2655  ##########################################################
2656  size = None
2657  shapeType = None
2658  inputMesh = None
2659  outputSurface = None
2660 
2661  @staticmethod
2662  def creator():
2663  return apiMeshCreator()
2664 
2665  @staticmethod
2666  def initialize():
2667  typedAttr = om.MFnTypedAttribute()
2668  numericAttr = om.MFnNumericAttribute()
2669  enumAttr = om.MFnEnumAttribute()
2670 
2671  ## ----------------------- INPUTS -------------------------
2672  apiMeshCreator.size = numericAttr.create( "size", "sz", om.MFnNumericData.kDouble, 1 )
2673  numericAttr.array = False
2674  numericAttr.usesArrayDataBuilder = False
2675  numericAttr.hidden = False
2676  numericAttr.keyable = True
2677  om.MPxNode.addAttribute( apiMeshCreator.size )
2678 
2679  apiMeshCreator.shapeType = enumAttr.create( "shapeType", "st", 0 )
2680  enumAttr.addField( "cube", 0 )
2681  enumAttr.addField( "sphere", 1 )
2682  enumAttr.hidden = False
2683  enumAttr.keyable = True
2684  om.MPxNode.addAttribute( apiMeshCreator.shapeType )
2685 
2686  apiMeshCreator.inputMesh = typedAttr.create( "inputMesh", "im", om.MFnData.kMesh, om.MObject.kNullObj )
2687  typedAttr.hidden = True
2688  om.MPxNode.addAttribute( apiMeshCreator.inputMesh )
2689 
2690  ## ----------------------- OUTPUTS -------------------------
2691  apiMeshCreator.outputSurface = typedAttr.create( "outputSurface", "os", apiMeshData.id, om.MObject.kNullObj )
2692  typedAttr.writable = False
2693  om.MPxNode.addAttribute( apiMeshCreator.outputSurface )
2694 
2695  ## ---------- Specify what inputs affect the outputs ----------
2696  ##
2697  om.MPxNode.attributeAffects( apiMeshCreator.inputMesh, apiMeshCreator.outputSurface )
2698  om.MPxNode.attributeAffects( apiMeshCreator.size, apiMeshCreator.outputSurface )
2699  om.MPxNode.attributeAffects( apiMeshCreator.shapeType, apiMeshCreator.outputSurface )
2700 
2701  def __init__(self):
2702  om.MPxNode.__init__(self)
2703 
2704  ##########################################################
2705  ##
2706  ## Overrides
2707  ##
2708  ##########################################################
2709 
2710  def compute(self, plug, datablock):
2711  ##
2712  ## Description
2713  ##
2714  ## When input attributes are dirty this method will be called to
2715  ## recompute the output attributes.
2716  ##
2717 
2718  if plug == apiMeshCreator.outputSurface:
2719  ## Create some user defined geometry data and access the
2720  ## geometry so we can set it
2721  ##
2722  fnDataCreator = om.MFnPluginData()
2723  fnDataCreator.create( apiMeshData.id )
2724 
2725  newData = fnDataCreator.data()
2726  if not isinstance(newData, apiMeshData):
2727  raise RuntimeError("compute : invalid proxy cached apiMeshData object")
2728 
2729  geometry = newData.fGeometry
2730 
2731  ## If there is an input mesh then copy it's values
2732  ## and construct some apiMeshGeom for it.
2733  ##
2734  hasHistory = self.computeInputMesh( plug, datablock, geometry )
2735 
2736  ## There is no input mesh so check the shapeType attribute
2737  ## and create either a cube or a sphere.
2738  ##
2739  if not hasHistory:
2740  sizeHandle = datablock.inputValue( apiMeshCreator.size )
2741  shape_size = sizeHandle.asDouble()
2742  typeHandle = datablock.inputValue( apiMeshCreator.shapeType )
2743  shape_type = typeHandle.asShort()
2744 
2745  if shape_type == 0: ## build a cube
2746  self.buildCube( shape_size, geometry )
2747  elif shape_type == 1: ## build a sphere
2748  self.buildSphere( shape_size, 32, geometry )
2749 
2750  geometry.faceCount = len(geometry.face_counts)
2751 
2752  ## Assign the new data to the outputSurface handle
2753  ##
2754  outHandle = datablock.outputValue( apiMeshCreator.outputSurface )
2755  outHandle.setMPxData( newData )
2756  datablock.setClean( plug )
2757 
2758  ##########################################################
2759  ##
2760  ## Helper methods
2761  ##
2762  ##########################################################
2763 
2764  def computeInputMesh(self, plug, datablock, geometry):
2765  ##
2766  ## Description
2767  ##
2768  ## This function takes an input surface of type kMeshData and converts
2769  ## the geometry into this nodes attributes.
2770  ## Returns kFailure if nothing is connected.
2771  ##
2772 
2773  ## Get the input subdiv
2774  ##
2775  inputData = datablock.inputValue( apiMeshCreator.inputMesh )
2776  surf = inputData.asMesh()
2777 
2778  ## Check if anything is connected
2779  ##
2780  thisObj = self.thisMObject()
2781  surfPlug = om.MPlug( thisObj, apiMeshCreator.inputMesh )
2782  if not surfPlug.isConnected:
2783  datablock.setClean( plug )
2784  return False
2785 
2786  ## Extract the mesh data
2787  ##
2788  surfFn = om.MFnMesh(surf)
2789  geometry.vertices = surfFn.getPoints(om.MSpace.kObject)
2790 
2791  ## Check to see if we have UVs to copy.
2792  ##
2793  hasUVs = surfFn.numUVs() > 0
2794  uvs = surfFn.getUVs()
2795  geometry.uvcoords.ucoord = uvs[0]
2796  geometry.uvcoords.vcoord = uvs[1]
2797 
2798  for i in xrange(surfFn.numPolygons()):
2799  polyVerts = surfFn.getPolygonVertices(i)
2800 
2801  pvc = len(polyVerts)
2802  geometry.face_counts.append( pvc )
2803 
2804  for v in xrange(pvc):
2805  if hasUVs:
2806  uvId = surfFn.getPolygonUVid(i, v)
2807  geometry.uvcoords.faceVertexIndex.append( uvId )
2808  geometry.face_connects.append( polyVerts[v] )
2809 
2810  for n in xrange(len(geometry.vertices)):
2811  normal = surfFn.getVertexNormal(n)
2812  geometry.normals.append( normal )
2813 
2814  return True
2815 
2816  def buildCube(self, cube_size, geometry):
2817  ##
2818  ## Description
2819  ##
2820  ## Constructs a cube
2821  ##
2822 
2823  geometry.vertices.clear()
2824  geometry.normals.clear()
2825  geometry.face_counts.clear()
2826  geometry.face_connects.clear()
2827  geometry.uvcoords.reset()
2828 
2829  geometry.vertices.append( om.MPoint( -cube_size, -cube_size, -cube_size ) )
2830  geometry.vertices.append( om.MPoint( cube_size, -cube_size, -cube_size ) )
2831  geometry.vertices.append( om.MPoint( cube_size, -cube_size, cube_size ) )
2832  geometry.vertices.append( om.MPoint( -cube_size, -cube_size, cube_size ) )
2833  geometry.vertices.append( om.MPoint( -cube_size, cube_size, -cube_size ) )
2834  geometry.vertices.append( om.MPoint( -cube_size, cube_size, cube_size ) )
2835  geometry.vertices.append( om.MPoint( cube_size, cube_size, cube_size ) )
2836  geometry.vertices.append( om.MPoint( cube_size, cube_size, -cube_size ) )
2837 
2838  normal_value = 0.5775
2839  geometry.normals.append( om.MVector( -normal_value, -normal_value, -normal_value ) )
2840  geometry.normals.append( om.MVector( normal_value, -normal_value, -normal_value ) )
2841  geometry.normals.append( om.MVector( normal_value, -normal_value, normal_value ) )
2842  geometry.normals.append( om.MVector( -normal_value, -normal_value, normal_value ) )
2843  geometry.normals.append( om.MVector( -normal_value, normal_value, -normal_value ) )
2844  geometry.normals.append( om.MVector( -normal_value, normal_value, normal_value ) )
2845  geometry.normals.append( om.MVector( normal_value, normal_value, normal_value ) )
2846  geometry.normals.append( om.MVector( normal_value, normal_value, -normal_value ) )
2847 
2848  ## Define the UVs for the cube.
2849  ##
2850  uv_count = 14
2851  uv_pts = [ [ 0.375, 0.0 ],
2852  [ 0.625, 0.0 ],
2853  [ 0.625, 0.25 ],
2854  [ 0.375, 0.25 ],
2855  [ 0.625, 0.5 ],
2856  [ 0.375, 0.5 ],
2857  [ 0.625, 0.75 ],
2858  [ 0.375, 0.75 ],
2859  [ 0.625, 1.0 ],
2860  [ 0.375, 1.0 ],
2861  [ 0.875, 0.0 ],
2862  [ 0.875, 0.25 ],
2863  [ 0.125, 0.0 ],
2864  [ 0.125, 0.25 ] ]
2865 
2866  ## UV Face Vertex Id.
2867  ##
2868  num_face_connects = 24
2869  uv_fvid = [ 0, 1, 2, 3,
2870  3, 2, 4, 5,
2871  5, 4, 6, 7,
2872  7, 6, 8, 9,
2873  1, 10, 11, 2,
2874  12, 0, 3, 13 ]
2875 
2876  for i in xrange(uv_count):
2877  geometry.uvcoords.append_uv( uv_pts[i][0], uv_pts[i][1] )
2878 
2879  for i in xrange(num_face_connects):
2880  geometry.uvcoords.faceVertexIndex.append( uv_fvid[i] )
2881 
2882  ## Set up an array containing the number of vertices
2883  ## for each of the 6 cube faces (4 verticies per face)
2884  ##
2885  num_faces = 6
2886  face_counts = [ 4, 4, 4, 4, 4, 4 ]
2887 
2888  for i in xrange(num_faces):
2889  geometry.face_counts.append( face_counts[i] )
2890 
2891  ## Set up and array to assign vertices from vertices to each face
2892  ##
2893  face_connects = [ 0, 1, 2, 3,
2894  4, 5, 6, 7,
2895  3, 2, 6, 5,
2896  0, 3, 5, 4,
2897  0, 4, 7, 1,
2898  1, 7, 6, 2 ]
2899 
2900  for i in xrange(num_face_connects):
2901  geometry.face_connects.append( face_connects[i] )
2902 
2903  def buildSphere(self, radius, divisions, geometry):
2904  ##
2905  ## Description
2906  ##
2907  ## Create circles of vertices starting with
2908  ## the top pole ending with the botton pole
2909  ##
2910 
2911  geometry.vertices.clear()
2912  geometry.normals.clear()
2913  geometry.face_counts.clear()
2914  geometry.face_connects.clear()
2915  geometry.uvcoords.reset()
2916 
2917  u = -math.pi / 2.
2918  v = -math.pi
2919  u_delta = math.pi / divisions
2920  v_delta = 2 * math.pi / divisions
2921 
2922  topPole = om.MPoint( 0.0, radius, 0.0 )
2923  botPole = om.MPoint( 0.0, -radius, 0.0 )
2924 
2925  ## Build the vertex and normal table
2926  ##
2927  geometry.vertices.append( botPole )
2928  geometry.normals.append( botPole - om.MPoint.kOrigin )
2929 
2930  for i in xrange(divisions-1):
2931  u += u_delta
2932  v = -math.pi
2933 
2934  for j in xrange(divisions):
2935  x = radius * math.cos(u) * math.cos(v)
2936  y = radius * math.sin(u)
2937  z = radius * math.cos(u) * math.sin(v)
2938 
2939  pnt = om.MPoint( x, y, z )
2940  geometry.vertices.append( pnt )
2941  geometry.normals.append( pnt - om.MPoint.kOrigin )
2942  v += v_delta
2943 
2944  geometry.vertices.append( topPole )
2945  geometry.normals.append( topPole - om.MPoint.kOrigin )
2946 
2947  ## Create the connectivity lists
2948  ##
2949  vid = 1
2950  numV = 0
2951  for i in xrange(divisions):
2952  for j in xrange(divisions):
2953  if i == 0:
2954  geometry.face_counts.append( 3 )
2955 
2956  geometry.face_connects.append( 0 )
2957  geometry.face_connects.append( j + vid )
2958  if j == divisions-1:
2959  geometry.face_connects.append( vid )
2960  else:
2961  geometry.face_connects.append( j + vid + 1 )
2962 
2963  elif i == divisions-1:
2964  geometry.face_counts.append( 3 )
2965 
2966  geometry.face_connects.append( j + vid + 1 - divisions )
2967  geometry.face_connects.append( vid + 1 )
2968  if j == divisions-1:
2969  geometry.face_connects.append( vid + 1 - divisions )
2970  else:
2971  geometry.face_connects.append( j + vid + 2 - divisions )
2972 
2973  else:
2974  geometry.face_counts.append( 4 )
2975 
2976  geometry.face_connects.append( j + vid + 1 - divisions )
2977  geometry.face_connects.append( j + vid + 1 )
2978  if j == divisions-1:
2979  geometry.face_connects.append( vid + 1 )
2980  geometry.face_connects.append( vid + 1 - divisions )
2981  else:
2982  geometry.face_connects.append( j + vid + 2 )
2983  geometry.face_connects.append( j + vid + 2 - divisions )
2984 
2985  numV += 1
2986 
2987  vid = numV
2988 
2989 ## Helper class for link lost callback
2990 class ShadedItemUserData(om.MUserData):
2991  def __init__(self, override):
2992  om.MUserData.__init__(self, False)
2993  self.fOverride = override
2994 
2995 ## Custom user data class to attach to face selection render item
2996 ## to help with viewport 2.0 selection
2997 class apiMeshHWSelectionUserData(om.MUserData):
2998  def __init__(self):
2999  om.MUserData.__init__(self, True) ## let Maya clean up
3000  self.fMeshGeom = None
3001 
3002 ################################################################################
3003 ##
3004 ## apiMeshSubSceneOverride
3005 ##
3006 ## Handles vertex data preparation for drawing the user defined shape in
3007 ## Viewport 2.0.
3008 ##
3009 ################################################################################
3010 ## Custom component converter to select components
3011 ## Attached to the vertex, edge and face selection render items
3012 ## respectively apiMeshSubSceneOverride.sVertexSelectionName, apiMeshSubSceneOverride.sEdgeSelectionName
3013 ## and apiMeshSubSceneOverride.sFaceSelectionName
3014 class simpleComponentConverter_subsceneOverride(omr.MPxComponentConverter):
3015  def __init__(self, componentType, selectionType):
3016  omr.MPxComponentConverter.__init__(self)
3017 
3018  self.fComponentType = componentType
3019  self.fSelectionType = selectionType
3020 
3021  self.fComponent = om.MFnSingleIndexedComponent()
3022  self.fComponentObject = om.MObject.kNullObj
3023  self.fLookupTable = []
3024 
3025  def initialize(self, renderItem):
3026  ## Create the component selection object
3027  self.fComponentObject = self.fComponent.create( self.fComponentType )
3028 
3029  ## For face selection,
3030  ## create a lookup table to match triangle intersection with face id :
3031  ## One face may contains more than one triangle
3032  if self.fComponentType == om.MFn.kMeshPolygonComponent:
3033  selectionData = renderItem.customData()
3034  if isinstance(selectionData, apiMeshHWSelectionUserData):
3035  meshGeom = selectionData.fMeshGeom
3036 
3037  ## Allocate faces lookup table
3038  numTriangles = 0
3039  for i in xrange(meshGeom.faceCount):
3040  numVerts = meshGeom.face_counts[i]
3041  if numVerts > 2:
3042  numTriangles += numVerts - 2
3043  self.fLookupTable = [0]*numTriangles
3044 
3045  ## Fill faces lookup table
3046  triId = 0
3047  for faceId in xrange(meshGeom.faceCount):
3048  ## ignore degenerate faces
3049  numVerts = meshGeom.face_counts[faceId]
3050  if numVerts > 2:
3051  for v in xrange(1, numVerts-1):
3052  self.fLookupTable[triId] = faceId
3053  triId += 1
3054 
3055  def addIntersection(self, intersection):
3056  ## Convert the intersection index, which represent the primitive position in the
3057  ## index buffer, to the correct component id
3058 
3059  ## For vertex and edge: the primitive index value is the same as the component id
3060  ## For face: get the face id that matches the triangle index from the lookup table
3061 
3062  if self.fComponentType == om.MFn.kMeshEdgeComponent:
3063  # Only accept edge selection intersection on draw instance #2 -- scaled by 2
3064  # and instance #-1 (when useDrawInstancingOnEdgeSelectionItem is False)
3065  if intersection.instanceID == 1 or intersection.instanceID == 3:
3066  return
3067 
3068  idx = intersection.index
3069 
3070  if self.fComponentType == om.MFn.kMeshPolygonComponent:
3071  if idx >= 0 and idx < len(self.fLookupTable):
3072  idx = self.fLookupTable[idx]
3073 
3074  self.fComponent.addElement(idx)
3075 
3076  def component(self):
3077  ## Return the component object that contains the ids of the selected components
3078  return self.fComponentObject
3079 
3080  def selectionMask(self):
3081  ## This converter is only valid for specified selection type
3082  return self.fSelectionType
3083 
3084  ## creator function to instanciate a component converter for vertex selection
3085  @staticmethod
3086  def creatorVertexSelection():
3087  mask = om.MSelectionMask(om.MSelectionMask.kSelectMeshVerts)
3088  mask.addMask(om.MSelectionMask.kSelectPointsForGravity)
3089  return simpleComponentConverter_subsceneOverride(om.MFn.kMeshVertComponent, mask)
3090 
3091  ## creator function to instanciate a component converter for edge selection
3092  @staticmethod
3093  def creatorEdgeSelection():
3094  return simpleComponentConverter_subsceneOverride(om.MFn.kMeshEdgeComponent, om.MSelectionMask.kSelectMeshEdges)
3095 
3096  ## creator function to instanciate a component converter for face selection
3097  @staticmethod
3098  def creatorFaceSelection():
3099  return simpleComponentConverter_subsceneOverride(om.MFn.kMeshPolygonComponent, om.MSelectionMask.kSelectMeshFaces)
3100 
3101 class apiMeshSubSceneOverride(omr.MPxSubSceneOverride):
3102  sWireName = "apiMeshWire"
3103  sSelectName = "apiMeshSelection"
3104  sBoxName = "apiMeshBox"
3105  sSelectedBoxName = "apiMeshBoxSelection"
3106  sShadedName = "apiMeshShaded"
3107  sTexturedName = "apiMeshTextured"
3108 
3109  sVertexSelectionName = "apiMeshVertexSelection"
3110  sEdgeSelectionName = "apiMeshEdgeSelection"
3111  sFaceSelectionName = "apiMeshFaceSelection"
3112 
3113  sActiveVertexName = "apiMeshActiveVertex"
3114  sActiveEdgeName = "apiMeshActiveEdge"
3115  sActiveFaceName = "apiMeshActiveFace"
3116 
3117  class InstanceInfo:
3118  def __init__(self, transform, isSelected):
3119  self.fTransform = transform
3120  self.fIsSelected = isSelected
3121 
3122  @staticmethod
3123  def creator(obj):
3124  return apiMeshSubSceneOverride(obj)
3125 
3126  @staticmethod
3127  def shadedItemLinkLost(userData):
3128  if not userData is None and not userData.fOverride is None:
3129  if not userData.fOverride.fMesh is None:
3130  userData.fOverride.fMesh.setMaterialDirty(True)
3131  userData.fOverride = None
3132  userData = None
3133 
3134  def __init__(self, obj):
3135  omr.MPxSubSceneOverride.__init__(self, obj)
3136 
3137  node = om.MFnDependencyNode(obj)
3138  self.fMesh = node.userNode()
3139  self.fObject = om.MObject(obj)
3140  self.fWireShader = None
3141  self.fThickWireShader = None
3142  self.fSelectShader = None
3143  self.fThickSelectShader = None
3144  self.fShadedShader = None
3145  self.fVertexComponentShader = None
3146  self.fEdgeComponentShader = None
3147  self.fFaceComponentShader = None
3148  self.fPositionBuffer = None
3149  self.fNormalBuffer = None
3150  self.fBoxPositionBuffer = None
3151  self.fWireIndexBuffer = None
3152  self.fBoxIndexBuffer = None
3153  self.fShadedIndexBuffer = None
3154  self.fActiveVerticesIndexBuffer = None
3155  self.fActiveEdgesIndexBuffer = None
3156  self.fActiveFacesIndexBuffer = None
3157  self.fThickLineWidth = -1.0
3158  self.fQueuedLineWidth = -1.0
3159  self.fNumInstances = 0
3160  self.fIsInstanceMode = False
3161  self.fQueueUpdate = False
3162  self.fUseQueuedLineUpdate = False ## Set to True to run sample line width update code
3163 
3164  self.fInstanceInfoCache = collections.defaultdict(set)
3165 
3166  self.fActiveVerticesSet = set()
3167  self.fActiveEdgesSet = set()
3168  self.fActiveFacesSet = set()
3169 
3170  def __del__(self):
3171  self.fMesh = None
3172 
3173  shaderMgr = omr.MRenderer.getShaderManager()
3174  if shaderMgr:
3175  if self.fWireShader:
3176  shaderMgr.releaseShader(self.fWireShader)
3177  self.fWireShader = None
3178 
3179  if self.fThickWireShader:
3180  shaderMgr.releaseShader(self.fThickWireShader)
3181  self.fThickWireShader = None
3182 
3183  if self.fSelectShader:
3184  shaderMgr.releaseShader(self.fSelectShader)
3185  self.fSelectShader = None
3186 
3187  if self.fThickSelectShader:
3188  shaderMgr.releaseShader(self.fThickSelectShader)
3189  self.fThickSelectShader = None
3190 
3191  if self.fShadedShader:
3192  shaderMgr.releaseShader(self.fShadedShader)
3193  self.fShadedShader = None
3194 
3195  if self.fVertexComponentShader:
3196  shaderMgr.releaseShader(self.fVertexComponentShader)
3197  self.fVertexComponentShader = None
3198 
3199  if self.fEdgeComponentShader:
3200  shaderMgr.releaseShader(self.fEdgeComponentShader)
3201  self.fEdgeComponentShader = None
3202 
3203  if self.fFaceComponentShader:
3204  shaderMgr.releaseShader(self.fFaceComponentShader)
3205  self.fFaceComponentShader = None
3206 
3207  self.clearBuffers()
3208 
3209  def supportedDrawAPIs(self):
3210  ## this plugin supports both GL and DX
3211  return omr.MRenderer.kOpenGL | omr.MRenderer.kDirectX11 | omr.MRenderer.kOpenGLCoreProfile
3212 
3213  def requiresUpdate(self, container, frameContext):
3214  # Nothing in the container, definitely need to update
3215  if len(container) == 0:
3216  return True
3217 
3218  # Update always. This could be optimized to only update when the
3219  # actual shape node detects a change.
3220  return True
3221 
3222  def update(self, container, frameContext):
3223  # Update render items based on current set of instances
3224  self.manageRenderItems(container, frameContext, self.fMesh.shapeDirty() or len(container) == 0)
3225 
3226  # Always reset shape dirty flag
3227  self.fMesh.resetShapeDirty()
3228 
3229  def furtherUpdateRequired(self, frameContext):
3230  if self.fUseQueuedLineUpdate:
3231  if not frameContext.inUserInteraction() and not frameContext.userChangingViewContext():
3232  return self.fQueueUpdate
3233 
3234  return False
3235 
3236  def manageRenderItems(self, container, frameContext, updateGeometry):
3237  ## Preamble
3238  if not self.fMesh or self.fObject.isNull():
3239  return
3240 
3241  shaderMgr = omr.MRenderer.getShaderManager()
3242  if not shaderMgr:
3243  return
3244 
3245  node = om.MFnDagNode(self.fObject)
3246  instances = node.getAllPaths()
3247  if len(instances) == 0:
3248  return
3249 
3250  ## Constants
3251  sRed = [1.0, 0.0, 0.0, 1.0]
3252  sGreen = [0.0, 1.0, 0.0, 1.0]
3253  sWhite = [1.0, 1.0, 1.0, 1.0]
3254 
3255  ## Set up shared shaders if needed
3256  if not self.fWireShader:
3257  self.fWireShader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
3258  self.fWireShader.setParameter("solidColor", sRed)
3259 
3260  if not self.fThickWireShader:
3261  self.fThickWireShader = shaderMgr.getStockShader(omr.MShaderManager.k3dThickLineShader)
3262  self.fThickWireShader.setParameter("solidColor", sRed)
3263 
3264  if not self.fSelectShader:
3265  self.fSelectShader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
3266  self.fSelectShader.setParameter("solidColor", sGreen)
3267 
3268  if not self.fThickSelectShader:
3269  self.fThickSelectShader = shaderMgr.getStockShader(omr.MShaderManager.k3dThickLineShader)
3270  self.fThickSelectShader.setParameter("solidColor", sGreen)
3271 
3272  if not self.fVertexComponentShader:
3273  self.fVertexComponentShader = shaderMgr.getStockShader(omr.MShaderManager.k3dFatPointShader)
3274  self.fVertexComponentShader.setParameter("solidColor", sWhite)
3275  self.fVertexComponentShader.setParameter("pointSize", [5.0, 5.0])
3276 
3277  if not self.fEdgeComponentShader:
3278  self.fEdgeComponentShader = shaderMgr.getStockShader(omr.MShaderManager.k3dThickLineShader)
3279  self.fEdgeComponentShader.setParameter("solidColor", sWhite)
3280  self.fEdgeComponentShader.setParameter("lineWidth", [2.0, 2.0])
3281 
3282  if not self.fFaceComponentShader:
3283  self.fFaceComponentShader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
3284  self.fFaceComponentShader.setParameter("solidColor", sWhite)
3285 
3286  if not self.fShadedShader:
3287  self.fShadedShader = shaderMgr.getStockShader(omr.MShaderManager.k3dBlinnShader)
3288 
3289  ## Set up shared geometry if necessary
3290  if updateGeometry:
3291  self.rebuildGeometryBuffers()
3292 
3293  if not all((self.fPositionBuffer, self.fNormalBuffer, self.fBoxPositionBuffer, self.fWireIndexBuffer, self.fBoxIndexBuffer, self.fShadedIndexBuffer)):
3294  print "At least one buffer is not set"
3295  return
3296 
3297  selectedList = om.MGlobal.getActiveSelectionList()
3298 
3299  anyMatrixChanged = False
3300  itemsChanged = False
3301  instanceArrayLength = len(instances)
3302  numInstanceSelected = 0
3303  numInstanceUnselected = 0
3304  numInstances = 0
3305 
3306  instanceMatrixArray = om.MMatrixArray(instanceArrayLength)
3307  selectedInstanceMatrixArray = om.MMatrixArray(instanceArrayLength)
3308  unselectedInstanceMatrixArray = om.MMatrixArray(instanceArrayLength)
3309 
3310  for instance in instances:
3311  ## If expecting large numbers of instances, then walking through the whole
3312  ## list of instances every time to look for changes is not efficient
3313  ## enough. Watching for change events and changing only the required
3314  ## instances should be done instead. This method of checking for selection
3315  ## status is also not fast.
3316  if not instance.isValid or not instance.isVisible:
3317  continue
3318 
3319  instanceNum = instance.instanceNumber()
3320 
3321  instanceInfo = apiMeshSubSceneOverride.InstanceInfo(instance.inclusiveMatrix(), useSelectHighlight(selectedList, instance))
3322 
3323  if instanceNum not in self.fInstanceInfoCache or self.fInstanceInfoCache[instanceNum].fIsSelected != instanceInfo.fIsSelected or not self.fInstanceInfoCache[instanceNum].fTransform.isEquivalent(instanceInfo.fTransform):
3324  self.fInstanceInfoCache[instanceNum] = instanceInfo
3325  anyMatrixChanged = True
3326 
3327  instanceMatrixArray[numInstances] = instanceInfo.fTransform
3328  numInstances += 1
3329  if instanceInfo.fIsSelected:
3330  selectedInstanceMatrixArray[numInstanceSelected] = instanceInfo.fTransform
3331  numInstanceSelected += 1
3332  else:
3333  unselectedInstanceMatrixArray[numInstanceUnselected] = instanceInfo.fTransform
3334  numInstanceUnselected += 1
3335 
3336  instanceMatrixArray.setLength(numInstances) ## collapse to correct length
3337  selectedInstanceMatrixArray.setLength(numInstanceSelected)
3338  unselectedInstanceMatrixArray.setLength(numInstanceUnselected)
3339  if self.fNumInstances != numInstances:
3340  anyMatrixChanged = True
3341  self.fNumInstances = numInstances
3342 
3343  anyInstanceSelected = numInstanceSelected > 0
3344  anyInstanceUnselected = numInstanceUnselected > 0
3345 
3346  activeVerticesSet = set()
3347  activeEdgesSet = set()
3348  activeFacesSet = set()
3349 
3350  meshGeom = self.fMesh.meshGeom()
3351  if meshGeom and self.fMesh.hasActiveComponents():
3352  activeComponents = self.fMesh.activeComponents()
3353  if len(activeComponents) > 0:
3354  fnComponent = om.MFnSingleIndexedComponent( activeComponents[0] )
3355  if fnComponent.elementCount > 0:
3356  activeIds = fnComponent.getElements()
3357 
3358  if fnComponent.componentType == om.MFn.kMeshVertComponent:
3359  activeVerticesSet = set(activeIds)
3360 
3361  elif fnComponent.componentType == om.MFn.kMeshEdgeComponent:
3362  activeEdgesSet = set(activeIds)
3363 
3364  elif fnComponent.componentType == om.MFn.kMeshPolygonComponent:
3365  activeFacesSet = set(activeIds)
3366 
3367  ## Update index buffer of active items if necessary
3368  updateActiveItems = updateGeometry or self.fActiveVerticesSet != activeVerticesSet or self.fActiveEdgesSet != activeEdgesSet or self.fActiveFacesSet != activeFacesSet
3369  self.fActiveVerticesSet = activeVerticesSet
3370  self.fActiveEdgesSet = activeEdgesSet
3371  self.fActiveFacesSet = activeFacesSet
3372 
3373  if updateActiveItems:
3374  self.rebuildActiveComponentIndexBuffers()
3375 
3376  anyVertexSelected = bool(self.fActiveVerticesSet)
3377  anyEdgeSelected = bool(self.fActiveEdgesSet)
3378  anyFaceSelected = bool(self.fActiveFacesSet)
3379 
3380  if (anyVertexSelected and not self.fActiveVerticesIndexBuffer) or (anyEdgeSelected and not self.fActiveEdgesIndexBuffer) or (anyFaceSelected and not self.fActiveFacesIndexBuffer):
3381  return
3382 
3383  ## Add render items if necessary. Remove any pre-existing render items
3384  ## that are no longer needed.
3385  wireItem = container.find(self.sWireName)
3386  if not wireItem and anyInstanceUnselected:
3387  wireItem = omr.MRenderItem.create( self.sWireName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
3388  wireItem.setDrawMode(omr.MGeometry.kWireframe)
3389  wireItem.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
3390  wireItem.setShader(self.fWireShader)
3391  container.add(wireItem)
3392  itemsChanged = True
3393 
3394  elif wireItem and not anyInstanceUnselected:
3395  container.remove(self.sWireName)
3396  wireItem = None
3397  itemsChanged = True
3398 
3399  selectItem = container.find(self.sSelectName)
3400  if not selectItem and anyInstanceSelected:
3401  selectItem = omr.MRenderItem.create( self.sSelectName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
3402  selectItem.setDrawMode(omr.MGeometry.kWireframe | omr.MGeometry.kShaded | omr.MGeometry.kTextured)
3403  selectItem.setDepthPriority(omr.MRenderItem.sActiveWireDepthPriority)
3404  selectItem.setShader(self.fSelectShader)
3405  container.add(selectItem)
3406  itemsChanged = True
3407 
3408  elif selectItem and not anyInstanceSelected:
3409  container.remove(self.sSelectName)
3410  selectItem = None
3411  itemsChanged = True
3412 
3413  boxItem = container.find(self.sBoxName)
3414  if not boxItem and anyInstanceUnselected:
3415  boxItem = omr.MRenderItem.create( self.sBoxName, omr.MRenderItem.NonMaterialSceneItem, omr.MGeometry.kLines)
3416  boxItem.setDrawMode(omr.MGeometry.kBoundingBox)
3417  boxItem.setShader(self.fWireShader)
3418  container.add(boxItem)
3419  itemsChanged = True
3420 
3421  elif boxItem and not anyInstanceUnselected:
3422  container.remove(self.sBoxName)
3423  boxItem = None
3424  itemsChanged = True
3425 
3426  selectedBoxItem = container.find(self.sSelectedBoxName)
3427  if not selectedBoxItem and anyInstanceSelected:
3428  selectedBoxItem = omr.MRenderItem.create( self.sSelectedBoxName, omr.MRenderItem.NonMaterialSceneItem, omr.MGeometry.kLines)
3429  selectedBoxItem.setDrawMode(omr.MGeometry.kBoundingBox)
3430  selectedBoxItem.setShader(self.fSelectShader)
3431  container.add(selectedBoxItem)
3432  itemsChanged = True
3433 
3434  elif selectedBoxItem and not anyInstanceSelected:
3435  container.remove(self.sSelectedBoxName)
3436  selectedBoxItem = None
3437  itemsChanged = True
3438 
3439  shadedItem = container.find(self.sShadedName)
3440  if not shadedItem:
3441  ## We always want a shaded item
3442  shadedItem = omr.MRenderItem.create( self.sShadedName, omr.MRenderItem.MaterialSceneItem, omr.MGeometry.kTriangles)
3443  shadedItem.setDrawMode(omr.MGeometry.kShaded)
3444  shadedItem.setExcludedFromPostEffects(False)
3445  shadedItem.setCastsShadows(True)
3446  shadedItem.setReceivesShadows(True)
3447  container.add(shadedItem)
3448  itemsChanged = True
3449 
3450  texturedItem = container.find(self.sTexturedName)
3451  if not texturedItem:
3452  ## We always want a textured item
3453  texturedItem = omr.MRenderItem.create( self.sTexturedName, omr.MRenderItem.MaterialSceneItem, omr.MGeometry.kTriangles)
3454  texturedItem.setDrawMode(omr.MGeometry.kTextured)
3455  texturedItem.setExcludedFromPostEffects(False)
3456  texturedItem.setCastsShadows(True)
3457  texturedItem.setReceivesShadows(True)
3458  container.add(texturedItem)
3459  itemsChanged = True
3460 
3461  ## Grab shading node from first component of first instance of the
3462  ## object and use it to get an MShaderInstance. This could be expanded
3463  ## to support full instancing and components if necessary.
3464  try:
3465  connectedPlugs = om.MPlugArray()
3466  (sets, comps) = node.getConnectedSetsAndMembers(0, True)
3467  for obj in sets:
3468  dn = om.MFnDependencyNode(obj)
3469  shaderPlug = dn.findPlug("surfaceShader", True)
3470  connectedPlugs = shaderPlug.connectedTo(True, False)
3471  if len(connectedPlugs) > 0:
3472  break;
3473 
3474  ## Update shader for shaded item
3475  if self.fMesh.materialDirty() or not shadedItem.isShaderFromNode():
3476  if len(connectedPlugs) == 0 or \
3477  not shadedItem.setShaderFromNode(connectedPlugs[0].node(),
3478  instances[0],
3479  apiMeshSubSceneOverride.shadedItemLinkLost,
3480  ShadedItemUserData(self),
3481  True):
3482  shadedItem.setShader(self.fShadedShader)
3483 
3484  ## Update shader for textured item
3485  if self.fMesh.materialDirty() or not texturedItem.isShaderFromNode():
3486  if len(connectedPlugs) == 0 or \
3487  not texturedItem.setShaderFromNode(connectedPlugs[0].node(),
3488  instances[0],
3489  apiMeshSubSceneOverride.shadedItemLinkLost,
3490  ShadedItemUserData(self),
3491  False):
3492  texturedItem.setShader(self.fShadedShader)
3493 
3494  ## Reset the dirty flag since shader has been set for both shaded
3495  ## item and textured item.
3496  self.fMesh.setMaterialDirty(False)
3497  except:
3498  pass
3499 
3500  # render item for vertex selection
3501  vertexSelectionItem = container.find(self.sVertexSelectionName)
3502  if not vertexSelectionItem:
3503  vertexSelectionItem = omr.MRenderItem.create( self.sVertexSelectionName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
3504  # use for selection only : not visible in viewport
3505  vertexSelectionItem.setDrawMode(omr.MGeometry.kSelectionOnly)
3506  # set the selection mask to kSelectMeshVerts : we want the render item to be used for Vertex Components selection
3507  mask = om.MSelectionMask(om.MSelectionMask.kSelectMeshVerts)
3508  mask.addMask(om.MSelectionMask.kSelectPointsForGravity)
3509  vertexSelectionItem.setSelectionMask( mask )
3510  # set selection priority : on top
3511  vertexSelectionItem.setDepthPriority(omr.MRenderItem.sSelectionDepthPriority)
3512  vertexSelectionItem.setShader(self.fVertexComponentShader)
3513  container.add(vertexSelectionItem)
3514  itemsChanged = True
3515 
3516  # change this value to enable item instancing : same item will be rendered multiple times
3517  # the edge selection item will be visible in the viewport and rendered 3 times (with different scaling)
3518  # only the second instance (scale 1.5) will be selectable (see simpleComponentConverter_subsceneOverride)
3519  useDrawInstancingOnEdgeSelectionItem = False
3520 
3521  # render item for edge selection
3522  edgeSelectionItem = container.find(self.sEdgeSelectionName)
3523  if not edgeSelectionItem:
3524  # use for selection only : not visible in viewport
3525  drawMode = omr.MGeometry.kSelectionOnly
3526  depthPriority = omr.MRenderItem.sSelectionDepthPriority
3527  if useDrawInstancingOnEdgeSelectionItem:
3528  # display in viewport and in selection
3529  drawMode = omr.MGeometry.kAll
3530  # reduce priority so we can see the active item
3531  depthPriority = omr.MRenderItem.sActiveWireDepthPriority-1
3532 
3533  edgeSelectionItem = omr.MRenderItem.create( self.sEdgeSelectionName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
3534  edgeSelectionItem.setDrawMode(drawMode)
3535  # set selection priority : on top
3536  edgeSelectionItem.setDepthPriority(depthPriority)
3537  # set the selection mask to kSelectMeshEdges : we want the render item to be used for Edge Components selection
3538  edgeSelectionItem.setSelectionMask( om.MSelectionMask.kSelectMeshEdges )
3539  edgeSelectionItem.setShader(self.fWireShader)
3540  container.add(edgeSelectionItem)
3541  itemsChanged = True
3542 
3543  # render item for face selection - not visible in viewport
3544  faceSelectionItem = container.find(self.sFaceSelectionName)
3545  if not faceSelectionItem:
3546  faceSelectionItem = omr.MRenderItem.create( self.sFaceSelectionName, omr.MRenderItem.DecorationItem, omr.MGeometry.kTriangles)
3547  # use for selection only : not visible in viewport
3548  faceSelectionItem.setDrawMode(omr.MGeometry.kSelectionOnly)
3549  # set the selection mask to kSelectMeshFaces : we want the render item to be used for Face Components selection
3550  faceSelectionItem.setSelectionMask( om.MSelectionMask.kSelectMeshFaces )
3551  # set selection priority : on top
3552  faceSelectionItem.setDepthPriority(omr.MRenderItem.sSelectionDepthPriority)
3553  faceSelectionItem.setShader(self.fFaceComponentShader)
3554  container.add(faceSelectionItem)
3555  itemsChanged = True
3556 
3557  ## create and add a custom data to help the face component converter
3558  if updateGeometry:
3559  mySelectionData = apiMeshHWSelectionUserData()
3560  mySelectionData.fMeshGeom = self.fMesh.meshGeom()
3561  faceSelectionItem.setCustomData(mySelectionData)
3562 
3563  # render item to display active (selected) vertices
3564  activeVertexItem = container.find(self.sActiveVertexName)
3565  if not activeVertexItem and anyVertexSelected:
3566  activeVertexItem = omr.MRenderItem.create( self.sActiveVertexName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
3567  activeVertexItem.setDrawMode(omr.MGeometry.kAll)
3568  activeVertexItem.setDepthPriority(omr.MRenderItem.sActivePointDepthPriority)
3569  activeVertexItem.setShader(self.fVertexComponentShader)
3570  container.add(activeVertexItem)
3571  itemsChanged = True
3572 
3573  elif activeVertexItem and not anyVertexSelected:
3574  container.remove(self.sActiveVertexName)
3575  activeVertexItem = None
3576  itemsChanged = True
3577 
3578  # render item to display active (selected) edges
3579  activeEdgeItem = container.find(self.sActiveEdgeName)
3580  if not activeEdgeItem and anyEdgeSelected:
3581  activeEdgeItem = omr.MRenderItem.create( self.sActiveEdgeName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
3582  activeEdgeItem.setDrawMode(omr.MGeometry.kAll)
3583  activeEdgeItem.setDepthPriority(omr.MRenderItem.sActiveLineDepthPriority)
3584  activeEdgeItem.setShader(self.fEdgeComponentShader)
3585  container.add(activeEdgeItem)
3586  itemsChanged = True
3587 
3588  elif activeEdgeItem and not anyEdgeSelected:
3589  container.remove(self.sActiveEdgeName)
3590  activeEdgeItem = None
3591  itemsChanged = True
3592 
3593  # render item to display active (selected) faces
3594  activeFaceItem = container.find(self.sActiveFaceName)
3595  if not activeFaceItem and anyFaceSelected:
3596  activeFaceItem = omr.MRenderItem.create( self.sActiveFaceName, omr.MRenderItem.DecorationItem, omr.MGeometry.kTriangles)
3597  activeFaceItem.setDrawMode(omr.MGeometry.kAll)
3598  activeFaceItem.setDepthPriority(omr.MRenderItem.sActiveLineDepthPriority)
3599  activeFaceItem.setShader(self.fFaceComponentShader)
3600  container.add(activeFaceItem)
3601  itemsChanged = True
3602 
3603  elif activeFaceItem and not anyFaceSelected:
3604  container.remove(self.sActiveFaceName)
3605  activeFaceItem = None
3606  itemsChanged = True
3607 
3608  ## update the line width on the shader for our wire items if it changed
3609  lineWidth = frameContext.getGlobalLineWidth()
3610  userWidthChange = not floatApproxEqual(lineWidth, self.fThickLineWidth)
3611 
3612  doUpdate = False
3613  targetRefinedLineWidth = 50.0
3614  if userWidthChange:
3615  self.fThickLineWidth = lineWidth
3616  doUpdate = True
3617 
3618  ## First user change will trigger an update requirement
3619  if self.fUseQueuedLineUpdate:
3620  self.fQueuedLineWidth = lineWidth
3621  if self.fQueuedLineWidth < targetRefinedLineWidth:
3622  self.fQueueUpdate = True
3623 
3624  else:
3625  ## Increment by 1 until we reach the target width.
3626  ## If we haven't reached it yet then queue another update
3627  ## so we can increment and retest against the target width.
3628  if self.fUseQueuedLineUpdate and self.fQueueUpdate:
3629  if self.fQueuedLineWidth < targetRefinedLineWidth:
3630  lineWidth = self.fQueuedLineWidth
3631  self.fQueuedLineWidth += 1
3632  self.fQueueUpdate = True
3633  doUpdate = True
3634 
3635  else:
3636  ## Reached target. Stop asking for more refinement
3637  self.fQueueUpdate = False
3638 
3639  if doUpdate:
3640  if not floatApproxEqual(lineWidth, 1.0):
3641  ## Only set the shader if the line width changes (or the first time)
3642  lineWidthArray = [ lineWidth, lineWidth ]
3643  self.fThickWireShader.setParameter("lineWidth", lineWidthArray)
3644  self.fThickSelectShader.setParameter("lineWidth", lineWidthArray)
3645  if wireItem:
3646  wireItem.setShader(self.fThickWireShader)
3647  if selectItem:
3648  selectItem.setShader(self.fThickSelectShader)
3649 
3650  else:
3651  if wireItem:
3652  wireItem.setShader(self.fWireShader)
3653  if selectItem:
3654  selectItem.setShader(self.fSelectShader)
3655 
3656  ## Update geometry if required
3657  if itemsChanged or updateGeometry:
3658  bounds = self.fMesh.boundingBox()
3659 
3660  wireBuffers = omr.MVertexBufferArray()
3661  wireBuffers.append(self.fPositionBuffer, "positions")
3662  if wireItem:
3663  self.setGeometryForRenderItem(wireItem, wireBuffers, self.fWireIndexBuffer, bounds)
3664  if selectItem:
3665  self.setGeometryForRenderItem(selectItem, wireBuffers, self.fWireIndexBuffer, bounds)
3666 
3667  boxBuffers = omr.MVertexBufferArray()
3668  boxBuffers.append(self.fBoxPositionBuffer, "positions")
3669  if boxItem:
3670  self.setGeometryForRenderItem(boxItem, boxBuffers, self.fBoxIndexBuffer, bounds)
3671  if selectedBoxItem:
3672  self.setGeometryForRenderItem(selectedBoxItem, boxBuffers, self.fBoxIndexBuffer, bounds)
3673 
3674  shadedBuffers = omr.MVertexBufferArray()
3675  shadedBuffers.append(self.fPositionBuffer, "positions")
3676  shadedBuffers.append(self.fNormalBuffer, "normals")
3677  self.setGeometryForRenderItem(shadedItem, shadedBuffers, self.fShadedIndexBuffer, bounds)
3678  self.setGeometryForRenderItem(texturedItem, shadedBuffers, self.fShadedIndexBuffer, bounds)
3679 
3680  # The vertexSelectionItem has a sequential index buffer, use non-indexed draw:
3681  self.setGeometryForRenderItem(vertexSelectionItem, wireBuffers, objectBox = bounds)
3682  self.setGeometryForRenderItem(edgeSelectionItem, wireBuffers, self.fWireIndexBuffer, bounds)
3683  self.setGeometryForRenderItem(faceSelectionItem, wireBuffers, self.fShadedIndexBuffer, bounds)
3684 
3685  ## Update active component items if required
3686  if itemsChanged or updateActiveItems:
3687  bounds = self.fMesh.boundingBox()
3688 
3689  vertexBuffer = omr.MVertexBufferArray()
3690  vertexBuffer.append(self.fPositionBuffer, "positions")
3691 
3692  if activeVertexItem:
3693  self.setGeometryForRenderItem(activeVertexItem, vertexBuffer, self.fActiveVerticesIndexBuffer, bounds)
3694  if activeEdgeItem:
3695  self.setGeometryForRenderItem(activeEdgeItem, vertexBuffer, self.fActiveEdgesIndexBuffer, bounds)
3696  if activeFaceItem:
3697  self.setGeometryForRenderItem(activeFaceItem, vertexBuffer, self.fActiveFacesIndexBuffer, bounds)
3698 
3699  ## Update render item matrices if necessary
3700  if itemsChanged or anyMatrixChanged:
3701  if not self.fIsInstanceMode and numInstances == 1:
3702  ## When not dealing with multiple instances, don't convert the render items into instanced
3703  ## mode. Set the matrices on them directly.
3704  objToWorld = instanceMatrixArray[0]
3705 
3706  if wireItem:
3707  wireItem.setMatrix(objToWorld)
3708  if selectItem:
3709  selectItem.setMatrix(objToWorld)
3710  if boxItem:
3711  boxItem.setMatrix(objToWorld)
3712  if selectedBoxItem:
3713  selectedBoxItem.setMatrix(objToWorld)
3714  shadedItem.setMatrix(objToWorld)
3715  texturedItem.setMatrix(objToWorld)
3716 
3717  vertexSelectionItem.setMatrix(objToWorld)
3718  edgeSelectionItem.setMatrix(objToWorld)
3719  faceSelectionItem.setMatrix(objToWorld)
3720 
3721  if useDrawInstancingOnEdgeSelectionItem:
3722  ## create/update draw instances
3723  ## first instance : no scaling - won't be selectable see simpleComponentConverter_subsceneOverride.addIntersection
3724  transform1 = objToWorld
3725  transform1[15] = 1 # make sure we don't scale the w
3726  ## second instance : scaled by 2
3727  transform2 = objToWorld * 2
3728  transform2[15] = 1 # make sure we don't scale the w
3729  ## third instance : scaled by 3 - won't be selectable see simpleComponentConverter_subsceneOverride.addIntersection
3730  transform3 = objToWorld * 3
3731  transform3[15] = 1 # make sure we don't scale the w
3732 
3733  if True:
3734  transforms = om.MMatrixArray((transform1, transform2, transform3))
3735  self.setInstanceTransformArray(edgeSelectionItem, transforms)
3736  else:
3737  ## another way to set up the instances
3738  ## useful to get the instance ID
3739  try:
3740  self.removeAllInstances(edgeSelectionItem)
3741  except:
3742  # removeAllInstances raise an error if the item is not set up for instancing
3743  pass
3744  newInstanceId = self.addInstanceTransform(edgeSelectionItem, transform1)
3745  print "newInstanceId " + str(newInstanceId)
3746  newInstanceId = self.addInstanceTransform(edgeSelectionItem, transform2)
3747  print "newInstanceId " + str(newInstanceId)
3748  newInstanceId = self.addInstanceTransform(edgeSelectionItem, transform3)
3749  print "newInstanceId " + str(newInstanceId)
3750 
3751  if activeVertexItem:
3752  activeVertexItem.setMatrix(objToWorld)
3753  if activeEdgeItem:
3754  activeEdgeItem.setMatrix(objToWorld)
3755  if activeFaceItem:
3756  activeFaceItem.setMatrix(objToWorld)
3757 
3758  else:
3759  ## If we have DAG instances of this shape then use the MPxSubSceneOverride instance
3760  ## transform API to set up instance copies of the render items. This will be faster
3761  ## to render than creating render items for each instance, especially for large numbers
3762  ## of instances.
3763  ## Note: for simplicity, this code is not tracking the actual shaded material binding
3764  ## of the shape. MPxSubSceneOverride render item instances require that the shader
3765  ## and other properties of the instances all match. So if we were to bind a shader based
3766  ## on the DAG shape material binding, then we would need one render item per material. We
3767  ## could then group up the instance transforms based matching materials.
3768 
3769  ## Note this has to happen after the geometry and shaders are set, otherwise it will fail.
3770  if wireItem:
3771  self.setInstanceTransformArray(wireItem, unselectedInstanceMatrixArray)
3772  if selectItem:
3773  self.setInstanceTransformArray(selectItem, selectedInstanceMatrixArray)
3774  if boxItem:
3775  self.setInstanceTransformArray(boxItem, unselectedInstanceMatrixArray)
3776  if selectedBoxItem:
3777  self.setInstanceTransformArray(selectedBoxItem, selectedInstanceMatrixArray)
3778  self.setInstanceTransformArray(shadedItem, instanceMatrixArray)
3779  self.setInstanceTransformArray(texturedItem, instanceMatrixArray)
3780 
3781  self.setInstanceTransformArray(vertexSelectionItem, instanceMatrixArray)
3782  self.setInstanceTransformArray(edgeSelectionItem, instanceMatrixArray)
3783  self.setInstanceTransformArray(faceSelectionItem, instanceMatrixArray)
3784 
3785  if activeVertexItem:
3786  self.setInstanceTransformArray(activeVertexItem, instanceMatrixArray)
3787  if activeEdgeItem:
3788  self.setInstanceTransformArray(activeEdgeItem, instanceMatrixArray)
3789  if activeFaceItem:
3790  self.setInstanceTransformArray(activeFaceItem, instanceMatrixArray)
3791 
3792  ## Once we change the render items into instance rendering they can't be changed back without
3793  ## being deleted and re-created. So if instances are deleted to leave only one remaining,
3794  ## just keep treating them the instance way.
3795  self.fIsInstanceMode = True
3796 
3797  if itemsChanged or anyMatrixChanged or updateGeometry:
3798  ## On transform or geometry change, force recalculation of shadow maps
3799  omr.MRenderer.setLightsAndShadowsDirty()
3800 
3801  def rebuildGeometryBuffers(self):
3802  ## Preamble
3803  meshGeom = self.fMesh.meshGeom()
3804  if not meshGeom:
3805  return
3806  bounds = self.fMesh.boundingBox()
3807 
3808  ## Clear old
3809  self.clearGeometryBuffers()
3810 
3811  ## Compute mesh data size
3812  numTriangles = 0
3813  totalVerts = 0
3814  totalPoints = len(meshGeom.vertices)
3815  for i in xrange(meshGeom.faceCount):
3816  numVerts = meshGeom.face_counts[i]
3817  if numVerts > 2:
3818  numTriangles += numVerts - 2
3819  totalVerts += numVerts
3820 
3821  ## Acquire vertex buffer resources
3822  posDesc = omr.MVertexBufferDescriptor("", omr.MGeometry.kPosition, omr.MGeometry.kFloat, 3)
3823  normalDesc = omr.MVertexBufferDescriptor("", omr.MGeometry.kNormal, omr.MGeometry.kFloat, 3)
3824 
3825  self.fPositionBuffer = omr.MVertexBuffer(posDesc)
3826  self.fNormalBuffer = omr.MVertexBuffer(normalDesc)
3827  self.fBoxPositionBuffer = omr.MVertexBuffer(posDesc)
3828 
3829  positionDataAddress = self.fPositionBuffer.acquire(totalPoints, True)
3830  normalDataAddress = self.fNormalBuffer.acquire(totalPoints, True)
3831  boxPositionDataAddress = self.fBoxPositionBuffer.acquire(8, True)
3832 
3833  ## Acquire index buffer resources
3834  self.fWireIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
3835  self.fBoxIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
3836  self.fShadedIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
3837 
3838  wireBufferDataAddress = self.fWireIndexBuffer.acquire(2*totalVerts, True)
3839  boxBufferDataAddress = self.fBoxIndexBuffer.acquire(24, True)
3840  shadedBufferDataAddress = self.fShadedIndexBuffer.acquire(3*numTriangles, True)
3841 
3842  ## Sanity check
3843  if not all((positionDataAddress, normalDataAddress, boxPositionDataAddress, wireBufferDataAddress, boxBufferDataAddress, shadedBufferDataAddress)):
3844  print "At least one buffer data is not valid"
3845  self.clearGeometryBuffers()
3846  return
3847 
3848  positionData = ((ctypes.c_float * 3)*totalPoints).from_address(positionDataAddress)
3849  normalData = ((ctypes.c_float * 3)*totalPoints).from_address(normalDataAddress)
3850  boxPositionData = ((ctypes.c_float * 3)*8).from_address(boxPositionDataAddress)
3851 
3852  wireBufferData = (ctypes.c_uint * (2*totalVerts)).from_address(wireBufferDataAddress)
3853  boxBufferData = (ctypes.c_uint * 24).from_address(boxBufferDataAddress)
3854  shadedBufferData = ((ctypes.c_uint * 3)*numTriangles).from_address(shadedBufferDataAddress)
3855 
3856  ## Fill vertex data for shaded/wireframe
3857  for vid,position in enumerate(meshGeom.vertices):
3858  positionData[vid][0] = position[0]
3859  positionData[vid][1] = position[1]
3860  positionData[vid][2] = position[2]
3861 
3862  for vid,normal in enumerate(meshGeom.normals):
3863  normalData[vid][0] = normal[0]
3864  normalData[vid][1] = normal[1]
3865  normalData[vid][2] = normal[2]
3866 
3867  self.fPositionBuffer.commit(positionDataAddress)
3868  positionDataAddress = None
3869  self.fNormalBuffer.commit(normalDataAddress)
3870  normalDataAddress = None
3871 
3872  ## Fill vertex data for bounding box
3873  bbmin = bounds.min
3874  bbmax = bounds.max
3875  boxPositionData[0][0] = bbmin.x
3876  boxPositionData[0][1] = bbmin.y
3877  boxPositionData[0][2] = bbmin.z
3878 
3879  boxPositionData[1][0] = bbmin.x
3880  boxPositionData[1][1] = bbmin.y
3881  boxPositionData[1][2] = bbmax.z
3882 
3883  boxPositionData[2][0] = bbmax.x
3884  boxPositionData[2][1] = bbmin.y
3885  boxPositionData[2][2] = bbmax.z
3886 
3887  boxPositionData[3][0] = bbmax.x
3888  boxPositionData[3][1] = bbmin.y
3889  boxPositionData[3][2] = bbmin.z
3890 
3891  boxPositionData[4][0] = bbmin.x
3892  boxPositionData[4][1] = bbmax.y
3893  boxPositionData[4][2] = bbmin.z
3894 
3895  boxPositionData[5][0] = bbmin.x
3896  boxPositionData[5][1] = bbmax.y
3897  boxPositionData[5][2] = bbmax.z
3898 
3899  boxPositionData[6][0] = bbmax.x
3900  boxPositionData[6][1] = bbmax.y
3901  boxPositionData[6][2] = bbmax.z
3902 
3903  boxPositionData[7][0] = bbmax.x
3904  boxPositionData[7][1] = bbmax.y
3905  boxPositionData[7][2] = bbmin.z
3906 
3907  self.fBoxPositionBuffer.commit(boxPositionDataAddress)
3908  boxPositionDataAddress = None
3909 
3910  ## Fill index data for wireframe
3911  vid = 0
3912  first = 0
3913  idx = 0
3914  for i in xrange(meshGeom.faceCount):
3915  ## Ignore degenerate faces
3916  numVerts = meshGeom.face_counts[i]
3917  if numVerts > 2:
3918  first = vid
3919  for v in xrange(numVerts-1):
3920  wireBufferData[idx] = meshGeom.face_connects[vid]
3921  idx += 1
3922  vid += 1
3923  wireBufferData[idx] = meshGeom.face_connects[vid]
3924  idx += 1
3925 
3926  wireBufferData[idx] = meshGeom.face_connects[vid]
3927  idx += 1
3928  vid += 1
3929  wireBufferData[idx] = meshGeom.face_connects[first]
3930  idx += 1
3931 
3932  else:
3933  vid += numVerts
3934 
3935  self.fWireIndexBuffer.commit(wireBufferDataAddress)
3936  wireBufferDataAddress = None
3937 
3938  ## Fill index data for bounding box
3939  indexData = [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ]
3940  for i in xrange(24):
3941  boxBufferData[i] = indexData[i]
3942 
3943  self.fBoxIndexBuffer.commit(boxBufferDataAddress)
3944  boxBufferDataAddress = None
3945 
3946  ## Fill index data for shaded
3947  base = 0
3948  idx = 0
3949  for i in xrange(meshGeom.faceCount):
3950  ## Ignore degenerate faces
3951  numVerts = meshGeom.face_counts[i]
3952  if numVerts > 2:
3953  for v in xrange(1, numVerts-1):
3954  shadedBufferData[idx][0] = meshGeom.face_connects[base]
3955  shadedBufferData[idx][1] = meshGeom.face_connects[base+v]
3956  shadedBufferData[idx][2] = meshGeom.face_connects[base+v+1]
3957  idx += 1
3958 
3959  base += numVerts
3960 
3961  self.fShadedIndexBuffer.commit(shadedBufferDataAddress)
3962  shadedBufferDataAddress = None
3963 
3964  def rebuildActiveComponentIndexBuffers(self):
3965  ## Preamble
3966  meshGeom = self.fMesh.meshGeom()
3967  if not meshGeom:
3968  return
3969 
3970  ## Clear old
3971  self.clearActiveComponentIndexBuffers()
3972 
3973  ## Acquire and fill index buffer for active vertices
3974  numActiveVertices = len(self.fActiveVerticesSet)
3975  if numActiveVertices > 0:
3976  self.fActiveVerticesIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
3977  activeVerticesDataAddress = self.fActiveVerticesIndexBuffer.acquire(numActiveVertices, True)
3978  if activeVerticesDataAddress:
3979  activeVerticesData = (ctypes.c_uint*numActiveVertices).from_address(activeVerticesDataAddress)
3980 
3981  idx = 0
3982  for vid in self.fActiveVerticesSet:
3983  activeVerticesData[idx] = vid
3984  idx += 1
3985 
3986  self.fActiveVerticesIndexBuffer.commit(activeVerticesDataAddress)
3987  activeVerticesDataAddress = None
3988 
3989  ## Acquire and fill index buffer for active edges
3990  numActiveEdges = len(self.fActiveEdgesSet)
3991  if numActiveEdges > 0:
3992  self.fActiveEdgesIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
3993  activeEdgesDataAddress = self.fActiveEdgesIndexBuffer.acquire(2*numActiveEdges, True)
3994  if activeEdgesDataAddress:
3995  activeEdgesData = ((ctypes.c_uint * 2)*numActiveEdges).from_address(activeEdgesDataAddress)
3996 
3997  eid = 0
3998  first = 0
3999  vid = 0
4000  idx = 0
4001  for i in xrange(meshGeom.faceCount):
4002  ## Ignore degenerate faces
4003  numVerts = meshGeom.face_counts[i]
4004  if numVerts > 2:
4005  first = vid
4006  for v in xrange(numVerts-1):
4007  if eid in self.fActiveEdgesSet:
4008  activeEdgesData[idx][0] = meshGeom.face_connects[vid]
4009  activeEdgesData[idx][1] = meshGeom.face_connects[vid + 1]
4010  idx += 1
4011  vid += 1
4012  eid += 1
4013 
4014  if eid in self.fActiveEdgesSet:
4015  activeEdgesData[idx][0] = meshGeom.face_connects[vid]
4016  activeEdgesData[idx][1] = meshGeom.face_connects[first]
4017  idx += 1
4018  vid += 1
4019  eid += 1
4020  else:
4021  vid += numVerts
4022 
4023  self.fActiveEdgesIndexBuffer.commit(activeEdgesDataAddress)
4024  activeEdgesDataAddress = None
4025 
4026  ## Acquire and fill index buffer for active faces
4027  numActiveFaces = len(self.fActiveFacesSet)
4028  if numActiveFaces > 0:
4029  numActiveFacesTriangles = 0
4030  for i in xrange(meshGeom.faceCount):
4031  if i in self.fActiveFacesSet:
4032  numVerts = meshGeom.face_counts[i]
4033  if numVerts > 2:
4034  numActiveFacesTriangles += numVerts - 2
4035 
4036  self.fActiveFacesIndexBuffer = omr.MIndexBuffer(omr.MGeometry.kUnsignedInt32)
4037  activeFacesDataAddress = self.fActiveFacesIndexBuffer.acquire(3*numActiveFacesTriangles, True)
4038  if activeFacesDataAddress:
4039  activeFacesData = ((ctypes.c_uint * 3)*numActiveFacesTriangles).from_address(activeFacesDataAddress)
4040 
4041  idx = 0
4042  vid = 0
4043  for i in xrange(meshGeom.faceCount):
4044  numVerts = meshGeom.face_counts[i]
4045  if numVerts > 2:
4046  if i in self.fActiveFacesSet:
4047  for v in xrange(1, numVerts-1):
4048  activeFacesData[idx][0] = meshGeom.face_connects[vid]
4049  activeFacesData[idx][1] = meshGeom.face_connects[vid+v]
4050  activeFacesData[idx][2] = meshGeom.face_connects[vid+v+1]
4051  idx += 1
4052 
4053  vid += numVerts
4054 
4055  self.fActiveFacesIndexBuffer.commit(activeFacesDataAddress)
4056  activeFacesDataAddress = None
4057 
4058  def clearBuffers(self):
4059  self.clearGeometryBuffers()
4060  self.clearActiveComponentIndexBuffers()
4061 
4062  def clearGeometryBuffers(self):
4063  self.fPositionBuffer = None
4064  self.fNormalBuffer = None
4065  self.fBoxPositionBuffer = None
4066  self.fWireIndexBuffer = None
4067  self.fBoxIndexBuffer = None
4068  self.fShadedIndexBuffer = None
4069 
4070  def clearActiveComponentIndexBuffers(self):
4071  self.fActiveVerticesIndexBuffer = None
4072  self.fActiveEdgesIndexBuffer = None
4073  self.fActiveFacesIndexBuffer = None
4074 
4075  def updateSelectionGranularity(self, path, selectionContext):
4076  ## This is method is called during the pre-filtering phase of the viewport 2.0 selection
4077  ## and is used to setup the selection context of the given DAG object.
4078 
4079  ## We want the whole shape to be selectable, so we set the selection level to kObject so that the shape
4080  ## will be processed by the selection.
4081 
4082  ## In case we are currently in component selection mode (vertex, edge or face),
4083  ## since we have created render items that can be use in the selection phase (kSelectionOnly draw mode)
4084  ## and we also registered component converters to handle this render items,
4085  ## we can set the selection level to kComponent so that the shape will also be processed by the selection.
4086  displayStatus = omr.MGeometryUtilities.displayStatus(path)
4087  if displayStatus == omr.MGeometryUtilities.kHilite:
4088 
4089  globalComponentMask = om.MGlobal.objectSelectionMask()
4090  if om.MGlobal.selectionMode() == om.MGlobal.kSelectComponentMode:
4091  globalComponentMask = om.MGlobal.componentSelectionMask()
4092 
4093  supportedComponentMask = om.MSelectionMask( om.MSelectionMask.kSelectMeshVerts )
4094  supportedComponentMask.addMask( om.MSelectionMask.kSelectMeshEdges )
4095  supportedComponentMask.addMask( om.MSelectionMask.kSelectMeshFaces )
4096  supportedComponentMask.addMask( om.MSelectionMask.kSelectPointsForGravity )
4097 
4098  if globalComponentMask.intersects(supportedComponentMask):
4099  selectionContext.selectionLevel = omr.MSelectionContext.kComponent
4100  elif omr.MPxSubSceneOverride.pointSnappingActive():
4101  selectionContext.selectionLevel = omr.MSelectionContext.kComponent
4102 
4103 
4104 ################################################################################
4105 ##
4106 ## apiMeshGeometryOverride
4107 ##
4108 ## Handles vertex data preparation for drawing the user defined shape in
4109 ## Viewport 2.0.
4110 ##
4111 ################################################################################
4112 ## Custom user data class to attach to render items
4113 class apiMeshUserData(om.MUserData):
4114  def __init__(self):
4115  om.MUserData.__init__(self, True) ## let Maya clean up
4116  self.fMessage = ""
4117  self.fNumModifications = 0
4118 
4119 ## Pre/Post callback helper
4120 def callbackDataPrint(context, renderItemList):
4121  for item in renderItemList:
4122  if item:
4123  path = item.sourceDagPath()
4124  print "\tITEM: '" + item.name() + "' -- SOURCE: '" + path.fullPathName() + "'"
4125 
4126  passCtx = context.getPassContext()
4127  passId = passCtx.passIdentifier()
4128  passSem = passCtx.passSemantics()
4129  print "\tAPI mesh drawing in pass[" + passId + "], semantic[" + passSem + "]"
4130 
4131 ## Custom pre-draw callback
4132 def apiMeshPreDrawCallback(context, renderItemList, shaderInstance):
4133  print "PRE-draw callback triggered for render item list with data:"
4134  callbackDataPrint(context, renderItemList)
4135 
4136 ## Custom post-draw callback
4137 def apiMeshPostDrawCallback(context, renderItemList, shaderInstance):
4138  print "POST-draw callback triggered for render item list with data:"
4139  callbackDataPrint(context, renderItemList)
4140 
4141 ## Custom component converter to select vertices
4142 ## Attached to the dormant vertices render item (apiMeshGeometryOverride.sVertexItemName)
4143 class meshVertComponentConverter_geometryOverride(omr.MPxComponentConverter):
4144  def __init__(self):
4145  omr.MPxComponentConverter.__init__(self)
4146 
4147  self.fComponent = om.MFnSingleIndexedComponent()
4148  self.fComponentObject = om.MObject.kNullObj
4149  self.fVertices = []
4150 
4151  def initialize(self, renderItem):
4152  ## Create the component selection object .. here we are selecting vertex component
4153  self.fComponentObject = self.fComponent.create( om.MFn.kMeshVertComponent )
4154 
4155  ## Build a lookup table to match each primitive position in the index buffer of the render item geometry
4156  ## to the correponding vertex component of the object
4157  ## Use same algorithm as in apiMeshGeometryOverride.updateIndexingForDormantVertices
4158 
4159  selectionData = renderItem.customData()
4160  if isinstance(selectionData, apiMeshHWSelectionUserData):
4161  meshGeom = selectionData.fMeshGeom
4162 
4163  ## Allocate vertices lookup table
4164  numTriangles = 0
4165  for i in xrange(meshGeom.faceCount):
4166  numVerts = meshGeom.face_counts[i]
4167  if numVerts > 2:
4168  numTriangles += numVerts - 2
4169  self.fVertices = [0]*(3*numTriangles)
4170 
4171  ## Fill vertices lookup table
4172  base = 0
4173  idx = 0
4174  for faceIdx in xrange(meshGeom.faceCount):
4175  ## ignore degenerate faces
4176  numVerts = meshGeom.face_counts[faceIdx]
4177  if numVerts > 2:
4178  for v in xrange(1, numVerts-1):
4179  self.fVertices[idx] = meshGeom.face_connects[base]
4180  self.fVertices[idx+1] = meshGeom.face_connects[base+v]
4181  self.fVertices[idx+2] = meshGeom.face_connects[base+v+1]
4182  idx += 3
4183  base += numVerts
4184 
4185  def addIntersection(self, intersection):
4186  ## Convert the intersection index, which represent the primitive position in the
4187  ## index buffer, to the correct vertex component
4188  rawIdx = intersection.index
4189  idx = 0
4190  if rawIdx >= 0 and rawIdx < len(self.fVertices):
4191  idx = self.fVertices[rawIdx]
4192  self.fComponent.addElement(idx)
4193 
4194  def component(self):
4195  ## Return the component object that contains the ids of the selected vertices
4196  return self.fComponentObject
4197 
4198  def selectionMask(self):
4199  ## This converter is only valid for vertex selection
4200  mask = om.MSelectionMask(om.MSelectionMask.kSelectMeshVerts)
4201  mask.addMask(om.MSelectionMask.kSelectPointsForGravity)
4202  return mask
4203 
4204  @staticmethod
4205  def creator():
4206  return meshVertComponentConverter_geometryOverride()
4207 
4208 ## Custom component converter to select edges
4209 ## Attached to the edge selection render item (apiMeshGeometryOverride.sEdgeSelectionItemName)
4210 class meshEdgeComponentConverter_geometryOverride(omr.MPxComponentConverter):
4211  def __init__(self):
4212  omr.MPxComponentConverter.__init__(self)
4213 
4214  self.fComponent = om.MFnSingleIndexedComponent()
4215  self.fComponentObject = om.MObject.kNullObj
4216  self.fEdges = []
4217 
4218  def initialize(self, renderItem):
4219  ## Create the component selection object .. here we are selecting edge component
4220  self.fComponentObject = self.fComponent.create( om.MFn.kMeshEdgeComponent )
4221 
4222  ## Build a lookup table to match each primitive position in the index buffer of the render item geometry
4223  ## to the correponding edge component of the object
4224  ## Use same algorithm as in apiMeshGeometryOverride.updateIndexingForEdges
4225 
4226  ## in updateIndexingForEdges the index buffer is allocated with "totalEdges = 2*totalVerts"
4227  ## but since we are drawing lines, we'll get only half of the data as primitive positions :
4228  ## indices 0 & 1 : primitive #0
4229  ## indices 2 & 3 : primitive #1
4230  ## ...
4231 
4232  selectionData = renderItem.customData()
4233  if isinstance(selectionData, apiMeshHWSelectionUserData):
4234  meshGeom = selectionData.fMeshGeom
4235 
4236  ## Allocate edges lookup table
4237  totalVerts = 0
4238  for i in xrange(meshGeom.faceCount):
4239  numVerts = meshGeom.face_counts[i]
4240  if numVerts > 2:
4241  totalVerts += numVerts
4242  self.fEdges = [0]*(totalVerts)
4243 
4244  ## Fill edges lookup table
4245  idx = 0
4246  edgeId = 0
4247  for faceIdx in xrange(meshGeom.faceCount):
4248  ## ignore degenerate faces
4249  numVerts = meshGeom.face_counts[faceIdx]
4250  if numVerts > 2:
4251  for v in xrange(numVerts):
4252  self.fEdges[idx] = edgeId
4253  edgeId += 1
4254  idx += 1
4255 
4256  def addIntersection(self, intersection):
4257  ## Convert the intersection index, which represent the primitive position in the
4258  ## index buffer, to the correct edge component
4259  rawIdx = intersection.index
4260  idx = 0
4261  if rawIdx >= 0 and rawIdx < len(self.fEdges):
4262  idx = self.fEdges[rawIdx]
4263  self.fComponent.addElement(idx)
4264 
4265  def component(self):
4266  ## Return the component object that contains the ids of the selected edges
4267  return self.fComponentObject
4268 
4269  def selectionMask(self):
4270  ## This converter is only valid for edge selection
4271  return om.MSelectionMask(om.MSelectionMask.kSelectMeshEdges)
4272 
4273  @staticmethod
4274  def creator():
4275  return meshEdgeComponentConverter_geometryOverride()
4276 
4277 ## Custom component converter to select faces
4278 ## Attached to the face selection render item (apiMeshGeometryOverride.sFaceSelectionItemName)
4279 class meshFaceComponentConverter_geometryOverride(omr.MPxComponentConverter):
4280  def __init__(self):
4281  omr.MPxComponentConverter.__init__(self)
4282 
4283  self.fComponent = om.MFnSingleIndexedComponent()
4284  self.fComponentObject = om.MObject.kNullObj
4285  self.fFaces = []
4286 
4287  def initialize(self, renderItem):
4288  ## Create the component selection object .. here we are selecting face component
4289  self.fComponentObject = self.fComponent.create( om.MFn.kMeshPolygonComponent )
4290 
4291  ## Build a lookup table to match each primitive position in the index buffer of the render item geometry
4292  ## to the correponding face component of the object
4293  ## Use same algorithm as in apiMeshGeometryOverride.updateIndexingForFaces
4294 
4295  ## in updateIndexingForFaces the index buffer is allocated with "numTriangleVertices = 3*numTriangles"
4296  ## but since we are drawing triangles, we'll get only a third of the data as primitive positions :
4297  ## indices 0, 1 & 2 : primitive #0
4298  ## indices 3, 4 & 5 : primitive #1
4299  ## ...
4300 
4301  selectionData = renderItem.customData()
4302  if isinstance(selectionData, apiMeshHWSelectionUserData):
4303  meshGeom = selectionData.fMeshGeom
4304 
4305  ## Allocate faces lookup table
4306  numTriangles = 0
4307  for i in xrange(meshGeom.faceCount):
4308  numVerts = meshGeom.face_counts[i]
4309  if numVerts > 2:
4310  numTriangles += numVerts - 2
4311  self.fFaces = [0]*numTriangles
4312 
4313  ## Fill faces lookup table
4314  idx = 0
4315  for faceIdx in xrange(meshGeom.faceCount):
4316  ## ignore degenerate faces
4317  numVerts = meshGeom.face_counts[faceIdx]
4318  if numVerts > 2:
4319  for v in xrange(1, numVerts-1):
4320  self.fFaces[idx] = faceIdx
4321  idx += 1
4322 
4323  def addIntersection(self, intersection):
4324  ## Convert the intersection index, which represent the primitive position in the
4325  ## index buffer, to the correct face component
4326  rawIdx = intersection.index
4327  idx = 0
4328  if rawIdx >= 0 and rawIdx < len(self.fFaces):
4329  idx = self.fFaces[rawIdx]
4330  self.fComponent.addElement(idx)
4331 
4332  def component(self):
4333  ## Return the component object that contains the ids of the selected faces
4334  return self.fComponentObject
4335 
4336  def selectionMask(self):
4337  ## This converter is only valid for face selection
4338  return om.MSelectionMask(om.MSelectionMask.kSelectMeshFaces)
4339 
4340  @staticmethod
4341  def creator():
4342  return meshFaceComponentConverter_geometryOverride()
4343 
4344 class apiMeshGeometryOverride(omr.MPxGeometryOverride):
4345  ## Render item names
4346  sWireframeItemName = "apiMeshWire"
4347  sShadedTemplateItemName = "apiMeshShadedTemplateWire"
4348  sSelectedWireframeItemName = "apiMeshSelectedWireFrame"
4349  sVertexItemName = "apiMeshVertices"
4350  sEdgeSelectionItemName = "apiMeshEdgeSelection"
4351  sFaceSelectionItemName = "apiMeshFaceSelection"
4352  sActiveVertexItemName = "apiMeshActiveVertices"
4353  sVertexIdItemName = "apiMeshVertexIds"
4354  sVertexPositionItemName = "apiMeshVertexPositions"
4355  sShadedModeFaceCenterItemName = "apiMeshFaceCenterInShadedMode"
4356  sWireframeModeFaceCenterItemName = "apiMeshFaceCenterInWireframeMode"
4357  sShadedProxyItemName = "apiShadedProxy"
4358  sAffectedEdgeItemName = "apiMeshAffectedEdges"
4359  sAffectedFaceItemName = "apiMeshAffectedFaces"
4360  sActiveVertexStreamName = "apiMeshSharedVertexStream"
4361  sFaceCenterStreamName = "apiMeshFaceCenterStream"
4362 
4363  @staticmethod
4364  def creator(obj):
4365  return apiMeshGeometryOverride(obj)
4366 
4367  def __init__(self, obj):
4368  omr.MPxGeometryOverride.__init__(self, obj)
4369 
4370  node = om.MFnDependencyNode(obj)
4371  self.fMesh = node.userNode()
4372  self.fMeshGeom = None
4373  self.fColorRemapTexture = None
4374 
4375  self.fActiveVertices = om.MIntArray()
4376  self.fActiveVerticesSet = set()
4377  self.fActiveEdgesSet = set()
4378  self.fActiveFacesSet = set()
4379  self.fCastsShadows = False
4380  self.fReceivesShadows = False
4381 
4382  ## Stream names used for filling in different data
4383  ## for different streams required for different render items,
4384  ## and toggle to choose whether to use name streams
4385  ##
4386  self.fDrawSharedActiveVertices = True
4387  ## Turn on to view active vertices with colours lookedup from a 1D texture.
4388  self.fDrawActiveVerticesWithRamp = False
4389  self.fLinearSampler = None
4390 
4391  ##Vertex stream for face centers which is calculated from faces.
4392  self.fDrawFaceCenters = True
4393 
4394  if self.fDrawActiveVerticesWithRamp:
4395  self.fDrawFaceCenters = False ## Too cluttered showing the face centers at the same time.
4396 
4397  ## Can set the following to True, but then the logic to
4398  ## determine what color to set is the responsibility of the plugin.
4399  ##
4400  self.fUseCustomColors = False
4401 
4402  ## Can change this to choose a different shader to use when
4403  ## no shader node is assigned to the object.
4404  ## self.fProxyShader = omr.MShaderManager.k3dSolidShader ## - Basic line shader
4405  ## self.fProxyShader = omr.MShaderManager.k3dStippleShader ## - For filled stipple faces (triangles)
4406  ## self.fProxyShader = omr.MShaderManager.k3dThickLineShader ## For thick solid colored lines
4407  ## self.fProxyShader = omr.MShaderManager.k3dCPVThickLineShader ## For thick colored lines. Black if no CPV
4408  ## self.fProxyShader = omr.MShaderManager.k3dDashLineShader ## - For dashed solid color lines
4409  ## self.fProxyShader = omr.MShaderManager.k3dCPVDashLineShader ##- For dashed coloured lines. Black if no CPV
4410  ## self.fProxyShader = omr.MShaderManager.k3dThickDashLineShader ## For thick dashed solid color lines. black if no cpv
4411  self.fProxyShader = omr.MShaderManager.k3dCPVThickDashLineShader ##- For thick, dashed and coloured lines
4412 
4413  ## Set to True to test that overriding internal items has no effect
4414  ## for shadows and effects overrides
4415  self.fInternalItems_NoShadowCast = False
4416  self.fInternalItems_NoShadowReceive = False
4417  self.fInternalItems_NoPostEffects = False
4418 
4419  ## Use the existing shadow casts / receives flags on the shape
4420  ## to drive settings for applicable render items.
4421  self.fExternalItems_NoShadowCast = False
4422  self.fExternalItems_NoShadowReceive = False
4423  self.fExternalItemsNonTri_NoShadowCast = False
4424  self.fExternalItemsNonTri_NoShadowReceive = False
4425 
4426  ## Set to True to ignore post-effects for wireframe items.
4427  ## Shaded items will still have effects applied.
4428  self.fExternalItems_NoPostEffects = True
4429  self.fExternalItemsNonTri_NoPostEffects = True
4430 
4431  def __del__(self):
4432  self.fMesh = None
4433  self.fMeshGeom = None
4434 
4435  if self.fColorRemapTexture:
4436  textureMgr = omr.MRenderer.getTextureManager()
4437  if textureMgr:
4438  textureMgr.releaseTexture(self.fColorRemapTexture)
4439  self.fColorRemapTexture = None
4440 
4441  if self.fLinearSampler:
4442  omr.MStateManager.releaseSamplerState(self.fLinearSampler)
4443  self.fLinearSampler = None
4444 
4445  def supportedDrawAPIs(self):
4446  ## this plugin supports both GL and DX
4447  return omr.MRenderer.kOpenGL | omr.MRenderer.kDirectX11 | omr.MRenderer.kOpenGLCoreProfile
4448 
4449  def updateDG(self):
4450  ## Pull the actual outMesh from the shape, as well
4451  ## as any active components
4452  self.fActiveVertices.clear()
4453  self.fActiveVerticesSet = set()
4454  self.fActiveEdgesSet = set()
4455  self.fActiveFacesSet = set()
4456  if self.fMesh:
4457  self.fMeshGeom = self.fMesh.meshGeom()
4458 
4459  if self.fMeshGeom and self.fMesh.hasActiveComponents():
4460  activeComponents = self.fMesh.activeComponents()
4461  if len(activeComponents) > 0:
4462  fnComponent = om.MFnSingleIndexedComponent( activeComponents[0] )
4463  if fnComponent.elementCount > 0:
4464  activeIds = fnComponent.getElements()
4465 
4466  if fnComponent.componentType == om.MFn.kMeshVertComponent:
4467  self.fActiveVertices = activeIds
4468  self.fActiveVerticesSet = set(activeIds)
4469 
4470  elif fnComponent.componentType == om.MFn.kMeshEdgeComponent:
4471  self.fActiveEdgesSet = set(activeIds)
4472 
4473  elif fnComponent.componentType == om.MFn.kMeshPolygonComponent:
4474  self.fActiveFacesSet = set(activeIds)
4475 
4476  def updateRenderItems(self, path, list):
4477  ## Update render items. Shaded render item is provided so this
4478  ## method will be adding and updating UI render items only.
4479 
4480  shaderMgr = omr.MRenderer.getShaderManager()
4481  if not shaderMgr:
4482  return
4483 
4484  dagNode = om.MFnDagNode(path)
4485  castsShadowsPlug = dagNode.findPlug("castsShadows", False)
4486  self.fCastsShadows = castsShadowsPlug.asBool()
4487  receiveShadowsPlug = dagNode.findPlug("receiveShadows", False)
4488  self.fReceivesShadows = receiveShadowsPlug.asBool()
4489 
4490  ##1 Update wireframe render items
4491  self.updateDormantAndTemplateWireframeItems(path, list, shaderMgr)
4492  self.updateActiveWireframeItem(path, list, shaderMgr)
4493 
4494  ## Update vertex render items
4495  self.updateDormantVerticesItem(path, list, shaderMgr)
4496  self.updateActiveVerticesItem(path, list, shaderMgr)
4497 
4498  ## Update vertex numeric render items
4499  self.updateVertexNumericItems(path, list, shaderMgr)
4500 
4501  ## Update face center item
4502  if self.fDrawFaceCenters:
4503  self.updateWireframeModeFaceCenterItem(path, list, shaderMgr)
4504  self.updateShadedModeFaceCenterItem(path, list, shaderMgr)
4505 
4506  ## Update "affected" edge and face render items
4507  self.updateAffectedComponentItems(path, list, shaderMgr)
4508 
4509  ## Update faces and edges selection items
4510  self.updateSelectionComponentItems(path, list, shaderMgr)
4511 
4512  ## Update proxy shaded render item
4513  self.updateProxyShadedItem(path, list, shaderMgr)
4514 
4515  ## Test overrides on existing shaded items.
4516  ## In this case it is not valid to override these states
4517  ## so there should be no change in behaviour.
4518  ##
4519  testShadedOverrides = self.fInternalItems_NoShadowCast or self.fInternalItems_NoShadowReceive or self.fInternalItems_NoPostEffects
4520  if testShadedOverrides:
4521  for item in list:
4522  if not item:
4523  continue
4524 
4525  drawMode = item.drawMode()
4526  if drawMode == omr.MGeometry.kShaded or drawMode == omr.MGeometry.kTextured:
4527  if item.name() != self.sShadedTemplateItemName:
4528  item.setCastsShadows( not self.fInternalItems_NoShadowCast and self.fCastsShadows )
4529  item.setReceivesShadows( not self.fInternalItems_NoShadowReceive and self.fReceivesShadows )
4530  item.setExcludedFromPostEffects( self.fInternalItems_NoPostEffects )
4531 
4532  def populateGeometry(self, requirements, renderItems, data):
4533  ## Fill in data and index streams based on the requirements passed in.
4534  ## Associate indexing with the render items passed in.
4535  ##
4536  ## Note that we leave both code paths to either draw shared or non-shared active vertices.
4537  ## The choice of which to use is up to the circumstances per plug-in.
4538  ## When drawing shared vertices, this requires an additional position buffer to be
4539  ## created so will use more memory. If drawing unshared vertices redundent extra
4540  ## vertices are drawn but will use less memory. The data member fDrawSharedActiveVertices
4541  ## can be set to decide on which implementation to use.
4542 
4543  debugPopulateGeometry = False
4544  if debugPopulateGeometry:
4545  print "> Begin populate geometry"
4546 
4547  ## Get the active vertex count
4548  activeVertexCount = len(self.fActiveVertices)
4549 
4550  ## Compute the number of triangles, assume polys are always convex
4551  numTriangles = 0
4552  totalVerts = 0
4553  for i in xrange(self.fMeshGeom.faceCount):
4554  numVerts = self.fMeshGeom.face_counts[i]
4555  if numVerts > 2:
4556  numTriangles += numVerts - 2
4557  totalVerts += numVerts
4558 
4559  ## Update data streams based on geometry requirements
4560  self.updateGeometryRequirements(requirements, data, activeVertexCount, totalVerts, debugPopulateGeometry)
4561 
4562  ## Update indexing data for all appropriate render items
4563  wireIndexBuffer = None ## reuse same index buffer for both wireframe and selected
4564 
4565  for item in renderItems:
4566  if not item:
4567  continue
4568 
4569  ## Enable to debug vertex buffers that are associated with each render item.
4570  ## Can also use to generate indexing better, but we don't need that here.
4571  ## Also debugs custom data on the render item.
4572  debugStuff = False
4573  if debugStuff:
4574  itemBuffers = item.requiredVertexBuffers()
4575  for desc in itemBuffers:
4576  print "Buffer Required for Item '" + item.name() + "':"
4577  print "\tBufferName: " + desc.name
4578  print "\tDataType: " + omr.MGeometry.dataTypeString(desc.dataType) + " (dimension " + str(desc.dimension) + ")"
4579  print "\tSemantic: " + omr.MGeometry.semanticString(desc.semantic)
4580  print ""
4581 
4582  ## Just print a message for illustration purposes. Note that the custom data is also
4583  ## accessible from the MRenderItem in MPxShaderOverride::draw().
4584  myCustomData = item.customData()
4585  if isinstance(myCustomData, apiMeshUserData):
4586  print "Custom data '" + myCustomData.fMessage + "', modified count='" + str(myCustomData.fNumModifications) + "'"
4587  else:
4588  print "No custom data"
4589 
4590  ## Update indexing for active vertex item
4591  ##
4592  if item.name() == self.sActiveVertexItemName:
4593  self.updateIndexingForVertices( item, data, numTriangles, activeVertexCount, debugPopulateGeometry)
4594 
4595  ## Update indexing for face center item in wireframe mode and shaded mode
4596  ##
4597  if self.fDrawFaceCenters and (item.name() == self.sShadedModeFaceCenterItemName or item.name() == self.sWireframeModeFaceCenterItemName):
4598  self.updateIndexingForFaceCenters( item, data, debugPopulateGeometry)
4599 
4600  ## Create indexing for dormant and numeric vertex render items
4601  ##
4602  elif item.name() == self.sVertexItemName or item.name() == self.sVertexIdItemName or item.name() == self.sVertexPositionItemName:
4603  self.updateIndexingForDormantVertices( item, data, numTriangles )
4604 
4605  ## Create indexing for wireframe render items
4606  ##
4607  elif item.name() == self.sWireframeItemName or item.name() == self.sShadedTemplateItemName or item.name() == self.sSelectedWireframeItemName or (item.primitive() != omr.MGeometry.kTriangles and item.name() == self.sShadedProxyItemName):
4608  self.updateIndexingForWireframeItems(wireIndexBuffer, item, data, totalVerts)
4609 
4610  ## Handle indexing for affected edge render items
4611  ## For each face we check the edges. If the edges are in the active vertex
4612  ## list we add indexing for the 2 vertices on the edge to the index buffer.
4613  ##
4614  elif item.name() == self.sAffectedEdgeItemName:
4615  self.updateIndexingForEdges(item, data, totalVerts, True) ## Filter edges using active edges or actives vertices set
4616  elif item.name() == self.sEdgeSelectionItemName:
4617  self.updateIndexingForEdges(item, data, totalVerts, False) ## No filter : all edges
4618 
4619  ## Handle indexing for affected edge render items
4620  ## For each triangle we check the vertices. If any of the vertices are in the active vertex
4621  ## list we add indexing for the triangle to the index buffer.
4622  ##
4623  elif item.name() == self.sAffectedFaceItemName:
4624  self.updateIndexingForFaces(item, data, numTriangles, True) ## Filter faces using active faces or actives vertices set
4625  elif item.name() == self.sFaceSelectionItemName:
4626  self.updateIndexingForFaces(item, data, numTriangles, False) ## No filter : all faces
4627 
4628  ## Create indexing for filled (shaded) render items
4629  ##
4630  elif item.primitive() == omr.MGeometry.kTriangles:
4631  self.updateIndexingForShadedTriangles(item, data, numTriangles)
4632 
4633  if debugPopulateGeometry:
4634  print "> End populate geometry"
4635 
4636  def cleanUp(self):
4637  self.fMeshGeom = None
4638  self.fActiveVertices.clear()
4639  self.fActiveVerticesSet = set()
4640  self.fActiveEdgesSet = set()
4641  self.fActiveFacesSet = set()
4642 
4643  def updateSelectionGranularity(self, path, selectionContext):
4644  ## This is method is called during the pre-filtering phase of the viewport 2.0 selection
4645  ## and is used to setup the selection context of the given DAG object.
4646 
4647  ## We want the whole shape to be selectable, so we set the selection level to kObject so that the shape
4648  ## will be processed by the selection.
4649 
4650  ## In case we are currently in component selection mode (vertex, edge or face),
4651  ## since we have created render items that can be use in the selection phase (kSelectionOnly draw mode)
4652  ## and we also registered component converters to handle this render items,
4653  ## we can set the selection level to kComponent so that the shape will also be processed by the selection.
4654 
4655  displayStatus = omr.MGeometryUtilities.displayStatus(path)
4656  if displayStatus == omr.MGeometryUtilities.kHilite:
4657 
4658  globalComponentMask = om.MGlobal.objectSelectionMask()
4659  if om.MGlobal.selectionMode() == om.MGlobal.kSelectComponentMode:
4660  globalComponentMask = om.MGlobal.componentSelectionMask()
4661 
4662  supportedComponentMask = om.MSelectionMask( om.MSelectionMask.kSelectMeshVerts )
4663  supportedComponentMask.addMask( om.MSelectionMask.kSelectMeshEdges )
4664  supportedComponentMask.addMask( om.MSelectionMask.kSelectMeshFaces )
4665  supportedComponentMask.addMask( om.MSelectionMask.kSelectPointsForGravity )
4666 
4667  if globalComponentMask.intersects(supportedComponentMask):
4668  selectionContext.selectionLevel = omr.MSelectionContext.kComponent
4669  elif omr.MPxGeometryOverride.pointSnappingActive():
4670  selectionContext.selectionLevel = omr.MSelectionContext.kComponent
4671 
4672  def printShader(self, shader):
4673  ## Some example code to print out shader parameters
4674  if not shader:
4675  return
4676 
4677  params = shader.parameterList()
4678  print "DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH " + str(len(params))
4679 
4680  for param in params:
4681  paramType = shader.parameterType(param)
4682  isArray = shader.isArrayParameter(param)
4683 
4684  typeAsStr = "Unknown"
4685  if paramType == omr.MShaderInstance.kInvalid:
4686  typeAsStr = "Invalid"
4687  elif paramType == omr.MShaderInstance.kBoolean:
4688  typeAsStr = "Boolean"
4689  elif paramType == omr.MShaderInstance.kInteger:
4690  typeAsStr = "Integer"
4691  elif paramType == omr.MShaderInstance.kFloat:
4692  typeAsStr = "Float"
4693  elif paramType == omr.MShaderInstance.kFloat2:
4694  typeAsStr = "Float2"
4695  elif paramType == omr.MShaderInstance.kFloat3:
4696  typeAsStr = "Float3"
4697  elif paramType == omr.MShaderInstance.kFloat4:
4698  typeAsStr = "Float4"
4699  elif paramType == omr.MShaderInstance.kFloat4x4Row:
4700  typeAsStr = "Float4x4Row"
4701  elif paramType == omr.MShaderInstance.kFloat4x4Col:
4702  typeAsStr = "Float4x4Col"
4703  elif paramType == omr.MShaderInstance.kTexture1:
4704  typeAsStr = "1D Texture"
4705  elif paramType == omr.MShaderInstance.kTexture2:
4706  typeAsStr = "2D Texture"
4707  elif paramType == omr.MShaderInstance.kTexture3:
4708  typeAsStr = "3D Texture"
4709  elif paramType == omr.MShaderInstance.kTextureCube:
4710  typeAsStr = "Cube Texture"
4711  elif paramType == omr.MShaderInstance.kSampler:
4712  typeAsStr = "Sampler"
4713 
4714  print "ParamName='" + param + "', ParamType='" + typeAsStr + "', IsArrayParameter:'" + str(isArray) + "'"
4715 
4716  print "END PARAM LIST"
4717 
4718  def setSolidColor(self, shaderInstance, defaultColor, customColor=None):
4719  ## Set the solid color for solid color shaders
4720  if shaderInstance:
4721  color = defaultColor
4722  if self.fUseCustomColors and customColor:
4723  color = customColor
4724  try:
4725  shaderInstance.setParameter("solidColor", color)
4726  except:
4727  pass
4728 
4729  def setSolidPointSize(self, shaderInstance, size):
4730  ## Set the point size for solid color shaders
4731  if shaderInstance:
4732  try:
4733  shaderInstance.setParameter("pointSize", [size, size])
4734  except:
4735  pass
4736 
4737  def setLineWidth(self, shaderInstance, width):
4738  ## Set the line width for solid color shaders
4739  if shaderInstance:
4740  try:
4741  shaderInstance.setParameter("lineWidth", [width, width])
4742  except:
4743  pass
4744 
4745  def enableActiveComponentDisplay(self, path):
4746  ## Test to see if active components should be enabled.
4747  ## Based on active vertices + non-template state
4748 
4749  enable = True
4750 
4751  ## If there are components then we need to check
4752  ## either the display status of the object, or
4753  ## in the case of a templated object make sure
4754  ## to hide components to be consistent with how
4755  ## internal objects behave
4756  ##
4757  displayStatus = omr.MGeometryUtilities.displayStatus(path)
4758  if displayStatus == omr.MGeometryUtilities.kHilite or displayStatus == omr.MGeometryUtilities.kActiveComponent:
4759  enable = False
4760 
4761  else:
4762  ## Do an explicit path test for templated
4763  ## since display status does not indicate this.
4764  if path.isTemplated():
4765  enable = False
4766 
4767  return enable
4768 
4769  ## Render item handling methods
4770  def updateDormantAndTemplateWireframeItems(self, path, list, shaderMgr):
4771  ## Update render items for dormant and template wireframe drawing.
4772  ##
4773  ## 1) If the object is dormant and not templated then we require
4774  ## a render item to display when wireframe drawing is required (display modes
4775  ## is wire or wire-on-shaded)
4776  ##
4777  ## 2a) If the object is templated then we use the same render item as in 1)
4778  ## when in wireframe drawing is required.
4779  ## 2b) However we also require a render item to display when in shaded mode.
4780 
4781  ## Stock colors
4782  dormantColor = [ 0.0, 0.0, 1.0, 1.0 ]
4783  templateColor = [ 0.45, 0.45, 0.45, 1.0 ]
4784  activeTemplateColor = [ 1.0, 0.5, 0.5, 1.0 ]
4785 
4786  ## Some local options to show debug interface
4787  ##
4788  debugShader = False
4789 
4790  ## Get render item used for draw in wireframe mode
4791  ## (Mode to draw in is omr.MGeometry.kWireframe)
4792  ##
4793  wireframeItem = None
4794  index = list.indexOf(self.sWireframeItemName)
4795  if index < 0:
4796  wireframeItem = omr.MRenderItem.create( self.sWireframeItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
4797  wireframeItem.setDrawMode(omr.MGeometry.kWireframe)
4798 
4799  ## Set dormant wireframe with appropriate priority to not clash with
4800  ## any active wireframe which may overlap in depth.
4801  wireframeItem.setDepthPriority( omr.MRenderItem.sDormantWireDepthPriority )
4802 
4803  list.append(wireframeItem)
4804 
4805  preCb = None
4806  postCb = None
4807  if debugShader:
4808  preCb = apiMeshPreDrawCallback
4809  postCb = apiMeshPostDrawCallback
4810 
4811  shader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader, preCb, postCb)
4812  if shader:
4813  ## assign shader
4814  wireframeItem.setShader(shader)
4815 
4816  ## sample debug code
4817  if debugShader:
4818  self.printShader( shader )
4819 
4820  ## once assigned, no need to hold on to shader instance
4821  shaderMgr.releaseShader(shader)
4822  else:
4823  wireframeItem = list[index]
4824 
4825  ## Get render item for handling mode shaded template drawing
4826  ##
4827  shadedTemplateItem = None
4828  index = list.indexOf(self.sShadedTemplateItemName)
4829  if index < 0:
4830  shadedTemplateItem = omr.MRenderItem.create( self.sShadedTemplateItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
4831  shadedTemplateItem.setDrawMode(omr.MGeometry.kAll)
4832 
4833  ## Set shaded item as being dormant wire since it should still be raised
4834  ## above any shaded items, but not as high as active items.
4835  shadedTemplateItem.setDepthPriority( omr.MRenderItem.sDormantWireDepthPriority )
4836 
4837  list.append(shadedTemplateItem)
4838 
4839  shader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
4840  if shader:
4841  ## assign shader
4842  shadedTemplateItem.setShader(shader)
4843 
4844  ## sample debug code
4845  if debugShader:
4846  self.printShader( shader )
4847 
4848  ## once assigned, no need to hold on to shader instance
4849  shaderMgr.releaseShader(shader)
4850  else:
4851  shadedTemplateItem = list[index]
4852 
4853  ## Sample code to disable cast, receives shadows, and post effects.
4854  shadedTemplateItem.setCastsShadows( not self.fExternalItemsNonTri_NoShadowCast )
4855  shadedTemplateItem.setReceivesShadows( not self.fExternalItemsNonTri_NoShadowReceive )
4856  shadedTemplateItem.setExcludedFromPostEffects( self.fExternalItemsNonTri_NoPostEffects )
4857 
4858  displayStatus = omr.MGeometryUtilities.displayStatus(path)
4859  wireColor = omr.MGeometryUtilities.wireframeColor(path)
4860 
4861  ## Enable / disable wireframe item and update the shader parameters
4862  ##
4863  if wireframeItem:
4864  shader = wireframeItem.getShader()
4865 
4866  if displayStatus == omr.MGeometryUtilities.kTemplate:
4867  self.setSolidColor( shader, wireColor, templateColor)
4868  wireframeItem.enable(True)
4869 
4870  elif displayStatus == omr.MGeometryUtilities.kActiveTemplate:
4871  self.setSolidColor( shader, wireColor, activeTemplateColor)
4872  wireframeItem.enable(True)
4873 
4874  elif displayStatus == omr.MGeometryUtilities.kDormant:
4875  self.setSolidColor( shader, wireColor, dormantColor)
4876  wireframeItem.enable(True)
4877 
4878  elif displayStatus == omr.MGeometryUtilities.kActiveAffected:
4879  theColor = [ 0.5, 0.0, 1.0, 1.0 ]
4880  self.setSolidColor( shader, wireColor, theColor)
4881  wireframeItem.enable(True)
4882 
4883  else:
4884  wireframeItem.enable(False)
4885 
4886  ## Enable / disable shaded/template item and update the shader parameters
4887  ##
4888  if shadedTemplateItem:
4889  isTemplate = path.isTemplated()
4890  shader = shadedTemplateItem.getShader()
4891 
4892  if displayStatus == omr.MGeometryUtilities.kTemplate:
4893  self.setSolidColor( shader, wireColor, templateColor)
4894  shadedTemplateItem.enable(isTemplate)
4895 
4896  elif displayStatus == omr.MGeometryUtilities.kActiveTemplate:
4897  self.setSolidColor( shader, wireColor, activeTemplateColor)
4898  shadedTemplateItem.enable(isTemplate)
4899 
4900  elif displayStatus == omr.MGeometryUtilities.kDormant:
4901  self.setSolidColor( shader, wireColor, dormantColor)
4902  shadedTemplateItem.enable(isTemplate)
4903 
4904  else:
4905  shadedTemplateItem.enable(False)
4906 
4907  def updateActiveWireframeItem(self, path, list, shaderMgr):
4908  ## Create a render item for active wireframe if it does not exist. Updating
4909  ## shading parameters as necessary.
4910 
4911  selectItem = None
4912  index = list.indexOf(self.sSelectedWireframeItemName)
4913  if index < 0:
4914  selectItem = omr.MRenderItem.create(self.sSelectedWireframeItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
4915  selectItem.setDrawMode(omr.MGeometry.kAll)
4916  ## This is the same as setting the argument raiseAboveShaded = True,/
4917  ## since it sets the priority value to be the same. This line is just
4918  ## an example of another way to do the same thing after creation of
4919  ## the render item.
4920  selectItem.setDepthPriority( omr.MRenderItem.sActiveWireDepthPriority )
4921  list.append(selectItem)
4922 
4923  ## For active wireframe we will use a shader which allows us to draw thick lines
4924  ##
4925  drawThick = False
4926  shaderId = omr.MShaderManager.k3dSolidShader
4927  if drawThick:
4928  shaderId = omr.MShaderManager.k3dThickLineShader
4929 
4930  shader = shaderMgr.getStockShader(shaderId)
4931  if shader:
4932  ## assign shader
4933  selectItem.setShader(shader)
4934  ## once assigned, no need to hold on to shader instance
4935  shaderMgr.releaseShader(shader)
4936  else:
4937  selectItem = list[index]
4938 
4939  shader = None
4940  if selectItem:
4941  shader = selectItem.getShader()
4942 
4943  displayStatus = omr.MGeometryUtilities.displayStatus(path)
4944  wireColor = omr.MGeometryUtilities.wireframeColor(path)
4945 
4946  if displayStatus == omr.MGeometryUtilities.kLead:
4947  theColor = [ 0.0, 0.8, 0.0, 1.0 ]
4948  self.setSolidColor( shader, wireColor, theColor)
4949  selectItem.enable(True)
4950 
4951  elif displayStatus == omr.MGeometryUtilities.kActive:
4952  theColor = [ 1.0, 1.0, 1.0, 1.0 ]
4953  self.setSolidColor( shader, wireColor, theColor)
4954  selectItem.enable(True)
4955 
4956  elif displayStatus == omr.MGeometryUtilities.kHilite or displayStatus == omr.MGeometryUtilities.kActiveComponent:
4957  theColor = [ 0.0, 0.5, 0.7, 1.0 ]
4958  self.setSolidColor( shader, wireColor, theColor)
4959  selectItem.enable(True)
4960 
4961  else:
4962  selectItem.enable(False)
4963 
4964  ## Add custom user data to selection item
4965  myCustomData = selectItem.customData()
4966  if not myCustomData:
4967  ## create the custom data
4968  myCustomData = apiMeshUserData()
4969  myCustomData.fMessage = "I'm custom data!"
4970  selectItem.setCustomData(myCustomData)
4971  else:
4972  ## modify the custom data
4973  myCustomData.fNumModifications += 1
4974 
4975  def updateWireframeModeFaceCenterItem(self, path, list, shaderMgr):
4976  ## Add render item for face centers in wireframe mode, always show face centers
4977  ## in wireframe mode except it is drawn as template.
4978 
4979  wireframeModeFaceCenterItem = None
4980  index = list.indexOf(self.sWireframeModeFaceCenterItemName)
4981  if index < 0:
4982  wireframeModeFaceCenterItem = omr.MRenderItem.create(self.sWireframeModeFaceCenterItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
4983  wireframeModeFaceCenterItem.setDrawMode(omr.MGeometry.kWireframe)
4984  wireframeModeFaceCenterItem.setDepthPriority( omr.MRenderItem.sActiveWireDepthPriority )
4985 
4986  list.append(wireframeModeFaceCenterItem)
4987 
4988  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dFatPointShader )
4989  if shader:
4990  ## Set the point size parameter. Make it slightly larger for face centers
4991  pointSize = 5.0
4992  self.setSolidPointSize( shader, pointSize )
4993 
4994  wireframeModeFaceCenterItem.setShader(shader, self.sFaceCenterStreamName )
4995 
4996  ## once assigned, no need to hold on to shader instance
4997  shaderMgr.releaseShader(shader)
4998  else:
4999  wireframeModeFaceCenterItem = list[index]
5000 
5001  if wireframeModeFaceCenterItem:
5002  shader = wireframeModeFaceCenterItem.getShader()
5003  if shader:
5004  ## Set face center color in wireframe mode
5005  theColor = [ 0.0, 0.0, 1.0, 1.0 ]
5006  self.setSolidColor( shader, theColor )
5007 
5008  ## disable the face center item when template
5009  isTemplate = path.isTemplated()
5010  wireframeModeFaceCenterItem.enable( not isTemplate )
5011 
5012  def updateShadedModeFaceCenterItem(self, path, list, shaderMgr):
5013  ##Add render item for face centers in shaded mode. If the geometry is not selected,
5014  ## face centers are not drawn.
5015 
5016  shadedModeFaceCenterItem = None
5017  index = list.indexOf(self.sShadedModeFaceCenterItemName)
5018  if index < 0:
5019  shadedModeFaceCenterItem = omr.MRenderItem.create( self.sShadedModeFaceCenterItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
5020  shadedModeFaceCenterItem.setDrawMode(omr.MGeometry.kShaded | omr.MGeometry.kTextured)
5021 
5022  shadedModeFaceCenterItem.setDepthPriority(omr.MRenderItem.sActivePointDepthPriority)
5023 
5024  list.append(shadedModeFaceCenterItem)
5025 
5026  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dFatPointShader )
5027  if shader:
5028  ## Set the point size parameter. Make it slightly larger for face centers
5029  pointSize = 5.0
5030  self.setSolidPointSize( shader, pointSize )
5031 
5032  shadedModeFaceCenterItem.setShader(shader, self.sFaceCenterStreamName )
5033 
5034  ## once assigned, no need to hold on to shader instance
5035  shaderMgr.releaseShader(shader)
5036  else:
5037  shadedModeFaceCenterItem = list[index]
5038 
5039  if shadedModeFaceCenterItem:
5040  shadedModeFaceCenterItem.setExcludedFromPostEffects(True)
5041 
5042  shader = shadedModeFaceCenterItem.getShader()
5043  wireColor = omr.MGeometryUtilities.wireframeColor(path)
5044 
5045  if shader:
5046  ## Set face center color in shaded mode
5047  self.setSolidColor( shader, wireColor )
5048 
5049  displayStatus = omr.MGeometryUtilities.displayStatus(path)
5050  if displayStatus == omr.MGeometryUtilities.kActive or displayStatus == omr.MGeometryUtilities.kLead or displayStatus == omr.MGeometryUtilities.kActiveComponent or displayStatus == omr.MGeometryUtilities.kLive or displayStatus == omr.MGeometryUtilities.kHilite:
5051  shadedModeFaceCenterItem.enable(True)
5052 
5053  else:
5054  shadedModeFaceCenterItem.enable(False)
5055 
5056  def updateDormantVerticesItem(self, path, list, shaderMgr):
5057  ## Create a render item for dormant vertices if it does not exist. Updating
5058  ## shading parameters as necessary.
5059 
5060  vertexItem = None
5061  index = list.indexOf(self.sVertexItemName)
5062  if index < 0:
5063  vertexItem = omr.MRenderItem.create(self.sVertexItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
5064 
5065  ## Set draw mode to kAll : the item will be visible in the viewport and also during viewport 2.0 selection
5066  vertexItem.setDrawMode(omr.MGeometry.kAll)
5067 
5068  ## Set the selection mask to kSelectMeshVerts : we want the render item to be used for Vertex Components selection
5069  mask = om.MSelectionMask(om.MSelectionMask.kSelectMeshVerts)
5070  mask.addMask(om.MSelectionMask.kSelectPointsForGravity)
5071  vertexItem.setSelectionMask( mask )
5072 
5073  ## Set depth priority higher than wireframe and shaded render items,
5074  ## but lower than active points.
5075  ## Raising higher than wireframe will make them not seem embedded into the surface
5076  vertexItem.setDepthPriority( omr.MRenderItem.sDormantPointDepthPriority )
5077  list.append(vertexItem)
5078 
5079  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dFatPointShader )
5080  if shader:
5081  ## Set the point size parameter
5082  pointSize = 3.0
5083  self.setSolidPointSize( shader, pointSize )
5084 
5085  ## assign shader
5086  vertexItem.setShader(shader)
5087 
5088  ## once assigned, no need to hold on to shader instance
5089  shaderMgr.releaseShader(shader)
5090  else:
5091  vertexItem = list[index]
5092 
5093  if vertexItem:
5094  shader = vertexItem.getShader()
5095  ## set color
5096  theColor = [ 0.0, 0.0, 1.0, 1.0 ]
5097  self.setSolidColor( shader, theColor )
5098 
5099  displayStatus = omr.MGeometryUtilities.displayStatus(path)
5100 
5101  ## Generally if the display status is hilite then we
5102  ## draw components.
5103  if displayStatus == omr.MGeometryUtilities.kHilite or omr.MPxGeometryOverride.pointSnappingActive():
5104  ## In case the object is templated
5105  ## we will hide the components to be consistent
5106  ## with how internal objects behave.
5107  if path.isTemplated():
5108  vertexItem.enable(False)
5109  else:
5110  vertexItem.enable(True)
5111  else:
5112  vertexItem.enable(False)
5113 
5114  mySelectionData = vertexItem.customData()
5115  if not isinstance(mySelectionData, apiMeshHWSelectionUserData):
5116  ## create the custom data
5117  mySelectionData = apiMeshHWSelectionUserData()
5118  vertexItem.setCustomData(mySelectionData)
5119  ## update the custom data
5120  mySelectionData.fMeshGeom = self.fMeshGeom
5121 
5122  def updateActiveVerticesItem(self, path, list, shaderMgr):
5123  ## Create a render item for active vertices if it does not exist. Updating
5124  ## shading parameters as necessary.
5125 
5126  activeItem = None
5127  index = list.indexOf(self.sActiveVertexItemName)
5128  if index < 0:
5129  activeItem = omr.MRenderItem.create(self.sActiveVertexItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
5130  activeItem.setDrawMode(omr.MGeometry.kAll)
5131  ## Set depth priority to be active point. This should offset it
5132  ## to be visible above items with "dormant point" priority.
5133  activeItem.setDepthPriority( omr.MRenderItem.sActivePointDepthPriority )
5134  list.append(activeItem)
5135 
5136  shaderId = omr.MShaderManager.k3dFatPointShader
5137  if self.fDrawActiveVerticesWithRamp:
5138  shaderId = omr.MShaderManager.k3dColorLookupFatPointShader
5139 
5140  shader = shaderMgr.getStockShader( shaderId )
5141  if shader:
5142  ## Set the point size parameter. Make it slightly larger for active vertices
5143  pointSize = 5.0
5144  self.setSolidPointSize( shader, pointSize )
5145 
5146  ## 1D Ramp color lookup option
5147  ##
5148  if self.fDrawActiveVerticesWithRamp:
5149  textureMgr = omr.MRenderer.getTextureManager()
5150 
5151  ## Assign dummy ramp lookup
5152  if not self.fColorRemapTexture:
5153  ## Sample 3 colour ramp
5154  colorArray = [ 1.0, 0.0, 0.0, 1.0,
5155  0.0, 1.0, 0.0, 1.0,
5156  0.0, 0.0, 1.0, 1.0 ]
5157 
5158  arrayLen = 3
5159  textureDesc = omr.MTextureDescription()
5160  textureDesc.setToDefault2DTexture()
5161  textureDesc.fWidth = arrayLen
5162  textureDesc.fHeight = 1
5163  textureDesc.fDepth = 1
5164  textureDesc.fBytesPerSlice = textureDesc.fBytesPerRow = 24*arrayLen
5165  textureDesc.fMipmaps = 1
5166  textureDesc.fArraySlices = 1
5167  textureDesc.fTextureType = omr.MRenderer.kImage1D
5168  textureDesc.fFormat = omr.MRenderer.kR32G32B32A32_FLOAT
5169  self.fColorRemapTexture = textureMgr.acquireTexture("", textureDesc, colorArray, False)
5170 
5171  if not self.fLinearSampler:
5172  samplerDesc = omr.MSamplerStateDesc()
5173  samplerDesc.addressU = omr.MSamplerState.kTexClamp
5174  samplerDesc.addressV = omr.MSamplerState.kTexClamp
5175  samplerDesc.addressW = omr.MSamplerState.kTexClamp
5176  samplerDesc.filter = omr.MSamplerState.kMinMagMipLinear
5177  fLinearSampler = omr.MStateManager.acquireSamplerState(samplerDesc)
5178 
5179  if self.fColorRemapTexture and self.fLinearSampler:
5180  ## Set up the ramp lookup
5181  shader.setParameter("map", self.fColorRemapTexture)
5182  shader.setParameter("samp", self.fLinearSampler)
5183 
5184  ## No remapping. The initial data created in the xrange 0...1
5185  ##
5186  rampValueRange = om.MFloatVector(0.0, 1.0)
5187  shader.setParameter("UVRange", rampValueRange)
5188 
5189  ## Assign shader. Use a named stream if we want to supply a different
5190  ## set of "shared" vertices for drawing active vertices
5191  if self.fDrawSharedActiveVertices:
5192  activeItem.setShader(shader, self.sActiveVertexStreamName)
5193  else:
5194  activeItem.setShader(shader, None)
5195 
5196  ## once assigned, no need to hold on to shader instance
5197  shaderMgr.releaseShader(shader)
5198 
5199  else:
5200  activeItem = list[index]
5201 
5202  if activeItem:
5203  shader = activeItem.getShader()
5204  if shader:
5205  ## Set active color
5206  theColor = [ 1.0, 1.0, 0.0, 1.0 ]
5207  self.setSolidColor( shader, theColor )
5208 
5209  enable = (bool(self.fActiveVerticesSet) and self.enableActiveComponentDisplay(path))
5210  activeItem.enable( enable )
5211 
5212  def updateVertexNumericItems(self, path, list, shaderMgr):
5213  ## Create render items for numeric display, and update shaders as necessary
5214 
5215  ## Enable to show numeric render items
5216  enableNumericDisplay = False
5217 
5218  ## Vertex id item
5219  ##
5220  vertexItem = None
5221  index = list.indexOf(self.sVertexIdItemName)
5222  if index < 0:
5223  vertexItem = omr.MRenderItem.create( self.sVertexIdItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
5224  vertexItem.setDrawMode(omr.MGeometry.kAll)
5225  vertexItem.setDepthPriority( omr.MRenderItem.sDormantPointDepthPriority )
5226  list.append(vertexItem)
5227 
5228  ## Use single integer numeric shader
5229  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dIntegerNumericShader )
5230  if shader:
5231  ## Label the fields so that they can be found later on.
5232  vertexItem.setShader(shader, self.sVertexIdItemName)
5233  shaderMgr.releaseShader(shader)
5234  else:
5235  vertexItem = list[index]
5236 
5237  if vertexItem:
5238  shader = vertexItem.getShader()
5239  if shader:
5240  ## set color
5241  theColor = [ 1.0, 1.0, 0.0, 1.0 ]
5242  self.setSolidColor( shader, theColor )
5243 
5244  vertexItem.enable(enableNumericDisplay)
5245 
5246  ## Vertex position numeric render item
5247  ##
5248  vertexItem = None
5249  index = list.indexOf(self.sVertexPositionItemName)
5250  if index < 0:
5251  vertexItem = omr.MRenderItem.create( self.sVertexPositionItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kPoints)
5252  vertexItem.setDrawMode(omr.MGeometry.kAll)
5253  vertexItem.setDepthPriority( omr.MRenderItem.sDormantPointDepthPriority )
5254  list.append(vertexItem)
5255 
5256  ## Use triple float numeric shader
5257  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dFloat3NumericShader )
5258  if shader:
5259  ##vertexItem.setShader(shader)
5260  vertexItem.setShader(shader, self.sVertexPositionItemName)
5261  shaderMgr.releaseShader(shader)
5262  else:
5263  vertexItem = list[index]
5264 
5265  if vertexItem:
5266  shader = vertexItem.getShader()
5267  if shader:
5268  ## set color
5269  theColor = [ 0.0, 1.0, 1.0, 1.0 ]
5270  self.setSolidColor( shader, theColor)
5271 
5272  vertexItem.enable(enableNumericDisplay)
5273 
5274  def updateAffectedComponentItems(self, path, list, shaderMgr):
5275  ## Example of adding in items to hilite edges and faces. In this
5276  ## case these are edges and faces which are connected to vertices
5277  ## and we thus call them "affected" components.
5278 
5279  ## Create / update "affected/active" edges component render item.
5280  ##
5281  componentItem = None
5282  index = list.indexOf(self.sAffectedEdgeItemName)
5283  if index < 0:
5284  componentItem = omr.MRenderItem.create( self.sAffectedEdgeItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
5285  componentItem.setDrawMode(omr.MGeometry.kAll)
5286 
5287  ## Set depth priority to be active line so that it is above wireframe
5288  ## but below dormant and active points.
5289  componentItem.setDepthPriority( omr.MRenderItem.sActiveLineDepthPriority )
5290  list.append(componentItem)
5291 
5292  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dThickLineShader )
5293  if shader:
5294  ## Set lines a bit thicker to stand out
5295  lineSize = 1.0
5296  self.setLineWidth( shader, lineSize )
5297 
5298  ## Assign shader.
5299  componentItem.setShader(shader, None)
5300 
5301  ## once assigned, no need to hold on to shader instance
5302  shaderMgr.releaseShader(shader)
5303  else:
5304  componentItem = list[index]
5305 
5306  if componentItem:
5307  shader = componentItem.getShader()
5308  if shader:
5309  ## Set affected color
5310  theColor = [ 1.0, 1.0, 1.0, 1.0 ]
5311  self.setSolidColor( shader, theColor )
5312 
5313  enable = ((bool(self.fActiveVerticesSet) or bool(self.fActiveEdgesSet)) and self.enableActiveComponentDisplay(path))
5314  componentItem.enable( enable )
5315 
5316  ################################################################################
5317 
5318  ## Create / update "affected/active" faces component render item
5319  ##
5320  componentItem = None
5321  index = list.indexOf(self.sAffectedFaceItemName)
5322  if index < 0:
5323  componentItem = omr.MRenderItem.create( self.sAffectedFaceItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kTriangles)
5324  componentItem.setDrawMode(omr.MGeometry.kAll)
5325  ## Set depth priority to be dormant wire so that edge and vertices
5326  ## show on top.
5327  componentItem.setDepthPriority( omr.MRenderItem.sDormantWireDepthPriority )
5328  list.append(componentItem)
5329 
5330  shader = shaderMgr.getStockShader( omr.MShaderManager.k3dStippleShader )
5331  if shader:
5332  ## Assign shader.
5333  componentItem.setShader(shader, None)
5334 
5335  ## once assigned, no need to hold on to shader instance
5336  shaderMgr.releaseShader(shader)
5337  else:
5338  componentItem = list[index]
5339 
5340  if componentItem:
5341  shader = componentItem.getShader()
5342  if shader:
5343  ## Set affected color
5344  theColor = [ 1.0, 1.0, 1.0, 1.0 ]
5345  self.setSolidColor( shader, theColor )
5346 
5347  enable = ((bool(self.fActiveVerticesSet) or bool(self.fActiveFacesSet)) and self.enableActiveComponentDisplay(path))
5348  componentItem.enable( enable )
5349 
5350  def updateSelectionComponentItems(self, path, list, shaderMgr):
5351  ## Example of adding in items for edges and faces selection.
5352 
5353  ## For the vertex selection, we already have a render item that display all the vertices (sVertexItemName)
5354  ## we could use it for the selection as well.
5355 
5356  ## But we have none that display the complete edges or faces,
5357  ## sAffectedEdgeItemName and sAffectedFaceItemName only display a subset of the edges and faces
5358  ## that are active or affected (one of their vertices is selected).
5359 
5360  ## In order to be able to perform the selection of this components we'll create new render items
5361  ## that will only be used for the selection (they will not be visible in the viewport)
5362 
5363  ## Create / update selection edges component render item.
5364  ##
5365  selectionItem = None
5366  index = list.indexOf(self.sEdgeSelectionItemName)
5367  if index < 0:
5368  selectionItem = omr.MRenderItem.create( self.sEdgeSelectionItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kLines)
5369 
5370  ## Set draw mode to kSelectionOnly : the item will not be visible in the viewport, but during viewport 2.0 selection
5371  selectionItem.setDrawMode(omr.MGeometry.kSelectionOnly)
5372 
5373  ## Set the selection mask to kSelectMeshEdges : we want the render item to be used for Edge Components selection
5374  selectionItem.setSelectionMask( om.MSelectionMask.kSelectMeshEdges )
5375 
5376  ## Set depth priority to be selection so that it is above everything
5377  selectionItem.setDepthPriority( omr.MRenderItem.sSelectionDepthPriority )
5378  list.append(selectionItem)
5379 
5380  shader = shaderMgr.getStockShader(omr.MShaderManager.k3dThickLineShader)
5381  if shader:
5382  ## Assign shader.
5383  selectionItem.setShader(shader, None)
5384 
5385  ## once assigned, no need to hold on to shader instance
5386  shaderMgr.releaseShader(shader)
5387  else:
5388  selectionItem = list[index]
5389 
5390  if selectionItem:
5391  selectionItem.enable(True)
5392 
5393  mySelectionData = selectionItem.customData()
5394  if not isinstance(mySelectionData, apiMeshHWSelectionUserData):
5395  ## create the custom data
5396  mySelectionData = apiMeshHWSelectionUserData()
5397  selectionItem.setCustomData(mySelectionData)
5398  ## update the custom data
5399  mySelectionData.fMeshGeom = self.fMeshGeom
5400 
5401  ## Create / update selection faces component render item.
5402  ##
5403  index = list.indexOf(self.sFaceSelectionItemName)
5404  if index < 0:
5405  selectionItem = omr.MRenderItem.create( self.sFaceSelectionItemName, omr.MRenderItem.DecorationItem, omr.MGeometry.kTriangles)
5406 
5407  ## Set draw mode to kSelectionOnly : the item will not be visible in the viewport, but during viewport 2.0 selection
5408  selectionItem.setDrawMode(omr.MGeometry.kSelectionOnly)
5409 
5410  ## Set the selection mask to kSelectMeshFaces : we want the render item to be used for Face Components selection
5411  selectionItem.setSelectionMask( om.MSelectionMask.kSelectMeshFaces )
5412 
5413  ## Set depth priority to be selection so that it is above everything
5414  selectionItem.setDepthPriority( omr.MRenderItem.sSelectionDepthPriority )
5415  list.append(selectionItem)
5416 
5417  shader = shaderMgr.getStockShader(omr.MShaderManager.k3dSolidShader)
5418  if shader:
5419  ## Assign shader.
5420  selectionItem.setShader(shader, None)
5421 
5422  ## once assigned, no need to hold on to shader instance
5423  shaderMgr.releaseShader(shader)
5424  else:
5425  selectionItem = list[index]
5426 
5427  if selectionItem:
5428  selectionItem.enable(True)
5429 
5430  mySelectionData = selectionItem.customData()
5431  if not isinstance(mySelectionData, apiMeshHWSelectionUserData):
5432  ## create the custom data
5433  mySelectionData = apiMeshHWSelectionUserData()
5434  selectionItem.setCustomData(mySelectionData)
5435  ## update the custom data
5436  mySelectionData.fMeshGeom = self.fMeshGeom
5437 
5438  def updateProxyShadedItem(self, path, list, shaderMgr):
5439  ## In the event there are no shaded items we create a proxy
5440  ## render item so we can still see where the object is.
5441 
5442  ## Stock colors
5443  dormantColor = [ 0.0, 0.0, 1.0, 1.0 ]
5444  templateColor = [ 0.45, 0.45, 0.45, 1.0 ]
5445  activeTemplateColor = [ 1.0, 0.5, 0.5, 1.0 ]
5446 
5447  ## Note that we still want to raise it above shaded even though
5448  ## we don't have a shaded render item for this override.
5449  ## This will handle in case where there is another shaded object
5450  ## which overlaps this object in depth
5451  ##
5452  raiseAboveShaded = True
5453  shadedDrawMode = omr.MGeometry.kShaded | omr.MGeometry.kTextured
5454  ## Mark proxy item as wireframe if not using a material shader
5455  ##
5456  useFragmentShader = self.fProxyShader < 0
5457  if not useFragmentShader:
5458  shadedDrawMode |= omr.MGeometry.kWireframe
5459 
5460  ## Fragment + stipple shaders required triangles. All others
5461  ## in the possible list requires lines
5462  ##
5463  itemType = omr.MRenderItem.NonMaterialSceneItem
5464  primitive = omr.MGeometry.kLines
5465  filledProxy = useFragmentShader or self.fProxyShader == omr.MShaderManager.k3dStippleShader
5466  if filledProxy:
5467  itemType = omr.MRenderItem.MaterialSceneItem
5468  primitive = omr.MGeometry.kTriangles
5469 
5470  depthPriority = omr.MRenderItem.sDormantWireDepthPriority
5471  if raiseAboveShaded:
5472  depthPriority = omr.MRenderItem.sActiveWireDepthPriority
5473 
5474  proxyItem = None
5475  index = list.indexOf(self.sShadedProxyItemName)
5476  if index < 0:
5477  proxyItem = omr.MRenderItem.create( self.sShadedProxyItemName, itemType, primitive)
5478  proxyItem.setDrawMode(shadedDrawMode)
5479  proxyItem.setDepthPriority( depthPriority )
5480 
5481  proxyItem.setCastsShadows( not self.fExternalItems_NoShadowCast and self.fCastsShadows )
5482  proxyItem.setReceivesShadows( not self.fExternalItems_NoShadowReceive and self.fReceivesShadows )
5483  proxyItem.setExcludedFromPostEffects( self.fExternalItems_NoPostEffects )
5484 
5485  list.append(proxyItem)
5486 
5487  ## We'll draw the proxy with a proxy shader as a visual cue
5488  ##
5489  shader = None
5490  if useFragmentShader:
5491  shader = shaderMgr.getFragmentShader("mayaLambertSurface", "outSurfaceFinal", True)
5492  sBlue = [ 0.4, 0.4, 1.0 ]
5493  shader.setParameter("color", sBlue)
5494  shader.setIsTransparent(False)
5495  else:
5496  shader = shaderMgr.getStockShader( self.fProxyShader )
5497 
5498  if shader:
5499  if not filledProxy:
5500  self.setLineWidth(shader, 10.0)
5501 
5502  ## assign shader
5503  proxyItem.setShader(shader)
5504  ## once assigned, no need to hold on to shader instance
5505  shaderMgr.releaseShader(shader)
5506  else:
5507  proxyItem = list[index]
5508 
5509  ## As this is a shaded item it is up to the plug-in to determine
5510  ## on each update how to handle shadowing and effects.
5511  ## Especially note that shadowing changes on the DAG object will trigger
5512  ## a call to updateRenderItems()
5513  ##
5514  proxyItem.setCastsShadows( not self.fExternalItems_NoShadowCast and self.fCastsShadows )
5515  proxyItem.setReceivesShadows( not self.fExternalItems_NoShadowReceive and self.fReceivesShadows )
5516  proxyItem.setExcludedFromPostEffects( self.fExternalItems_NoPostEffects )
5517 
5518  ## Check for any shaded render items. A lack of one indicates
5519  ## there is no shader assigned to the object.
5520  ##
5521  haveShadedItems = False
5522  for item in list:
5523  if not item:
5524  continue
5525  drawMode = item.drawMode()
5526  if drawMode == omr.MGeometry.kShaded or drawMode == omr.MGeometry.kTextured:
5527  if item.name() != self.sShadedTemplateItemName:
5528  haveShadedItems = True
5529  break
5530 
5531  displayStatus = omr.MGeometryUtilities.displayStatus(path)
5532  wireColor = omr.MGeometryUtilities.wireframeColor(path)
5533 
5534  ## Note that we do not toggle the item on and off just based on
5535  ## display state. If this was so then call to MRenderer::setLightsAndShadowsDirty()
5536  ## would be required as shadow map update does not monitor display state.
5537  ##
5538  if proxyItem:
5539  shader = proxyItem.getShader()
5540 
5541  if displayStatus == omr.MGeometryUtilities.kTemplate:
5542  self.setSolidColor( shader, wireColor, templateColor )
5543 
5544  elif displayStatus == omr.MGeometryUtilities.kActiveTemplate:
5545  self.setSolidColor( shader, wireColor, activeTemplateColor )
5546 
5547  elif displayStatus == omr.MGeometryUtilities.kDormant:
5548  self.setSolidColor( shader, wireColor, dormantColor )
5549 
5550  ## If we are missing shaded render items then enable
5551  ## the proxy. Otherwise disable it.
5552  ##
5553  if filledProxy:
5554  ## If templated then hide filled proxy
5555  if path.isTemplated():
5556  proxyItem.enable(False)
5557  else:
5558  proxyItem.enable(not haveShadedItems)
5559  else:
5560  proxyItem.enable(not haveShadedItems)
5561 
5562  ## Data stream (geometry requirements) handling
5563  def updateGeometryRequirements(self, requirements, data, activeVertexCount, totalVerts, debugPopulateGeometry):
5564  ## Examine the geometry requirements and create / update the
5565  ## appropriate data streams. As render items specify both named and
5566  ## unnamed data streams, both need to be handled here.
5567 
5568  ## Vertex data
5569  positionBuffer = None
5570  positionDataAddress = None
5571  positionData = None
5572 
5573  vertexNumericIdBuffer = None
5574  vertexNumericIdDataAddress = None
5575  vertexNumericIdData = None
5576 
5577  vertexNumericIdPositionBuffer = None
5578  vertexNumericIdPositionDataAddress = None
5579  vertexNumericIdPositionData = None
5580 
5581  vertexNumericLocationBuffer = None
5582  vertexNumericLocationDataAddress = None
5583  vertexNumericLocationData = None
5584 
5585  vertexNumericLocationPositionBuffer = None
5586  vertexNumericLocationPositionDataAddress = None
5587  vertexNumericLocationPositionData = None
5588 
5589  activeVertexPositionBuffer = None
5590  activeVertexPositionDataAddress = None
5591  activeVertexPositionData = None
5592 
5593  activeVertexUVBuffer = None
5594  activeVertexUVDataAddress = None
5595  activeVertexUVData = None
5596 
5597  faceCenterPositionBuffer = None
5598  faceCenterPositionDataAddress = None
5599  faceCenterPositionData = None
5600 
5601  normalBuffer = None
5602  normalDataAddress = None
5603  normalData = None
5604 
5605  cpvBuffer = None
5606  cpvDataAddress = None
5607  cpvData = None
5608 
5609  uvBuffer = None
5610  uvDataAddress = None
5611  uvData = None
5612 
5613  numUVs = self.fMeshGeom.uvcoords.uvcount()
5614 
5615  descList = requirements.vertexRequirements()
5616  satisfiedRequirements = [False,] * len(descList)
5617  for i in xrange(len(descList)):
5618  desc = descList[i]
5619  ## Fill in vertex data for drawing active vertex components (if drawSharedActiveVertices=True)
5620  ##
5621  if self.fDrawSharedActiveVertices and (desc.name == self.sActiveVertexStreamName):
5622  if desc.semantic == omr.MGeometry.kPosition:
5623  if not activeVertexPositionBuffer:
5624  activeVertexPositionBuffer = data.createVertexBuffer(desc)
5625  if activeVertexPositionBuffer:
5626  satisfiedRequirements[i] = True
5627  if debugPopulateGeometry:
5628  print ">>> Fill in data for active vertex requirement '" + desc.name + "'. Semantic = kPosition"
5629  activeVertexPositionDataAddress = activeVertexPositionBuffer.acquire(activeVertexCount, True) ## writeOnly - we don't need the current buffer values
5630  if activeVertexPositionDataAddress:
5631  activeVertexPositionData = ((ctypes.c_float * 3)*activeVertexCount).from_address(activeVertexPositionDataAddress)
5632 
5633  elif desc.semantic == omr.MGeometry.kTexture:
5634  if not activeVertexUVBuffer:
5635  activeVertexUVBuffer = data.createVertexBuffer(desc)
5636  if activeVertexUVBuffer:
5637  satisfiedRequirements[i] = True
5638  if debugPopulateGeometry:
5639  print ">>> Fill in data for active vertex requirement '" + desc.name + "'. Semantic = kTexture"
5640  activeVertexUVDataAddress = activeVertexUVBuffer.acquire(activeVertexCount, True) ## writeOnly - we don't need the current buffer values
5641  if activeVertexUVDataAddress:
5642  activeVertexUVData = ((ctypes.c_float * 3)*activeVertexCount).from_address(activeVertexUVDataAddress)
5643  else:
5644  ## do nothing for stuff we don't understand
5645  pass
5646 
5647  ## Fill in vertex data for drawing face center components (if fDrawFaceCenters=True)
5648  ##
5649  elif self.fDrawFaceCenters and desc.name == self.sFaceCenterStreamName:
5650  if desc.semantic == omr.MGeometry.kPosition:
5651  if not faceCenterPositionBuffer:
5652  faceCenterPositionBuffer = data.createVertexBuffer(desc)
5653  if faceCenterPositionBuffer:
5654  satisfiedRequirements[i] = True
5655  if debugPopulateGeometry:
5656  print ">>> Fill in data for face center vertex requirement '" + desc.name + "'. Semantic = kPosition"
5657  faceCenterPositionDataAddress = faceCenterPositionBuffer.acquire(self.fMeshGeom.faceCount, True) ## writeOnly - we don't need the current buffer values
5658  if faceCenterPositionDataAddress:
5659  faceCenterPositionData = ((ctypes.c_float * 3)*self.fMeshGeom.faceCount).from_address(faceCenterPositionDataAddress)
5660 
5661  else:
5662  ## do nothing for stuff we don't understand
5663  pass
5664 
5665  ## Fill vertex stream data used for dormant vertex, wireframe and shaded drawing.
5666  ## Fill also for active vertices if (fDrawSharedActiveVertices=False)
5667  else:
5668  if desc.semantic == omr.MGeometry.kPosition:
5669  if desc.name == self.sVertexIdItemName:
5670  if not vertexNumericIdPositionBuffer:
5671  vertexNumericIdPositionBuffer = data.createVertexBuffer(desc)
5672  if vertexNumericIdPositionBuffer:
5673  satisfiedRequirements[i] = True
5674  if debugPopulateGeometry:
5675  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kPosition"
5676  print "Acquire 3loat-numeric position buffer"
5677  vertexNumericIdPositionDataAddress = vertexNumericIdPositionBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5678  if vertexNumericIdPositionDataAddress:
5679  vertexNumericIdPositionData = ((ctypes.c_float * 3)*totalVerts).from_address(vertexNumericIdPositionDataAddress)
5680 
5681  elif desc.name == self.sVertexPositionItemName:
5682  if not vertexNumericLocationPositionBuffer:
5683  vertexNumericLocationPositionBuffer = data.createVertexBuffer(desc)
5684  if vertexNumericLocationPositionBuffer:
5685  satisfiedRequirements[i] = True
5686  if debugPopulateGeometry:
5687  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kPosition"
5688  print "Acquire 3loat-numeric position buffer"
5689  vertexNumericLocationPositionDataAddress = vertexNumericLocationPositionBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5690  if vertexNumericLocationPositionDataAddress:
5691  vertexNumericLocationPositionData = ((ctypes.c_float * 3)*totalVerts).from_address(vertexNumericLocationPositionDataAddress)
5692 
5693  else:
5694  if not positionBuffer:
5695  positionBuffer = data.createVertexBuffer(desc)
5696  if positionBuffer:
5697  satisfiedRequirements[i] = True
5698  if debugPopulateGeometry:
5699  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kPosition"
5700  print "Acquire unnamed position buffer"
5701  positionDataAddress = positionBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5702  if positionDataAddress:
5703  positionData = ((ctypes.c_float * 3)*totalVerts).from_address(positionDataAddress)
5704 
5705  elif desc.semantic == omr.MGeometry.kNormal:
5706  if not normalBuffer:
5707  normalBuffer = data.createVertexBuffer(desc)
5708  if normalBuffer:
5709  satisfiedRequirements[i] = True
5710  if debugPopulateGeometry:
5711  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kNormal"
5712  normalDataAddress = normalBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5713  if normalDataAddress:
5714  normalData = ((ctypes.c_float * 3)*totalVerts).from_address(normalDataAddress)
5715 
5716  elif desc.semantic == omr.MGeometry.kTexture:
5717  numericValue = "numericvalue"
5718  numeric3Value ="numeric3value"
5719 
5720  ## Fill in single numeric field
5721  if desc.semanticName.lower() == numericValue and desc.name == self.sVertexIdItemName:
5722  if not vertexNumericIdBuffer:
5723  vertexNumericIdBuffer = data.createVertexBuffer(desc)
5724  if vertexNumericIdBuffer:
5725  satisfiedRequirements[i] = True
5726  if debugPopulateGeometry:
5727  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kTexture"
5728  print "Acquire 1loat numeric buffer"
5729  vertexNumericIdDataAddress = vertexNumericIdBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5730  if vertexNumericIdDataAddress:
5731  vertexNumericIdData = ((ctypes.c_float * 1)*totalVerts).from_address(vertexNumericIdDataAddress)
5732 
5733  ## Fill in triple numeric field
5734  elif desc.semanticName.lower() == numeric3Value and desc.name == self.sVertexPositionItemName:
5735  if not vertexNumericLocationBuffer:
5736  vertexNumericLocationBuffer = data.createVertexBuffer(desc)
5737  if vertexNumericLocationBuffer:
5738  satisfiedRequirements[i] = True
5739  if debugPopulateGeometry:
5740  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kTexture"
5741  print "Acquire 3loat numeric location buffer"
5742  vertexNumericLocationDataAddress = vertexNumericLocationBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5743  if vertexNumericLocationDataAddress:
5744  vertexNumericLocationData = ((ctypes.c_float * 3)*totalVerts).from_address(vertexNumericLocationDataAddress)
5745 
5746  ## Fill in uv values
5747  elif desc.name != self.sVertexIdItemName and desc.name != self.sVertexPositionItemName:
5748  if not uvBuffer:
5749  uvBuffer = data.createVertexBuffer(desc)
5750  if uvBuffer:
5751  satisfiedRequirements[i] = True
5752  if debugPopulateGeometry:
5753  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kTexture"
5754  print "Acquire a uv buffer"
5755  uvDataAddress = uvBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5756  if uvDataAddress:
5757  uvData = ((ctypes.c_float * 2)*totalVerts).from_address(uvDataAddress)
5758 
5759  elif desc.semantic == omr.MGeometry.kColor:
5760  if not cpvBuffer:
5761  cpvBuffer = data.createVertexBuffer(desc)
5762  if cpvBuffer:
5763  satisfiedRequirements[i] = True
5764  if debugPopulateGeometry:
5765  print ">>> Fill in data for requirement '" + desc.name + "'. Semantic = kColor"
5766  cpvDataAddress = cpvBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
5767  if cpvDataAddress:
5768  cpvData = ((ctypes.c_float * 4)*totalVerts).from_address(cpvDataAddress)
5769 
5770  else:
5771  ## do nothing for stuff we don't understand
5772  pass
5773 
5774  vid = 0
5775  pid = 0
5776  nid = 0
5777  uvid = 0
5778  cid = 0
5779  for i in xrange(self.fMeshGeom.faceCount):
5780  ## ignore degenerate faces
5781  numVerts = self.fMeshGeom.face_counts[i]
5782  if numVerts > 2:
5783  for v in xrange(numVerts):
5784  if any((positionData, vertexNumericIdPositionData, vertexNumericLocationPositionData, vertexNumericLocationData)):
5785  position = self.fMeshGeom.vertices[self.fMeshGeom.face_connects[vid]]
5786  ## Position used as position
5787  if positionData:
5788  positionData[pid][0] = position[0]
5789  positionData[pid][1] = position[1]
5790  positionData[pid][2] = position[2]
5791 
5792  ## Move the id's a bit to avoid overlap. Position used as position.
5793  if vertexNumericIdPositionData:
5794  vertexNumericIdPositionData[pid][0] = position[0]+1.0
5795  vertexNumericIdPositionData[pid][1] = position[1]+1.0
5796  vertexNumericIdPositionData[pid][2] = position[2]+1.0
5797 
5798  ## Move the locations a bit to avoid overlap. Position used as position.
5799  if vertexNumericLocationPositionData:
5800  vertexNumericLocationPositionData[pid][0] = position[0]+3.0
5801  vertexNumericLocationPositionData[pid][1] = position[1]+3.0
5802  vertexNumericLocationPositionData[pid][2] = position[2]+3.0
5803 
5804  ## Position used as numeric display.
5805  if vertexNumericLocationData:
5806  vertexNumericLocationData[pid][0] = position[0]
5807  vertexNumericLocationData[pid][1] = position[1]
5808  vertexNumericLocationData[pid][2] = position[2]
5809 
5810  pid += 1
5811 
5812  if normalData:
5813  normal = self.fMeshGeom.normals[self.fMeshGeom.face_connects[vid]]
5814  normalData[nid][0] = normal[0]
5815  normalData[nid][1] = normal[1]
5816  normalData[nid][2] = normal[2]
5817 
5818  nid += 1
5819 
5820  if uvData:
5821  uv = [0, 0]
5822  if numUVs > 0:
5823  uvNum = self.fMeshGeom.uvcoords.uvId(vid)
5824  uv = self.fMeshGeom.uvcoords.getUV(uvNum)
5825  uvData[uvid][0] = uv[0]
5826  uvData[uvid][1] = uv[0]
5827 
5828  uvid += 1
5829 
5830  ## Just same fake colors to show filling in requirements for
5831  ## color-per-vertex (CPV)
5832  if cpvData:
5833  position = self.fMeshGeom.vertices[self.fMeshGeom.face_connects[vid]]
5834  cpvData[cid][0] = position[0]
5835  cpvData[cid][1] = position[1]
5836  cpvData[cid][2] = position[2]
5837  cpvData[cid][3] = 1.0
5838 
5839  cid += 1
5840 
5841  ## Vertex id's used for numeric display
5842  if vertexNumericIdData:
5843  vertexNumericIdData[vid] = fMeshGeom.face_connects[vid]
5844  pass
5845 
5846  vid += 1
5847 
5848  elif numVerts > 0:
5849  vid += numVerts
5850 
5851  if positionDataAddress:
5852  positionBuffer.commit(positionDataAddress)
5853 
5854  if normalDataAddress:
5855  normalBuffer.commit(normalDataAddress)
5856 
5857  if uvDataAddress:
5858  uvBuffer.commit(uvDataAddress)
5859 
5860  if cpvDataAddress:
5861  cpvBuffer.commit(cpvDataAddress)
5862 
5863  if vertexNumericIdDataAddress:
5864  vertexNumericIdBuffer.commit(vertexNumericIdDataAddress)
5865 
5866  if vertexNumericIdPositionDataAddress:
5867  vertexNumericIdPositionBuffer.commit(vertexNumericIdPositionDataAddress)
5868 
5869  if vertexNumericLocationDataAddress:
5870  vertexNumericLocationBuffer.commit(vertexNumericLocationDataAddress)
5871 
5872  if vertexNumericLocationPositionDataAddress:
5873  vertexNumericLocationPositionBuffer.commit(vertexNumericLocationPositionDataAddress)
5874 
5875  ## Fill in active vertex data buffer (only when fDrawSharedActiveVertices=True
5876  ## which results in activeVertexPositionDataAddress and activeVertexPositionBuffer being non-NULL)
5877  ##
5878  if activeVertexPositionData:
5879  if debugPopulateGeometry:
5880  print ">>> Fill in the data for active vertex position buffer base on component list"
5881 
5882  ## Fill in position buffer with positions based on active vertex indexing list
5883  ##
5884  if activeVertexCount > len(self.fMeshGeom.vertices):
5885  activeVertexCount = len(self.fMeshGeom.vertices)
5886 
5887  for i in xrange(activeVertexCount):
5888  position = self.fMeshGeom.vertices[ self.fActiveVertices[i] ]
5889  activeVertexPositionData[i][0] = position[0]
5890  activeVertexPositionData[i][1] = position[1]
5891  activeVertexPositionData[i][2] = position[2]
5892 
5893  activeVertexPositionBuffer.commit(activeVertexPositionDataAddress)
5894 
5895  if activeVertexUVData:
5896  if debugPopulateGeometry:
5897  print ">>> Fill in the data for active vertex uv buffer base on component list"
5898 
5899  ## Fill in position buffer with positions based on active vertex indexing list
5900  ##
5901  if activeVertexCount > len(self.fMeshGeom.vertices):
5902  activeVertexCount = len(self.fMeshGeom.vertices)
5903 
5904  for i in xrange(activeVertexCount):
5905  activeVertexUVData[i] = i / activeVertexCount
5906 
5907  activeVertexUVBuffer.commit(activeVertexUVDataAddress)
5908 
5909  ## Fill in face center data buffer (only when fDrawFaceCenter=True
5910  ## which results in faceCenterPositionDataAddress and faceCenterPositionBuffer being non-NULL)
5911  ##
5912  if faceCenterPositionData:
5913  if debugPopulateGeometry:
5914  print ">>> Fill in the data for face center position buffer"
5915 
5916  ## Fill in face center buffer with positions based on realtime calculations.
5917  ##
5918  pid = 0
5919  vid = 0
5920  for faceId in xrange(self.fMeshGeom.faceCount):
5921  ##tmp variables for calculating the face center position.
5922  x = 0.0
5923  y = 0.0
5924  z = 0.0
5925 
5926  faceCenterPosition = om.MPoint()
5927 
5928  ## ignore degenerate faces
5929  numVerts = self.fMeshGeom.face_counts[faceId]
5930  if numVerts > 2:
5931  for v in xrange(numVerts):
5932  face_vertex_position = self.fMeshGeom.vertices[self.fMeshGeom.face_connects[vid]]
5933  x += face_vertex_position[0]
5934  y += face_vertex_position[1]
5935  z += face_vertex_position[2]
5936 
5937  vid += 1
5938 
5939  faceCenterPosition = om.MPoint(x, y, z) / numVerts
5940 
5941  elif numVerts > 0:
5942  vid += numVerts
5943 
5944  faceCenterPositionData[faceId][0] = faceCenterPosition[0]
5945  faceCenterPositionData[faceId][1] = faceCenterPosition[1]
5946  faceCenterPositionData[faceId][2] = faceCenterPosition[2]
5947 
5948  faceCenterPositionBuffer.commit(faceCenterPositionDataAddress)
5949 
5950  ## Run around a second time and handle duplicate buffers and unknown buffers
5951  ##
5952  for i in xrange(len(descList)):
5953  if satisfiedRequirements[i]:
5954  continue
5955  desc = descList[i]
5956  if self.fDrawSharedActiveVertices and (desc.name == self.sActiveVertexStreamName):
5957  if desc.semantic == omr.MGeometry.kPosition:
5958  satisfiedRequirements[i] = True
5959  self.cloneVertexBuffer(activeVertexPositionBuffer, data, desc, activeVertexCount, debugPopulateGeometry)
5960  elif desc.semantic == omr.MGeometry.kTexture:
5961  satisfiedRequirements[i] = True
5962  self.cloneVertexBuffer(activeVertexUVBuffer, data, desc, activeVertexCount, debugPopulateGeometry)
5963  elif self.fDrawFaceCenters and desc.name == self.sFaceCenterStreamName:
5964  if desc.semantic == omr.MGeometry.kPosition:
5965  satisfiedRequirements[i] = True
5966  self.cloneVertexBuffer(faceCenterPositionBuffer, data, desc, self.fMeshGeom.faceCount, debugPopulateGeometry)
5967  else:
5968  if desc.semantic == omr.MGeometry.kPosition:
5969  if desc.name == self.sVertexIdItemName:
5970  satisfiedRequirements[i] = True
5971  self.cloneVertexBuffer(vertexNumericIdPositionBuffer, data, desc, totalVerts, debugPopulateGeometry)
5972  elif desc.name == self.sVertexPositionItemName:
5973  satisfiedRequirements[i] = True
5974  self.cloneVertexBuffer(vertexNumericLocationPositionBuffer, data, desc, totalVerts, debugPopulateGeometry)
5975  else:
5976  satisfiedRequirements[i] = True
5977  self.cloneVertexBuffer(positionBuffer, data, desc, totalVerts, debugPopulateGeometry)
5978  elif desc.semantic == omr.MGeometry.kNormal:
5979  satisfiedRequirements[i] = True
5980  self.cloneVertexBuffer(normalBuffer, data, desc, totalVerts, debugPopulateGeometry)
5981  elif desc.semantic == omr.MGeometry.kTexture:
5982  numericValue = "numericvalue"
5983  numeric3Value ="numeric3value"
5984  if desc.semanticName.lower() == numericValue and desc.name == self.sVertexIdItemName:
5985  satisfiedRequirements[i] = True
5986  self.cloneVertexBuffer(vertexNumericIdBuffer, data, desc, totalVerts, debugPopulateGeometry)
5987  elif desc.semanticName.lower() == numeric3Value and desc.name == self.sVertexPositionItemName:
5988  satisfiedRequirements[i] = True
5989  self.cloneVertexBuffer(vertexNumericLocationBuffer, data, desc, totalVerts, debugPopulateGeometry)
5990  elif desc.name != self.sVertexIdItemName and desc.name != self.sVertexPositionItemName:
5991  satisfiedRequirements[i] = True
5992  self.cloneVertexBuffer(uvBuffer, data, desc, totalVerts, debugPopulateGeometry)
5993  elif desc.semantic == omr.MGeometry.kColor:
5994  satisfiedRequirements[i] = True
5995  self.cloneVertexBuffer(cpvBuffer, data, desc, totalVerts, debugPopulateGeometry)
5996 
5997  if not satisfiedRequirements[i]:
5998  ## We have a strange buffer request we do not understand. Provide a set of Zeros sufficient to cover
5999  ## totalVerts:
6000  destBuffer = data.createVertexBuffer(desc)
6001  if destBuffer:
6002  satisfiedRequirements[i] = True
6003  if debugPopulateGeometry:
6004  print ">>> Fill in dummy requirement '%s'" % (desc.name, )
6005  destBufferDataAddress = destBuffer.acquire(totalVerts, True) ## writeOnly - we don't need the current buffer values
6006  if destBufferDataAddress:
6007  destBufferData = ((ctypes.c_float * desc.dimension)*totalVerts).from_address(destBufferDataAddress)
6008  if destBufferData:
6009  for j in xrange(totalVerts):
6010  if desc.dimension == 4:
6011  destBufferData[j] = (1.0, 0.0, 0.0, 1.0)
6012  elif desc.dimension == 3:
6013  destBufferData[j] = (1.0, 0.0, 0.0)
6014  else:
6015  for k in xrange(desc.dimension):
6016  destBufferData[j][k] = 0.0
6017  destBuffer.commit(destBufferDataAddress)
6018 
6019  ## Clone a vertex buffer to fulfill a duplicate requirement.
6020  ## Can happen for effects asking for multiple UV streams by
6021  ## name.
6022  def cloneVertexBuffer(self, srcBuffer, data, desc, dataSize, debugPopulateGeometry):
6023  if srcBuffer:
6024  destBuffer = data.createVertexBuffer(desc)
6025  if destBuffer:
6026  if debugPopulateGeometry:
6027  print ">>> Cloning requirement '%s'" % (desc.name, )
6028  destBufferDataAddress = destBuffer.acquire(dataSize, True) ## writeOnly - we don't need the current buffer values
6029  srcBufferDataAddress = srcBuffer.map()
6030  if destBufferDataAddress and srcBufferDataAddress:
6031  destBufferData = ((ctypes.c_float * desc.dimension)*dataSize).from_address(destBufferDataAddress)
6032  srcBufferData = ((ctypes.c_float * desc.dimension)*dataSize).from_address(srcBufferDataAddress)
6033  if destBufferData and srcBufferData:
6034  for j in xrange(dataSize):
6035  for k in xrange(desc.dimension):
6036  destBufferData[j][k] = srcBufferData[j][k]
6037  destBuffer.commit(destBufferDataAddress)
6038  srcBuffer.unmap()
6039 
6040  ## Indexing for render item handling methods
6041  def updateIndexingForWireframeItems(self, wireIndexBuffer, item, data, totalVerts):
6042  ## Create / update indexing required to draw wireframe render items.
6043  ## There can be more than one render item using the same wireframe indexing
6044  ## so it is passed in as an argument. If it is not null then we can
6045  ## reuse it instead of creating new indexing.
6046 
6047  ## Wireframe index buffer is same for both wireframe and selected render item
6048  ## so we only compute and allocate it once, but reuse it for both render items
6049  if not wireIndexBuffer:
6050  wireIndexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6051  if wireIndexBuffer:
6052  dataAddress = wireIndexBuffer.acquire(2*totalVerts, True) ## writeOnly - we don't need the current buffer values
6053  if dataAddress:
6054  data = (ctypes.c_uint * (2*totalVerts)).from_address(dataAddress)
6055  vid = 0
6056  first = 0
6057  idx = 0
6058  for faceIdx in xrange(self.fMeshGeom.faceCount):
6059  ## ignore degenerate faces
6060  numVerts = self.fMeshGeom.face_counts[faceIdx]
6061  if numVerts > 2:
6062  first = vid
6063  for v in xrange(numVerts-1):
6064  data[idx] = vid
6065  vid += 1
6066  idx += 1
6067  data[idx] = vid
6068  idx += 1
6069 
6070  data[idx] = vid
6071  vid += 1
6072  idx += 1
6073  data[idx] = first
6074  idx += 1
6075 
6076  else:
6077  vid += numVerts
6078 
6079  wireIndexBuffer.commit(dataAddress)
6080 
6081  ## Associate same index buffer with either render item
6082  if wireIndexBuffer:
6083  item.associateWithIndexBuffer(wireIndexBuffer)
6084 
6085  def updateIndexingForDormantVertices(self, item, data, numTriangles):
6086  ## Create / update indexing for render items which draw dormant vertices
6087 
6088  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6089  if indexBuffer:
6090  dataAddress = indexBuffer.acquire(3*numTriangles, True) ## writeOnly - we don't need the current buffer values
6091  if dataAddress:
6092  data = (ctypes.c_uint*(3*numTriangles)).from_address(dataAddress)
6093  ## compute index data for triangulated convex polygons sharing
6094  ## poly vertex data among triangles
6095  base = 0
6096  idx = 0
6097  for faceIdx in xrange(self.fMeshGeom.faceCount):
6098  ## ignore degenerate faces
6099  numVerts = self.fMeshGeom.face_counts[faceIdx]
6100  if numVerts > 2:
6101  for v in xrange(1, numVerts-1):
6102  data[idx] = base
6103  data[idx+1] = base+v
6104  data[idx+2] = base+v+1
6105  idx += 3
6106 
6107  base += numVerts
6108 
6109  indexBuffer.commit(dataAddress)
6110 
6111  item.associateWithIndexBuffer(indexBuffer)
6112 
6113  def updateIndexingForFaceCenters(self, item, data, debugPopulateGeometry):
6114  ## Create / update indexing for render items which draw face centers
6115 
6116  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6117  if indexBuffer:
6118  dataAddress = indexBuffer.acquire(self.fMeshGeom.faceCount, True) ## writeOnly - we don't need the current buffer values
6119  if dataAddress:
6120  data = (ctypes.c_uint * self.fMeshGeom.faceCount).from_address(dataAddress)
6121  if debugPopulateGeometry:
6122  print ">>> Set up indexing for face centers"
6123 
6124  for i in xrange(self.fMeshGeom.faceCount):
6125  data[i] = 0
6126  pass
6127 
6128  idx = 0
6129  for i in xrange(self.fMeshGeom.faceCount):
6130  ## ignore degenerate faces
6131  numVerts = self.fMeshGeom.face_counts[i]
6132  if numVerts > 2:
6133  data[idx] = idx
6134  idx += 1
6135 
6136  indexBuffer.commit(dataAddress)
6137 
6138  item.associateWithIndexBuffer(indexBuffer)
6139 
6140  def updateIndexingForVertices(self, item, data, numTriangles, activeVertexCount, debugPopulateGeometry):
6141  ## Create / update indexing for render items which draw active vertices
6142 
6143  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6144  if indexBuffer:
6145  dataAddress = None
6146 
6147  ## If drawing shared active vertices then the indexing degenerates into
6148  ## a numerically increasing index value. Otherwise a remapping from
6149  ## the active vertex list indexing to the unshared position stream is required.
6150  ##
6151  ## 1. Create indexing for shared positions. In this case it
6152  ## is a degenerate list since the position buffer was created
6153  ## in linear ascending order.
6154  ##
6155  if self.fDrawSharedActiveVertices:
6156  dataAddress = indexBuffer.acquire(activeVertexCount, True) ## writeOnly - we don't need the current buffer values
6157  if dataAddress:
6158  data = (ctypes.c_uint*activeVertexCount).from_address(dataAddress)
6159  if debugPopulateGeometry:
6160  print ">>> Set up indexing for shared vertices"
6161 
6162  for i in xrange(activeVertexCount):
6163  data[i] = i
6164 
6165  ## 2. Create indexing to remap to unshared positions
6166  ##
6167  else:
6168  if debugPopulateGeometry:
6169  print ">>> Set up indexing for unshared vertices"
6170 
6171  vertexCount = 3*numTriangles
6172  dataAddress = indexBuffer.acquire(vertexCount, True) ## writeOnly - we don't need the current buffer values
6173  if dataAddress:
6174  data = (ctypes.c_uint*vertexCount).from_address(dataAddress)
6175  for i in xrange(vertexCount):
6176  data[i] = vertexCount+1
6177 
6178  selectionIdSet = self.fActiveVerticesSet
6179 
6180  ## compute index data for triangulated convex polygons sharing
6181  ## poly vertex data among triangles
6182  base = 0
6183  lastFound = 0
6184  idx = 0
6185  for faceIdx in xrange(self.fMeshGeom.faceCount):
6186  ## ignore degenerate faces
6187  numVerts = self.fMeshGeom.face_counts[faceIdx]
6188  if numVerts > 2:
6189  for v in xrange(1, numVerts-1):
6190  vertexId = self.fMeshGeom.face_connects[base]
6191  if vertexId in selectionIdSet:
6192  lastFound = base
6193  data[idx] = lastFound
6194  idx += 1
6195 
6196  vertexId = self.fMeshGeom.face_connects[base+v]
6197  if vertexId in selectionIdSet:
6198  lastFound = base+v
6199  data[idx] = lastFound
6200  idx += 1
6201 
6202  vertexId = self.fMeshGeom.face_connects[base+v+1]
6203  if vertexId in selectionIdSet:
6204  lastFound = base+v+1
6205  data[idx] = lastFound
6206  idx +1
6207 
6208  base += numVerts
6209 
6210  for i in xrange(vertexCount):
6211  if data[i] == vertexCount+1:
6212  data[i] = lastFound
6213 
6214  if dataAddress:
6215  indexBuffer.commit(dataAddress)
6216 
6217  item.associateWithIndexBuffer(indexBuffer)
6218 
6219  def updateIndexingForEdges(self, item, data, totalVerts, fromSelection):
6220  ## Create / update indexing for render items which draw affected edges
6221 
6222  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6223  if indexBuffer:
6224  totalEdges = 2*totalVerts
6225  totalEdgesP1 = 2*totalVerts+1
6226  dataAddress = indexBuffer.acquire(totalEdges, True) ## writeOnly - we don't need the current buffer values
6227  if dataAddress:
6228  data = (ctypes.c_uint*totalEdges).from_address(dataAddress)
6229  for i in xrange(totalEdges):
6230  data[i] = totalEdgesP1
6231  pass
6232 
6233  displayAll = not fromSelection
6234  displayActives = (not displayAll and bool(self.fActiveEdgesSet))
6235  displayAffected = (not displayAll and not displayActives)
6236 
6237  selectionIdSet = None
6238  if displayActives:
6239  selectionIdSet = self.fActiveEdgesSet
6240  elif displayAffected:
6241  selectionIdSet = self.fActiveVerticesSet
6242 
6243  base = 0
6244  lastFound = 0
6245  idx = 0
6246  edgeId = 0
6247  for faceIdx in xrange(self.fMeshGeom.faceCount):
6248  ## ignore degenerate faces
6249  numVerts = self.fMeshGeom.face_counts[faceIdx]
6250  if numVerts > 2:
6251  for v in xrange(numVerts):
6252  enableEdge = displayAll
6253  vindex1 = base + (v % numVerts)
6254  vindex2 = base + ((v+1) % numVerts)
6255 
6256  if displayAffected:
6257  ## Check either ends of an "edge" to see if the
6258  ## vertex is in the active vertex list
6259  ##
6260  vertexId = self.fMeshGeom.face_connects[vindex1]
6261  if vertexId in selectionIdSet:
6262  enableEdge = True
6263  lastFound = vindex1
6264 
6265  if not enableEdge:
6266  vertexId2 = self.fMeshGeom.face_connects[vindex2]
6267  if vertexId2 in selectionIdSet:
6268  enableEdge = True
6269  lastFound = vindex2
6270 
6271  elif displayActives:
6272  ## Check if the edge is active
6273  ##
6274  if edgeId in selectionIdSet:
6275  enableEdge = True
6276  lastFound = vindex1
6277 
6278  ## Add indices for "edge"
6279  if enableEdge:
6280  data[idx] = vindex1
6281  idx += 1
6282  data[idx] = vindex2
6283  idx += 1
6284  edgeId += 1
6285 
6286  base += numVerts
6287 
6288  if not displayAll:
6289  for i in xrange(totalEdges):
6290  if data[i] == totalEdgesP1:
6291  data[i] = lastFound
6292 
6293  indexBuffer.commit(dataAddress)
6294 
6295  item.associateWithIndexBuffer(indexBuffer)
6296 
6297  def updateIndexingForFaces(self, item, data, numTriangles, fromSelection):
6298  ## Create / update indexing for render items which draw affected/active faces
6299 
6300  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6301  if indexBuffer:
6302  numTriangleVertices = 3*numTriangles
6303  dataAddress = indexBuffer.acquire(numTriangleVertices, True) ## writeOnly - we don't need the current buffer values
6304  if dataAddress:
6305  data = (ctypes.c_uint*numTriangleVertices).from_address(dataAddress)
6306  for i in xrange(numTriangleVertices):
6307  data[i] = numTriangleVertices+1
6308  pass
6309 
6310  displayAll = not fromSelection
6311  displayActives = (not displayAll and bool(self.fActiveFacesSet))
6312  displayAffected = (not displayAll and not displayActives)
6313 
6314  selectionIdSet = None
6315  if displayActives:
6316  selectionIdSet = self.fActiveFacesSet
6317  elif displayAffected:
6318  selectionIdSet = self.fActiveVerticesSet
6319 
6320  base = 0
6321  lastFound = 0
6322  idx = 0
6323  for faceIdx in xrange(self.fMeshGeom.faceCount):
6324  ## ignore degenerate faces
6325  numVerts = self.fMeshGeom.face_counts[faceIdx]
6326  if numVerts > 2:
6327  enableFace = displayAll
6328 
6329  if displayAffected:
6330  ## Scan for any vertex in the active list
6331  ##
6332  for v in xrange(1, numVerts-1):
6333  vertexId = self.fMeshGeom.face_connects[base]
6334  if vertexId in selectionIdSet:
6335  enableFace = True
6336  lastFound = base
6337 
6338  if not enableFace:
6339  vertexId2 = self.fMeshGeom.face_connects[base+v]
6340  if vertexId2 in selectionIdSet:
6341  enableFace = True
6342  lastFound = base+v
6343 
6344  if not enableFace:
6345  vertexId3 = self.fMeshGeom.face_connects[base+v+1]
6346  if vertexId3 in selectionIdSet:
6347  enableFace = True
6348  lastFound = base+v+1
6349 
6350  elif displayActives:
6351  ## Check if the face is active
6352  ##
6353  if faceIdx in selectionIdSet:
6354  enableFace = True
6355  lastFound = base
6356 
6357  ## Found an active face
6358  ## or one active vertex on the triangle so add indexing for the entire triangle.
6359  ##
6360  if enableFace:
6361  for v in xrange(1, numVerts-1):
6362  data[idx] = base
6363  data[idx+1] = base+v
6364  data[idx+2] = base+v+1
6365  idx += 3
6366 
6367  base += numVerts
6368 
6369  if not displayAll:
6370  for i in xrange(numTriangleVertices):
6371  if data[i] == numTriangleVertices+1:
6372  data[i] = lastFound
6373 
6374  indexBuffer.commit(dataAddress)
6375 
6376  item.associateWithIndexBuffer(indexBuffer)
6377 
6378  def updateIndexingForShadedTriangles(self, item, data, numTriangles):
6379  ## Create / update indexing for render items which draw filled / shaded
6380  ## triangles.
6381 
6382  indexBuffer = data.createIndexBuffer(omr.MGeometry.kUnsignedInt32)
6383  if indexBuffer:
6384  dataAddress = indexBuffer.acquire(3*numTriangles, True) ## writeOnly - we don't need the current buffer values
6385  if dataAddress:
6386  data = (ctypes.c_uint * (3*numTriangles)).from_address(dataAddress)
6387  ## compute index data for triangulated convex polygons sharing
6388  ## poly vertex data among triangles
6389  base = 0
6390  idx = 0
6391  for faceIdx in xrange(self.fMeshGeom.faceCount):
6392  ## ignore degenerate faces
6393  numVerts = self.fMeshGeom.face_counts[faceIdx]
6394  if numVerts > 2:
6395  for v in xrange(1, numVerts-1):
6396  data[idx] = base
6397  data[idx+1] = base+v
6398  data[idx+2] = base+v+1
6399  idx += 3
6400 
6401  base += numVerts
6402 
6403  indexBuffer.commit(dataAddress)
6404 
6405  item.associateWithIndexBuffer(indexBuffer)
6406 
6407 ################################################################################
6408 ##
6409 ## Node registry
6410 ##
6411 ## Registers/Deregisters apiMeshData geometry data,
6412 ## apiMeshCreator DG node, and apiMeshShape user defined shape.
6413 ##
6414 ################################################################################
6415 ##
6416 ## Strings for registering vp2 draw overrides. Plugin includes implementations
6417 ## of MPxSubSceneOverride and MPxGeometryOverride, set the boolean flag below
6418 ## to choose which is used.
6419 sUseSubSceneOverride = False
6420 sDrawDbClassification = "drawdb/geometry/apiMesh"
6421 if sUseSubSceneOverride:
6422  sDrawDbClassification = "drawdb/subscene/apiMesh"
6423 sDrawRegistrantId = "apiMeshPlugin"
6424 
6425 
6426 def initializePlugin(obj):
6427  plugin = om.MFnPlugin(obj, "Autodesk", "3.0", "Any")
6428 
6429  global sUseSubSceneOverride, sDrawDbClassification, sDrawRegistrantId
6430 
6431  try:
6432  plugin.registerData("apiMeshData", apiMeshData.id, apiMeshData.creator, om.MPxData.kGeometryData)
6433  except:
6434  sys.stderr.write("Failed to register data\n")
6435  raise
6436 
6437  try:
6438  plugin.registerShape("apiMesh", apiMesh.id, apiMesh.creator, apiMesh.initialize, apiMeshUI.creator, sDrawDbClassification)
6439  except:
6440  sys.stderr.write("Failed to register node\n")
6441  raise
6442 
6443  try:
6444  plugin.registerNode("apiMeshCreator", apiMeshCreator.id, apiMeshCreator.creator, apiMeshCreator.initialize)
6445  except:
6446  sys.stderr.write("Failed to register node\n")
6447  raise
6448 
6449  try:
6450  if sUseSubSceneOverride:
6451  omr.MDrawRegistry.registerSubSceneOverrideCreator(sDrawDbClassification, sDrawRegistrantId, apiMeshSubSceneOverride.creator)
6452  else:
6453  omr.MDrawRegistry.registerGeometryOverrideCreator(sDrawDbClassification, sDrawRegistrantId, apiMeshGeometryOverride.creator)
6454  except:
6455  sys.stderr.write("Failed to register override\n")
6456  raise
6457 
6458  try:
6459  if sUseSubSceneOverride:
6460  omr.MDrawRegistry.registerComponentConverter(apiMeshSubSceneOverride.sVertexSelectionName, simpleComponentConverter_subsceneOverride.creatorVertexSelection)
6461  omr.MDrawRegistry.registerComponentConverter(apiMeshSubSceneOverride.sEdgeSelectionName, simpleComponentConverter_subsceneOverride.creatorEdgeSelection)
6462  omr.MDrawRegistry.registerComponentConverter(apiMeshSubSceneOverride.sFaceSelectionName, simpleComponentConverter_subsceneOverride.creatorFaceSelection)
6463  else:
6464  omr.MDrawRegistry.registerComponentConverter(apiMeshGeometryOverride.sVertexItemName, meshVertComponentConverter_geometryOverride.creator)
6465  omr.MDrawRegistry.registerComponentConverter(apiMeshGeometryOverride.sEdgeSelectionItemName, meshEdgeComponentConverter_geometryOverride.creator)
6466  omr.MDrawRegistry.registerComponentConverter(apiMeshGeometryOverride.sFaceSelectionItemName, meshFaceComponentConverter_geometryOverride.creator)
6467  except:
6468  sys.stderr.write("Failed to register component converters\n")
6469  raise
6470 
6471 def uninitializePlugin(obj):
6472  plugin = om.MFnPlugin(obj)
6473 
6474  global sUseSubSceneOverride, sDrawDbClassification, sDrawRegistrantId
6475 
6476  try:
6477  if sUseSubSceneOverride:
6478  omr.MDrawRegistry.deregisterComponentConverter(apiMeshSubSceneOverride.sVertexSelectionName)
6479  omr.MDrawRegistry.deregisterComponentConverter(apiMeshSubSceneOverride.sEdgeSelectionName)
6480  omr.MDrawRegistry.deregisterComponentConverter(apiMeshSubSceneOverride.sFaceSelectionName)
6481  else:
6482  omr.MDrawRegistry.deregisterComponentConverter(apiMeshGeometryOverride.sVertexItemName)
6483  omr.MDrawRegistry.deregisterComponentConverter(apiMeshGeometryOverride.sEdgeSelectionItemName)
6484  omr.MDrawRegistry.deregisterComponentConverter(apiMeshGeometryOverride.sFaceSelectionItemName)
6485  except:
6486  sys.stderr.write("Failed to deregister component converters\n")
6487  pass
6488 
6489  try:
6490  if sUseSubSceneOverride:
6491  omr.MDrawRegistry.deregisterSubSceneOverrideCreator(sDrawDbClassification, sDrawRegistrantId)
6492  else:
6493  omr.MDrawRegistry.deregisterGeometryOverrideCreator(sDrawDbClassification, sDrawRegistrantId)
6494  except:
6495  sys.stderr.write("Failed to deregister override\n")
6496  pass
6497 
6498  try:
6499  plugin.deregisterNode(apiMeshCreator.id)
6500  except:
6501  sys.stderr.write("Failed to deregister node\n")
6502  pass
6503 
6504  try:
6505  plugin.deregisterNode(apiMesh.id)
6506  except:
6507  sys.stderr.write("Failed to deregister node\n")
6508  pass
6509 
6510  try:
6511  plugin.deregisterData(apiMeshData.id)
6512  except:
6513  sys.stderr.write("Failed to deregister data\n")
6514  pass
6515