Merge branch 'blender2.7'
[blender.git] / release / scripts / modules / keyingsets_utils.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 # This file defines a set of methods that are useful for various
22 # Relative Keying Set (RKS) related operations, such as: callbacks
23 # for polling, iterator callbacks, and also generate callbacks.
24 # All of these can be used in conjunction with the others.
25
26 __all__ = (
27     "path_add_property",
28     "RKS_POLL_selected_objects",
29     "RKS_POLL_selected_bones",
30     "RKS_POLL_selected_items",
31     "RKS_ITER_selected_objects",
32     "RKS_ITER_selected_bones",
33     "RKS_ITER_selected_item",
34     "RKS_GEN_available",
35     "RKS_GEN_location",
36     "RKS_GEN_rotation",
37     "RKS_GEN_scaling",
38     "RKS_GEN_bendy_bones",
39 )
40
41 import bpy
42
43 ###########################
44 # General Utilities
45
46
47 # Append the specified property name on the the existing path
48 def path_add_property(path, prop):
49     if path:
50         return path + "." + prop
51     else:
52         return prop
53
54 ###########################
55 # Poll Callbacks
56
57
58 # selected objects (active object must be in object mode)
59 def RKS_POLL_selected_objects(ksi, context):
60     ob = context.active_object
61     if ob:
62         return ob.mode == 'OBJECT'
63     else:
64         return bool(context.selected_objects)
65
66
67 # selected bones
68 def RKS_POLL_selected_bones(ksi, context):
69     # we must be in Pose Mode, and there must be some bones selected
70     ob = context.active_object
71     if ob and ob.mode == 'POSE':
72         if context.active_pose_bone or context.selected_pose_bones:
73             return True
74
75     # nothing selected
76     return False
77
78
79 # selected bones or objects
80 def RKS_POLL_selected_items(ksi, context):
81     return (RKS_POLL_selected_bones(ksi, context) or
82             RKS_POLL_selected_objects(ksi, context))
83
84 ###########################
85 # Iterator Callbacks
86
87
88 # all selected objects or pose bones, depending on which we've got
89 def RKS_ITER_selected_item(ksi, context, ks):
90     ob = context.active_object
91     if ob and ob.mode == 'POSE':
92         for bone in context.selected_pose_bones:
93             ksi.generate(context, ks, bone)
94     else:
95         for ob in context.selected_objects:
96             ksi.generate(context, ks, ob)
97
98
99 # all selected objects only
100 def RKS_ITER_selected_objects(ksi, context, ks):
101     for ob in context.selected_objects:
102         ksi.generate(context, ks, ob)
103
104
105 # all seelcted bones only
106 def RKS_ITER_selected_bones(ksi, context, ks):
107     for bone in context.selected_pose_bones:
108         ksi.generate(context, ks, bone)
109
110 ###########################
111 # Generate Callbacks
112
113
114 # 'Available' F-Curves
115 def RKS_GEN_available(ksi, context, ks, data):
116     # try to get the animation data associated with the closest
117     # ID-block to the data (neither of which may exist/be easy to find)
118     id_block = data.id_data
119     adt = getattr(id_block, "animation_data", None)
120
121     # there must also be an active action...
122     if adt is None or adt.action is None:
123         return
124
125     # if we haven't got an ID-block as 'data', try to restrict
126     # paths added to only those which branch off from here
127     # i.e. for bones
128     if id_block != data:
129         basePath = data.path_from_id()
130     else:
131         basePath = None  # this is not needed...
132
133     # for each F-Curve, include a path to key it
134     # NOTE: we don't need to set the group settings here
135     for fcu in adt.action.fcurves:
136         if basePath:
137             if basePath in fcu.data_path:
138                 ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
139         else:
140             ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
141
142 # ------
143
144
145 # get ID block and based ID path for transform generators
146 # private function
147 def get_transform_generators_base_info(data):
148     # ID-block for the data
149     id_block = data.id_data
150
151     # get base path and grouping method/name
152     if isinstance(data, bpy.types.ID):
153         # no path in this case
154         path = ""
155
156         # transform data on ID-blocks directly should get grouped under a
157         # hardcoded label ("Object Transforms") so that they get grouped
158         # consistently when keyframed directly
159         grouping = "Object Transforms"
160     else:
161         # get the path to the ID-block
162         path = data.path_from_id()
163
164         # try to use the name of the data element to group the F-Curve
165         # else fallback on the KeyingSet name
166         grouping = getattr(data, "name", None)
167
168     # return the ID-block and the path
169     return id_block, path, grouping
170
171
172 # Location
173 def RKS_GEN_location(ksi, context, ks, data):
174     # get id-block and path info
175     id_block, base_path, grouping = get_transform_generators_base_info(data)
176
177     # add the property name to the base path
178     path = path_add_property(base_path, "location")
179
180     # add Keying Set entry for this...
181     if grouping:
182         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
183     else:
184         ks.paths.add(id_block, path)
185
186
187 # Rotation
188 def RKS_GEN_rotation(ksi, context, ks, data):
189     # get id-block and path info
190     id_block, base_path, grouping = get_transform_generators_base_info(data)
191
192     # add the property name to the base path
193     #   rotation mode affects the property used
194     if data.rotation_mode == 'QUATERNION':
195         path = path_add_property(base_path, "rotation_quaternion")
196     elif data.rotation_mode == 'AXIS_ANGLE':
197         path = path_add_property(base_path, "rotation_axis_angle")
198     else:
199         path = path_add_property(base_path, "rotation_euler")
200
201     # add Keying Set entry for this...
202     if grouping:
203         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
204     else:
205         ks.paths.add(id_block, path)
206
207
208 # Scaling
209 def RKS_GEN_scaling(ksi, context, ks, data):
210     # get id-block and path info
211     id_block, base_path, grouping = get_transform_generators_base_info(data)
212
213     # add the property name to the base path
214     path = path_add_property(base_path, "scale")
215
216     # add Keying Set entry for this...
217     if grouping:
218         ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
219     else:
220         ks.paths.add(id_block, path)
221
222 # ------
223
224
225 # Property identifiers for Bendy Bones
226 bbone_property_ids = (
227     "bbone_curveinx",
228     "bbone_curveiny",
229     "bbone_curveoutx",
230     "bbone_curveouty",
231
232     "bbone_rollin",
233     "bbone_rollout",
234
235     "bbone_scalein",
236     "bbone_scaleout",
237
238     # NOTE: These are in the nested bone struct
239     # Do it this way to force them to be included
240     # in whatever actions are being keyed here
241     "bone.bbone_in",
242     "bone.bbone_out",
243 )
244
245
246 # Add Keying Set entries for bendy bones
247 def RKS_GEN_bendy_bones(ksi, context, ks, data):
248     # get id-block and path info
249     # NOTE: This assumes that we're dealing with a bone here...
250     id_block, base_path, grouping = get_transform_generators_base_info(data)
251
252     # for each of the bendy bone properties, add a Keying Set entry for it...
253     for propname in bbone_property_ids:
254         # add the property name to the base path
255         path = path_add_property(base_path, propname)
256
257         # add Keying Set entry for this...
258         if grouping:
259             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
260         else:
261             ks.paths.add(id_block, path)