scripted/pyApiMeshShape.py

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