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
|
# -*- coding: utf-8 -*-
"""
:author:
Jeremy Ernst
:description:
This module contains the class for handling aim mode on a component. Aim mode is a mode that can be toggled that
determines whether joints aim at their children when the joint mover controls are edited.
"""
import pymel.core as pm
import artv2.utilities.general_utilities as utils
import artv2.tools.system.logger.output_logger as logger
class AimHelper(object):
"""
Class that handles toggling aim mode on a component. This class is instantiated on a JointMover object.
Aim mode is a mode that can be toggled that determines whether joints aim at their children when the joint mover
controls are moved or rotated. This setup is done using the joint mover markup tool and setting the attributes it
adds concerning aim mode.
The relevant attributes are:
* Can Aim
* Aim Joint
* Aim Axis
* Invert Aim Axis
* Up Axis
* Maintain Offset
.. glossary::
**Can Aim**
Whether this joint should aim at another joint when aim mode is turned on.
**Aim Joint**
Which joint this joint should aim at when aim mode is turned on. (This is ignored if Can Aim is False)
**Aim Axis**
Which axis represents the aim axis. (This is ignored if Can Aim is False)
**Invert Aim Axis**
If the aim axis should be inverted. (If your aim axis is set to X, but needs to be -X, this would be True)
(This is ignored if Can Aim is False)
**Up Axis**
The axis of the joint that is closes to the world up axis. (This is ignored if Can Aim is False)
**Maintain Offset**
This will probably not ever need to be used, but in the case of some special circumstance, this will create the
aim constraint while maintaining offsets.
(This is ignored if Can Aim is False)
"""
def __init__(self, metanode):
"""
:param metanode: This is the network node from a component, like a leg or torso. It contains all of the
metadata
"""
self.logger = logger.OutputLogger(self.__class__.__name__)
self._metanode = pm.PyNode(metanode)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def toggle_aim_mode(self):
"""
Toggles aim mode on a component. Aim mode determines whether joints aim at their children when the joint
mover controls are moved or rotated. Which joints and their settings are setup using the joint mover markup
tool.
"""
if self._metanode.isAiming.get():
self.logger.info("{0}.toggle_aim_mode: Turning aim mode off for {1}"
"".format(self.__class__.__name__, self._metanode.componentName.get()))
self.tear_down_aim_mode()
else:
self.logger.info("{0}.toggle_aim_mode: Turning aim mode on for {1}"
"".format(self.__class__.__name__, self._metanode.componentName.get()))
self.setup_aim_mode()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def setup_aim_mode(self):
"""
Sets up the aim constraints on the joint mover controls that have aim mode setup.
"""
aim_data = self._gather_aim_data()
constraints = []
for mover_grp in aim_data:
aim_target, aim_axis, up_axis, invert, offset = aim_data.get(mover_grp)
constraint = pm.aimConstraint(aim_target, mover_grp,
aimVector=self._convert_axis(aim_axis, invert),
upVector=self._convert_axis(up_axis),
worldUpType="objectrotation",
worldUpObject=aim_target,
worldUpVector=self._convert_axis(pm.upAxis(q=True, axis=True).upper()),
maintainOffset=offset)
constraints.append(constraint)
utils.set_attribute(self._metanode, "isAiming", True)
for constraint in constraints:
constraint.addAttr("metanode", at="message")
self._metanode.aimModeConstraints.connect(constraint.metanode)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def tear_down_aim_mode(self):
"""
Removes the aim constraints from the joint mover controls.
"""
constraints = self._metanode.aimModeConstraints.connections()
for each in constraints:
each.unlock()
pm.delete(each)
utils.set_attribute(self._metanode, "isAiming", False)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
def _gather_aim_data(self):
offset_movers = self._metanode.offset_movers.connections()
component_name = self._metanode.componentName.get()
aim_data = {}
for mover in offset_movers:
mover_grp = mover.getParent()
jnt = mover.connected_joint.connections()[0]
if jnt.hasAttr("canAim"):
can_aim = jnt.canAim.get()
if can_aim:
maintain_offsets = jnt.maintainOffset.get()
aim_axis = jnt.aimAxis.get(asString=True)
invert_aim = jnt.invertAimAxis.get()
up_axis = jnt.upAxis.get(asString=True)
aim_joint = jnt.aimJoint.get(asString=True)
aim_joint_node = pm.PyNode("{0}_{1}".format(component_name, aim_joint))
aim_target_connections = aim_joint_node.attr("message").connections()
for connection in aim_target_connections:
if isinstance(connection, pm.nodetypes.Transform):
aim_target = connection
aim_data[mover_grp] = [aim_target, aim_axis, up_axis, invert_aim, maintain_offsets]
return aim_data
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@staticmethod
def _convert_axis(axis, inverted=False):
"""
Converts a simple string axis value (like X) to a float tuple (like (1.0, 0.0, 0.0))
:return: A tuple containing three floats defining the given axis
"""
vector = None
if axis == "X":
if inverted:
vector = [-1.0, 0.0, 0.0]
else:
vector = [1.0, 0.0, 0.0]
if axis == "Y":
if inverted:
vector = [0.0, -1.0, 0.0]
else:
vector = [0.0, 1.0, 0.0]
if axis == "Z":
if inverted:
vector = [0.0, 0.0, -1.0]
else:
vector = [0.0, 0.0, 1.0]
return vector
|