scripted/simpleSolverNode.py

scripted/simpleSolverNode.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 # Example Plugin: simpleSolverNode.py
43 #
44 #
45 # Single-bone, single-plane ik-solver.
46 #
47 # This plugin demonstrates how to create and register an ik-solver.
48 # Due to the complex nature of ik solvers, this plugin only
49 # works with 2-joint skeletons (1 bone) in the x-y plane.
50 #
51 # To use the solver, create a single bone (joint tool).
52 # Then type the following in the command window:
53 #
54 # import maya.cmds as cmds
55 #
56 # cmds.createNode("spSimpleSolverNode", name="spSimpleSolverNode1")
57 # cmds.ikHandle(sol="spSimpleSolverNode1", sj="joint1", ee="joint2")
58 #
59 # This creates a handle that can be dragged around in the x-y
60 # plane.
61 #
62 
63 # imports
64 import maya.OpenMaya as OpenMaya
65 import maya.OpenMayaUI as OpenMayaUI
66 import maya.OpenMayaMPx as OpenMayaMPx
67 import maya.OpenMayaAnim as OpenMayaAnim
68 import math, sys
69 
70 # consts
71 kSolverNodeName = "spSimpleSolverNode"
72 kSolverNodeId = OpenMaya.MTypeId(0x8700a)
73 
74 
75 class simpleSolverNode(OpenMayaMPx.MPxIkSolverNode):
76  def __init__(self):
77  OpenMayaMPx.MPxIkSolverNode.__init__(self)
78 
79 
80  def solverTypeName(self):
81  return kSolverNodeName
82 
83 
84  def doSolve(self):
85  self.doSimpleSolver()
86 
87 
88  def doSimpleSolver(self):
89  """
90  Solve single joint in the x-y plane
91  - first it calculates the angle between the handle and the end-effector.
92  - then it determines which way to rotate the joint.
93  """
94  handle_group = self.handleGroup()
95  handle = handle_group.handle(0)
96  handlePath = OpenMaya.MDagPath.getAPathTo(handle)
97  fnHandle = OpenMayaAnim.MFnIkHandle(handlePath)
98 
99  # Get the position of the end_effector
100  end_effector = OpenMaya.MDagPath()
101  fnHandle.getEffector(end_effector)
102  tran = OpenMaya.MFnTransform(end_effector)
103  effector_position = tran.rotatePivot(OpenMaya.MSpace.kWorld)
104 
105  # Get the position of the handle
106  handle_position = fnHandle.rotatePivot(OpenMaya.MSpace.kWorld)
107 
108  # Get the start joint position
109  start_joint = OpenMaya.MDagPath()
110  fnHandle.getStartJoint(start_joint)
111  start_transform = OpenMaya.MFnTransform(start_joint)
112  start_position = start_transform.rotatePivot(OpenMaya.MSpace.kWorld)
113 
114  # Calculate the rotation angle
115  v1 = start_position - effector_position
116  v2 = start_position - handle_position
117  angle = v1.angle(v2)
118 
119  # -------- Figure out which way to rotate --------
120  #
121  # define two vectors U and V as follows
122  # U = EndEffector(E) - StartJoint(S)
123  # N = Normal to U passing through EndEffector
124  #
125  # Clip handle_position to half-plane U to determine the region it
126  # lies in. Use the region to determine the rotation direction.
127  #
128  # U
129  # ^ Region Rotation
130  # | B
131  # (E)---N A C-C-W
132  # A | B C-W
133  # | B
134  # |
135  # (S)
136  #
137 
138  rot = 0.0 # Rotation about Z-axis
139 
140  # U and N define a half-plane to clip the handle against
141  U = effector_position - start_position
142  U.normalize()
143 
144  # Get a normal to U
145  zAxis = OpenMaya.MVector(0.0, 0.0, 1.0)
146  N = U ^ zAxis # Cross product
147  N.normalize()
148 
149  # P is the handle position vector
150  P = handle_position - effector_position
151 
152  # Determine the rotation direction
153  PdotN = P[0]*N[0] + P[1]*N[1]
154  if PdotN < 0:
155  rot = angle # counter-clockwise
156  else:
157  rot = -1.0 * angle # clockwise
158 
159  # get and set the Joint Angles
160  jointAngles = OpenMaya.MDoubleArray()
161  try:
162  self._getJointAngles(jointAngles)
163  except:
164  # getting angles failed, do nothing
165  pass
166  else:
167  jointAngles.set(jointAngles[0] + rot, 0)
168  self._setJointAngles(jointAngles)
169 
170 
171 ##############################################################################
172 
173 
174 def nodeCreator():
175  return OpenMayaMPx.asMPxPtr(simpleSolverNode())
176 
177 
178 def nodeInitializer():
179  # nothing to initialize
180  pass
181 
182 
183 # initialize the script plug-in
184 def initializePlugin(mobject):
185  mplugin = OpenMayaMPx.MFnPlugin(mobject, "Autodesk", "1.0", "Any")
186 
187  try:
188  mplugin.registerNode(kSolverNodeName, kSolverNodeId, nodeCreator, nodeInitializer, OpenMayaMPx.MPxNode.kIkSolverNode)
189  except:
190  sys.stderr.write("Failed to register node: %s" % kSolverNodeName)
191  raise
192 
193 
194 # uninitialize the script plug-in
195 def uninitializePlugin(mobject):
196  mplugin = OpenMayaMPx.MFnPlugin(mobject)
197  try:
198  mplugin.deregisterNode(kSolverNodeId)
199  except:
200  sys.stderr.write("Failed to unregister node: %s" % kSolverNodeName)
201  raise