32 from __future__
import division
33 from builtins
import object
37 from maya.api
import OpenMaya, OpenMayaUI, OpenMayaRender
38 from maya
import OpenMayaRender
as OpenMayaRenderV1
40 logger = logging.getLogger(
'py2SquareScaleManipContext')
48 class PlaneMath(object):
50 This utility class represents a mathematical plane and performs intersection
55 Initialze the member variables of the class.
62 def set_plane( self, point_on_plane, normal_to_plane ):
64 Define the plane by supplying a point on the plane and the plane's normal.
67 _normal_to_plane.normalize()
70 self.a = _normal_to_plane.x
71 self.b = _normal_to_plane.y
72 self.c = _normal_to_plane.z
73 self.d = -(self.a*point_on_plane.x + self.b*point_on_plane.y + self.c*point_on_plane.z)
75 def intersect( self, line ):
77 Intersect the plane with the given line. Return the intersection point if an intersection
78 occurs. Otherwise, raise an exception.
80 denominator = self.a*line[1].x + self.b*line[1].y + self.c*line[1].z
83 if (denominator < .00001):
86 t = -(self.d + self.a*line[0].x + self.b*line[0].y + self.c*line[0].z) / denominator
89 return line[0] + t * line[1]
91 class LineMath(object):
93 This utility class represents a mathematical line and returns the closest point
94 on the line to a given point.
98 Initialze the member variables of the class.
103 def set_line( self, line_point, line_direction ):
105 Define the line by supplying a point on the line and the line's direction.
109 self.direction.normalize()
111 def closest_point( self, to_point ):
113 Determine and return the point on the line which is closest to the given point.
115 t = self.direction * ( to_point - self.point )
116 return self.point + ( self.direction * t )
118 class SquareGeometry(object):
120 This utility class defines methods for returning the four corner points of a unit square
126 Return the top left corner of the square.
132 Return the top right corner of the square.
138 Return the bottom left corner of the square.
144 Return the bottom right corner of the square.
154 This is the subclassed manipulator node. It scales the selected objects
155 in the X direction based on dragging movements by the user.
157 kNodeName =
'py2SquareScaleContextManipulator'
162 Initialize the manipulator member variables.
167 self.point_on_plane = SquareGeometry.top_left()
171 self.right_index = -1
172 self.bottom_index = -1
176 self.bottom_name = -1
183 self.translate_x = 0.0
184 self.translate_y = 0.0
185 self.translate_z = 0.0
190 self.normal_to_plane = v1 ^ v2
193 self.normal_to_plane.normalize()
194 self.plane = PlaneMath()
195 self.plane.set_plane( self.point_on_plane, self.normal_to_plane )
207 def postConstructor(self):
208 self.top_index = self.addDoubleValue(
'topValue', 0 )
209 self.right_index = self.addDoubleValue(
'rightValue', 0 )
210 self.bottom_index = self.addDoubleValue(
'bottomValue', 0 )
211 self.left_index = self.addDoubleValue(
'leftValue', 0 )
213 gl_pickable_item = self.glFirstHandle()
214 self.top_name = gl_pickable_item
215 self.bottom_name = gl_pickable_item + 1
216 self.right_name = gl_pickable_item + 2
217 self.left_name = gl_pickable_item + 3
220 def connectToDependNode(self, depend_node):
222 Connect the manipulator to the given dependency node.
231 scale_x_plug = nodeFn.findPlug(
'scaleX',
True)
233 logger.info(
" Could not find scaleX plug!")
238 plug_index = self.connectPlugToValue(scale_x_plug, self.right_index)
240 logger.info(
" Could not connectPlugToValue!")
243 self.finishAddingManips()
248 Update the region dragged by the mouse.
252 tl = SquareGeometry.top_left()
253 tr = SquareGeometry.top_right()
254 bl = SquareGeometry.bottom_left()
255 br = SquareGeometry.bottom_right()
259 active = self.glActiveName()
261 if ( active == self.top_name ):
262 tl += self.mouse_point_gl_name
263 tr += self.mouse_point_gl_name
264 if ( active == self.bottom_name ):
265 bl += self.mouse_point_gl_name
266 br += self.mouse_point_gl_name
267 if ( active == self.right_name ):
268 tr += self.mouse_point_gl_name
269 br += self.mouse_point_gl_name
270 if ( active == self.left_name ):
271 tl += self.mouse_point_gl_name
272 bl += self.mouse_point_gl_name
274 return [tl, tr, bl, br]
277 def draw(self, view, path, style, status):
279 Draw the manupulator in a legacy viewport.
283 gl_renderer = OpenMayaRenderV1.MHardwareRenderer.theRenderer()
284 gl_ft = gl_renderer.glFunctionTable()
286 [tl, tr, bl, br] = self.pre_draw()
293 gl_ft.glMatrixMode( OpenMayaRenderV1.MGL_MODELVIEW )
295 gl_ft.glTranslatef( self.translate_x, self.translate_y, self.translate_z )
296 gl_ft.glRotatef( math.degrees(self.rotate_z), 0.0, 0.0, 1.0 )
297 gl_ft.glRotatef( math.degrees(self.rotate_y), 0.0, 1.0, 0.0 )
298 gl_ft.glRotatef( math.degrees(self.rotate_x), 1.0, 0.0, 0.0 )
302 self.colorAndName( view, self.top_name,
False, self.mainColor() )
303 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
304 gl_ft.glVertex3f( tl.x, tl.y, tl.z )
305 gl_ft.glVertex3f( tr.x, tr.y, tr.z )
309 self.colorAndName( view, self.right_name,
True, self.mainColor() )
310 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
311 gl_ft.glVertex3f( tr.x, tr.y, tr.z )
312 gl_ft.glVertex3f( br.x, br.y, br.z )
316 self.colorAndName( view, self.bottom_name,
False, self.mainColor() )
317 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
318 gl_ft.glVertex3f( br.x, br.y, br.z )
319 gl_ft.glVertex3f( bl.x, bl.y, bl.z )
323 self.colorAndName( view, self.left_name,
True, self.mainColor() )
324 gl_ft.glBegin( OpenMayaRenderV1.MGL_LINES )
325 gl_ft.glVertex3f( bl.x, bl.y, bl.z )
326 gl_ft.glVertex3f( tl.x, tl.y, tl.z )
336 def preDrawUI(self, view):
338 Cache the viewport for use in VP 2.0 drawing.
343 def drawUI(self, draw_manager, frame_context):
345 Draw the manupulator in a VP 2.0 viewport.
348 [tl, tr, bl, br] = self.pre_draw()
351 xform.rotateByComponents([math.degrees(self.rotate_x), \
352 math.degrees(self.rotate_y), \
353 math.degrees(self.rotate_z), \
354 OpenMaya.MTransformationMatrix.kZYX], \
355 OpenMaya.MSpace.kWorld)
357 mat = xform.asMatrix()
364 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kNonSelectable, self.top_name)
365 self.setHandleColor(draw_manager, self.top_name, self.dimmedColor())
366 draw_manager.line(tl, tr)
367 draw_manager.endDrawable()
370 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kSelectable, self.right_name)
371 self.setHandleColor(draw_manager, self.right_name, self.mainColor())
372 draw_manager.line(tr, br)
373 draw_manager.endDrawable()
376 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kNonSelectable, self.bottom_name)
377 self.setHandleColor(draw_manager, self.bottom_name, self.dimmedColor())
378 draw_manager.line(br, bl)
379 draw_manager.endDrawable()
382 draw_manager.beginDrawable(OpenMayaRender.MUIDrawManager.kSelectable, self.left_name)
383 self.setHandleColor(draw_manager, self.left_name, self.mainColor())
384 draw_manager.line(bl, tl)
385 draw_manager.endDrawable()
388 def doPress( self, view ):
390 Handle the mouse press event in a VP2.0 viewport.
393 self.mouse_point_gl_name = OpenMaya.MPoint.kOrigin
394 self.update_drag_information()
397 def doDrag( self, view ):
399 Handle the mouse drag event in a VP2.0 viewport.
401 self.update_drag_information()
404 def doRelease( self, view ):
406 Handle the mouse release event in a VP2.0 viewport.
410 def set_draw_transform_info( self, rotation, translation ):
412 Store the given rotation and translation.
414 self.rotate_x = rotation[0]
415 self.rotate_y = rotation[1]
416 self.rotate_z = rotation[2]
417 self.translate_x = translation[0]
418 self.translate_y = translation[1]
419 self.translate_z = translation[2]
421 def update_drag_information( self ):
423 Update the mouse's intersection location with the manipulator
426 self.local_mouse = self.mouseRay()
429 mouse_intersection_with_manip_plane = self.plane.intersect( self.local_mouse )
431 self.mouse_point_gl_name = mouse_intersection_with_manip_plane
433 active = self.glActiveName()
437 if ( active == self.top_name ):
440 if ( active == self.bottom_name ):
443 if ( active == self.right_name ):
446 if ( active == self.left_name ):
458 line.set_line( start, vab )
462 cpt = line.closest_point( self.mouse_point_gl_name )
463 self.mouse_point_gl_name -= cpt
465 min_change_value = min( self.mouse_point_gl_name.x, self.mouse_point_gl_name.y, self.mouse_point_gl_name.z )
466 max_change_value = max( self.mouse_point_gl_name.x, self.mouse_point_gl_name.y, self.mouse_point_gl_name.z )
467 if ( active == self.right_name ):
468 self.setDoubleValue( self.right_index, max_change_value )
469 if ( active == self.left_name ):
470 self.setDoubleValue( self.right_index, min_change_value )
476 This command class is used to create instances of the SquareScaleManipContext class.
478 kPluginCmdName =
"py2SquareScaleManipContext"
481 kNopLongFlag =
"-noOperation"
488 return SquareScaleManipContextCmd()
490 def doQueryFlags(self):
491 theParser = self.parser()
492 if( theParser.isFlagSet(SquareScaleManipContextCmd.kNopFlag) ):
493 print(
"Doing absolutely nothing")
498 Create and return an instance of the SquareScaleManipContext class.
500 return SquareScaleManipContext()
502 def appendSyntax(self):
503 theSyntax = self.syntax()
504 theSyntax.addFlag(SquareScaleManipContextCmd.kNopFlag, SquareScaleManipContextCmd.kNopLongFlag)
509 This context handles all mouse interaction in the viewport when activated.
510 When activated, it creates and manages an instance of the SquareScaleManuplator
511 class on the selected objects.
514 kContextName =
'SquareScaleManipContext'
522 Initialize the members of the SquareScaleManipContext class.
525 self.setTitleString(
'Plug-in manipulator: ' + SquareScaleManipContext.kContextName)
526 self.manipulator_class_ptr =
None
527 self.first_object_selected =
None
528 self.active_list_modified_msg_id = -1
531 def toolOnSetup(self, event):
533 Set the help string and selection list callback.
535 self.setHelpString(
'Move the object using the manipulator')
537 SquareScaleManipContext.update_manipulators_cb(self)
540 OpenMaya.MModelMessage.kActiveListModified, \
541 SquareScaleManipContext.update_manipulators_cb, self)
547 def toolOffCleanup(self):
549 Unregister the selection list callback.
553 self.active_list_modified_msg_id = -1
560 def namesOfAttributes(self, attribute_names):
562 Return the names of the attributes of the selected objects this context will be modifying.
564 attribute_names.append(
'scaleX')
567 def setInitialState(self):
569 Set the initial transform of the manipulator.
572 xformMatrix = xform.transformation()
573 translation = xformMatrix.translation( OpenMaya.MSpace.kWorld )
574 rotation = xformMatrix.rotation(
False)
576 self.manipulator_class_ptr.set_draw_transform_info( rotation, translation )
579 def valid_geometry_selected(self):
581 Check to make sure the selected objects have transforms.
589 logger.info(
" Could not get active selection")
592 if (
not list)
or (list.length() == 0):
595 while not iter.isDone():
596 depend_node = iter.getDependNode()
597 if (depend_node.isNull()
or (
not depend_node.hasFn(OpenMaya.MFn.kTransform))):
603 def update_manipulators_cb(ctx):
605 Callback that creates the manipulator if valid geometry is selected. Also removes
606 the manipulator if no geometry is selected. Handles connecting the manipulator to
607 multiply selected nodes.
610 ctx.deleteManipulators()
612 logger.info(
" No manipulators to delete")
615 if not ctx.valid_geometry_selected():
619 ctx.manipulator_class_ptr =
None
620 ctx.first_object_selected = OpenMaya.MObject.kNullObj
622 (manipulator, manip_object) = SquareScaleManipulator.newManipulator(
'py2SquareScaleContextManipulator')
626 ctx.manipulator_class_ptr = manipulator
629 ctx.addManipulator(manip_object)
634 while not iter.isDone():
635 depend_node = iter.getDependNode()
639 if (
not manipulator.connectToDependNode(depend_node)):
644 if ( ctx.first_object_selected == OpenMaya.MObject.kNullObj ):
645 ctx.first_object_selected = depend_node
649 ctx.setInitialState()
655 update_manipulators_cb = staticmethod(update_manipulators_cb)
659 def initializePlugin(plugin):
663 pluginFn.registerContextCommand( SquareScaleManipContextCmd.kPluginCmdName, SquareScaleManipContextCmd.creator)
665 sys.stderr.write(
"Failed to register context command: %s\n" % SquareScaleManipContextCmd.kPluginCmdName)
669 pluginFn.registerNode( SquareScaleManipulator.kNodeName, SquareScaleManipulator.kTypeId, \
670 SquareScaleManipulator.creator, SquareScaleManipulator.initialize, \
671 OpenMaya.MPxNode.kManipulatorNode)
673 sys.stderr.write(
"Failed to register node: %s\n" % SquareScaleManipulator.kNodeName)
678 def uninitializePlugin(plugin):
681 pluginFn.deregisterContextCommand(SquareScaleManipContextCmd.kPluginCmdName)
684 "Failed to unregister command: %s\n" % SquareScaleManipContextCmd.kPluginCmdName)
688 pluginFn.deregisterNode(SquareScaleManipulator.kTypeId)
691 "Failed to unregister node: %s\n" % SquareScaleManipulator.kNodeName)