import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaRender as OpenMayaRender
import maya.OpenMayaUI as OpenMayaUI
import math
import sys
kPluginNodeTypeName = "spBasicShape"
spBasicShapeNodeId = OpenMaya.MTypeId(0x87018)
glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
glFT = glRenderer.glFunctionTable()
kLeadColor = 18
kActiveColor = 15
kActiveAffectedColor = 8
kDormantColor = 4
kHiliteColor = 17
kDefaultRadius = 1.0
kDefaultHeight = 2.0
kDefaultWidth = 2.0
kDefaultShapeType = 0
class basicGeom:
radius = kDefaultRadius
height = kDefaultHeight
width = kDefaultWidth
shapeType = kDefaultShapeType
class basicShape(OpenMayaMPx.MPxSurfaceShape):
def __init__(self):
OpenMayaMPx.MPxSurfaceShape.__init__(self)
aShapeType = OpenMaya.MObject()
aRadius = OpenMaya.MObject()
aHeight = OpenMaya.MObject()
aWidth = OpenMaya.MObject()
self.__myGeometry = basicGeom()
def postConstructor(self):
"""
When instances of this node are created internally, the MObject associated
with the instance is not created until after the constructor of this class
is called. This means that no member functions of MPxSurfaceShape can
be called in the constructor.
The postConstructor solves this problem. Maya will call this function
after the internal object has been created.
As a general rule do all of your initialization in the postConstructor.
"""
self.setRenderable(True)
def compute(self, plug, dataBlock):
"""
Since there are no output attributes this is not necessary but
if we wanted to compute an output mesh for rendering it would
be done here base on the inputs.
"""
return OpenMaya.kUnknownParameter
def getInternalValue(self, plug, datahandle):
"""
Handle internal attributes.
In order to impose limits on our attribute values we
mark them internal and use the values in fGeometry intead.
"""
if (plug == basicShape.aRadius):
datahandle.setDouble(self.__myGeometry.radius)
elif (plug == basicShape.aHeight):
datahandle.setDouble(self.__myGeometry.height)
elif (plug == basicShape.aWidth):
datahandle.setDouble(self.__myGeometry.width)
else:
return OpenMayaMPx.MPxSurfaceShape.getInternalValue(self, plug, datahandle)
return True
def setInternalValue(self, plug, datahandle):
"""
Handle internal attributes.
In order to impose limits on our attribute values we
mark them internal and use the values in fGeometry intead.
"""
if (plug == basicShape.aRadius):
radius = datahandle.asDouble()
if (radius < 0):
radius = 0
self.__myGeometry.radius = radius
elif (plug == basicShape.aHeight):
val = datahandle.asDouble()
if (val <= 0):
val = 0.1
self.__myGeometry.height = val
elif (plug == basicShape.aWidth):
val = datahandle.asDouble()
if (val <= 0):
val = 0.1
self.__myGeometry.width = val
else:
return OpenMayaMPx.MPxSurfaceShape.setInternalValue(self, plug, datahandle)
return True
def isBounded(self):
return True
def boundingBox(self):
"""
Returns the bounding box for the shape.
In this case just use the radius and height attributes
to determine the bounding box.
"""
result = OpenMaya.MBoundingBox()
geom = self.geometry()
r = geom.radius
result.expand(OpenMaya.MPoint(r,r,r))
result.expand(OpenMaya.MPoint(-r,-r,-r))
r = geom.height/2.0
result.expand(OpenMaya.MPoint(r,r,r))
result.expand(OpenMaya.MPoint(-r,-r,-r))
r = geom.width/2.0
result.expand(OpenMaya.MPoint(r,r,r))
result.expand(OpenMaya.MPoint(-r,-r,-r))
return result
def geometry(self):
"""
This function gets the values of all the attributes and
assigns them to the fGeometry. Calling MPlug::getValue
will ensure that the values are up-to-date.
"""
this_object = self.thisMObject()
plug = OpenMaya.MPlug(this_object, basicShape.aRadius)
self.__myGeometry.radius = plug.asDouble()
plug.setAttribute(basicShape.aHeight)
self.__myGeometry.height = plug.asDouble()
plug.setAttribute(basicShape.aWidth)
self.__myGeometry.width = plug.asDouble()
plug.setAttribute(basicShape.aShapeType)
self.__myGeometry.shapeType = plug.asShort()
return self.__myGeometry
def printMsg(msg):
print msg
stream=OpenMaya.MStreamUtils.stdOutStream()
OpenMaya.MStreamUtils.writeCharBuffer(stream,msg)
class basicShapeUI(OpenMayaMPx.MPxSurfaceShapeUI):
__kDrawRectangle, __kDrawCircle, __kDrawTriangle = range(3)
__kDrawWireframe, __kDrawWireframeOnShaded, __kDrawSmoothShaded, __kDrawFlatShaded, __kLastToken = range(5)
def __init__(self):
OpenMayaMPx.MPxSurfaceShapeUI.__init__(self)
def getDrawRequests(self, info, objectAndActiveOnly, queue):
"""
The draw data is used to pass geometry through the
draw queue. The data should hold all the information
needed to draw the shape.
"""
data = OpenMayaUI.MDrawData()
request = info.getPrototype(self)
shapeNode = self.surfaceShape()
geom = shapeNode.geometry()
self.getDrawData(geom, data)
request.setDrawData(data)
if (not info.objectDisplayStatus(OpenMayaUI.M3dView.kDisplayMeshes)):
return
if (info.displayStyle() == OpenMayaUI.M3dView.kWireFrame):
self.getDrawRequestsWireframe(request, info)
queue.add(request)
elif (info.displayStyle() == OpenMayaUI.M3dView.kGouraudShaded):
request.setToken(basicShapeUI.__kDrawSmoothShaded)
self.getDrawRequestsShaded(request, info, queue, data)
queue.add(request)
elif (info.displayStyle() == OpenMayaUI.M3dView.kFlatShaded):
request.setToken(basicShapeUI.__kDrawFlatShaded)
self.getDrawRequestsShaded(request, info, queue, data)
queue.add(request)
return
def draw(self, request, view):
"""
From the given draw request, get the draw data and determine
which basic to draw and with what values.
"""
data = request.drawData()
shapeNode = self.surfaceShape()
geom = shapeNode.geometry()
token = request.token()
drawTexture = False
su = OpenMaya.MScriptUtil()
su.createFromDouble(1.0,1.0,1.0,1.0)
if ((token == basicShapeUI.__kDrawSmoothShaded) or
(token == basicShapeUI.__kDrawFlatShaded)):
material = request.material()
material.setMaterial(request.multiPath(), request.isTransparent())
drawTexture = material.materialIsTextured() and not view.usingDefaultMaterial()
if (drawTexture):
material.applyTexture(view, data)
glFT.glPushAttrib( OpenMayaRender.MGL_ALL_ATTRIB_BITS )
if ((token == basicShapeUI.__kDrawSmoothShaded) or
(token == basicShapeUI.__kDrawFlatShaded)):
glFT.glEnable(OpenMayaRender.MGL_POLYGON_OFFSET_FILL)
glFT.glPolygonMode(OpenMayaRender.MGL_FRONT_AND_BACK, OpenMayaRender.MGL_FILL)
if (drawTexture):
glFT.glEnable(OpenMayaRender.MGL_TEXTURE_2D)
else:
glFT.glPolygonMode(OpenMayaRender.MGL_FRONT_AND_BACK, OpenMayaRender.MGL_LINE)
if (geom.shapeType == basicShapeUI.__kDrawCircle):
glFT.glBegin(OpenMayaRender.MGL_POLYGON)
for i in range(0,360):
rad = (i*2*math.pi)/360;
glFT.glNormal3f(0.0, 0.0, 1.0)
if (i == 360):
glFT.glTexCoord3f(geom.radius*math.cos(0), geom.radius*math.sin(0), 0.0)
glFT.glVertex3f(geom.radius*math.cos(0), geom.radius*math.sin(0), 0.0)
else:
glFT.glTexCoord3f(geom.radius*math.cos(rad), geom.radius*math.sin(rad), 0.0)
glFT.glVertex3f(geom.radius*math.cos(rad), geom.radius*math.sin(rad), 0.0)
glFT.glEnd()
elif (geom.shapeType == basicShapeUI.__kDrawRectangle):
glFT.glBegin(OpenMayaRender.MGL_QUADS)
glFT.glTexCoord2f(-1*(geom.width/2), -1*(geom.height/2))
glFT.glVertex3f(-1*(geom.width/2), -1*(geom.height/2), 0.0)
glFT.glNormal3f(0, 0, 1.0)
glFT.glTexCoord2f(-1*(geom.width/2), (geom.height/2))
glFT.glVertex3f(-1*(geom.width/2), (geom.height/2), 0.0)
glFT.glNormal3f(0, 0, 1.0)
glFT.glTexCoord2f((geom.width/2), (geom.height/2))
glFT.glVertex3f((geom.width/2), (geom.height/2), 0.0)
glFT.glNormal3f(0, 0, 1.0)
glFT.glTexCoord2f((geom.width/2), -1*(geom.height/2))
glFT.glVertex3f((geom.width/2), -1*(geom.height/2), 0.0)
glFT.glNormal3f(0, 0, 1.0)
glFT.glEnd()
else:
glFT.glBegin(OpenMayaRender.MGL_TRIANGLES)
glFT.glTexCoord2f(-1*(geom.width/2), -1*(geom.height/2))
glFT.glVertex3f(-1*(geom.width/2), -1*(geom.height/2), 0.0)
glFT.glNormal3f(0.0, 0.0, 1.0)
glFT.glTexCoord2f(0.0, (geom.height/2))
glFT.glVertex3f(0.0, (geom.height/2), 0.0)
glFT.glNormal3f(0.0, 0.0, 1.0)
glFT.glTexCoord2f((geom.width/2), -1*(geom.height/2))
glFT.glVertex3f((geom.width/2), -1*(geom.height/2), 0.0)
glFT.glNormal3f(0.0, 0.0, 1.0)
glFT.glEnd()
if ((token == basicShapeUI.__kDrawSmoothShaded) or
(token == basicShapeUI.__kDrawFlatShaded)):
glFT.glDisable(OpenMayaRender.MGL_POLYGON_OFFSET_FILL)
if (drawTexture):
glFT.glDisable(OpenMayaRender.MGL_TEXTURE_2D)
glFT.glPopAttrib()
def select(self, selectInfo, selectionList, worldSpaceSelectPts):
"""
Select function. Gets called when the bbox for the object is selected.
This function just selects the object without doing any intersection tests.
"""
priorityMask = OpenMaya.MSelectionMask(OpenMaya.MSelectionMask.kSelectObjectsMask)
item = OpenMaya.MSelectionList()
item.add(selectInfo.selectPath())
xformedPt = OpenMaya.MPoint()
selectInfo.addSelection(item, xformedPt, selectionList,
worldSpaceSelectPts, priorityMask, False)
return True
def getDrawRequestsWireframe(self, request, info):
request.setToken(basicShapeUI.__kDrawWireframe)
displayStatus = info.displayStatus()
activeColorTable = OpenMayaUI.M3dView.kActiveColors
dormantColorTable = OpenMayaUI.M3dView.kDormantColors
if (displayStatus == OpenMayaUI.M3dView.kLead):
request.setColor(kLeadColor, activeColorTable)
elif (displayStatus == OpenMayaUI.M3dView.kActive):
request.setColor(kActiveColor, activeColorTable)
elif (displayStatus == OpenMayaUI.M3dView.kActiveAffected):
request.setColor(kActiveAffectedColor, activeColorTable)
elif (displayStatus == OpenMayaUI.M3dView.kDormant):
request.setColor(kDormantColor, dormantColorTable)
elif (displayStatus == OpenMayaUI.M3dView.kHilite):
request.setColor(kHiliteColor, activeColorTable)
def getDrawRequestsShaded(self, request, info, queue, data):
path = info.multiPath()
view = info.view()
material = OpenMayaMPx.MPxSurfaceShapeUI.material(self, path)
usingDefaultMat = view.usingDefaultMaterial()
if usingDefaultMat:
material = OpenMayaUI.MMaterial.defaultMaterial()
displayStatus = info.displayStatus()
try:
material.evaluateMaterial(view, path)
except RuntimeError:
print "Couldn't evaluate material"
raise
drawTexture = not usingDefaultMat
if (drawTexture and material.materialIsTextured()):
material.evaluateTexture(data)
request.setMaterial(material)
if ((displayStatus == OpenMayaUI.M3dView.kActive) or
(displayStatus == OpenMayaUI.M3dView.kLead) or
(displayStatus == OpenMayaUI.M3dView.kHilite)):
wireRequest = info.getPrototype(self)
wireRequest.setDrawData(data)
self.getDrawRequestsWireframe(wireRequest, info)
wireRequest.setToken(basicShapeUI.__kDrawWireframeOnShaded)
wireRequest.setDisplayStyle(OpenMayaUI.M3dView.kWireFrame)
queue.add(wireRequest)
def nodeCreator():
return OpenMayaMPx.asMPxPtr( basicShape() )
def uiCreator():
return OpenMayaMPx.asMPxPtr( basicShapeUI() )
def nodeInitializer():
enumAttr = OpenMaya.MFnEnumAttribute()
basicShape.aShapeType = enumAttr.create("shapeType", "st", kDefaultShapeType)
enumAttr.addField("rectangle", 0)
enumAttr.addField("circle", 1)
enumAttr.addField("triangle", 2)
enumAttr.setHidden(False)
enumAttr.setKeyable(True)
basicShape.addAttribute(basicShape.aShapeType)
def setOptions(attr):
attr.setHidden(False)
attr.setKeyable(True)
attr.setInternal(True)
numericAttr = OpenMaya.MFnNumericAttribute()
basicShape.aRadius = numericAttr.create("radius", "r", OpenMaya.MFnNumericData.kDouble, kDefaultRadius)
setOptions(numericAttr)
basicShape.addAttribute(basicShape.aRadius)
basicShape.aHeight = numericAttr.create("height", "ht", OpenMaya.MFnNumericData.kDouble, kDefaultHeight)
setOptions(numericAttr)
basicShape.addAttribute(basicShape.aHeight)
basicShape.aWidth = numericAttr.create("width2", "wt2", OpenMaya.MFnNumericData.kDouble, kDefaultWidth)
setOptions(numericAttr)
basicShape.addAttribute(basicShape.aWidth)
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "8.5", "Any")
try:
mplugin.registerShape( kPluginNodeTypeName, spBasicShapeNodeId,
nodeCreator, nodeInitializer, uiCreator )
except:
sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
raise
def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterNode( spBasicShapeNodeId )
except:
sys.stderr.write( "Failed to deregister node: %s" % kPluginNodeTypeName )
raise