Python API 2.0 Reference
python/api1/py1BasicShape.py
1 from __future__ import division
2 #-
3 # ==========================================================================
4 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
5 # rights reserved.
6 #
7 # The coded instructions, statements, computer programs, and/or related
8 # material (collectively the "Data") in these files contain unpublished
9 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
10 # licensors, which is protected by U.S. and Canadian federal copyright
11 # law and by international treaties.
12 #
13 # The Data is provided for use exclusively by You. You have the right
14 # to use, modify, and incorporate this Data into other products for
15 # purposes authorized by the Autodesk software license agreement,
16 # without fee.
17 #
18 # The copyright notices in the Software and this entire statement,
19 # including the above license grant, this restriction and the
20 # following disclaimer, must be included in all copies of the
21 # Software, in whole or in part, and all derivative works of
22 # the Software, unless such copies or derivative works are solely
23 # in the form of machine-executable object code generated by a
24 # source language processor.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
27 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
28 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
29 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
30 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
31 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
32 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
33 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
34 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
35 # OR PROBABILITY OF SUCH DAMAGES.
36 #
37 # ==========================================================================
38 #+
39 
40 ###############################################################################
41 # DESCRIPTION:
42 #
43 # Produces a surface shape node "spBasicShape".
44 #
45 # This plug-in implements a proxy surface shape node that will display rectangles,
46 # triangles, and circles using standard OpenGL calls.
47 #
48 # There are no output attributes for this shape. The following input attributes
49 # define the type of shape to draw:
50 #
51 # shapeType : 0=rectangle, 1=circle, 2=triangle
52 # radius : circle radius
53 # height : rectangle and triangle height
54 # width : rectangle and triangle width
55 #
56 # To create one of these nodes, enter the following commands after the plug-in is loaded:
57 #
58 # import maya.cmds as cmds
59 # cmds.createNode("spBasicShape")
60 #
61 # An object will be created with reference to the node. By default, it will be a rectangle.
62 # Use the different node options to change the type of shape or its size and shape.
63 # Add textures or manipulate as with any other object.
64 #
65 ################################################################################
66 
67 from builtins import object
68 from builtins import range
69 import maya.OpenMaya as OpenMaya
70 import maya.OpenMayaMPx as OpenMayaMPx
71 import maya.OpenMayaRender as OpenMayaRender
72 import maya.OpenMayaUI as OpenMayaUI
73 
74 import math
75 import sys
76 
77 
78 kPluginNodeTypeName = "spBasicShape"
79 spBasicShapeNodeId = OpenMaya.MTypeId(0x0008005e)
80 
81 glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
82 glFT = glRenderer.glFunctionTable()
83 
84 kLeadColor = 18 # green
85 kActiveColor = 15 # white
86 kActiveAffectedColor = 8 # purple
87 kDormantColor = 4 # blue
88 kHiliteColor = 17 # pale blue
89 
90 kDefaultRadius = 1.0
91 kDefaultHeight = 2.0
92 kDefaultWidth = 2.0
93 kDefaultShapeType = 0
94 
95 
96 #####################################################################
97 ##
98 ## Geometry class
99 ##
100 class basicGeom(object):
101  radius = kDefaultRadius
102  height = kDefaultHeight
103  width = kDefaultWidth
104  shapeType = kDefaultShapeType
105 
106 
107 #####################################################################
108 ##
109 ## Shape class - defines the non-UI part of a shape node
110 ##
111 class basicShape(OpenMayaMPx.MPxSurfaceShape):
112  def __init__(self):
113  OpenMayaMPx.MPxSurfaceShape.__init__(self)
114 
115  # class variables
116  aShapeType = OpenMaya.MObject()
117  aRadius = OpenMaya.MObject()
118  aHeight = OpenMaya.MObject()
119  aWidth = OpenMaya.MObject()
120 
121  # geometry
122  self.__myGeometry = basicGeom()
123 
124 
125  # override
126  def postConstructor(self):
127  """
128  When instances of this node are created internally, the MObject associated
129  with the instance is not created until after the constructor of this class
130  is called. This means that no member functions of MPxSurfaceShape can
131  be called in the constructor.
132  The postConstructor solves this problem. Maya will call this function
133  after the internal object has been created.
134  As a general rule do all of your initialization in the postConstructor.
135  """
136  self.setRenderable(True)
137 
138 
139  # override
140  def compute(self, plug, dataBlock):
141  """
142  Since there are no output attributes this is not necessary but
143  if we wanted to compute an output mesh for rendering it would
144  be done here base on the inputs.
145  """
146  return OpenMaya.kUnknownParameter
147 
148 
149  # override
150  def getInternalValue(self, plug, datahandle):
151  """
152  Handle internal attributes.
153  In order to impose limits on our attribute values we
154  mark them internal and use the values in fGeometry intead.
155  """
156  if (plug == basicShape.aRadius):
157  datahandle.setDouble(self.__myGeometry.radius)
158 
159  elif (plug == basicShape.aHeight):
160  datahandle.setDouble(self.__myGeometry.height)
161 
162  elif (plug == basicShape.aWidth):
163  datahandle.setDouble(self.__myGeometry.width)
164 
165  else:
166  return OpenMayaMPx.MPxSurfaceShape.getInternalValue(self, plug, datahandle)
167 
168  return True
169 
170 
171  # override
172  def setInternalValue(self, plug, datahandle):
173  """
174  Handle internal attributes.
175  In order to impose limits on our attribute values we
176  mark them internal and use the values in fGeometry intead.
177  """
178 
179  # the minimum radius is 0
180  #
181  if (plug == basicShape.aRadius):
182  radius = datahandle.asDouble()
183 
184  if (radius < 0):
185  radius = 0
186 
187  self.__myGeometry.radius = radius
188 
189  elif (plug == basicShape.aHeight):
190  val = datahandle.asDouble()
191  if (val <= 0):
192  val = 0.1
193  self.__myGeometry.height = val
194 
195  elif (plug == basicShape.aWidth):
196  val = datahandle.asDouble()
197  if (val <= 0):
198  val = 0.1
199  self.__myGeometry.width = val
200 
201  else:
202  return OpenMayaMPx.MPxSurfaceShape.setInternalValue(self, plug, datahandle)
203 
204  return True
205 
206 
207  # override
208  def isBounded(self):
209  return True
210 
211 
212  # override
213  def boundingBox(self):
214  """
215  Returns the bounding box for the shape.
216  In this case just use the radius and height attributes
217  to determine the bounding box.
218  """
219  result = OpenMaya.MBoundingBox()
220 
221  geom = self.geometry()
222 
223  r = geom.radius
224  result.expand(OpenMaya.MPoint(r,r,r))
225  result.expand(OpenMaya.MPoint(-r,-r,-r))
226 
227  r = geom.height/2.0
228  result.expand(OpenMaya.MPoint(r,r,r))
229  result.expand(OpenMaya.MPoint(-r,-r,-r))
230 
231  r = geom.width/2.0
232  result.expand(OpenMaya.MPoint(r,r,r))
233  result.expand(OpenMaya.MPoint(-r,-r,-r))
234 
235  return result
236 
237 
238  def geometry(self):
239  """
240  This function gets the values of all the attributes and
241  assigns them to the fGeometry. Calling MPlug::getValue
242  will ensure that the values are up-to-date.
243  """
244  # return self.__myGeometry
245 
246  this_object = self.thisMObject()
247 
248  plug = OpenMaya.MPlug(this_object, basicShape.aRadius)
249  self.__myGeometry.radius = plug.asDouble()
250 
251  plug.setAttribute(basicShape.aHeight)
252  self.__myGeometry.height = plug.asDouble()
253 
254  plug.setAttribute(basicShape.aWidth)
255  self.__myGeometry.width = plug.asDouble()
256 
257  plug.setAttribute(basicShape.aShapeType)
258  self.__myGeometry.shapeType = plug.asShort() # enum????
259 
260  return self.__myGeometry
261 
262 def printMsg(msg):
263  print(msg)
264  stream=OpenMaya.MStreamUtils.stdOutStream()
265  OpenMaya.MStreamUtils.writeCharBuffer(stream,msg)
266 
267 #####################################################################
268 ##
269 ## UI class - defines the UI part of a shape node
270 ##
271 class basicShapeUI(OpenMayaMPx.MPxSurfaceShapeUI):
272  # private enums
273  __kDrawRectangle, __kDrawCircle, __kDrawTriangle = list(range(3))
274  __kDrawWireframe, __kDrawWireframeOnShaded, __kDrawSmoothShaded, __kDrawFlatShaded, __kLastToken = list(range(5))
275 
276  def __init__(self):
277  OpenMayaMPx.MPxSurfaceShapeUI.__init__(self)
278 
279 
280  # override
281  def getDrawRequests(self, info, objectAndActiveOnly, queue):
282  """
283  The draw data is used to pass geometry through the
284  draw queue. The data should hold all the information
285  needed to draw the shape.
286  """
287  data = OpenMayaUI.MDrawData()
288  # printMsg("**before getProtoype\n");
289  request = info.getPrototype(self)
290  # printMsg("**after getProtoype\n");
291  shapeNode = self.surfaceShape()
292  geom = shapeNode.geometry()
293  self.getDrawData(geom, data)
294  request.setDrawData(data)
295 
296  # Are we displaying meshes?
297  if (not info.objectDisplayStatus(OpenMayaUI.M3dView.kDisplayMeshes)):
298  return
299 
300  # Use display status to determine what color to draw the object
301  if (info.displayStyle() == OpenMayaUI.M3dView.kWireFrame):
302  self.getDrawRequestsWireframe(request, info)
303  queue.add(request)
304 
305  elif (info.displayStyle() == OpenMayaUI.M3dView.kGouraudShaded):
306  request.setToken(basicShapeUI.__kDrawSmoothShaded)
307  self.getDrawRequestsShaded(request, info, queue, data)
308  queue.add(request)
309 
310  elif (info.displayStyle() == OpenMayaUI.M3dView.kFlatShaded):
311  request.setToken(basicShapeUI.__kDrawFlatShaded)
312  self.getDrawRequestsShaded(request, info, queue, data)
313  queue.add(request)
314  return
315 
316 
317  # override
318  def draw(self, request, view):
319  """
320  From the given draw request, get the draw data and determine
321  which basic to draw and with what values.
322  """
323 
324  data = request.drawData()
325  shapeNode = self.surfaceShape()
326  geom = shapeNode.geometry()
327  token = request.token()
328  drawTexture = False
329 
330  #set up texturing if it is shaded
331  if ((token == basicShapeUI.__kDrawSmoothShaded) or
332  (token == basicShapeUI.__kDrawFlatShaded)):
333  # Set up the material
334  material = request.material()
335  material.setMaterial(request.multiPath(), request.isTransparent())
336 
337  # Enable texturing
338  #
339  # Note, Maya does not enable texturing when drawing with the
340  # default material. However, your custom shape is free to ignore
341  # this setting.
342  #
343  drawTexture = material.materialIsTextured() and not view.usingDefaultMaterial()
344 
345  # Apply the texture to the current view
346  if (drawTexture):
347  material.applyTexture(view, data)
348 
349  glFT.glPushAttrib( OpenMayaRender.MGL_ALL_ATTRIB_BITS )
350 
351  if ((token == basicShapeUI.__kDrawSmoothShaded) or
352  (token == basicShapeUI.__kDrawFlatShaded)):
353  glFT.glEnable(OpenMayaRender.MGL_POLYGON_OFFSET_FILL)
354  glFT.glPolygonMode(OpenMayaRender.MGL_FRONT_AND_BACK, OpenMayaRender.MGL_FILL)
355  if (drawTexture):
356  glFT.glEnable(OpenMayaRender.MGL_TEXTURE_2D)
357  else:
358  glFT.glPolygonMode(OpenMayaRender.MGL_FRONT_AND_BACK, OpenMayaRender.MGL_LINE)
359 
360  # draw the shapes
361  if (geom.shapeType == basicShapeUI.__kDrawCircle):
362  # circle
363  glFT.glBegin(OpenMayaRender.MGL_POLYGON)
364  for i in range(0,360):
365  rad = (i*2*math.pi)/360;
366  glFT.glNormal3f(0.0, 0.0, 1.0)
367  if (i == 360):
368  glFT.glTexCoord3f(geom.radius*math.cos(0), geom.radius*math.sin(0), 0.0)
369  glFT.glVertex3f(geom.radius*math.cos(0), geom.radius*math.sin(0), 0.0)
370  else:
371  glFT.glTexCoord3f(geom.radius*math.cos(rad), geom.radius*math.sin(rad), 0.0)
372  glFT.glVertex3f(geom.radius*math.cos(rad), geom.radius*math.sin(rad), 0.0)
373  glFT.glEnd()
374 
375  elif (geom.shapeType == basicShapeUI.__kDrawRectangle):
376  #rectangle
377  glFT.glBegin(OpenMayaRender.MGL_QUADS)
378 
379  glFT.glTexCoord2f(-1*(geom.width/2), -1*(geom.height/2))
380  glFT.glVertex3f(-1*(geom.width/2), -1*(geom.height/2), 0.0)
381  glFT.glNormal3f(0, 0, 1.0)
382 
383  glFT.glTexCoord2f(-1*(geom.width/2), (geom.height/2))
384  glFT.glVertex3f(-1*(geom.width/2), (geom.height/2), 0.0)
385  glFT.glNormal3f(0, 0, 1.0)
386 
387  glFT.glTexCoord2f((geom.width/2), (geom.height/2))
388  glFT.glVertex3f((geom.width/2), (geom.height/2), 0.0)
389  glFT.glNormal3f(0, 0, 1.0)
390 
391  glFT.glTexCoord2f((geom.width/2), -1*(geom.height/2))
392  glFT.glVertex3f((geom.width/2), -1*(geom.height/2), 0.0)
393  glFT.glNormal3f(0, 0, 1.0)
394  glFT.glEnd()
395 
396  else:
397  # triangle
398  glFT.glBegin(OpenMayaRender.MGL_TRIANGLES)
399  glFT.glTexCoord2f(-1*(geom.width/2), -1*(geom.height/2))
400  glFT.glVertex3f(-1*(geom.width/2), -1*(geom.height/2), 0.0)
401  glFT.glNormal3f(0.0, 0.0, 1.0)
402 
403  glFT.glTexCoord2f(0.0, (geom.height/2))
404  glFT.glVertex3f(0.0, (geom.height/2), 0.0)
405  glFT.glNormal3f(0.0, 0.0, 1.0)
406 
407  glFT.glTexCoord2f((geom.width/2), -1*(geom.height/2))
408  glFT.glVertex3f((geom.width/2), -1*(geom.height/2), 0.0)
409  glFT.glNormal3f(0.0, 0.0, 1.0)
410  glFT.glEnd()
411 
412  if ((token == basicShapeUI.__kDrawSmoothShaded) or
413  (token == basicShapeUI.__kDrawFlatShaded)):
414  glFT.glDisable(OpenMayaRender.MGL_POLYGON_OFFSET_FILL)
415  # Turn off texture mode
416  if (drawTexture):
417  glFT.glDisable(OpenMayaRender.MGL_TEXTURE_2D)
418 
419  glFT.glPopAttrib()
420 
421 
422 
423  # override
424  def select(self, selectInfo, selectionList, worldSpaceSelectPts):
425  """
426  Select function. Gets called when the bbox for the object is selected.
427  This function just selects the object without doing any intersection tests.
428  """
429 
430  priorityMask = OpenMaya.MSelectionMask(OpenMaya.MSelectionMask.kSelectObjectsMask)
431  item = OpenMaya.MSelectionList()
432  item.add(selectInfo.selectPath())
433  xformedPt = OpenMaya.MPoint()
434  selectInfo.addSelection(item, xformedPt, selectionList,
435  worldSpaceSelectPts, priorityMask, False)
436  return True
437 
438 
439  def getDrawRequestsWireframe(self, request, info):
440 
441  request.setToken(basicShapeUI.__kDrawWireframe)
442 
443  displayStatus = info.displayStatus()
444  activeColorTable = OpenMayaUI.M3dView.kActiveColors
445  dormantColorTable = OpenMayaUI.M3dView.kDormantColors
446 
447  if (displayStatus == OpenMayaUI.M3dView.kLead):
448  request.setColor(kLeadColor, activeColorTable)
449 
450  elif (displayStatus == OpenMayaUI.M3dView.kActive):
451  request.setColor(kActiveColor, activeColorTable)
452 
453  elif (displayStatus == OpenMayaUI.M3dView.kActiveAffected):
454  request.setColor(kActiveAffectedColor, activeColorTable)
455 
456  elif (displayStatus == OpenMayaUI.M3dView.kDormant):
457  request.setColor(kDormantColor, dormantColorTable)
458 
459  elif (displayStatus == OpenMayaUI.M3dView.kHilite):
460  request.setColor(kHiliteColor, activeColorTable)
461 
462 
463 
464  def getDrawRequestsShaded(self, request, info, queue, data):
465  # Need to get the material info
466  path = info.multiPath() # path to your dag object
467  view = info.view() # view to draw to
468  material = OpenMayaMPx.MPxSurfaceShapeUI.material(self, path)
469  usingDefaultMat = view.usingDefaultMaterial()
470  if usingDefaultMat:
472 
473  displayStatus = info.displayStatus()
474 
475  # Evaluate the material and if necessary, the texture.
476  try:
477  material.evaluateMaterial(view, path)
478  except RuntimeError:
479  print("Couldn't evaluate material")
480  raise
481 
482  drawTexture = not usingDefaultMat
483  if (drawTexture and material.materialIsTextured()):
484  material.evaluateTexture(data)
485 
486  request.setMaterial(material)
487 
488  #materialTransparent = False
489  #material.getHasTransparency(materialTransparent)
490  #if (materialTransparent):
491  # request.setIsTransparent(True)
492 
493  # create a draw request for wireframe on shaded if necessary.
494  if ((displayStatus == OpenMayaUI.M3dView.kActive) or
495  (displayStatus == OpenMayaUI.M3dView.kLead) or
496  (displayStatus == OpenMayaUI.M3dView.kHilite)):
497  wireRequest = info.getPrototype(self)
498  wireRequest.setDrawData(data)
499  self.getDrawRequestsWireframe(wireRequest, info)
500  wireRequest.setToken(basicShapeUI.__kDrawWireframeOnShaded)
501  wireRequest.setDisplayStyle(OpenMayaUI.M3dView.kWireFrame)
502  queue.add(wireRequest)
503 
504 #####################################################################
505 
506 def nodeCreator():
507  return OpenMayaMPx.asMPxPtr( basicShape() )
508 
509 
510 def uiCreator():
511  return OpenMayaMPx.asMPxPtr( basicShapeUI() )
512 
513 
514 def nodeInitializer():
515  # BASIC type enumerated attribute
516  enumAttr = OpenMaya.MFnEnumAttribute()
517  basicShape.aShapeType = enumAttr.create("shapeType", "st", kDefaultShapeType)
518  enumAttr.addField("rectangle", 0)
519  enumAttr.addField("circle", 1)
520  enumAttr.addField("triangle", 2)
521  enumAttr.setHidden(False)
522  enumAttr.setKeyable(True)
523  basicShape.addAttribute(basicShape.aShapeType)
524 
525  # BASIC numeric attributes
526  # utility func for numeric attrs
527  def setOptions(attr):
528  attr.setHidden(False)
529  attr.setKeyable(True)
530  attr.setInternal(True)
531 
532  numericAttr = OpenMaya.MFnNumericAttribute()
533 
534  basicShape.aRadius = numericAttr.create("radius", "r", OpenMaya.MFnNumericData.kDouble, kDefaultRadius)
535  setOptions(numericAttr)
536  basicShape.addAttribute(basicShape.aRadius)
537 
538  basicShape.aHeight = numericAttr.create("height", "ht", OpenMaya.MFnNumericData.kDouble, kDefaultHeight)
539  setOptions(numericAttr)
540  basicShape.addAttribute(basicShape.aHeight)
541 
542  basicShape.aWidth = numericAttr.create("width2", "wt2", OpenMaya.MFnNumericData.kDouble, kDefaultWidth)
543  setOptions(numericAttr)
544  basicShape.addAttribute(basicShape.aWidth)
545 
546 # initialize the script plug-in
547 def initializePlugin(mobject):
548  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "8.5", "Any")
549  try:
550  mplugin.registerShape( kPluginNodeTypeName, spBasicShapeNodeId,
551  nodeCreator, nodeInitializer, uiCreator )
552  except:
553  sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
554  raise
555 
556 
557 # uninitialize the script plug-in
558 def uninitializePlugin(mobject):
559  mplugin = OpenMayaMPx.MFnPlugin(mobject)
560  try:
561  mplugin.deregisterNode( spBasicShapeNodeId )
562  except:
563  sys.stderr.write( "Failed to deregister node: %s" % kPluginNodeTypeName )
564  raise