Merge branch 'blender-v2.81-release'
[blender.git] / release / scripts / startup / bl_ui / properties_scene.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 compliant>
20
21 import bpy
22 from bpy.types import (
23     Panel,
24     UIList,
25 )
26
27 from rna_prop_ui import PropertyPanel
28
29 from bl_ui.properties_physics_common import (
30     point_cache_ui,
31     effector_weights_ui,
32 )
33
34
35 class SCENE_UL_keying_set_paths(UIList):
36     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
37         # assert(isinstance(item, bpy.types.KeyingSetPath)
38         kspath = item
39         icon = layout.enum_item_icon(kspath, "id_type", kspath.id_type)
40         if self.layout_type in {'DEFAULT', 'COMPACT'}:
41             # Do not make this one editable in uiList for now...
42             layout.label(text=kspath.data_path, translate=False, icon_value=icon)
43         elif self.layout_type == 'GRID':
44             layout.alignment = 'CENTER'
45             layout.label(text="", icon_value=icon)
46
47
48 class SceneButtonsPanel:
49     bl_space_type = 'PROPERTIES'
50     bl_region_type = 'WINDOW'
51     bl_context = "scene"
52
53
54 class SCENE_PT_scene(SceneButtonsPanel, Panel):
55     bl_label = "Scene"
56
57     def draw(self, context):
58         layout = self.layout
59         layout.use_property_split = True
60         layout.use_property_decorate = False
61
62         scene = context.scene
63
64         layout.prop(scene, "camera")
65         layout.prop(scene, "background_set")
66         layout.prop(scene, "active_clip", text="Active Clip")
67
68
69 class SCENE_PT_unit(SceneButtonsPanel, Panel):
70     bl_label = "Units"
71     bl_options = {'DEFAULT_CLOSED'}
72
73     def draw(self, context):
74         layout = self.layout
75
76         unit = context.scene.unit_settings
77
78         layout.use_property_split = True
79         layout.use_property_decorate = False
80
81         layout.prop(unit, "system")
82
83         col = layout.column()
84         col.enabled = unit.system != 'NONE'
85         col.prop(unit, "scale_length")
86         col.prop(unit, "use_separate")
87
88         col = layout.column()
89         col.prop(unit, "system_rotation", text="Rotation")
90         subcol = col.column()
91         subcol.enabled = unit.system != 'NONE'
92         subcol.prop(unit, "length_unit", text="Length")
93         subcol.prop(unit, "mass_unit", text="Mass")
94         subcol.prop(unit, "time_unit", text="Time")
95
96
97 class SceneKeyingSetsPanel:
98
99     @staticmethod
100     def draw_keyframing_settings(context, layout, ks, ksp):
101         SceneKeyingSetsPanel._draw_keyframing_setting(
102             context, layout, ks, ksp, "Needed",
103             "use_insertkey_override_needed", "use_insertkey_needed",
104             userpref_fallback="use_keyframe_insert_needed",
105         )
106         SceneKeyingSetsPanel._draw_keyframing_setting(
107             context, layout, ks, ksp, "Visual",
108             "use_insertkey_override_visual", "use_insertkey_visual",
109             userpref_fallback="use_visual_keying",
110         )
111         SceneKeyingSetsPanel._draw_keyframing_setting(
112             context, layout, ks, ksp, "XYZ to RGB",
113             "use_insertkey_override_xyz_to_rgb", "use_insertkey_xyz_to_rgb",
114         )
115
116     @staticmethod
117     def _draw_keyframing_setting(context, layout, ks, ksp, label, toggle_prop, prop, userpref_fallback=None):
118         if ksp:
119             item = ksp
120
121             if getattr(ks, toggle_prop):
122                 owner = ks
123                 propname = prop
124             else:
125                 owner = context.preferences.edit
126                 if userpref_fallback:
127                     propname = userpref_fallback
128                 else:
129                     propname = prop
130         else:
131             item = ks
132
133             owner = context.preferences.edit
134             if userpref_fallback:
135                 propname = userpref_fallback
136             else:
137                 propname = prop
138
139         row = layout.row(align=True)
140
141         subrow = row.row(align=True)
142         subrow.active = getattr(item, toggle_prop)
143
144         if subrow.active:
145             subrow.prop(item, prop, text=label)
146         else:
147             subrow.prop(owner, propname, text=label)
148
149         row.prop(item, toggle_prop, text="", icon='STYLUS_PRESSURE', toggle=True)  # XXX: needs dedicated icon
150
151
152 class SCENE_PT_keying_sets(SceneButtonsPanel, SceneKeyingSetsPanel, Panel):
153     bl_label = "Keying Sets"
154     bl_options = {'DEFAULT_CLOSED'}
155
156     def draw(self, context):
157         layout = self.layout
158
159         scene = context.scene
160
161         row = layout.row()
162
163         col = row.column()
164         col.template_list("UI_UL_list", "keying_sets", scene, "keying_sets", scene.keying_sets, "active_index", rows=1)
165
166         col = row.column(align=True)
167         col.operator("anim.keying_set_add", icon='ADD', text="")
168         col.operator("anim.keying_set_remove", icon='REMOVE', text="")
169
170         layout.use_property_split = True
171         layout.use_property_decorate = False  # No animation.
172
173         flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
174
175         ks = scene.keying_sets.active
176         if ks and ks.is_path_absolute:
177             col = flow.column()
178             col.prop(ks, "bl_description")
179
180             subcol = flow.column()
181             subcol.operator_context = 'INVOKE_DEFAULT'
182             subcol.operator("anim.keying_set_export", text="Export to File").filepath = "keyingset.py"
183
184
185 class SCENE_PT_keyframing_settings(SceneButtonsPanel, SceneKeyingSetsPanel, Panel):
186     bl_label = "Keyframing Settings"
187     bl_parent_id = "SCENE_PT_keying_sets"
188
189     @classmethod
190     def poll(cls, context):
191         ks = context.scene.keying_sets.active
192         return (ks and ks.is_path_absolute)
193
194     def draw(self, context):
195         layout = self.layout
196         layout.use_property_split = True
197         layout.use_property_decorate = False  # No animation.
198
199         scene = context.scene
200         ks = scene.keying_sets.active
201
202         flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True)
203
204         col = flow.column(align=True)
205         col.alignment = 'RIGHT'
206         col.label(text="General Override")
207
208         self.draw_keyframing_settings(context, col, ks, None)
209
210         ksp = ks.paths.active
211         if ksp:
212             col.separator()
213
214             col = flow.column(align=True)
215             col.alignment = 'RIGHT'
216             col.label(text="Active Set Override")
217
218             self.draw_keyframing_settings(context, col, ks, ksp)
219
220
221 class SCENE_PT_keying_set_paths(SceneButtonsPanel, SceneKeyingSetsPanel, Panel):
222     bl_label = "Active Keying Set"
223     bl_parent_id = "SCENE_PT_keying_sets"
224
225     @classmethod
226     def poll(cls, context):
227         ks = context.scene.keying_sets.active
228         return (ks and ks.is_path_absolute)
229
230     def draw(self, context):
231         layout = self.layout
232
233         scene = context.scene
234         ks = scene.keying_sets.active
235
236         row = layout.row()
237         row.label(text="Paths:")
238
239         row = layout.row()
240
241         col = row.column()
242         col.template_list("SCENE_UL_keying_set_paths", "", ks, "paths", ks.paths, "active_index", rows=1)
243
244         col = row.column(align=True)
245         col.operator("anim.keying_set_path_add", icon='ADD', text="")
246         col.operator("anim.keying_set_path_remove", icon='REMOVE', text="")
247
248         # TODO: 1) the template_any_ID needs to be fixed for the text alignment.
249         #       2) use_property_decorate has to properly skip the non animatable properties.
250         #          Properties affected with needless draw:
251         #          group_method, template_any_ID dropdown, use_entire_array
252
253         layout.use_property_split = True
254         layout.use_property_decorate = False  # No animation (remove this later on).
255
256         flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=True)
257
258         ksp = ks.paths.active
259         if ksp:
260             col = flow.column(align=True)
261             col.alignment = 'RIGHT'
262
263             col.template_any_ID(ksp, "id", "id_type", text="Target ID-Block")
264
265             col.separator()
266
267             col.template_path_builder(ksp, "data_path", ksp.id, text="Data Path")
268
269             col = flow.column()
270
271             col.prop(ksp, "use_entire_array", text="Array All Items")
272
273             if not ksp.use_entire_array:
274                 col.prop(ksp, "array_index", text="Index")
275
276             col.separator()
277
278             col.prop(ksp, "group_method", text="F-Curve Grouping")
279             if ksp.group_method == 'NAMED':
280                 col.prop(ksp, "group")
281
282
283 class SCENE_PT_audio(SceneButtonsPanel, Panel):
284     bl_label = "Audio"
285     bl_options = {'DEFAULT_CLOSED'}
286
287     def draw(self, context):
288         layout = self.layout
289         layout.use_property_split = True
290
291         scene = context.scene
292         rd = context.scene.render
293         ffmpeg = rd.ffmpeg
294
295         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
296
297         col = flow.column()
298         col.prop(scene, "audio_volume")
299
300         col.separator()
301
302         col.prop(scene, "audio_distance_model")
303         col.prop(ffmpeg, "audio_channels")
304
305         col.separator()
306
307         col = flow.column()
308         col.prop(ffmpeg, "audio_mixrate", text="Sample Rate")
309
310         col.separator()
311
312         col = col.column(align=True)
313         col.prop(scene, "audio_doppler_speed", text="Doppler Speed")
314         col.prop(scene, "audio_doppler_factor", text="Doppler Factor")
315
316         col.separator()
317
318         layout.operator("sound.bake_animation")
319
320
321 class SCENE_PT_physics(SceneButtonsPanel, Panel):
322     bl_label = "Gravity"
323     bl_options = {'DEFAULT_CLOSED'}
324
325     def draw_header(self, context):
326         self.layout.prop(context.scene, "use_gravity", text="")
327
328     def draw(self, context):
329         layout = self.layout
330         layout.use_property_split = True
331
332         scene = context.scene
333
334         layout.active = scene.use_gravity
335
336         layout.prop(scene, "gravity")
337
338
339 class SCENE_PT_rigid_body_world(SceneButtonsPanel, Panel):
340     bl_label = "Rigid Body World"
341     bl_options = {'DEFAULT_CLOSED'}
342
343     def draw_header(self, context):
344         scene = context.scene
345         rbw = scene.rigidbody_world
346         if rbw is not None:
347             self.layout.prop(rbw, "enabled", text="")
348
349     def draw(self, context):
350         layout = self.layout
351         layout.use_property_split = True
352
353         scene = context.scene
354         rbw = scene.rigidbody_world
355
356         if rbw is None:
357             layout.operator("rigidbody.world_add")
358         else:
359             layout.operator("rigidbody.world_remove")
360
361
362 class RigidBodySubPanel(SceneButtonsPanel):
363     bl_parent_id = "SCENE_PT_rigid_body_world"
364
365     @classmethod
366     def poll(cls, context):
367         scene = context.scene
368         return scene and scene.rigidbody_world
369
370
371 class SCENE_PT_rigid_body_world_settings(RigidBodySubPanel, Panel):
372     bl_label = "Settings"
373
374     def draw(self, context):
375         layout = self.layout
376         layout.use_property_split = True
377
378         scene = context.scene
379         rbw = scene.rigidbody_world
380
381         if rbw:
382             flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
383
384             col = flow.column()
385             col.active = rbw.enabled
386
387             col = col.column()
388             col.prop(rbw, "collection")
389             col.prop(rbw, "constraints")
390
391             col = col.column()
392             col.prop(rbw, "time_scale", text="Speed")
393
394             col = flow.column()
395             col.active = rbw.enabled
396             col.prop(rbw, "use_split_impulse")
397
398             col = col.column()
399             col.prop(rbw, "steps_per_second", text="Steps Per Second")
400             col.prop(rbw, "solver_iterations", text="Solver Iterations")
401
402
403 class SCENE_PT_rigid_body_cache(RigidBodySubPanel, Panel):
404     bl_label = "Cache"
405     bl_options = {'DEFAULT_CLOSED'}
406
407     def draw(self, context):
408         scene = context.scene
409         rbw = scene.rigidbody_world
410
411         point_cache_ui(self, rbw.point_cache, rbw.point_cache.is_baked is False and rbw.enabled, 'RIGID_BODY')
412
413
414 class SCENE_PT_rigid_body_field_weights(RigidBodySubPanel, Panel):
415     bl_label = "Field Weights"
416     bl_parent_id = "SCENE_PT_rigid_body_world"
417     bl_options = {'DEFAULT_CLOSED'}
418
419     def draw(self, context):
420         scene = context.scene
421         rbw = scene.rigidbody_world
422
423         effector_weights_ui(self, rbw.effector_weights, 'RIGID_BODY')
424
425
426 class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, Panel):
427     _context_path = "scene"
428     _property_type = bpy.types.Scene
429
430
431 classes = (
432     SCENE_UL_keying_set_paths,
433     SCENE_PT_scene,
434     SCENE_PT_unit,
435     SCENE_PT_physics,
436     SCENE_PT_keying_sets,
437     SCENE_PT_keying_set_paths,
438     SCENE_PT_keyframing_settings,
439     SCENE_PT_audio,
440     SCENE_PT_rigid_body_world,
441     SCENE_PT_rigid_body_world_settings,
442     SCENE_PT_rigid_body_cache,
443     SCENE_PT_rigid_body_field_weights,
444     SCENE_PT_custom_props,
445 )
446
447 if __name__ == "__main__":  # only for live edit.
448     from bpy.utils import register_class
449     for cls in classes:
450         register_class(cls)