# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # """User interface to camera frame, optics distortions, and environment with world, sky, atmospheric effects such as rainbows or smoke """ import bpy from bpy.utils import register_class, unregister_class from bpy.types import Operator, Menu, Panel from bl_operators.presets import AddPresetBase from bl_ui import properties_data_camera for member in dir(properties_data_camera): subclass = getattr(properties_data_camera, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_data_camera # -------- Use only a subset of the world panels # from bl_ui import properties_world # # TORECREATE##DEPRECATED#properties_world.WORLD_PT_preview.COMPAT_ENGINES.add('POVRAY_RENDER') # properties_world.WORLD_PT_context_world.COMPAT_ENGINES.add('POVRAY_RENDER') # # TORECREATE##DEPRECATED#properties_world.WORLD_PT_world.COMPAT_ENGINES.add('POVRAY_RENDER') # del properties_world # -------- # # Physics Main wrapping every class 'as is' from bl_ui import properties_physics_common for member in dir(properties_physics_common): subclass = getattr(properties_physics_common, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_common # Physics Rigid Bodies wrapping every class 'as is' from bl_ui import properties_physics_rigidbody for member in dir(properties_physics_rigidbody): subclass = getattr(properties_physics_rigidbody, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_rigidbody # Physics Rigid Body Constraint wrapping every class 'as is' from bl_ui import properties_physics_rigidbody_constraint for member in dir(properties_physics_rigidbody_constraint): subclass = getattr(properties_physics_rigidbody_constraint, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_rigidbody_constraint # Physics Smoke and fluids wrapping every class 'as is' from bl_ui import properties_physics_fluid for member in dir(properties_physics_fluid): subclass = getattr(properties_physics_fluid, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_fluid # Physics softbody wrapping every class 'as is' from bl_ui import properties_physics_softbody for member in dir(properties_physics_softbody): subclass = getattr(properties_physics_softbody, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_softbody # Physics Field wrapping every class 'as is' from bl_ui import properties_physics_field for member in dir(properties_physics_field): subclass = getattr(properties_physics_field, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_field # Physics Cloth wrapping every class 'as is' from bl_ui import properties_physics_cloth for member in dir(properties_physics_cloth): subclass = getattr(properties_physics_cloth, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_cloth # Physics Dynamic Paint wrapping every class 'as is' from bl_ui import properties_physics_dynamicpaint for member in dir(properties_physics_dynamicpaint): subclass = getattr(properties_physics_dynamicpaint, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_physics_dynamicpaint from bl_ui import properties_particle for member in dir(properties_particle): # add all "particle" panels from blender subclass = getattr(properties_particle, member) if hasattr(subclass, "COMPAT_ENGINES"): subclass.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_particle class CameraDataButtonsPanel: """Use this class to define buttons from the camera data tab of properties window.""" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "data" COMPAT_ENGINES = {'POVRAY_RENDER'} @classmethod def poll(cls, context): cam = context.camera rd = context.scene.render return cam and (rd.engine in cls.COMPAT_ENGINES) class WorldButtonsPanel: """Use this class to define buttons from the world tab of properties window.""" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "world" COMPAT_ENGINES = {'POVRAY_RENDER'} @classmethod def poll(cls, context): wld = context.world rd = context.scene.render return wld and (rd.engine in cls.COMPAT_ENGINES) # ---------------------------------------------------------------- # # Camera Settings # ---------------------------------------------------------------- # class CAMERA_PT_POV_cam_dof(CameraDataButtonsPanel, Panel): """Use this class for camera depth of field focal blur buttons.""" bl_label = "POV Aperture" COMPAT_ENGINES = {'POVRAY_RENDER'} bl_parent_id = "DATA_PT_camera_dof_aperture" bl_options = {'HIDE_HEADER'} # def draw_header(self, context): # cam = context.camera # self.layout.prop(cam.pov, "dof_enable", text="") def draw(self, context): layout = self.layout cam = context.camera layout.active = cam.dof.use_dof layout.use_property_split = True # Active single-column layout flow = layout.grid_flow( row_major=True, columns=0, even_columns=True, even_rows=False, align=False ) col = flow.column() col.label(text="F-Stop value will export as") col.label(text="POV aperture : " + "%.3f" % (1 / cam.dof.aperture_fstop * 1000)) col = flow.column() col.prop(cam.pov, "dof_samples_min") col.prop(cam.pov, "dof_samples_max") col.prop(cam.pov, "dof_variance") col.prop(cam.pov, "dof_confidence") class CAMERA_PT_POV_cam_nor(CameraDataButtonsPanel, Panel): """Use this class for camera normal perturbation buttons.""" bl_label = "POV Perturbation" COMPAT_ENGINES = {'POVRAY_RENDER'} def draw_header(self, context): cam = context.camera self.layout.prop(cam.pov, "normal_enable", text="") def draw(self, context): layout = self.layout cam = context.camera layout.active = cam.pov.normal_enable layout.prop(cam.pov, "normal_patterns") layout.prop(cam.pov, "cam_normal") layout.prop(cam.pov, "turbulence") layout.prop(cam.pov, "scale") class CAMERA_PT_POV_replacement_text(CameraDataButtonsPanel, Panel): """Use this class for camera text replacement field.""" bl_label = "Custom POV Code" COMPAT_ENGINES = {'POVRAY_RENDER'} def draw(self, context): layout = self.layout cam = context.camera col = layout.column() col.label(text="Replace properties with:") col.prop(cam.pov, "replacement_text", text="") # ---------------------------------------------------------------- # # World background and sky sphere Settings # ---------------------------------------------------------------- # class WORLD_PT_POV_world(WorldButtonsPanel, Panel): """Use this class to define pov world buttons""" bl_label = "World" COMPAT_ENGINES = {'POVRAY_RENDER'} def draw(self, context): layout = self.layout world = context.world.pov row = layout.row(align=True) row.menu(WORLD_MT_POV_presets.__name__, text=WORLD_MT_POV_presets.bl_label) row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='ADD') row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True row = layout.row() row.prop(world, "use_sky_paper") row.prop(world, "use_sky_blend") row.prop(world, "use_sky_real") row = layout.row() row.column().prop(world, "horizon_color") col = row.column() col.prop(world, "zenith_color") col.active = world.use_sky_blend row.column().prop(world, "ambient_color") # row = layout.row() # row.prop(world, "exposure") #Re-implement later as a light multiplier # row.prop(world, "color_range") class WORLD_PT_POV_mist(WorldButtonsPanel, Panel): """Use this class to define pov mist buttons.""" bl_label = "Mist" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'POVRAY_RENDER'} def draw_header(self, context): world = context.world self.layout.prop(world.mist_settings, "use_mist", text="") def draw(self, context): layout = self.layout world = context.world layout.active = world.mist_settings.use_mist split = layout.split() col = split.column() col.prop(world.mist_settings, "intensity") col.prop(world.mist_settings, "start") col = split.column() col.prop(world.mist_settings, "depth") col.prop(world.mist_settings, "height") layout.prop(world.mist_settings, "falloff") class WORLD_MT_POV_presets(Menu): """Apply world preset to all concerned properties""" bl_label = "World Presets" preset_subdir = "pov/world" preset_operator = "script.execute_preset" draw = bpy.types.Menu.draw_preset class WORLD_OT_POV_add_preset(AddPresetBase, Operator): """Add a World Preset recording current values""" bl_idname = "object.world_preset_add" bl_label = "Add World Preset" preset_menu = "WORLD_MT_POV_presets" # variable used for all preset values preset_defines = ["scene = bpy.context.scene"] # properties to store in the preset preset_values = [ "scene.world.use_sky_blend", "scene.world.horizon_color", "scene.world.zenith_color", "scene.world.ambient_color", "scene.world.mist_settings.use_mist", "scene.world.mist_settings.intensity", "scene.world.mist_settings.depth", "scene.world.mist_settings.start", "scene.pov.media_enable", "scene.pov.media_scattering_type", "scene.pov.media_samples", "scene.pov.media_diffusion_scale", "scene.pov.media_diffusion_color", "scene.pov.media_absorption_scale", "scene.pov.media_absorption_color", "scene.pov.media_eccentricity", ] # where to store the preset preset_subdir = "pov/world" class RENDER_PT_POV_media(WorldButtonsPanel, Panel): """Use this class to define a pov global atmospheric media buttons.""" bl_label = "Atmosphere Media" COMPAT_ENGINES = {'POVRAY_RENDER'} def draw_header(self, context): scene = context.scene self.layout.prop(scene.pov, "media_enable", text="") def draw(self, context): layout = self.layout scene = context.scene layout.active = scene.pov.media_enable col = layout.column() col.prop(scene.pov, "media_scattering_type", text="") col = layout.column() col.prop(scene.pov, "media_samples", text="Samples") split = layout.split() col = split.column(align=True) col.label(text="Scattering:") col.prop(scene.pov, "media_diffusion_scale") col.prop(scene.pov, "media_diffusion_color", text="") col = split.column(align=True) col.label(text="Absorption:") col.prop(scene.pov, "media_absorption_scale") col.prop(scene.pov, "media_absorption_color", text="") if scene.pov.media_scattering_type == '5': col = layout.column() col.prop(scene.pov, "media_eccentricity", text="Eccentricity") # ---------------------------------------------------------------- # # Lights settings # ---------------------------------------------------------------- # # ---------------------------------------------------------------- # from bl_ui import properties_data_light # for member in dir(properties_data_light): # subclass = getattr(properties_data_light, member) # try: # subclass.COMPAT_ENGINES.add('POVRAY_RENDER') # except BaseException as e: # print e.__doc__ # print('An exception occurred: {}'.format(e)) # pass # del properties_data_light # -------- LIGHTS -------- # from bl_ui import properties_data_light # -------- These panels are kept # properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER') # properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER') # make some native panels contextual to some object variable # by recreating custom panels inheriting their properties class PovLightButtonsPanel(properties_data_light.DataButtonsPanel): """Use this class to define buttons from the light data tab of properties window.""" COMPAT_ENGINES = {'POVRAY_RENDER'} POV_OBJECT_TYPES = {'RAINBOW'} @classmethod def poll(cls, context): obj = context.object # We use our parent class poll func too, avoids to re-define too much things... return ( super(PovLightButtonsPanel, cls).poll(context) and obj and obj.pov.object_as not in cls.POV_OBJECT_TYPES ) # We cannot inherit from RNA classes (like e.g. properties_data_mesh.DATA_PT_vertex_groups). # Complex py/bpy/rna interactions (with metaclass and all) simply do not allow it to work. # So we simply have to explicitly copy here the interesting bits. ;) from bl_ui import properties_data_light # for member in dir(properties_data_light): # subclass = getattr(properties_data_light, member) # try: # subclass.COMPAT_ENGINES.add('POVRAY_RENDER') # except BaseException as e: # print(e.__doc__) # print('An exception occurred: {}'.format(e)) # pass # Now only These panels are kept properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER') properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER') class LIGHT_PT_POV_preview(PovLightButtonsPanel, Panel): # XXX Needs update and docstring bl_label = properties_data_light.DATA_PT_preview.bl_label draw = properties_data_light.DATA_PT_preview.draw class LIGHT_PT_POV_light(PovLightButtonsPanel, Panel): """UI panel to main pov light parameters""" # bl_label = properties_data_light.DATA_PT_light.bl_label # draw = properties_data_light.DATA_PT_light.draw # class DATA_PT_POV_light(DataButtonsPanel, Panel): bl_label = "Light" # COMPAT_ENGINES = {'POVRAY_RENDER'} def draw(self, context): layout = self.layout light = context.light layout.row().prop(light, "type", expand=True) split = layout.split() col = split.column() sub = col.column() sub.prop(light, "color", text="") sub.prop(light, "energy") if light.type in {'POINT', 'SPOT'}: sub.label(text="Falloff:") sub.prop(light, "falloff_type", text="") sub.prop(light, "distance") if light.falloff_type == 'LINEAR_QUADRATIC_WEIGHTED': col.label(text="Attenuation Factors:") sub = col.column(align=True) sub.prop(light, "linear_attenuation", slider=True, text="Linear") sub.prop(light, "quadratic_attenuation", slider=True, text="Quadratic") elif light.falloff_type == 'INVERSE_COEFFICIENTS': col.label(text="Inverse Coefficients:") sub = col.column(align=True) sub.prop(light, "constant_coefficient", text="Constant") sub.prop(light, "linear_coefficient", text="Linear") sub.prop(light, "quadratic_coefficient", text="Quadratic") if light.type == 'AREA': col.prop(light, "distance") # restore later as interface to POV light groups ? # col = split.column() # col.prop(light, "use_own_layer", text="This Layer Only") class LIGHT_MT_POV_presets(Menu): """Use this class to define preset menu for pov lights.""" bl_label = "Lamp Presets" preset_subdir = "pov/light" preset_operator = "script.execute_preset" draw = bpy.types.Menu.draw_preset class LIGHT_OT_POV_add_preset(AddPresetBase, Operator): """Operator to add a Light Preset""" bl_idname = "object.light_preset_add" bl_label = "Add Light Preset" preset_menu = "LIGHT_MT_POV_presets" # variable used for all preset values preset_defines = ["lightdata = bpy.context.object.data"] # properties to store in the preset preset_values = ["lightdata.type", "lightdata.color"] # where to store the preset preset_subdir = "pov/light" # Draw into the existing light panel def light_panel_func(self, context): """Menu to browse and add light preset""" layout = self.layout row = layout.row(align=True) row.menu(LIGHT_MT_POV_presets.__name__, text=LIGHT_MT_POV_presets.bl_label) row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='ADD') row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True '''#TORECREATE##DEPRECATED# class LIGHT_PT_POV_sunsky(PovLightButtonsPanel, Panel): bl_label = properties_data_light.DATA_PT_sunsky.bl_label @classmethod def poll(cls, context): lamp = context.light engine = context.scene.render.engine return (lamp and lamp.type == 'SUN') and (engine in cls.COMPAT_ENGINES) draw = properties_data_light.DATA_PT_sunsky.draw ''' class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel): # Todo : update and docstring bl_label = "Shadow" @classmethod def poll(cls, context): light = context.light engine = context.scene.render.engine return light and (engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout light = context.light layout.row().prop(light.pov, "shadow_method", expand=True) split = layout.split() col = split.column() col.prop(light.pov, "use_halo") sub = col.column(align=True) sub.active = light.pov.use_halo sub.prop(light.pov, "halo_intensity", text="Intensity") if light.pov.shadow_method == 'NOSHADOW' and light.type == 'AREA': split = layout.split() col = split.column() col.label(text="Form factor sampling:") sub = col.row(align=True) if light.shape == 'SQUARE': sub.prop(light, "shadow_ray_samples_x", text="Samples") elif light.shape == 'RECTANGLE': sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X") sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y") if light.pov.shadow_method != 'NOSHADOW': split = layout.split() col = split.column() col.prop(light, "shadow_color", text="") # col = split.column() # col.prop(light.pov, "use_shadow_layer", text="This Layer Only") # col.prop(light.pov, "use_only_shadow") if light.pov.shadow_method == 'RAY_SHADOW': split = layout.split() col = split.column() col.label(text="Sampling:") if light.type in {'POINT', 'SUN', 'SPOT'}: sub = col.row() sub.prop(light.pov, "shadow_ray_samples_x", text="Samples") # any equivalent in pov? # sub.prop(light, "shadow_soft_size", text="Soft Size") elif light.type == 'AREA': sub = col.row(align=True) if light.shape == 'SQUARE': sub.prop(light.pov, "shadow_ray_samples_x", text="Samples") elif light.shape == 'RECTANGLE': sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X") sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y") class LIGHT_PT_POV_area(PovLightButtonsPanel, Panel): """Area light UI panel""" bl_label = properties_data_light.DATA_PT_area.bl_label bl_parent_id = "LIGHT_PT_POV_light" bl_context = "data" @classmethod def poll(cls, context): lamp = context.light engine = context.scene.render.engine return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES) draw = properties_data_light.DATA_PT_area.draw class LIGHT_PT_POV_spot(PovLightButtonsPanel, Panel): bl_label = properties_data_light.DATA_PT_spot.bl_label bl_parent_id = "LIGHT_PT_POV_light" bl_context = "data" @classmethod def poll(cls, context): lamp = context.light engine = context.scene.render.engine return (lamp and lamp.type == 'SPOT') and (engine in cls.COMPAT_ENGINES) draw = properties_data_light.DATA_PT_spot.draw class LIGHT_PT_POV_falloff_curve(PovLightButtonsPanel, Panel): bl_label = properties_data_light.DATA_PT_falloff_curve.bl_label bl_options = properties_data_light.DATA_PT_falloff_curve.bl_options @classmethod def poll(cls, context): lamp = context.light engine = context.scene.render.engine return ( lamp and lamp.type in {'POINT', 'SPOT'} and lamp.falloff_type == 'CUSTOM_CURVE' ) and (engine in cls.COMPAT_ENGINES) draw = properties_data_light.DATA_PT_falloff_curve.draw class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel): """Use this class to define buttons from the rainbow panel of properties window. inheriting lamp buttons panel class""" bl_label = "POV-Ray Rainbow" COMPAT_ENGINES = {'POVRAY_RENDER'} # bl_options = {'HIDE_HEADER'} @classmethod def poll(cls, context): engine = context.scene.render.engine obj = context.object return obj and obj.pov.object_as == 'RAINBOW' and (engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout obj = context.object col = layout.column() if obj.pov.object_as == 'RAINBOW': if not obj.pov.unlock_parameters: col.prop( obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' ) col.label(text="Rainbow projection angle: " + str(obj.data.spot_size)) col.label(text="Rainbow width: " + str(obj.data.spot_blend)) col.label(text="Rainbow distance: " + str(obj.data.shadow_buffer_clip_start)) col.label(text="Rainbow arc angle: " + str(obj.pov.arc_angle)) col.label(text="Rainbow falloff angle: " + str(obj.pov.falloff_angle)) else: col.prop( obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED' ) col.label(text="3D view proxy may get out of synch") col.active = obj.pov.unlock_parameters layout.operator("pov.cone_update", text="Update", icon="MESH_CONE") # col.label(text="Parameters:") col.prop(obj.data, "spot_size", text="Rainbow Projection Angle") col.prop(obj.data, "spot_blend", text="Rainbow width") col.prop(obj.data, "shadow_buffer_clip_start", text="Visibility distance") col.prop(obj.pov, "arc_angle") col.prop(obj.pov, "falloff_angle") del properties_data_light classes = ( WORLD_PT_POV_world, WORLD_MT_POV_presets, WORLD_OT_POV_add_preset, WORLD_PT_POV_mist, RENDER_PT_POV_media, LIGHT_PT_POV_preview, LIGHT_PT_POV_light, LIGHT_PT_POV_shadow, LIGHT_PT_POV_spot, LIGHT_PT_POV_area, LIGHT_MT_POV_presets, LIGHT_OT_POV_add_preset, OBJECT_PT_POV_rainbow, CAMERA_PT_POV_cam_dof, CAMERA_PT_POV_cam_nor, CAMERA_PT_POV_replacement_text, ) def register(): for cls in classes: register_class(cls) LIGHT_PT_POV_light.prepend(light_panel_func) def unregister(): LIGHT_PT_POV_light.remove(light_panel_func) for cls in reversed(classes): unregister_class(cls)