-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathops_anim.py
256 lines (189 loc) · 8.13 KB
/
ops_anim.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
# SPDX-FileCopyrightText: 2014-2024 Mikhail Rachinskiy
# SPDX-License-Identifier: GPL-3.0-or-later
from collections.abc import Sequence
from typing import Optional
from bpy.props import EnumProperty
from bpy.types import Action, NlaTrack, Object, Operator
NlaTracks = Sequence[NlaTrack]
class _Animation:
__slots__ = (
"action_ob",
"action_data",
"action_sk",
"action_mat",
"action_node",
"nla_tracks_ob",
"nla_tracks_data",
"nla_tracks_sk",
"nla_tracks_mat",
"nla_tracks_node",
)
def __init__(self) -> None:
self.action_ob: Optional[Action] = None
self.action_data: Optional[Action] = None
self.action_sk: Optional[Action] = None
self.action_mat: dict[int, Action] = {}
self.action_node: dict[int, Action] = {}
self.nla_tracks_ob: Optional[NlaTracks] = None
self.nla_tracks_data: Optional[NlaTracks] = None
self.nla_tracks_sk: Optional[NlaTracks] = None
self.nla_tracks_mat: dict[int, NlaTracks] = {}
self.nla_tracks_node: dict[int, NlaTracks] = {}
def _anim_get(ob: Object) -> _Animation:
Anim = _Animation()
ad = ob.animation_data
if ad:
Anim.action_ob = ob.animation_data.action
Anim.nla_tracks_ob = ob.animation_data.nla_tracks
if ob.data:
ad = ob.data.animation_data
if ad:
Anim.action_data = ad.action
Anim.nla_tracks_data = ad.nla_tracks
try:
ad = ob.data.shape_keys.animation_data
if ad:
Anim.action_sk = ad.action
Anim.nla_tracks_sk = ad.nla_tracks
except AttributeError:
pass
if ob.material_slots:
for i, slot in enumerate(ob.material_slots):
if not (mat := slot.material):
continue
if (ad := mat.animation_data):
Anim.action_mat[i] = ad.action
Anim.nla_tracks_mat[i] = ad.nla_tracks
if mat.node_tree and (ad := mat.node_tree.animation_data):
Anim.action_node[i] = ad.action
Anim.nla_tracks_node[i] = ad.nla_tracks
return Anim
class _AdCopy:
def execute(self, context):
ob_active = context.object
obs = list(context.selected_objects)
if ob_active.select_get():
obs.remove(ob_active)
Anim = _anim_get(ob_active)
is_anim_mat = bool(Anim.action_mat or Anim.action_node or Anim.nla_tracks_mat or Anim.nla_tracks_node)
for ob in obs:
if Anim.action_ob:
self.action_copy(ob, Anim.action_ob)
if Anim.nla_tracks_ob:
self.nla_copy(ob, Anim.nla_tracks_ob)
if ob.data:
if Anim.action_data:
self.action_copy(ob.data, Anim.action_data)
if Anim.nla_tracks_data:
self.nla_copy(ob.data, Anim.nla_tracks_data)
try:
if Anim.action_sk:
self.action_copy(ob.data.shape_keys, Anim.action_sk)
if Anim.nla_tracks_sk:
self.nla_copy(ob.data.shape_keys, Anim.nla_tracks_sk)
except AttributeError:
pass
if is_anim_mat and ob.material_slots:
for i, slot in enumerate(ob.material_slots):
if not (mat := slot.material):
continue
if i in Anim.action_mat:
self.action_copy(mat, Anim.action_mat[i])
if i in Anim.nla_tracks_mat:
self.nla_copy(mat, Anim.nla_tracks_mat[i])
if i in Anim.action_node:
self.action_copy(mat.node_tree, Anim.action_node[i])
if i in Anim.nla_tracks_node:
self.nla_copy(mat.node_tree, Anim.nla_tracks_node[i])
return {"FINISHED"}
def action_copy(self, data, action: Action) -> None:
if not data.animation_data:
data.animation_data_create()
data.animation_data.action = action if self.use_link else action.copy()
def nla_copy(self, data, nla_tracks: NlaTracks) -> None:
if not data.animation_data:
data.animation_data_create()
ob_nla_tracks = data.animation_data.nla_tracks
if ob_nla_tracks:
for track in ob_nla_tracks:
ob_nla_tracks.remove(track)
for track in nla_tracks:
track_new = ob_nla_tracks.new()
track_new.name = track.name
track_new.select = False
for strip in track.strips:
if self.use_link:
strip_new = track_new.strips.new(strip.name, int(strip.frame_start), strip.action)
else:
strip_new = track_new.strips.new(strip.name, int(strip.frame_start), strip.action.copy())
strip_new.name = strip.name
strip_new.select = strip.select
strip_new.frame_start = strip.frame_start
strip_new.frame_end = strip.frame_end
strip_new.extrapolation = strip.extrapolation
strip_new.blend_type = strip.blend_type
strip_new.use_auto_blend = strip.use_auto_blend
strip_new.blend_in = strip.blend_in
strip_new.blend_out = strip.blend_out
strip_new.mute = strip.mute
strip_new.use_reverse = strip.use_reverse
strip_new.action_frame_start = strip.action_frame_start
strip_new.action_frame_end = strip.action_frame_end
strip_new.use_sync_length = strip.use_sync_length
strip_new.repeat = strip.repeat
strip_new.use_animated_influence = strip.use_animated_influence
strip_new.influence = strip.influence
strip_new.use_animated_time_cyclic = strip.use_animated_time_cyclic
strip_new.strip_time = strip.strip_time
class ANIM_OT_animation_copy(_AdCopy, Operator):
bl_label = "Copy Animation"
bl_description = "Copy animation from active to selected objects (can also use this to unlink animation)"
bl_idname = "anim.commotion_animation_copy"
bl_options = {"REGISTER", "UNDO"}
use_link = False
class ANIM_OT_animation_link(_AdCopy, Operator):
bl_label = "Link Animation"
bl_description = "Link animation from active to selected objects"
bl_idname = "anim.commotion_animation_link"
bl_options = {"REGISTER", "UNDO"}
use_link = True
class ANIM_OT_animation_convert(Operator):
bl_label = "Convert to"
bl_description = "Convert action to another type for selected objects"
bl_idname = "anim.commotion_animation_convert"
bl_options = {"REGISTER", "UNDO"}
ad_type: EnumProperty(
name="Animation Data",
description="Animation data type",
items=(
("FCURVES", "F-Curves", "", "IPO_BEZIER", 0),
("STRIPS", "Strips", "", "NLA", 1),
),
)
def execute(self, context):
from . import lib
use_to_strips = self.ad_type == "STRIPS"
for ob in context.selected_objects:
ads = lib.ad_get(ob)
if not ads:
continue
for ad in ads:
if use_to_strips:
if not (ad and ad.action):
continue
nla_tracks = ad.nla_tracks
if not nla_tracks:
nla_tracks.new()
frame_start, frame_end = ad.action.frame_range
strip_new = nla_tracks.active.strips.new("name", int(frame_start), ad.action)
strip_new.frame_start = frame_start
strip_new.frame_end = frame_end
ad.action = None
else:
nla_tracks = ad.nla_tracks
if not nla_tracks:
continue
ad.action = nla_tracks.active.strips[0].action
for track in nla_tracks:
nla_tracks.remove(track)
return {"FINISHED"}