3181dab7b8a5fad0ca44bb324a5d97dbf3f994bb
[blender.git] / release / scripts / startup / bl_ui / properties_data_camera.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 import bpy
21 from bpy.types import Panel
22 from rna_prop_ui import PropertyPanel
23 from bl_ui.utils import PresetPanel
24
25
26 class CameraButtonsPanel:
27     bl_space_type = 'PROPERTIES'
28     bl_region_type = 'WINDOW'
29     bl_context = "data"
30
31     @classmethod
32     def poll(cls, context):
33         engine = context.engine
34         return context.camera and (engine in cls.COMPAT_ENGINES)
35
36
37 class CAMERA_PT_presets(PresetPanel, Panel):
38     bl_label = "Camera Presets"
39     preset_subdir = "camera"
40     preset_operator = "script.execute_preset"
41     preset_add_operator = "camera.preset_add"
42     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
43
44
45 class SAFE_AREAS_PT_presets(PresetPanel, Panel):
46     bl_label = "Camera Presets"
47     preset_subdir = "safe_areas"
48     preset_operator = "script.execute_preset"
49     preset_add_operator = "safe_areas.preset_add"
50     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
51
52
53 class DATA_PT_context_camera(CameraButtonsPanel, Panel):
54     bl_label = ""
55     bl_options = {'HIDE_HEADER'}
56     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
57
58     def draw(self, context):
59         layout = self.layout
60
61         ob = context.object
62         cam = context.camera
63         space = context.space_data
64
65         if ob:
66             layout.template_ID(ob, "data")
67         elif cam:
68             layout.template_ID(space, "pin_id")
69
70
71 class DATA_PT_lens(CameraButtonsPanel, Panel):
72     bl_label = "Lens"
73     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
74
75     def draw(self, context):
76         layout = self.layout
77         layout.use_property_split = True
78
79         cam = context.camera
80
81         layout.prop(cam, "type")
82
83         col = layout.column()
84         col.separator()
85
86         if cam.type == 'PERSP':
87             col = layout.column()
88             if cam.lens_unit == 'MILLIMETERS':
89                 col.prop(cam, "lens")
90             elif cam.lens_unit == 'FOV':
91                 col.prop(cam, "angle")
92             col.prop(cam, "lens_unit")
93
94         elif cam.type == 'ORTHO':
95             col.prop(cam, "ortho_scale")
96
97         elif cam.type == 'PANO':
98             engine = context.engine
99             if engine == 'CYCLES':
100                 ccam = cam.cycles
101                 col.prop(ccam, "panorama_type")
102                 if ccam.panorama_type == 'FISHEYE_EQUIDISTANT':
103                     col.prop(ccam, "fisheye_fov")
104                 elif ccam.panorama_type == 'FISHEYE_EQUISOLID':
105                     col.prop(ccam, "fisheye_lens", text="Lens")
106                     col.prop(ccam, "fisheye_fov")
107                 elif ccam.panorama_type == 'EQUIRECTANGULAR':
108                     sub = col.column(align=True)
109                     sub.prop(ccam, "latitude_min", text="Latitude Min")
110                     sub.prop(ccam, "latitude_max", text="Max")
111                     sub = col.column(align=True)
112                     sub.prop(ccam, "longitude_min", text="Longitude Min")
113                     sub.prop(ccam, "longitude_max", text="Max")
114             elif engine in {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}:
115                 if cam.lens_unit == 'MILLIMETERS':
116                     col.prop(cam, "lens")
117                 elif cam.lens_unit == 'FOV':
118                     col.prop(cam, "angle")
119                 col.prop(cam, "lens_unit")
120
121         col = layout.column()
122         col.separator()
123
124         sub = col.column(align=True)
125         sub.prop(cam, "shift_x", text="Shift X")
126         sub.prop(cam, "shift_y", text="Y")
127
128         col.separator()
129         sub = col.column(align=True)
130         sub.prop(cam, "clip_start", text="Clip Start")
131         sub.prop(cam, "clip_end", text="End")
132
133
134 class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel):
135     bl_label = "Stereoscopy"
136     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
137
138     @classmethod
139     def poll(cls, context):
140         render = context.scene.render
141         return (super().poll(context) and render.use_multiview and
142                 render.views_format == 'STEREO_3D')
143
144     def draw(self, context):
145         layout = self.layout
146         layout.use_property_split = True
147
148         render = context.scene.render
149         st = context.camera.stereo
150         cam = context.camera
151
152         is_spherical_stereo = cam.type != 'ORTHO' and render.use_spherical_stereo
153         use_spherical_stereo = is_spherical_stereo and st.use_spherical_stereo
154
155         layout.prop(st, "convergence_mode")
156
157         col = layout.column()
158         sub = col.column()
159         sub.active = st.convergence_mode != 'PARALLEL'
160         sub.prop(st, "convergence_distance")
161
162         col.prop(st, "interocular_distance")
163
164         if is_spherical_stereo:
165             col.separator()
166             col.prop(st, "use_spherical_stereo")
167             sub = col.column()
168             sub.active = st.use_spherical_stereo
169             sub.prop(st, "use_pole_merge")
170
171             sub = col.column(align=True)
172             sub.active = st.use_pole_merge
173             sub.prop(st, "pole_merge_angle_from", text="Pole Merge Angle Start")
174             sub.prop(st, "pole_merge_angle_to", text="End")
175
176         col = layout.column()
177         col.active = not use_spherical_stereo
178         col.separator()
179         col.prop(st, "pivot")
180
181
182 class DATA_PT_camera(CameraButtonsPanel, Panel):
183     bl_label = "Camera"
184     bl_options = {'DEFAULT_CLOSED'}
185     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
186
187     def draw_header_preset(self, _context):
188         CAMERA_PT_presets.draw_panel_header(self.layout)
189
190     def draw(self, context):
191         layout = self.layout
192
193         cam = context.camera
194
195         layout.use_property_split = True
196
197         col = layout.column()
198         col.prop(cam, "sensor_fit")
199
200         if cam.sensor_fit == 'AUTO':
201             col.prop(cam, "sensor_width", text="Size")
202         else:
203             sub = col.column(align=True)
204             sub.active = cam.sensor_fit == 'HORIZONTAL'
205             sub.prop(cam, "sensor_width", text="Width")
206
207             sub = col.column(align=True)
208             sub.active = cam.sensor_fit == 'VERTICAL'
209             sub.prop(cam, "sensor_height", text="Height")
210
211
212 class DATA_PT_camera_dof(CameraButtonsPanel, Panel):
213     bl_label = "Depth of Field"
214     bl_options = {'DEFAULT_CLOSED'}
215     COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
216
217     def draw_header(self, context):
218         cam = context.camera
219         dof = cam.dof
220         self.layout.prop(dof, "use_dof", text="")
221
222
223     def draw(self, context):
224         layout = self.layout
225         layout.use_property_split = True
226
227         cam = context.camera
228         dof = cam.dof
229         layout.active = dof.use_dof
230
231         col = layout.column()
232         col.prop(dof, "focus_object", text="Focus on Object")
233         sub = col.column()
234         sub.active = (dof.focus_object is None)
235         sub.prop(dof, "focus_distance", text="Focus Distance")
236
237
238 class DATA_PT_camera_dof_aperture(CameraButtonsPanel, Panel):
239     bl_label = "Aperture"
240     bl_parent_id = "DATA_PT_camera_dof"
241     COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
242
243     def draw(self, context):
244         layout = self.layout
245         layout.use_property_split = True
246
247         cam = context.camera
248         dof = cam.dof
249         layout.active = dof.use_dof
250
251         flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
252
253         col = flow.column()
254         col.prop(dof, "aperture_fstop")
255
256         col = flow.column()
257         col.prop(dof, "aperture_blades")
258         col.prop(dof, "aperture_rotation")
259         col.prop(dof, "aperture_ratio")
260
261
262 class DATA_PT_camera_background_image(CameraButtonsPanel, Panel):
263     bl_label = "Background Images"
264     bl_options = {'DEFAULT_CLOSED'}
265     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
266
267     def draw_header(self, context):
268         cam = context.camera
269
270         self.layout.prop(cam, "show_background_images", text="")
271
272     def draw(self, context):
273         layout = self.layout
274         layout.use_property_split = True
275         layout.use_property_decorate = False
276
277         cam = context.camera
278         use_multiview = context.scene.render.use_multiview
279
280         col = layout.column()
281         col.operator("view3d.background_image_add", text="Add Image")
282
283         for i, bg in enumerate(cam.background_images):
284             layout.active = cam.show_background_images
285             box = layout.box()
286             row = box.row(align=True)
287             row.prop(bg, "show_expanded", text="", emboss=False)
288             if bg.source == 'IMAGE' and bg.image:
289                 row.prop(bg.image, "name", text="", emboss=False)
290             elif bg.source == 'MOVIE_CLIP' and bg.clip:
291                 row.prop(bg.clip, "name", text="", emboss=False)
292             elif bg.source and bg.use_camera_clip:
293                 row.label(text="Camera Clip")
294             else:
295                 row.label(text="Not Set")
296
297             row.prop(
298                 bg,
299                 "show_background_image",
300                 text="",
301                 emboss=False,
302                 icon='RESTRICT_VIEW_OFF' if bg.show_background_image else 'RESTRICT_VIEW_ON',
303             )
304
305             row.operator("view3d.background_image_remove", text="", emboss=False, icon='X').index = i
306
307             if bg.show_expanded:
308                 row = box.row()
309                 row.prop(bg, "source", expand=True)
310
311                 has_bg = False
312                 if bg.source == 'IMAGE':
313                     row = box.row()
314                     row.template_ID(bg, "image", open="image.open")
315                     if bg.image is not None:
316                         box.template_image(bg, "image", bg.image_user, compact=True)
317                         has_bg = True
318
319                         if use_multiview:
320                             box.prop(bg.image, "use_multiview")
321
322                             column = box.column()
323                             column.active = bg.image.use_multiview
324
325                             column.label(text="Views Format:")
326                             column.row().prop(bg.image, "views_format", expand=True)
327
328                             sub = column.box()
329                             sub.active = bg.image.views_format == 'STEREO_3D'
330                             sub.template_image_stereo_3d(bg.image.stereo_3d_format)
331
332                 elif bg.source == 'MOVIE_CLIP':
333                     box.prop(bg, "use_camera_clip")
334
335                     column = box.column()
336                     column.active = not bg.use_camera_clip
337                     column.template_ID(bg, "clip", open="clip.open")
338
339                     if bg.clip:
340                         column.template_movieclip(bg, "clip", compact=True)
341
342                     if bg.use_camera_clip or bg.clip:
343                         has_bg = True
344
345                     column = box.column()
346                     column.active = has_bg
347                     column.prop(bg.clip_user, "use_render_undistorted")
348                     column.prop(bg.clip_user, "proxy_render_size")
349
350                 if has_bg:
351                     col = box.column()
352                     col.prop(bg, "alpha", slider=True)
353                     col.row().prop(bg, "display_depth", expand=True)
354
355                     col.row().prop(bg, "frame_method", expand=True)
356
357                     row = box.row()
358                     row.prop(bg, "offset")
359
360                     col = box.column()
361                     col.prop(bg, "rotation")
362                     col.prop(bg, "scale")
363
364                     col.prop(bg, "use_flip_x")
365                     col.prop(bg, "use_flip_y")
366
367
368 class DATA_PT_camera_display(CameraButtonsPanel, Panel):
369     bl_label = "Viewport Display"
370     bl_options = {'DEFAULT_CLOSED'}
371     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
372
373     def draw(self, context):
374         layout = self.layout
375         layout.use_property_split = True
376
377         cam = context.camera
378
379         col = layout.column(align=True)
380
381         col.separator()
382
383         col.prop(cam, "display_size", text="Size")
384
385         col.separator()
386
387         flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
388
389         col = flow.column()
390         col.prop(cam, "show_limits", text="Limits")
391         col = flow.column()
392         col.prop(cam, "show_mist", text="Mist")
393         col = flow.column()
394         col.prop(cam, "show_sensor", text="Sensor")
395         col = flow.column()
396         col.prop(cam, "show_name", text="Name")
397
398
399 class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel):
400     bl_label = "Composition Guides"
401     bl_parent_id = "DATA_PT_camera_display"
402     bl_options = {'DEFAULT_CLOSED'}
403     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
404
405     def draw(self, context):
406         layout = self.layout
407         layout.use_property_split = True
408
409         cam = context.camera
410
411         flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
412
413         col = flow.column()
414         col.prop(cam, "show_composition_center")
415         col = flow.column()
416         col.prop(cam, "show_composition_center_diagonal")
417         col = flow.column()
418         col.prop(cam, "show_composition_thirds")
419         col = flow.column()
420         col.prop(cam, "show_composition_golden")
421         col = flow.column()
422         col.prop(cam, "show_composition_golden_tria_a")
423         col = flow.column()
424         col.prop(cam, "show_composition_golden_tria_b")
425         col = flow.column()
426         col.prop(cam, "show_composition_harmony_tri_a")
427         col = flow.column()
428         col.prop(cam, "show_composition_harmony_tri_b")
429
430
431 class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel):
432     bl_label = "Passepartout"
433     bl_parent_id = "DATA_PT_camera_display"
434     bl_options = {'DEFAULT_CLOSED'}
435     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
436
437     def draw_header(self, context):
438         cam = context.camera
439
440         self.layout.prop(cam, "show_passepartout", text="")
441
442     def draw(self, context):
443         layout = self.layout
444         layout.use_property_split = True
445
446         cam = context.camera
447
448         layout.active = cam.show_passepartout
449         layout.prop(cam, "passepartout_alpha", text="Opacity", slider=True)
450
451
452 class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel):
453     bl_label = "Safe Areas"
454     bl_options = {'DEFAULT_CLOSED'}
455     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
456
457     def draw_header(self, context):
458         cam = context.camera
459
460         self.layout.prop(cam, "show_safe_areas", text="")
461
462     def draw_header_preset(self, _context):
463         SAFE_AREAS_PT_presets.draw_panel_header(self.layout)
464
465     def draw(self, context):
466         layout = self.layout
467         safe_data = context.scene.safe_areas
468         camera = context.camera
469
470         layout.use_property_split = True
471
472         layout.active = camera.show_safe_areas
473
474         col = layout.column()
475
476         sub = col.column()
477         sub.prop(safe_data, "title", slider=True)
478         sub.prop(safe_data, "action", slider=True)
479
480
481 class DATA_PT_camera_safe_areas_center_cut(CameraButtonsPanel, Panel):
482     bl_label = "Center-Cut Safe Areas"
483     bl_parent_id = "DATA_PT_camera_safe_areas"
484     bl_options = {'DEFAULT_CLOSED'}
485     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
486
487     def draw_header(self, context):
488         cam = context.camera
489
490         layout = self.layout
491         layout.active = cam.show_safe_areas
492         layout.prop(cam, "show_safe_center", text="")
493
494     def draw(self, context):
495         layout = self.layout
496         safe_data = context.scene.safe_areas
497         camera = context.camera
498
499         layout.use_property_split = True
500
501         layout.active = camera.show_safe_areas and camera.show_safe_center
502
503         col = layout.column()
504         col.prop(safe_data, "title_center", slider=True)
505
506
507 class DATA_PT_custom_props_camera(CameraButtonsPanel, PropertyPanel, Panel):
508     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
509     _context_path = "object.data"
510     _property_type = bpy.types.Camera
511
512
513 def draw_display_safe_settings(layout, safe_data, settings):
514     show_safe_areas = settings.show_safe_areas
515     show_safe_center = settings.show_safe_center
516
517     layout.use_property_split = True
518
519     col = layout.column()
520     col.active = show_safe_areas
521
522     sub = col.column()
523     sub.prop(safe_data, "title", slider=True)
524     sub.prop(safe_data, "action", slider=True)
525
526     col.separator()
527
528     col.prop(settings, "show_safe_center", text="Center-Cut Safe Areas")
529
530     sub = col.column()
531     sub.active = show_safe_areas and show_safe_center
532     sub.prop(safe_data, "title_center", slider=True)
533     sub.prop(safe_data, "action_center", slider=True)
534
535
536 classes = (
537     CAMERA_PT_presets,
538     SAFE_AREAS_PT_presets,
539     DATA_PT_context_camera,
540     DATA_PT_lens,
541     DATA_PT_camera_dof,
542     DATA_PT_camera_dof_aperture,
543     DATA_PT_camera,
544     DATA_PT_camera_stereoscopy,
545     DATA_PT_camera_safe_areas,
546     DATA_PT_camera_safe_areas_center_cut,
547     DATA_PT_camera_background_image,
548     DATA_PT_camera_display,
549     DATA_PT_camera_display_composition_guides,
550     DATA_PT_camera_display_passepartout,
551     DATA_PT_custom_props_camera,
552 )
553
554 if __name__ == "__main__":  # only for live edit.
555     from bpy.utils import register_class
556     for cls in classes:
557         register_class(cls)