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