-
Notifications
You must be signed in to change notification settings - Fork 0
/
mr_tempPin.py
364 lines (301 loc) · 14.8 KB
/
mr_tempPin.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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
"""
# ------------------------------------------------------------------------------ #
# SCRIPT: mr_tempPin.py
# VERSION: 0011
#
# CREATORS: Maria Robertson
# ---------------------------------------
# Last tested for Autodesk Maya 2023.3
# ---------------------------------------
# DESCRIPTION:
# ---------------------------------------
# This script has two main functions:
#
# single:
# - Pin selected objects to a single worldspace locator.
# - The locator will be at the objects' average positon and orientation.
# - When the script runs again, its constrained objects are keyed on current frame, and temp locators get deleted.
#
# multiple:
# - Pin selected objects to a worldspace locator each.
# - All new locators get parented under one null. Use it to pivot all objects at once.
# - The null will be created at the objects average positon and orientation.
# - When the script runs again, constrained objects are keyed on the current frame, and temp locators get deleted.
#
# The script has options to chose what type of constraints and pivot position is wanted.
#
# EXAMPLE USES:
# ---------------------------------------
# - Temporarily hold controls in place for on-frame adjustments.
#
# INSTRUCTIONS:
# ---------------------------------------
# To choose the type of constraints used, pick one of the three modes:
# - "both" - constrain translate and rotate attributes
# - "translate"
# - "rotate"
#
# To choose where the pivot positon should be made when using the "multiple" function, use either:
# - "average"
# - "last_selected"
#
# ---------------------------------------
# RUN COMMANDS:
# ---------------------------------------
import importlib
import mr_tempPin
importlib.reload(mr_tempPin)
# USE ONE OF THE FOLLOWING COMMANDS.
mr_tempPin.single("both")
mr_tempPin.single("translate")
mr_tempPin.single("rotate")
mr_tempPin.multiple("both", "average")
mr_tempPin.multiple("translate", "average")
mr_tempPin.multiple("rotate", "average")
mr_tempPin.multiple("both", "last_selected")
mr_tempPin.multiple("translate", "last_selected")
mr_tempPin.multiple("rotate", "last_selected")
# ---------------------------------------
# REQUIREMENTS:
# ---------------------------------------
# Must have mr_find_constraint_targets_and_drivers.py in order to use mr_find_targets_of_selected()
#
# ------------------------------------------------------------------------------ #
"""
import maya.cmds as cmds
import maya.mel as mel
import importlib
import mr_find_constraint_targets_and_drivers
importlib.reload(mr_find_constraint_targets_and_drivers)
##################################################################################################################################################
def single(mode=None):
temp_pin = "TEMP_worldspace_locator"
# -------------------------------------------------------------------
# 00. CHECK IF THE USER'S COMMAND INPUTS ARE VALID.
# -------------------------------------------------------------------
if mode not in ["both", "translate", "rotate"]:
cmds.warning("Please specify in the run command if the script should constrain only \"translate\" or \"rotate\", or \"both\".")
return
# -------------------------------------------------------------------
# 00. CHECK IF TEMP PIN ALREADY EXISTS.
# -------------------------------------------------------------------
if cmds.objExists(temp_pin):
key_targets(temp_pin)
cmds.delete(temp_pin)
# Refresh the viewport.
# This helps when using the script to reposition objects on animation layers, while BaseAnimation is locked.
current_time = cmds.currentTime(query=True)
cmds.currentTime(current_time)
# -------------------------------------------------------------------
# 00. CREATE TEMP PIN FOR EACH SELECTED OBJECT.
# -------------------------------------------------------------------
else:
sel = cmds.ls(selection=True)
# Create a locator.
loc = cmds.spaceLocator(name=temp_pin)[0]
cmds.setAttr(loc + "Shape.localScaleX", 10)
cmds.setAttr(loc + "Shape.localScaleY", 10)
cmds.setAttr(loc + "Shape.localScaleZ", 10)
match_average_position_of_objects(sel, loc)
for item in sel:
constrain_unlocked_attributes(loc, item, mode)
lock_and_hide_same_attributes(loc, item, mode)
# Set a blank keyframe, to remember the frame the loc was created on.
cmds.setKeyframe(loc)
cmds.select(loc)
##################################################################################################################################################
def multiple(mode=None, position=None):
temp_pin_group = "TEMP_worldspace_locator_grp"
# -------------------------------------------------------------------
# 00. CHECK IF THE USER'S COMMAND INPUTS ARE VALID.
# -------------------------------------------------------------------
if mode not in ["both", "translate", "rotate"]:
cmds.warning("Please specify in the run command if the script should constrain only \"translate\" or \"rotate\", or \"both\".")
return
if position not in ["average", "last_selected"]:
cmds.warning("Please specify in the run command if the pivot position should be at \"average\" or \"last_selected\".")
return
# -------------------------------------------------------------------
# 00. CHECK IF TEMP GROUP ALREADY EXISTS.
# -------------------------------------------------------------------
if cmds.objExists(temp_pin_group):
children = cmds.listRelatives(temp_pin_group, children=True)
key_targets(children)
cmds.delete(temp_pin_group)
# -------------------------------------------------------------------
# 00. CREATE TEMP PIN FOR EACH SELECTED OBJECT.
# -------------------------------------------------------------------
else:
sel = cmds.ls(selection=True)
# If nothing is selected,
if not sel:
# reminds user to select something.
# Make sure title is unique, otherwise dialog won't trigger.
cmds.warning("Select objects to pin.")
else:
# Create a group.
cmds.group(empty=True, name=temp_pin_group)
if position == "average":
match_average_position_of_objects(sel, temp_pin_group)
if position == "last_selected":
match_position_of_object(sel[-1], temp_pin_group)
for item in sel:
# Create a locator.
loc = cmds.spaceLocator(name=("TEMP_worldspace_" + item + "_loc"))[0]
cmds.setAttr(loc + "Shape.localScaleX", 10)
cmds.setAttr(loc + "Shape.localScaleY", 10)
cmds.setAttr(loc + "Shape.localScaleZ", 10)
# Position and orient it at its target.
cmds.pointConstraint(item, loc)
cmds.orientConstraint(item, loc)
cmds.delete(loc, constraints=True)
# Parent into temp_pin_group.
cmds.parent(loc, temp_pin_group)
constrain_unlocked_attributes(loc, item, mode)
lock_and_hide_same_attributes(loc, item, mode)
# Set a blank keyframe, to remember the frame the temp_pin_group was created on.
cmds.setKeyframe(temp_pin_group)
cmds.select(temp_pin_group)
# End with rotate manipulator active.
mel.eval("buildRotateMM ;")
mel.eval("destroySTRSMarkingMenu RotateTool ;")
##################################################################################################################################################
########################################################################
# #
# SUPPORTING FUNCTIONS #
# #
########################################################################
# Place the target at the average position and orientation of source objects.
def match_average_position_of_objects(sources, target):
constraints = []
for item in sources:
point_constraint = cmds.pointConstraint(item, target)
orient_constraint = cmds.orientConstraint(item, target)
constraints.extend(point_constraint)
constraints.extend(orient_constraint)
# Delete with variables instead of cmds.delete(constraints=True), because if the target it a null, it gets deleted when constraints are removed.
cmds.delete(constraints)
##################################################################################################################################################
# Place the target at the average position and orientation of source objects.
def match_position_of_object(source, target):
constraints = []
point_constraint = cmds.pointConstraint(source, target)
orient_constraint = cmds.orientConstraint(source, target)
constraints.extend(point_constraint)
constraints.extend(orient_constraint)
# Delete with variables instead of cmds.delete(constraints=True), because if the target it a null, it gets deleted when constraints are removed.
cmds.delete(constraints)
##################################################################################################################################################
def key_targets(drivers):
cmds.select(drivers)
mr_find_constraint_targets_and_drivers.mr_find_targets_of_selected()
mel.eval("SetKeyTranslate;")
mel.eval("SetKeyRotate;")
##################################################################################################################################################
# Lock and hide attributes on the locator if corresponding ones on the target are locked.
def lock_and_hide_same_attributes(source, target, mode):
main_attributes_to_lock_hide = ["translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ"]
extra_attributes_to_lock_hide = ["scaleX", "scaleY", "scaleZ", "visibility"]
def lock_hide_attribute(source, attr):
source_attr = source + "." + attr
cmds.setAttr(source_attr, keyable=False)
cmds.setAttr(source_attr, lock=True)
# Hide attributes on target that are locked and / or unkeyable on source.
for attr in main_attributes_to_lock_hide:
target_attr = target + "." + attr
target_lock = cmds.getAttr(target_attr, lock=True)
target_keyable = cmds.getAttr(target_attr, keyable=True)
if target_lock or not target_keyable:
lock_hide_attribute(source, attr)
for attr in extra_attributes_to_lock_hide:
lock_hide_attribute(source, attr)
if mode == "translate":
rotation_attributes = ["rotateX", "rotateY", "rotateZ"]
for attr in rotation_attributes:
lock_hide_attribute(source, attr)
elif mode == "rotate":
translation_attributes = ["translateX", "translateY", "translateZ"]
for attr in translation_attributes:
lock_hide_attribute(source, attr)
##################################################################################################################################################
def constrain_unlocked_attributes(driver, target, mode):
if mode == "both":
constrain_unlocked_translates(driver, target)
constrain_unlocked_rotates(driver, target)
elif mode == "translate":
constrain_unlocked_translates(driver, target)
elif mode == "rotate":
constrain_unlocked_rotates(driver, target)
##################################################################################################################################################
def constrain_unlocked_translates(driver, item):
# Check if translate X, Y, Z are locked
skip_trans_axes = []
if cmds.getAttr(item + ".translateX", lock=True):
skip_trans_axes.append("x")
if cmds.getAttr(item + ".translateY", lock=True):
skip_trans_axes.append("y")
if cmds.getAttr(item + ".translateZ", lock=True):
skip_trans_axes.append("z")
if skip_trans_axes:
if skip_trans_axes == ['x', 'y', 'z']:
return
else:
# Apply point constraint with skipping specified axes
cmds.pointConstraint(driver, item, maintainOffset=True, weight=1, skip=skip_trans_axes)
else:
cmds.pointConstraint(driver, item, maintainOffset=True, weight=1)
##################################################################################################################################################
def constrain_unlocked_rotates(driver, item):
# Check if rotate X, Y, Z are locked
skip_rot_axes = []
if cmds.getAttr(item + ".rotateX", lock=True):
skip_rot_axes.append("x")
if cmds.getAttr(item + ".rotateY", lock=True):
skip_rot_axes.append("y")
if cmds.getAttr(item + ".rotateZ", lock=True):
skip_rot_axes.append("z")
if skip_rot_axes:
if skip_rot_axes == ['x', 'y', 'z']:
return
else:
# Apply orient constraint with skipping specified axes
cmds.orientConstraint(driver, item, maintainOffset=True, weight=1, skip=skip_rot_axes)
else:
cmds.orientConstraint(driver, item, maintainOffset=True, weight=1)
"""
##################################################################################################################################################
# ---------------------------------------
# CHANGELOG:
# ---------------------------------------
# 2024-01-18 - 0011:
# - Refresh viewport after deleting tempPin, to reflect changes on animation layers while BaseAnimation is locked.
# - Moved changlog to the bottom.
#
# 2024-01-14 - 0010:
# - End multiple() with rotate manipulator active.
#
# 2023-12-28 - 0009:
# - Adding check for valid command inputs + option to choose where pivot position is for the "multiple" function.
#
# 2023-12-28 - 0008:
# - Updating script to not point/orient constraint at all if those attributes are all locked.
#
# 2023-12-09 - 0007:
# - Forgot to update single() function, after making lock_and_hide_same_attributes() and constrain_unlocked_attributes() work for one object at a time.
#
# 2023-12-09 - 0006:
# - Fixing the multiple() function to respect locked attributes better.
#
# 2023-12-07 - 0005:
# - Converting and mergnig MEL script "mr_tempPin_createIndividualPins.mel" here.
#
# 2023-12-04 - 0004:
# - Converted original MEL script to Python.
#
# 2023-06-28 - 0003:
# - Updated script to use Python function to find constraint targets.
#
# 0002: Added catch command, to stop script from failing if any translation or rotation attributes are locked.
# ---------------------------------------
##################################################################################################################################################
"""