simpleSolverNode.py

#-
# ==========================================================================
# Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
# rights reserved.
#
# The coded instructions, statements, computer programs, and/or related 
# material (collectively the "Data") in these files contain unpublished 
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
# licensors, which is protected by U.S. and Canadian federal copyright 
# law and by international treaties.
#
# The Data is provided for use exclusively by You. You have the right 
# to use, modify, and incorporate this Data into other products for 
# purposes authorized by the Autodesk software license agreement, 
# without fee.
#
# The copyright notices in the Software and this entire statement, 
# including the above license grant, this restriction and the 
# following disclaimer, must be included in all copies of the 
# Software, in whole or in part, and all derivative works of 
# the Software, unless such copies or derivative works are solely 
# in the form of machine-executable object code generated by a 
# source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
# AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
# WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
# NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
# TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
# BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
# AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
# OR PROBABILITY OF SUCH DAMAGES.
#
# ==========================================================================
#+

#
# Creation Date:   4 October 2006
#
# Example Plugin: simpleSolverNode.py
#
#
# Single-bone, single-plane ik-solver.
#
# This plugin demonstrates how to create and register an ik-solver.
# Due to the complex nature of ik solvers, this plugin only 
# works with 2-joint skeletons (1 bone) in the x-y plane.
#
# To use the solver, create a single bone (joint tool).
# Then type the following in the command window:
#
#       import maya.cmds as cmds
#
#       cmds.createNode("spSimpleSolverNode", name="spSimpleSolverNode1")
#   cmds.ikHandle(sol="spSimpleSolverNode1", sj="joint1", ee="joint2")
#
# This creates a handle that can be dragged around in the x-y
# plane.
#

# imports
import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaAnim as OpenMayaAnim
import math, sys

# consts
kSolverNodeName = "spSimpleSolverNode"
kSolverNodeId = OpenMaya.MTypeId(0x8700a)


class simpleSolverNode(OpenMayaMPx.MPxIkSolverNode):
        def __init__(self):
                OpenMayaMPx.MPxIkSolverNode.__init__(self)


        def solverTypeName(self):
                return kSolverNodeName

        
        def doSolve(self):
                self.doSimpleSolver()


        def doSimpleSolver(self):
                """
                Solve single joint in the x-y plane
                        - first it calculates the angle between the handle and the end-effector.
                        - then it determines which way to rotate the joint.
                """
                handle_group = self.handleGroup()
                handle = handle_group.handle(0)
                handlePath = OpenMaya.MDagPath.getAPathTo(handle)
                fnHandle = OpenMayaAnim.MFnIkHandle(handlePath)

                # Get the position of the end_effector
                end_effector = OpenMaya.MDagPath()
                fnHandle.getEffector(end_effector)
                tran = OpenMaya.MFnTransform(end_effector)
                effector_position = tran.rotatePivot(OpenMaya.MSpace.kWorld)

                # Get the position of the handle
                handle_position = fnHandle.rotatePivot(OpenMaya.MSpace.kWorld)

                # Get the start joint position
                start_joint = OpenMaya.MDagPath()
                fnHandle.getStartJoint(start_joint)
                start_transform = OpenMaya.MFnTransform(start_joint)
                start_position = start_transform.rotatePivot(OpenMaya.MSpace.kWorld)

                # Calculate the rotation angle
                v1 = start_position - effector_position
                v2 = start_position - handle_position
                angle = v1.angle(v2)

                # -------- Figure out which way to rotate --------
                #
                #  define two vectors U and V as follows
                #  U   =   EndEffector(E) - StartJoint(S)
                #  N   =   Normal to U passing through EndEffector
                #
                #  Clip handle_position to half-plane U to determine the region it
                #  lies in. Use the region to determine  the rotation direction.
                #
                #             U
                #             ^              Region      Rotation
                #             |  B           
                #            (E)---N            A          C-C-W
                #         A   |                 B           C-W
                #             |  B
                #             |
                #            (S)
                #

                rot = 0.0       # Rotation about Z-axis

                # U and N define a half-plane to clip the handle against
                U = effector_position - start_position
                U.normalize()

                # Get a normal to U
                zAxis = OpenMaya.MVector(0.0, 0.0, 1.0)
                N = U ^ zAxis # Cross product
                N.normalize()

                # P is the handle position vector
                P = handle_position - effector_position

                # Determine the rotation direction
                PdotN = P[0]*N[0] + P[1]*N[1]
                if PdotN < 0:
                        rot = angle # counter-clockwise
                else:
                        rot = -1.0 * angle      # clockwise

                # get and set the Joint Angles 
                jointAngles = OpenMaya.MDoubleArray()
                try:
                        self._getJointAngles(jointAngles)
                except:
                        # getting angles failed, do nothing
                        pass
                else:
                        jointAngles.set(jointAngles[0] + rot, 0)
                        self._setJointAngles(jointAngles)


##############################################################################


def nodeCreator():
        return OpenMayaMPx.asMPxPtr(simpleSolverNode())


def nodeInitializer():
        # nothing to initialize
        pass


# initialize the script plug-in
def initializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")

        try:
                mplugin.registerNode(kSolverNodeName, kSolverNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kIkSolverNode)
        except:
                sys.stderr.write("Failed to register node: %s" % kSolverNodeName)
                raise


# uninitialize the script plug-in
def uninitializePlugin(mobject):
        mplugin = OpenMayaMPx.MFnPlugin(mobject)
        try:
                mplugin.deregisterNode(kSolverNodeId)
        except:
                sys.stderr.write("Failed to unregister node: %s" % kSolverNodeName)
                raise

Autodesk® Maya® 2009 © 1997-2008 Autodesk, Inc. All rights reserved. Generated with doxygen 1.5.6