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