Merging r40493 through r40509 from trunk into soc-2011-tomato
[blender.git] / release / scripts / startup / bl_operators / anim.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 if "bpy" in locals():
22     import imp
23     if "anim_utils" in locals():
24         imp.reload(anim_utils)
25
26 import bpy
27 from bpy.types import Operator
28 from bpy.props import (IntProperty,
29                        BoolProperty,
30                        EnumProperty,
31                        StringProperty,
32                        )
33
34
35 class ANIM_OT_keying_set_export(Operator):
36     "Export Keying Set to a python script"
37     bl_idname = "anim.keying_set_export"
38     bl_label = "Export Keying Set..."
39
40     filepath = StringProperty(
41             name="File Path",
42             )
43     filter_folder = BoolProperty(
44             name="Filter folders",
45             default=True,
46             options={'HIDDEN'},
47             )
48     filter_text = BoolProperty(
49             name="Filter text",
50             default=True,
51             options={'HIDDEN'},
52             )
53     filter_python = BoolProperty(
54             name="Filter python",
55             default=True,
56             options={'HIDDEN'},
57             )
58
59     def execute(self, context):
60         if not self.filepath:
61             raise Exception("Filepath not set")
62
63         f = open(self.filepath, "w")
64         if not f:
65             raise Exception("Could not open file")
66
67         scene = context.scene
68         ks = scene.keying_sets.active
69
70         f.write("# Keying Set: %s\n" % ks.name)
71
72         f.write("import bpy\n\n")
73         # XXX, why not current scene?
74         f.write("scene= bpy.data.scenes[0]\n\n")
75
76         # Add KeyingSet and set general settings
77         f.write("# Keying Set Level declarations\n")
78         f.write("ks= scene.keying_sets.new(name=\"%s\")\n" % ks.name)
79
80         if not ks.is_path_absolute:
81             f.write("ks.is_path_absolute = False\n")
82         f.write("\n")
83
84         f.write("ks.bl_options = %r\n" % ks.bl_options)
85         f.write("\n")
86
87         # --------------------------------------------------------
88         # generate and write set of lookups for id's used in paths
89
90         # cache for syncing ID-blocks to bpy paths + shorthands
91         id_to_paths_cache = {}
92
93         for ksp in ks.paths:
94             if ksp.id is None:
95                 continue
96             if ksp.id in id_to_paths_cache:
97                 continue
98
99             """
100             - idtype_list is used to get the list of id-datablocks from
101               bpy.data.* since this info isn't available elsewhere
102             - id.bl_rna.name gives a name suitable for UI,
103               with a capitalised first letter, but we need
104               the plural form that's all lower case
105             """
106
107             idtype_list = ksp.id.bl_rna.name.lower() + "s"
108             id_bpy_path = "bpy.data.%s[\"%s\"]" % (idtype_list, ksp.id.name)
109
110             # shorthand ID for the ID-block (as used in the script)
111             short_id = "id_%d" % len(id_to_paths_cache)
112
113             # store this in the cache now
114             id_to_paths_cache[ksp.id] = [short_id, id_bpy_path]
115
116         f.write("# ID's that are commonly used\n")
117         for id_pair in id_to_paths_cache.values():
118             f.write("%s = %s\n" % (id_pair[0], id_pair[1]))
119         f.write("\n")
120
121         # write paths
122         f.write("# Path Definitions\n")
123         for ksp in ks.paths:
124             f.write("ksp = ks.paths.add(")
125
126             # id-block + data_path
127             if ksp.id:
128                 # find the relevant shorthand from the cache
129                 id_bpy_path = id_to_paths_cache[ksp.id][0]
130             else:
131                 id_bpy_path = "None"  # XXX...
132             f.write("%s, '%s'" % (id_bpy_path, ksp.data_path))
133
134             # array index settings (if applicable)
135             if ksp.use_entire_array:
136                 f.write(", index=-1")
137             else:
138                 f.write(", index=%d" % ksp.array_index)
139
140             # grouping settings (if applicable)
141             # NOTE: the current default is KEYINGSET, but if this changes,
142             # change this code too
143             if ksp.group_method == 'NAMED':
144                 f.write(", group_method='%s', group_name=\"%s\"" %
145                         (ksp.group_method, ksp.group))
146             elif ksp.group_method != 'KEYINGSET':
147                 f.write(", group_method='%s'" % ksp.group_method)
148
149             # finish off
150             f.write(")\n")
151
152         f.write("\n")
153         f.close()
154
155         return {'FINISHED'}
156
157     def invoke(self, context, event):
158         wm = context.window_manager
159         wm.fileselect_add(self)
160         return {'RUNNING_MODAL'}
161
162
163 class BakeAction(Operator):
164     """Bake animation to an Action"""
165     bl_idname = "nla.bake"
166     bl_label = "Bake Action"
167     bl_options = {'REGISTER', 'UNDO'}
168
169     frame_start = IntProperty(
170             name="Start Frame",
171             description="Start frame for baking",
172             min=0, max=300000,
173             default=1,
174             )
175     frame_end = IntProperty(
176             name="End Frame",
177             description="End frame for baking",
178             min=1, max=300000,
179             default=250,
180             )
181     step = IntProperty(
182             name="Frame Step",
183             description="Frame Step",
184             min=1, max=120,
185             default=1,
186             )
187     only_selected = BoolProperty(
188             name="Only Selected",
189             default=True,
190             )
191     clear_consraints = BoolProperty(
192             name="Clear Constraints",
193             default=False,
194             )
195     bake_types = EnumProperty(
196             name="Bake Data",
197             options={'ENUM_FLAG'},
198             items=(('POSE', "Pose", ""),
199                    ('OBJECT', "Object", ""),
200                    ),
201             default={'POSE'},
202             )
203
204     def execute(self, context):
205
206         from bpy_extras import anim_utils
207
208         action = anim_utils.bake_action(self.frame_start,
209                                         self.frame_end,
210                                         self.step,
211                                         self.only_selected,
212                                         'POSE' in self.bake_types,
213                                         'OBJECT' in self.bake_types,
214                                         self.clear_consraints,
215                                         True,
216                                  )
217
218         if action is None:
219             self.report({'INFO'}, "Nothing to bake")
220             return {'CANCELLED'}
221
222         return {'FINISHED'}
223
224     def invoke(self, context, event):
225         wm = context.window_manager
226         return wm.invoke_props_dialog(self)
227
228
229 class ClearUselessActions(Operator):
230     """Mark actions with no F-Curves for deletion after save+reload of """ \
231     """file preserving "action libraries"""
232     bl_idname = "anim.clear_useless_actions"
233     bl_label = "Clear Useless Actions"
234     bl_options = {'REGISTER', 'UNDO'}
235
236     only_unused = BoolProperty(name="Only Unused",
237             description="Only unused (Fake User only) actions get considered",
238             default=True)
239
240     @classmethod
241     def poll(cls, context):
242         return len(bpy.data.actions) != 0
243
244     def execute(self, context):
245         removed = 0
246
247         for action in bpy.data.actions:
248             # if only user is "fake" user...
249             if ((self.only_unused is False) or
250                 (action.use_fake_user and action.users == 1)):
251
252                 # if it has F-Curves, then it's a "action library"
253                 # (i.e. walk, wave, jump, etc.)
254                 # and should be left alone as that's what fake users are for!
255                 if not action.fcurves:
256                     # mark action for deletion
257                     action.user_clear()
258                     removed += 1
259
260         self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions"
261                               % removed)
262         return {'FINISHED'}
263
264
265 class UpdateAnimData(Operator):
266     """Update data paths from 2.56 and previous versions, """ \
267     """modifying data paths of drivers and fcurves"""
268     bl_idname = "anim.update_data_paths"
269     bl_label = "Update Animation Data"
270
271     def execute(self, context):
272         import animsys_refactor
273         animsys_refactor.update_data_paths(animsys_refactor.data_2_56_to_2_59)
274         return {'FINISHED'}