73 import maya.OpenMaya 
as OpenMaya
 
   74 import maya.OpenMayaMPx 
as OpenMayaMPx
 
   75 import maya.OpenMayaRender 
as OpenMayaRender
 
   76 import maya.OpenMayaUI 
as OpenMayaUI
 
   81 kPluginNodeTypeName = 
"spInstanceShape" 
   84 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
 
   85 glFT = glRenderer.glFunctionTable()
 
   89 kActiveAffectedColor    = 8  
 
  103         self.radius = kDefaultRadius
 
  104         self.count = kDefaultCount
 
  105         self.instanceShape = 
None 
  106         self.drawQueueList = [] 
 
  112 class instanceShape(OpenMayaMPx.MPxSurfaceShape):
 
  119         OpenMayaMPx.MPxSurfaceShape.__init__(self)
 
  122         self.__myGeometry = instanceGeom()
 
  125     def postConstructor(self):
 
  127          When instances of this node are created internally, the 
  128          MObject associated with the instance is not created until 
  129          after the constructor of this class is called. This means 
  130          that no member functions of MPxSurfaceShape can be called in 
  131          the constructor.  The postConstructor solves this 
  132          problem. Maya will call this function after the internal 
  133          object has been created.  As a general rule do all of your 
  134          initialization in the postConstructor. 
  136         self.setRenderable(
True)
 
  139     def getInternalValue(self, plug, datahandle):
 
  141          Handle internal attributes. 
  142          In order to impose limits on our attribute values we 
  143          mark them internal and use the values in fGeometry instead. 
  145         if (plug == instanceShape.aRadius):
 
  146             datahandle.setDouble(self.__myGeometry.radius)
 
  147         elif (plug == instanceShape.aCount):
 
  148             datahandle.setInt(self.__myGeometry.count)
 
  150             return OpenMayaMPx.MPxSurfaceShape.getInternalValue(self, plug, datahandle)
 
  156     def setInternalValue(self, plug, datahandle):
 
  158          Handle internal attributes. 
  159          In order to impose limits on our attribute values we 
  160          mark them internal and use the values in fGeometry instead. 
  165         if (plug == instanceShape.aRadius):
 
  166             radius = datahandle.asDouble()
 
  171             self.__myGeometry.radius = radius
 
  173         elif (plug == instanceShape.aCount):
 
  174             count = datahandle.asInt()
 
  177             self.__myGeometry.count = count
 
  179             return OpenMayaMPx.MPxSurfaceShape.setInternalValue(self, plug, datahandle)
 
  189     def boundingBox(self):
 
  191          Returns the bounding box for the shape. 
  192          In this case just use the radius and height attributes 
  193          to determine the bounding box. 
  197         geom = self.geometry()
 
  200         if geom.instanceShape: 
 
  202             result = fnDag.boundingBox() 
 
  206         for c 
in range( geom.count ): 
 
  207             percent = float(c)/float(geom.count)
 
  208             rad = 2*math.pi * percent
 
  209             p = (r*math.cos(rad), r*math.sin(rad),0.0)
 
  213             trans.setTranslation( vec, OpenMaya.MSpace.kTransform ) 
 
  214             mmatrix = trans.asMatrix(); 
 
  215             newbbox.transformUsing( mmatrix ) 
 
  216             result.expand( newbbox ) 
 
  223          This function gets the values of all the attributes and 
  224          assigns them to the fGeometry. Calling MPlug::getValue 
  225          will ensure that the values are up-to-date. 
  229         this_object = self.thisMObject()
 
  232         self.__myGeometry.radius = plug.asDouble()
 
  234         self.__myGeometry.count = plug.asInt()
 
  238         plug.connectedTo( plugArray, 
True, 
False )
 
  239         if ( plugArray.length() > 0 ): 
 
  240             node = plugArray[0].node()
 
  243             dagNode.getPath(path)
 
  244             self.__myGeometry.instanceShape = path 
 
  246         return self.__myGeometry
 
  252 class instanceShapeUI(OpenMayaMPx.MPxSurfaceShapeUI):
 
  255         OpenMayaMPx.MPxSurfaceShapeUI.__init__(self)
 
  258     def getDrawRequests(self, info, objectAndActiveOnly, queue):
 
  260          The draw data is used to pass geometry through the  
  261          draw queue. The data should hold all the information 
  262          needed to draw the shape. 
  271         request = info.getPrototype(self)
 
  272         shapeNode = self.surfaceShape()
 
  273         path = info.multiPath()
 
  278         geom = shapeNode.geometry()
 
  281         if request.displayStyle() == OpenMayaUI.M3dView.kGouraudShaded:
 
  284         if geom.instanceShape:
 
  287             shapeUI = OpenMayaMPx.MPxSurfaceShapeUI.surfaceShapeUI(geom.instanceShape)
 
  289                 mainMaterial = shapeUI.material( geom.instanceShape )
 
  290                 mainMaterial.evaluateMaterial( view, geom.instanceShape ) 
 
  292                 for a 
