Python API 2.0 Reference
python/api1/py1SimpleSolverNode.py
1 #-
2 # ==========================================================================
3 # Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All
4 # rights reserved.
5 #
6 # The coded instructions, statements, computer programs, and/or related
7 # material (collectively the "Data") in these files contain unpublished
8 # information proprietary to Autodesk, Inc. ("Autodesk") and/or its
9 # licensors, which is protected by U.S. and Canadian federal copyright
10 # law and by international treaties.
11 #
12 # The Data is provided for use exclusively by You. You have the right
13 # to use, modify, and incorporate this Data into other products for
14 # purposes authorized by the Autodesk software license agreement,
15 # without fee.
16 #
17 # The copyright notices in the Software and this entire statement,
18 # including the above license grant, this restriction and the
19 # following disclaimer, must be included in all copies of the
20 # Software, in whole or in part, and all derivative works of
21 # the Software, unless such copies or derivative works are solely
22 # in the form of machine-executable object code generated by a
23 # source language processor.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
26 # AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
27 # WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
28 # NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
29 # PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
30 # TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
31 # BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
32 # DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
33 # AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
34 # OR PROBABILITY OF SUCH DAMAGES.
35 #
36 # ==========================================================================
37 #+
38 
39 #
40 # Creation Date: 4 October 2006
41 #
42 
43 ########################################################################
44 # DESCRIPTION:
45 #
46 # Registers the IK solver "simpleSolverNode".
47 # It is a single-bone, single-plane IK solver.
48 #
49 # This plug-in demonstrates how to create and register an IK solver.
50 # Due to the complex nature of IK solvers, this plug-in only
51 # works with 2-joint skeletons (1 bone) in the x-y plane.
52 #
53 # To use the solver, create a single IK bone (joint tool) with two joints by
54 # selecting Skeleton > Joint Tool in the Animation menu set. Then, enter the
55 # following commands in the command window to create an IK handle, which uses
56 # the new solver:
57 #
58 # import maya.cmds as cmds
59 # cmds.createNode("spSimpleSolverNode", name="spSimpleSolverNode1")
60 #
61 # This creates a handle that can be dragged around in the x-y plane to rotate the joint.
62 #
63 # The following command can be used to determine which solver a handle is using:
64 #
65 # cmds.ikHandle(sol="spSimpleSolverNode1", sj="joint1", ee="joint2")
66 #
67 ########################################################################
68 
69 # imports
70 import maya.OpenMaya as OpenMaya
71 import maya.OpenMayaUI as OpenMayaUI
72 import maya.OpenMayaMPx as OpenMayaMPx
73 import maya.OpenMayaAnim as OpenMayaAnim
74 import math, sys
75 
76 # consts
77 kSolverNodeName = "spSimpleSolverNode"
78 kSolverNodeId = OpenMaya.MTypeId(0x00080051)
79 
80 
81 class simpleSolverNode(OpenMayaMPx.MPxIkSolverNode):
82  def __init__(self):
83  OpenMayaMPx.MPxIkSolverNode.__init__(self)
84 
85 
86  def solverTypeName(self):
87  return kSolverNodeName
88 
89 
90  def doSolve(self):
91  self.doSimpleSolver()
92 
93 
94  def doSimpleSolver(self):
95  """
96  Solve single joint in the x-y plane
97  - first it calculates the angle between the handle and the end-effector.
98  - then it determines which way to rotate the joint.
99  """
100  handle_group = self.handleGroup()
101  handle = handle_group.handle(0)
102  handlePath = OpenMaya.MDagPath.getAPathTo(handle)
103  fnHandle = OpenMayaAnim.MFnIkHandle(handlePath)
104 
105  # Get the position of the end_effector
106  end_effector = OpenMaya.MDagPath()
107  fnHandle.getEffector(end_effector)
108  tran = OpenMaya.MFnTransform(end_effector)
109  effector_position = tran.rotatePivot(OpenMaya.MSpace.kWorld)
110 
111  # Get the position of the handle
112  handle_position = fnHandle.rotatePivot(OpenMaya.MSpace.kWorld)
113 
114  # Get the start joint position
115  start_joint = OpenMaya.MDagPath()
116  fnHandle.getStartJoint(start_joint)
117  start_transform = OpenMaya.MFnTransform(start_joint)
118  start_position = start_transform.rotatePivot(OpenMaya.MSpace.kWorld)
119 
120  # Calculate the rotation angle
121  v1 = start_position - effector_position
122  v2 = start_position - handle_position
123  angle = v1.angle(v2)
124 
125  # -------- Figure out which way to rotate --------
126  #
127  # define two vectors U and V as follows
128  # U = EndEffector(E) - StartJoint(S)
129  # N = Normal to U passing through EndEffector
130  #
131  # Clip handle_position to half-plane U to determine the region it
132  # lies in. Use the region to determine the rotation direction.
133  #
134  # U
135  # ^ Region Rotation
136  # | B
137  # (E)---N A C-C-W
138  # A | B C-W
139  # | B
140  # |
141  # (S)
142  #
143 
144  rot = 0.0 # Rotation about Z-axis
145 
146  # U and N define a half-plane to clip the handle against
147  U = effector_position - start_position
148  U.normalize()
149 
150  # Get a normal to U
151  zAxis = OpenMaya.MVector(0.0, 0.0, 1.0)
152  N = U ^ zAxis # Cross product
153  N.normalize()
154 
155  # P is the handle position vector
156  P = handle_position - effector_position
157 
158  # Determine the rotation direction
159  PdotN = P[0]*N[0] + P[1]*N[1]
160  if PdotN < 0:
161  rot = angle # counter-clockwise
162  else:
163  rot = -1.0 * angle # clockwise
164 
165  # get and set the Joint Angles
166  jointAngles = OpenMaya.MDoubleArray()
167  try:
168  self._getJointAngles(jointAngles)
169  except:
170  # getting angles failed, do nothing
171  pass
172  else:
173  jointAngles.set(jointAngles[0] + rot, 0)
174  self._setJointAngles(jointAngles)
175 
176 
177 ##############################################################################
178 
179 
180 def nodeCreator():
181  return OpenMayaMPx.asMPxPtr(simpleSolverNode())
182 
183 
184 def nodeInitializer():
185  # nothing to initialize
186  pass
187 
188 
189 # initialize the script plug-in
190 def initializePlugin(mobject):
191  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
192 
193  try:
194  mplugin.registerNode(kSolverNodeName, kSolverNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kIkSolverNode)
195  except:
196  sys.stderr.write("Failed to register node: %s" % kSolverNodeName)
197  raise
198 
199 
200 # uninitialize the script plug-in
201 def uninitializePlugin(mobject):
202  mplugin = OpenMayaMPx.MFnPlugin(mobject)
203  try:
204  mplugin.deregisterNode(kSolverNodeId)
205  except:
206  sys.stderr.write("Failed to unregister node: %s" % kSolverNodeName)
207  raise