scripted/pyApiMeshShape.py

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