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