in range(geom.count):
 
  293                     myQueue = OpenMayaUI.MDrawRequestQueue()
 
  294                     percent = float(a)/float(geom.count)
 
  295                     rad = 2*math.pi * percent
 
  296                     position = (r*math.cos(rad), r*math.sin(rad),0.0)
 
  304                     myinfo.setMultiPath( geom.instanceShape )
 
  305                     shapeUI.getDrawRequests( myinfo,
 
  306                                              objectAndActiveOnly, myQueue )
 
  307                     geom.drawQueueList.append( (myQueue, position) )
 
  309         info.setMultiPath( path )
 
  317                 mainMaterial = defaultMaterial 
 
  319                 request.setMaterial( mainMaterial )
 
  321                 request.setMaterial( defaultMaterial ) 
 
  324         self.getDrawData(geom, data)            
 
  325         request.setDrawData(data)
 
  331     def draw(self, request, view):
 
  333          From the given draw request, get the draw data and determine 
  334          which basic shape to draw and with what values. 
  337         data = request.drawData()
 
  338         shapeNode = self.surfaceShape()
 
  340         glFT.glMatrixMode( OpenMayaRender.MGL_MODELVIEW )
 
  341         if geom.instanceShape: 
 
  342             shapeUI = OpenMayaMPx.MPxSurfaceShapeUI.surfaceShapeUI(geom.instanceShape)
 
  343             for (queue, pos) 
in geom.drawQueueList:
 
  345                 glFT.glTranslatef( pos[0], pos[1], pos[2] )
 
  346                 while not queue.isEmpty():
 
  347                     request = queue.remove()
 
  348                     shapeUI.draw( request, view )
 
  355         glFT.glPushAttrib( OpenMayaRender.MGL_ALL_ATTRIB_BITS )
 
  356         glFT.glPolygonMode(OpenMayaRender.MGL_FRONT_AND_BACK,
 
  357                            OpenMayaRender.MGL_LINE)
 
  358         glFT.glBegin(OpenMayaRender.MGL_QUADS)
 
  359         glFT.glVertex3f(-1*(geom.radius), -1*(geom.radius), 0.0)
 
  360         glFT.glNormal3f(0, 0, 1.0)
 
  362         glFT.glVertex3f(-1*(geom.radius), (geom.radius), 0.0)
 
  363         glFT.glNormal3f(0, 0, 1.0)
 
  365         glFT.glVertex3f((geom.radius), (geom.radius), 0.0)
 
  366         glFT.glNormal3f(0, 0, 1.0)
 
  368         glFT.glVertex3f((geom.radius), -1*(geom.radius), 0.0)
 
  369         glFT.glNormal3f(0, 0, 1.0)
 
  374     def select(self, selectInfo, selectionList, worldSpaceSelectPts):
 
  376          Select function. Gets called when the bbox for the object is 
  377          selected.  This function just selects the object without 
  378          doing any intersection tests. 
  383         item.add(selectInfo.selectPath())
 
  385         selectInfo.addSelection(item, xformedPt, selectionList,
 
  386                                  worldSpaceSelectPts, priorityMask, 
False)
 
  395     return OpenMayaMPx.asMPxPtr( instanceShape() )
 
  399     return OpenMayaMPx.asMPxPtr( instanceShapeUI() )
 
  402 def nodeInitializer():
 
  404     def setOptions(attr):
 
  405         attr.setHidden(
False)
 
  406         attr.setKeyable(
True)
 
  407         attr.setInternal(
True)
 
  412     instanceShape.aInstanceShape = messageAttr.create(
"instanceShape", 
"is")
 
  413     instanceShape.addAttribute(instanceShape.aInstanceShape)
 
  415     instanceShape.aRadius = numericAttr.create(
"radius", 
"r", OpenMaya.MFnNumericData.kDouble, kDefaultRadius) 
  416     setOptions(numericAttr) 
  417     instanceShape.addAttribute(instanceShape.aRadius) 
  419     instanceShape.aCount = numericAttr.create("count", 
"ct", OpenMaya.MFnNumericData.kInt, kDefaultCount)
 
  420     setOptions(numericAttr)
 
  421     instanceShape.addAttribute(instanceShape.aCount)
 
  424 def initializePlugin(mobject):
 
  425     mplugin = OpenMayaMPx.MFnPlugin(mobject, 
"Autodesk", 
"2011", 
"Any")
 
  427         mplugin.registerShape( kPluginNodeTypeName, spInstanceShapeNodeId,
 
  428                                 nodeCreator, nodeInitializer, uiCreator )
 
  430         sys.stderr.write( 
"Failed to register node: %s" % kPluginNodeTypeName )
 
  435 def uninitializePlugin(mobject):
 
  436     mplugin = OpenMayaMPx.MFnPlugin(mobject)
 
  438         mplugin.deregisterNode( spInstanceShapeNodeId )
 
  440         sys.stderr.write( 
"Failed to deregister node: %s" % kPluginNodeTypeName )