Export_3ds: Improved distance cue node search
[blender-addons.git] / render_povray / scenography_gui.py
blob15c2919fdecfe230e4211032671f3397f74239b2
1 # SPDX-FileCopyrightText: 2021-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """User interface to camera frame, optics distortions, and environment
7 with world, sky, atmospheric effects such as rainbows or smoke """
9 import bpy
10 from bpy.utils import register_class, unregister_class
11 from bpy.types import Operator, Menu, Panel
12 from bl_operators.presets import AddPresetBase
14 from bl_ui import properties_data_camera
16 for member in dir(properties_data_camera):
17 subclass = getattr(properties_data_camera, member)
18 if hasattr(subclass, "COMPAT_ENGINES"):
19 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
20 del properties_data_camera
22 # -------- Use only a subset of the world panels
23 # from bl_ui import properties_world
25 # # TORECREATE##DEPRECATED#properties_world.WORLD_PT_preview.COMPAT_ENGINES.add('POVRAY_RENDER')
26 # properties_world.WORLD_PT_context_world.COMPAT_ENGINES.add('POVRAY_RENDER')
27 # # TORECREATE##DEPRECATED#properties_world.WORLD_PT_world.COMPAT_ENGINES.add('POVRAY_RENDER')
28 # del properties_world
30 # -------- #
31 # Physics Main wrapping every class 'as is'
32 from bl_ui import properties_physics_common
34 for member in dir(properties_physics_common):
35 subclass = getattr(properties_physics_common, member)
36 if hasattr(subclass, "COMPAT_ENGINES"):
37 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
38 del properties_physics_common
40 # Physics Rigid Bodies wrapping every class 'as is'
41 from bl_ui import properties_physics_rigidbody
43 for member in dir(properties_physics_rigidbody):
44 subclass = getattr(properties_physics_rigidbody, member)
45 if hasattr(subclass, "COMPAT_ENGINES"):
46 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
47 del properties_physics_rigidbody
49 # Physics Rigid Body Constraint wrapping every class 'as is'
50 from bl_ui import properties_physics_rigidbody_constraint
52 for member in dir(properties_physics_rigidbody_constraint):
53 subclass = getattr(properties_physics_rigidbody_constraint, member)
54 if hasattr(subclass, "COMPAT_ENGINES"):
55 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
56 del properties_physics_rigidbody_constraint
58 # Physics Smoke and fluids wrapping every class 'as is'
59 from bl_ui import properties_physics_fluid
61 for member in dir(properties_physics_fluid):
62 subclass = getattr(properties_physics_fluid, member)
63 if hasattr(subclass, "COMPAT_ENGINES"):
64 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
65 del properties_physics_fluid
67 # Physics softbody wrapping every class 'as is'
68 from bl_ui import properties_physics_softbody
70 for member in dir(properties_physics_softbody):
71 subclass = getattr(properties_physics_softbody, member)
72 if hasattr(subclass, "COMPAT_ENGINES"):
73 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
74 del properties_physics_softbody
76 # Physics Field wrapping every class 'as is'
77 from bl_ui import properties_physics_field
79 for member in dir(properties_physics_field):
80 subclass = getattr(properties_physics_field, member)
81 if hasattr(subclass, "COMPAT_ENGINES"):
82 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
83 del properties_physics_field
85 # Physics Cloth wrapping every class 'as is'
86 from bl_ui import properties_physics_cloth
88 for member in dir(properties_physics_cloth):
89 subclass = getattr(properties_physics_cloth, member)
90 if hasattr(subclass, "COMPAT_ENGINES"):
91 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
92 del properties_physics_cloth
94 # Physics Dynamic Paint wrapping every class 'as is'
95 from bl_ui import properties_physics_dynamicpaint
97 for member in dir(properties_physics_dynamicpaint):
98 subclass = getattr(properties_physics_dynamicpaint, member)
99 if hasattr(subclass, "COMPAT_ENGINES"):
100 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
101 del properties_physics_dynamicpaint
103 from bl_ui import properties_particle
105 for member in dir(properties_particle): # add all "particle" panels from blender
106 subclass = getattr(properties_particle, member)
107 if hasattr(subclass, "COMPAT_ENGINES"):
108 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
109 del properties_particle
112 class CameraDataButtonsPanel:
113 """Use this class to define buttons from the camera data tab of
114 properties window."""
116 bl_space_type = "PROPERTIES"
117 bl_region_type = "WINDOW"
118 bl_context = "data"
119 COMPAT_ENGINES = {"POVRAY_RENDER"}
121 @classmethod
122 def poll(cls, context):
123 cam = context.camera
124 rd = context.scene.render
125 return cam and (rd.engine in cls.COMPAT_ENGINES)
128 class WorldButtonsPanel:
129 """Use this class to define buttons from the world tab of
130 properties window."""
132 bl_space_type = "PROPERTIES"
133 bl_region_type = "WINDOW"
134 bl_context = "world"
135 COMPAT_ENGINES = {"POVRAY_RENDER"}
137 @classmethod
138 def poll(cls, context):
139 wld = context.world
140 rd = context.scene.render
141 return wld and (rd.engine in cls.COMPAT_ENGINES)
144 # ---------------------------------------------------------------- #
145 # Camera Settings
146 # ---------------------------------------------------------------- #
147 class CAMERA_PT_POV_cam_dof(CameraDataButtonsPanel, Panel):
148 """Use this class for camera depth of field focal blur buttons."""
150 bl_label = "POV Aperture"
151 COMPAT_ENGINES = {"POVRAY_RENDER"}
152 bl_parent_id = "DATA_PT_camera_dof_aperture"
153 bl_options = {"HIDE_HEADER"}
154 # def draw_header(self, context):
155 # cam = context.camera
157 # self.layout.prop(cam.pov, "dof_enable", text="")
159 def draw(self, context):
160 layout = self.layout
162 cam = context.camera
164 layout.active = cam.dof.use_dof
165 layout.use_property_split = True # Active single-column layout
167 flow = layout.grid_flow(
168 row_major=True, columns=0, even_columns=True, even_rows=False, align=False
171 col = flow.column()
172 col.label(text="F-Stop value will export as")
173 col.label(text="POV aperture : " + "%.3f" % (1 / cam.dof.aperture_fstop * 1000))
175 col = flow.column()
176 col.prop(cam.pov, "dof_samples_min")
177 col.prop(cam.pov, "dof_samples_max")
178 col.prop(cam.pov, "dof_variance")
179 col.prop(cam.pov, "dof_confidence")
182 class CAMERA_PT_POV_cam_nor(CameraDataButtonsPanel, Panel):
183 """Use this class for camera normal perturbation buttons."""
185 bl_label = "POV Perturbation"
186 COMPAT_ENGINES = {"POVRAY_RENDER"}
188 def draw_header(self, context):
189 cam = context.camera
191 self.layout.prop(cam.pov, "normal_enable", text="")
193 def draw(self, context):
194 layout = self.layout
196 cam = context.camera
198 layout.active = cam.pov.normal_enable
200 layout.prop(cam.pov, "normal_patterns")
201 layout.prop(cam.pov, "cam_normal")
202 layout.prop(cam.pov, "turbulence")
203 layout.prop(cam.pov, "scale")
206 class CAMERA_PT_POV_replacement_text(CameraDataButtonsPanel, Panel):
207 """Use this class for camera text replacement field."""
209 bl_label = "Custom POV Code"
210 COMPAT_ENGINES = {"POVRAY_RENDER"}
212 def draw(self, context):
213 layout = self.layout
215 cam = context.camera
217 col = layout.column()
218 col.label(text="Replace properties with:")
219 col.prop(cam.pov, "replacement_text", text="")
222 # ---------------------------------------------------------------- #
223 # World background and sky sphere Settings
224 # ---------------------------------------------------------------- #
227 class WORLD_PT_POV_world(WorldButtonsPanel, Panel):
228 """Use this class to define pov world buttons"""
230 bl_label = "World"
232 COMPAT_ENGINES = {"POVRAY_RENDER"}
234 def draw(self, context):
235 layout = self.layout
237 world = context.world.pov
239 row = layout.row(align=True)
240 row.menu(WORLD_MT_POV_presets.__name__, text=WORLD_MT_POV_presets.bl_label)
241 row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon="ADD")
242 row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon="REMOVE").remove_active = True
244 row = layout.row()
245 row.prop(world, "use_sky_paper")
246 row.prop(world, "use_sky_blend")
247 row.prop(world, "use_sky_real")
249 row = layout.row()
250 row.column().prop(world, "horizon_color")
251 col = row.column()
252 col.prop(world, "zenith_color")
253 col.active = world.use_sky_blend
254 row.column().prop(world, "ambient_color")
256 # row = layout.row()
257 # row.prop(world, "exposure") #Re-implement later as a light multiplier
258 # row.prop(world, "color_range")
261 class WORLD_PT_POV_mist(WorldButtonsPanel, Panel):
262 """Use this class to define pov mist buttons."""
264 bl_label = "Mist"
265 bl_options = {"DEFAULT_CLOSED"}
266 COMPAT_ENGINES = {"POVRAY_RENDER"}
268 def draw_header(self, context):
269 world = context.world
271 self.layout.prop(world.mist_settings, "use_mist", text="")
273 def draw(self, context):
274 layout = self.layout
276 world = context.world
278 layout.active = world.mist_settings.use_mist
280 split = layout.split()
282 col = split.column()
283 col.prop(world.mist_settings, "intensity")
284 col.prop(world.mist_settings, "start")
286 col = split.column()
287 col.prop(world.mist_settings, "depth")
288 col.prop(world.mist_settings, "height")
290 layout.prop(world.mist_settings, "falloff")
293 class WORLD_MT_POV_presets(Menu):
294 """Apply world preset to all concerned properties"""
296 bl_label = "World Presets"
297 preset_subdir = "pov/world"
298 preset_operator = "script.execute_preset"
299 draw = bpy.types.Menu.draw_preset
302 class WORLD_OT_POV_add_preset(AddPresetBase, Operator):
303 """Add a World Preset recording current values"""
305 bl_idname = "object.world_preset_add"
306 bl_label = "Add World Preset"
307 preset_menu = "WORLD_MT_POV_presets"
309 # variable used for all preset values
310 preset_defines = ["scene = bpy.context.scene"]
312 # properties to store in the preset
313 preset_values = [
314 "scene.world.use_sky_blend",
315 "scene.world.horizon_color",
316 "scene.world.zenith_color",
317 "scene.world.ambient_color",
318 "scene.world.mist_settings.use_mist",
319 "scene.world.mist_settings.intensity",
320 "scene.world.mist_settings.depth",
321 "scene.world.mist_settings.start",
322 "scene.pov.media_enable",
323 "scene.pov.media_scattering_type",
324 "scene.pov.media_samples",
325 "scene.pov.media_diffusion_scale",
326 "scene.pov.media_diffusion_color",
327 "scene.pov.media_absorption_scale",
328 "scene.pov.media_absorption_color",
329 "scene.pov.media_eccentricity",
332 # where to store the preset
333 preset_subdir = "pov/world"
336 class RENDER_PT_POV_media(WorldButtonsPanel, Panel):
337 """Use this class to define a pov global atmospheric media buttons."""
339 bl_label = "Atmosphere Media"
340 COMPAT_ENGINES = {"POVRAY_RENDER"}
342 def draw_header(self, context):
343 scene = context.scene
345 self.layout.prop(scene.pov, "media_enable", text="")
347 def draw(self, context):
348 layout = self.layout
350 scene = context.scene
352 layout.active = scene.pov.media_enable
354 col = layout.column()
355 col.prop(scene.pov, "media_scattering_type", text="")
356 col = layout.column()
357 col.prop(scene.pov, "media_samples", text="Samples")
358 split = layout.split()
359 col = split.column(align=True)
360 col.label(text="Scattering:")
361 col.prop(scene.pov, "media_diffusion_scale")
362 col.prop(scene.pov, "media_diffusion_color", text="")
363 col = split.column(align=True)
364 col.label(text="Absorption:")
365 col.prop(scene.pov, "media_absorption_scale")
366 col.prop(scene.pov, "media_absorption_color", text="")
367 if scene.pov.media_scattering_type == "5":
368 col = layout.column()
369 col.prop(scene.pov, "media_eccentricity", text="Eccentricity")
372 # ---------------------------------------------------------------- #
373 # Lights settings
374 # ---------------------------------------------------------------- #
376 # ----------------------------------------------------------------
377 # from bl_ui import properties_data_light
378 # for member in dir(properties_data_light):
379 # subclass = getattr(properties_data_light, member)
380 # try:
381 # subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
382 # except BaseException as e:
383 # print e.__doc__
384 # print('An exception occurred: {}'.format(e))
385 # pass
386 # del properties_data_light
387 # -------- LIGHTS -------- #
389 from bl_ui import properties_data_light
391 # -------- These panels are kept
392 # properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER')
393 # properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER')
395 # make some native panels contextual to some object variable
396 # by recreating custom panels inheriting their properties
399 class PovLightButtonsPanel(properties_data_light.DataButtonsPanel):
400 """Use this class to define buttons from the light data tab of
401 properties window."""
403 COMPAT_ENGINES = {"POVRAY_RENDER"}
404 POV_OBJECT_TYPES = {"RAINBOW"}
406 @classmethod
407 def poll(cls, context):
408 obj = context.object
409 # We use our parent class poll func too, avoids to re-define too much things...
410 return (
411 super(PovLightButtonsPanel, cls).poll(context)
412 and obj
413 and obj.pov.object_as not in cls.POV_OBJECT_TYPES
417 # We cannot inherit from RNA classes (like e.g. properties_data_mesh.DATA_PT_vertex_groups).
418 # Complex py/bpy/rna interactions (with metaclass and all) simply do not allow it to work.
419 # So we simply have to explicitly copy here the interesting bits. ;)
420 from bl_ui import properties_data_light
422 # for member in dir(properties_data_light):
423 # subclass = getattr(properties_data_light, member)
424 # try:
425 # subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
426 # except BaseException as e:
427 # print(e.__doc__)
428 # print('An exception occurred: {}'.format(e))
429 # pass
431 # Now only These panels are kept
432 properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add("POVRAY_RENDER")
433 properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add("POVRAY_RENDER")
436 class LIGHT_PT_POV_preview(PovLightButtonsPanel, Panel):
437 # XXX Needs update and docstring
438 bl_label = properties_data_light.DATA_PT_preview.bl_label
440 draw = properties_data_light.DATA_PT_preview.draw
443 class LIGHT_PT_POV_light(PovLightButtonsPanel, Panel):
444 """UI panel to main pov light parameters"""
446 # bl_label = properties_data_light.DATA_PT_light.bl_label
448 # draw = properties_data_light.DATA_PT_light.draw
449 # class DATA_PT_POV_light(DataButtonsPanel, Panel):
450 bl_label = "Light"
451 # COMPAT_ENGINES = {'POVRAY_RENDER'}
453 def draw(self, context):
454 layout = self.layout
456 light = context.light
458 layout.row().prop(light, "type", expand=True)
460 split = layout.split()
462 col = split.column()
463 sub = col.column()
464 sub.prop(light, "color", text="")
465 sub.prop(light, "energy")
467 if light.type in {"POINT", "SPOT"}:
468 sub.prop(light, "shadow_soft_size", text="Radius")
470 if light.type == "AREA":
471 col.prop(light, "shape")
473 sub = col.column(align=True)
475 if light.shape in {'SQUARE', 'DISK'}:
476 sub.prop(light, "size")
477 elif light.shape in {'RECTANGLE', 'ELLIPSE'}:
478 sub.prop(light, "size", text="Size X")
479 sub.prop(light, "size_y", text="Y")
481 # restore later as interface to POV light groups ?
482 # col = split.column()
483 # col.prop(light, "use_own_layer", text="This Layer Only")
486 class LIGHT_MT_POV_presets(Menu):
487 """Use this class to define preset menu for pov lights."""
489 bl_label = "Lamp Presets"
490 preset_subdir = "pov/light"
491 preset_operator = "script.execute_preset"
492 draw = bpy.types.Menu.draw_preset
495 class LIGHT_OT_POV_add_preset(AddPresetBase, Operator):
496 """Operator to add a Light Preset"""
498 bl_idname = "object.light_preset_add"
499 bl_label = "Add Light Preset"
500 preset_menu = "LIGHT_MT_POV_presets"
502 # variable used for all preset values
503 preset_defines = ["lightdata = bpy.context.object.data"]
505 # properties to store in the preset
506 preset_values = ["lightdata.type", "lightdata.color"]
508 # where to store the preset
509 preset_subdir = "pov/light"
512 # Draw into the existing light panel
513 def light_panel_func(self, context):
514 """Menu to browse and add light preset"""
515 layout = self.layout
517 row = layout.row(align=True)
518 row.menu(LIGHT_MT_POV_presets.__name__, text=LIGHT_MT_POV_presets.bl_label)
519 row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon="ADD")
520 row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon="REMOVE").remove_active = True
523 """#TORECREATE##DEPRECATED#
524 class LIGHT_PT_POV_sunsky(PovLightButtonsPanel, Panel):
525 bl_label = properties_data_light.DATA_PT_sunsky.bl_label
527 @classmethod
528 def poll(cls, context):
529 lamp = context.light
530 engine = context.scene.render.engine
531 return (lamp and lamp.type == 'SUN') and (engine in cls.COMPAT_ENGINES)
533 draw = properties_data_light.DATA_PT_sunsky.draw
538 class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
539 # Todo : update and docstring
540 bl_label = "Shadow"
542 @classmethod
543 def poll(cls, context):
544 light = context.light
545 engine = context.scene.render.engine
546 return light and (engine in cls.COMPAT_ENGINES)
548 def draw(self, context):
549 layout = self.layout
551 light = context.light
553 layout.row().prop(light.pov, "shadow_method", expand=True)
555 split = layout.split()
556 col = split.column()
558 col.prop(light.pov, "use_halo")
559 sub = col.column(align=True)
560 sub.active = light.pov.use_halo
561 sub.prop(light.pov, "halo_intensity", text="Intensity")
563 if light.pov.shadow_method == "NOSHADOW" and light.type == "AREA":
564 split = layout.split()
566 col = split.column()
567 col.label(text="Form factor sampling:")
569 sub = col.row(align=True)
571 if light.shape == "SQUARE":
572 sub.prop(light, "shadow_ray_samples_x", text="Samples")
573 elif light.shape == "RECTANGLE":
574 sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
575 sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
577 if light.pov.shadow_method != "NOSHADOW":
578 split = layout.split()
580 col = split.column()
581 col.prop(light, "shadow_color", text="")
583 # col = split.column()
584 # col.prop(light.pov, "use_shadow_layer", text="This Layer Only")
585 # col.prop(light.pov, "use_only_shadow")
587 if light.pov.shadow_method == "RAY_SHADOW":
588 split = layout.split()
590 col = split.column()
591 col.label(text="Sampling:")
593 if light.type in {"POINT", "SUN", "SPOT"}:
594 sub = col.row()
596 sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
597 # any equivalent in pov?
598 # sub.prop(light, "shadow_soft_size", text="Soft Size")
600 elif light.type == "AREA":
601 sub = col.row(align=True)
603 if light.shape == "SQUARE":
604 sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
605 elif light.shape == "RECTANGLE":
606 sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
607 sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
610 class LIGHT_PT_POV_spot(PovLightButtonsPanel, Panel):
611 bl_label = properties_data_light.DATA_PT_spot.bl_label
612 bl_parent_id = "LIGHT_PT_POV_light"
613 bl_context = "data"
615 @classmethod
616 def poll(cls, context):
617 lamp = context.light
618 engine = context.scene.render.engine
619 return (lamp and lamp.type == "SPOT") and (engine in cls.COMPAT_ENGINES)
621 draw = properties_data_light.DATA_PT_spot.draw
624 class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel):
625 """Use this class to define buttons from the rainbow panel of
626 properties window. inheriting lamp buttons panel class"""
628 bl_label = "POV-Ray Rainbow"
629 COMPAT_ENGINES = {"POVRAY_RENDER"}
630 # bl_options = {'HIDE_HEADER'}
632 @classmethod
633 def poll(cls, context):
634 engine = context.scene.render.engine
635 obj = context.object
636 return obj and obj.pov.object_as == "RAINBOW" and (engine in cls.COMPAT_ENGINES)
638 def draw(self, context):
639 layout = self.layout
641 obj = context.object
643 col = layout.column()
645 if obj.pov.object_as == "RAINBOW":
646 if not obj.pov.unlock_parameters:
647 col.prop(
648 obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
650 col.label(text="Rainbow projection angle: " + str(obj.data.spot_size))
651 col.label(text="Rainbow width: " + str(obj.data.spot_blend))
652 col.label(text="Rainbow distance: " + str(obj.data.shadow_buffer_clip_start))
653 col.label(text="Rainbow arc angle: " + str(obj.pov.arc_angle))
654 col.label(text="Rainbow falloff angle: " + str(obj.pov.falloff_angle))
656 else:
657 col.prop(
658 obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
660 col.label(text="3D view proxy may get out of synch")
661 col.active = obj.pov.unlock_parameters
663 layout.operator("pov.cone_update", text="Update", icon="MESH_CONE")
665 # col.label(text="Parameters:")
666 col.prop(obj.data, "spot_size", text="Rainbow Projection Angle")
667 col.prop(obj.data, "spot_blend", text="Rainbow width")
668 col.prop(obj.data, "shadow_buffer_clip_start", text="Visibility distance")
669 col.prop(obj.pov, "arc_angle")
670 col.prop(obj.pov, "falloff_angle")
673 del properties_data_light
676 classes = (
677 WORLD_PT_POV_world,
678 WORLD_MT_POV_presets,
679 WORLD_OT_POV_add_preset,
680 WORLD_PT_POV_mist,
681 RENDER_PT_POV_media,
682 LIGHT_PT_POV_preview,
683 LIGHT_PT_POV_light,
684 LIGHT_PT_POV_shadow,
685 LIGHT_PT_POV_spot,
686 LIGHT_MT_POV_presets,
687 LIGHT_OT_POV_add_preset,
688 OBJECT_PT_POV_rainbow,
689 CAMERA_PT_POV_cam_dof,
690 CAMERA_PT_POV_cam_nor,
691 CAMERA_PT_POV_replacement_text,
695 def register():
697 for cls in classes:
698 register_class(cls)
699 LIGHT_PT_POV_light.prepend(light_panel_func)
702 def unregister():
703 LIGHT_PT_POV_light.remove(light_panel_func)
704 for cls in reversed(classes):
705 unregister_class(cls)