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