-
Notifications
You must be signed in to change notification settings - Fork 2
/
libJoint.py
230 lines (201 loc) · 8.64 KB
/
libJoint.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# coding=utf-8
"""
@package PKD_Tools.libJoint
This package contains joint orientation based tools
"""
import libUtilities
reload(libUtilities)
import pymel.core as pm
from maya import cmds
def _get_joint_orient_info():
"""Internal function to retrieve the ideal settings for the joint orient function.
@return: Tuple of dictionary.
"""
detailed_info = dict()
reverse_lookup = dict()
# up forward
detailed_info["yzx"] = {"Pitch/Bend Forward": "X",
"Yaw/Twist": "Y",
"Forward": "Z",
"Up": "Y"}
reverse_lookup["yz"] = "yzx"
detailed_info["yxz"] = {"Pitch/Bend Forward": "Z",
"Yaw/Twist": "Y",
"Forward": "X",
"Up": "Y"}
reverse_lookup["yx"] = "yxz"
detailed_info["xyz"] = {"Pitch/Bend Forward": "Y",
"Yaw/Twist": "X",
"Forward": "Z",
"Up": "X"}
reverse_lookup["xz"] = "xyz"
detailed_info["xzy"] = {"Pitch/Bend Forward": "Z",
"Yaw/Twist": "X",
"Forward": "Y",
"Up": "X"}
reverse_lookup["xy"] = "xzy"
detailed_info["zyx"] = {"Pitch/Bend Forward": "X",
"Yaw/Twist": "Z",
"Forward": "Y",
"Up": "Z"}
reverse_lookup["zy"] = "zyx"
detailed_info["zxy"] = {"Pitch/Bend Forward": "Y",
"Yaw/Twist": "Z",
"Forward": "X",
"Up": "Z"}
reverse_lookup["zx"] = "zxy"
rotate_secondary_axis = {'yzx': 'zup', 'yxz': 'zup', 'xyz': 'xup', 'xzy': 'xup', 'zyx': 'yup', 'zxy': 'yup'}
return reverse_lookup, rotate_secondary_axis, detailed_info
def get_joint_children(joint):
"""
Return all the descendants in a hierarchical order
@param joint: (str/pynode) The target joint
@return: List of pynode of children
"""
joint = str(joint)
return libUtilities.pyList(sorted(cmds.listRelatives(joint, allDescendents=True, fullPath=True) or []))
def set_rotate_order(rotate_order, joint_list=None):
"""
Set the rotate order for the list of joints
@param rotate_order:
@param joint_list:
"""
if not joint_list:
selection = pm.selected()
if selection:
joint_list = selection[0] + get_joint_children(selection[0])
else:
raise RuntimeError("Joint list not defined and nothing is selected")
# Set the rotate order
for joint in joint_list:
if joint.rotateOrder.get(asString=True) != rotate_order:
joint.rotateOrder.set(rotate_order)
def orient_joint(**kwargs):
"""
Orient the joint
@param kwargs: Takes the following keyword arguments
- joint (node): the node which will be process. It picks up the default node by default
- up (str): in which direction do move up and down the joint
- forward (str): in which direction do you want to move it forward/back
- flip_forward (bool): whether the joint forward direction will be reversed
- flip up (bool): whether the joint up and down will be reversed
- details (bool)
@return the rotate order
"""
# Get all the keyword arguements
selection = pm.selected() or []
try:
target_joint = libUtilities.force_pynode(kwargs.get("joint") or pm.selected()[0])
except IndexError:
pm.confirmDialog(title="User Error",
message="{: <22}".format("Please select a joint"),
icon="critical")
return
up = kwargs.get("up", "y").lower()
forward = kwargs.get("forward", "z").lower()
flip_forward = kwargs.get("flip_forward", False)
flip_up = kwargs.get("flip_up", False)
details = kwargs.get("details", False)
# Get the preferred rotation axis
prefered_axis = "{}{}".format(up, forward)
# Get the joint mapping data
reverse_lookup, rotate_secondary_axis, detailed_info = _get_joint_orient_info()
rotate_order = reverse_lookup[prefered_axis]
pm.undoInfo(openChunk=True)
children_joints = get_joint_children(target_joint)
libUtilities.freeze_rotation([target_joint] + children_joints)
libUtilities.freeze_scale([target_joint] + children_joints)
secondary_orient = rotate_secondary_axis[rotate_order]
if children_joints:
if flip_forward:
secondary_orient = secondary_orient.replace("up", "down")
pm.joint(target_joint,
edit=True,
zeroScaleOrient=True,
children=True,
orientJoint=rotate_order,
secondaryAxisOrient=secondary_orient)
if flip_up:
joints = [target_joint]
if len(children_joints) > 1:
joints = reversed(joints + children_joints[:-1])
for flip_joint in joints:
childJoint = pm.listRelatives(flip_joint)[0]
childJoint.setParent(world=True)
flip_joint.attr('r{}'.format(forward)).set(180)
libUtilities.freeze_rotation([flip_joint])
libUtilities.freeze_scale([flip_joint])
childJoint.setParent(flip_joint)
children_joints[-1].jointOrient.set(0, 0, 0)
if details:
for key in ["Pitch/Bend Forward", "Yaw/Twist", "Forward", "Up"]:
print "{} : {}".format(key, detailed_info[rotate_order][key])
if flip_up:
print "The joint chain was flipped up in the end"
pm.select(selection)
all_joints = [target_joint] + children_joints
# Calculate the last axis to resolves
last_axis = (set("xyz") - set(prefered_axis)).pop()
preferred_rotate_order = "{}{}{}".format(last_axis, forward, up)
# Set the rotate order
set_rotate_order(preferred_rotate_order, all_joints)
pm.undoInfo(closeChunk=True)
return preferred_rotate_order
def zero_out_bend(**kwargs):
"""
Zero out all the bend joint
Here we assume it already has a optimised rotate order
- joint_list: (list) List of joint to zero out te bend
- axis: (str) Which axis is the joint bending
- rotate_order: (str) What is the current rotate order
@return the rotate order
"""
joint_list = libUtilities.pyList(kwargs.get("joint_list") or get_joint_children(pm.selected()[0]))
libUtilities.freeze_rotation(joint_list)
libUtilities.freeze_scale(joint_list)
rotate_order = kwargs.get("rotate_order") or joint_list[0].rotateOrder.get(asString=True)
target_axis = kwargs.get("axis", rotate_order[0])
new_rotate_order = None
if target_axis != rotate_order[0]:
new_rotate_order = "{}{}{}".format(target_axis, rotate_order[0], rotate_order[2])
pm.undoInfo(openChunk=True)
for joint in joint_list:
for rotate_axis in rotate_order:
if rotate_axis != target_axis:
joint.attr("jointOrient{}".format(rotate_axis.upper())).set(0)
if new_rotate_order:
joint.rotateOrder.set(new_rotate_order)
pm.undoInfo(closeChunk=True)
return new_rotate_order
def get_gimbal_data(rotate_order):
"""
Get detail information about the gimbal data based on the current rotate order
@param rotate_order: (str) The current rotate order
@return: dict of gimbal data
"""
return {"bend": rotate_order[0], "roll": rotate_order[1], "twist": rotate_order[2]}
def get_rotate_order(gimbal_data):
"""
Based on the gimbal generate the rotate order. Here we can decide whether the Gimbal will be on the twist or
on the roll
@param gimbal_data: dict which defines what axis are on the bend, roll and twist. Also defines where the
gimbal will reside
@return: The rotate order*
"""
if gimbal_data["gimbal"] == "roll":
return '{}{}{}'.format(gimbal_data["bend"], gimbal_data["roll"], gimbal_data["twist"])
elif gimbal_data["gimbal"] == "twist":
return '{}{}{}'.format(gimbal_data["bend"], gimbal_data["twist"], gimbal_data["roll"])
def freeze_rotation(joint):
"""
Freeze the rotation and scale on the joint chain
@param joint: The target joint
"""
joint_list = [joint] + get_joint_children(joint)
libUtilities.freeze_scale(joint_list)
libUtilities.freeze_rotation(joint_list)
def default_gimbal_data():
"""
@return: (dict) Gimbal settings data for joint created at origin
"""
return {"twist": "y", "bend": "x", "roll": "z", 'gimbal': 'roll', 'flip_forward': False, 'flip_up': False}