From fad5186bb6880aadbb0cd9d983035c7890801aa7 Mon Sep 17 00:00:00 2001 From: Maurice Raybaud <mauriceraybaud@hotmail.fr> Date: Wed, 26 May 2021 01:34:50 +0200 Subject: [PATCH] Formatting and fixes * Moved: some existing functions into new separate files to improve code readability (detailed in __init__.py docstring) * Remove: max_intersections deprecated in pov 3.8 * Add: Validate utf-8 characters with specific API function at session's first script init * Add : Icons to some text fields and inviting labels * Change default camera normal perturbation value to non zero since its use is first driven by a boolean toggle * Change: lists (vectors and indices) are now exported in one line by default for better manual scene overview and debugging * Change: a couple of tooltips corrections * Change : renamed many variables and functions to snake_case according to recommanded style guides * Fix : Heightfield primitive (forward slashes were expected for displacement texture path) * Fix : Text nippet insertion operator * Fix : added console print tip to check executable path on failure to process * Fix : tweaked finished render say command for Linux * Fix : interface of some shader nodes broken since 2.8 api changes * Fix : export hair particles --- presets/pov/light/02_(5400K)_High_Noon_Sun.py | 4 +- .../pov/light/03_(6000K)_Daylight_Window.py | 4 +- ...000K)_75W_Full_Spectrum_Fluorescent_T12.py | 4 +- .../10_(4300K)_40W_Vintage_Fluorescent_T12.py | 4 +- .../11_(5000K)_18W_Standard_Fluorescent_T8.py | 4 +- ...2_(4200K)_18W_Cool_White_Fluorescent_T8.py | 4 +- .../13_(3000K)_18W_Warm_Fluorescent_T8.py | 4 +- ...6500K)_54W_Grow_Light_Fluorescent_T5-HO.py | 4 +- presets/pov/light/21_(2700K)_7W_OLED_Panel.py | 4 +- render_povray/__init__.py | 5831 +---------------- render_povray/base_ui.py | 307 + render_povray/{df3.py => df3_library.py} | 253 +- render_povray/nodes.py | 1369 ---- render_povray/object_curve_topology.py | 974 +++ render_povray/object_gui.py | 731 +++ render_povray/object_mesh_topology.py | 1540 +++++ render_povray/object_particles.py | 255 + .../{primitives.py => object_primitives.py} | 1010 +-- render_povray/object_properties.py | 670 ++ render_povray/render.py | 5552 ++-------------- render_povray/render_gui.py | 562 ++ render_povray/render_properties.py | 687 ++ render_povray/scenography.py | 847 +++ render_povray/scenography_gui.py | 800 +++ render_povray/scenography_properties.py | 514 ++ render_povray/scripting.py | 529 ++ render_povray/scripting_gui.py | 268 + render_povray/scripting_properties.py | 57 + render_povray/shading.py | 2719 ++++---- render_povray/shading_gui.py | 688 ++ render_povray/shading_nodes.py | 2011 ++++++ render_povray/shading_properties.py | 2290 +++++++ render_povray/texturing.py | 902 +++ render_povray/texturing_gui.py | 1255 ++++ render_povray/texturing_properties.py | 1137 ++++ render_povray/ui.py | 4719 ------------- render_povray/update_files.py | 196 +- 37 files changed, 19671 insertions(+), 19038 deletions(-) mode change 100644 => 100755 presets/pov/light/02_(5400K)_High_Noon_Sun.py mode change 100644 => 100755 presets/pov/light/03_(6000K)_Daylight_Window.py mode change 100644 => 100755 presets/pov/light/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py mode change 100644 => 100755 presets/pov/light/10_(4300K)_40W_Vintage_Fluorescent_T12.py mode change 100644 => 100755 presets/pov/light/11_(5000K)_18W_Standard_Fluorescent_T8.py mode change 100644 => 100755 presets/pov/light/12_(4200K)_18W_Cool_White_Fluorescent_T8.py mode change 100644 => 100755 presets/pov/light/13_(3000K)_18W_Warm_Fluorescent_T8.py mode change 100644 => 100755 presets/pov/light/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py mode change 100644 => 100755 presets/pov/light/21_(2700K)_7W_OLED_Panel.py mode change 100644 => 100755 render_povray/__init__.py create mode 100755 render_povray/base_ui.py rename render_povray/{df3.py => df3_library.py} (57%) mode change 100644 => 100755 delete mode 100644 render_povray/nodes.py create mode 100755 render_povray/object_curve_topology.py create mode 100755 render_povray/object_gui.py create mode 100755 render_povray/object_mesh_topology.py create mode 100755 render_povray/object_particles.py rename render_povray/{primitives.py => object_primitives.py} (58%) mode change 100644 => 100755 create mode 100755 render_povray/object_properties.py mode change 100644 => 100755 render_povray/render.py create mode 100755 render_povray/render_gui.py create mode 100755 render_povray/render_properties.py create mode 100755 render_povray/scenography.py create mode 100755 render_povray/scenography_gui.py create mode 100755 render_povray/scenography_properties.py create mode 100755 render_povray/scripting.py create mode 100755 render_povray/scripting_gui.py create mode 100755 render_povray/scripting_properties.py mode change 100644 => 100755 render_povray/shading.py create mode 100755 render_povray/shading_gui.py create mode 100755 render_povray/shading_nodes.py create mode 100755 render_povray/shading_properties.py create mode 100755 render_povray/texturing.py create mode 100755 render_povray/texturing_gui.py create mode 100755 render_povray/texturing_properties.py delete mode 100644 render_povray/ui.py mode change 100644 => 100755 render_povray/update_files.py diff --git a/presets/pov/light/02_(5400K)_High_Noon_Sun.py b/presets/pov/light/02_(5400K)_High_Noon_Sun.py old mode 100644 new mode 100755 index 47499b526..7b5b9e2e8 --- a/presets/pov/light/02_(5400K)_High_Noon_Sun.py +++ b/presets/pov/light/02_(5400K)_High_Noon_Sun.py @@ -9,8 +9,8 @@ lampdata = bpy.context.object.data lampdata.shape = 'SQUARE' lampdata.size = 30000000#0.02 #lampdata.size_y = 0.02 -lampdata.shadow_ray_samples_x = 2 -#lampdata.shadow_ray_samples_y = 3 +lampdata.pov.shadow_ray_samples_x = 2 +#lampdata.pov.shadow_ray_samples_y = 3 lampdata.color = (1.0, 1.0, 1.0) lampdata.energy = 1.094316#91193 #lux lampdata.distance =695699968 diff --git a/presets/pov/light/03_(6000K)_Daylight_Window.py b/presets/pov/light/03_(6000K)_Daylight_Window.py old mode 100644 new mode 100755 index a9781f579..eae7cd16c --- a/presets/pov/light/03_(6000K)_Daylight_Window.py +++ b/presets/pov/light/03_(6000K)_Daylight_Window.py @@ -6,8 +6,8 @@ lampdata = bpy.context.object.data lampdata.size = 1.2 lampdata.size_y = 2.10 -lampdata.shadow_ray_samples_x = 2 -lampdata.shadow_ray_samples_y = 3 +lampdata.pov.shadow_ray_samples_x = 2 +lampdata.pov.shadow_ray_samples_y = 3 lampdata.color = (1.0, 1.0, 1.0) lampdata.energy = 1.094316#91193 #lux lampdata.distance = 1.0 diff --git a/presets/pov/light/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py b/presets/pov/light/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py old mode 100644 new mode 100755 index 78fa29cbe..6d09b96f5 --- a/presets/pov/light/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py +++ b/presets/pov/light/09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py @@ -6,8 +6,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.038 lampdata.size_y = 2.40284 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (1.0, 0.95686274766922, 0.9490200281143188) lampdata.energy = 4.45304#4775lm/21.446(=lux)*0.004(distance) *2 for distance is the point of half strength 6200lm? lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/10_(4300K)_40W_Vintage_Fluorescent_T12.py b/presets/pov/light/10_(4300K)_40W_Vintage_Fluorescent_T12.py old mode 100644 new mode 100755 index dc78bc5c5..5a5a7eb97 --- a/presets/pov/light/10_(4300K)_40W_Vintage_Fluorescent_T12.py +++ b/presets/pov/light/10_(4300K)_40W_Vintage_Fluorescent_T12.py @@ -6,8 +6,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.038 lampdata.size_y = 1.2192 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (0.901, 1.0, 0.979) lampdata.energy = 2.14492#2300lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/11_(5000K)_18W_Standard_Fluorescent_T8.py b/presets/pov/light/11_(5000K)_18W_Standard_Fluorescent_T8.py old mode 100644 new mode 100755 index a70cda3fa..5f7ce0a6b --- a/presets/pov/light/11_(5000K)_18W_Standard_Fluorescent_T8.py +++ b/presets/pov/light/11_(5000K)_18W_Standard_Fluorescent_T8.py @@ -6,8 +6,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.026 lampdata.size_y = 0.59 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (0.95686274766922, 1.0, 0.9803921580314636) lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/12_(4200K)_18W_Cool_White_Fluorescent_T8.py b/presets/pov/light/12_(4200K)_18W_Cool_White_Fluorescent_T8.py old mode 100644 new mode 100755 index c2a0d65d3..0bbf19656 --- a/presets/pov/light/12_(4200K)_18W_Cool_White_Fluorescent_T8.py +++ b/presets/pov/light/12_(4200K)_18W_Cool_White_Fluorescent_T8.py @@ -7,8 +7,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.026 lampdata.size_y = 0.59 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (0.8313725590705872, 0.9215686321258545, 1.0) lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/13_(3000K)_18W_Warm_Fluorescent_T8.py b/presets/pov/light/13_(3000K)_18W_Warm_Fluorescent_T8.py old mode 100644 new mode 100755 index e1cee557d..187b26d36 --- a/presets/pov/light/13_(3000K)_18W_Warm_Fluorescent_T8.py +++ b/presets/pov/light/13_(3000K)_18W_Warm_Fluorescent_T8.py @@ -7,8 +7,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.026 lampdata.size_y = 0.59 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (1.0, 0.95686274766922, 0.8980392217636108) lampdata.energy = 1.25898#1350lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 1.0 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py b/presets/pov/light/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py old mode 100644 new mode 100755 index 55f84ab81..c0b992ca8 --- a/presets/pov/light/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py +++ b/presets/pov/light/14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py @@ -6,8 +6,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.016 lampdata.size_y = 1.149 -lampdata.shadow_ray_samples_x = 1 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 1 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (1.0, 0.83, 0.986274528503418) lampdata.energy = 4.66287 #0.93257#4.66287#5000lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 0.1 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/presets/pov/light/21_(2700K)_7W_OLED_Panel.py b/presets/pov/light/21_(2700K)_7W_OLED_Panel.py old mode 100644 new mode 100755 index 8f2ebb8fe..8a6ba8d93 --- a/presets/pov/light/21_(2700K)_7W_OLED_Panel.py +++ b/presets/pov/light/21_(2700K)_7W_OLED_Panel.py @@ -7,8 +7,8 @@ lampdata = bpy.context.object.data lampdata.size = 0.033 lampdata.size_y = 0.133 -lampdata.shadow_ray_samples_x = 2 -lampdata.shadow_ray_samples_y = 2 +lampdata.pov.shadow_ray_samples_x = 2 +lampdata.pov.shadow_ray_samples_y = 2 lampdata.color = (1.0, 0.8292156958580017, 0.6966666865348816) lampdata.energy = 0.83932#900lm/21.446(=lux)*0.004*2.5(distance) *2 for distance is the point of half strength lampdata.distance = 1.18 #dist values multiplied by 10 for area lights for same power as bulb/spot/... diff --git a/render_povray/__init__.py b/render_povray/__init__.py old mode 100644 new mode 100755 index da7296a9a..58f6040d8 --- a/render_povray/__init__.py +++ b/render_povray/__init__.py @@ -24,29 +24,83 @@ These engines can be POV-Ray or Uberpov but others too, since POV is a Scene Description Language. The script has been split in as few files as possible : -___init__.py : +__init__.py : Initialize properties -update_files.py - Update new variables to values from older API. This file needs an update - -ui.py : +base_ui.py : Provide property buttons for the user to set up the variables -primitives.py : +scenography_properties.py + Initialize properties for translating Blender cam/light/environment parameters to pov + +scenography_gui.py + Display cam/light/environment properties from situation_properties.py for user to change them + +scenography.py + Translate cam/light/environment properties to corresponding pov features + +object_properties.py : + nitialize properties for translating Blender objects parameters to pov + +object_primitives.py : Display some POV native primitives in 3D view for input and output +object_mesh_topology.py : + Translate to POV the meshes geometries + +object_curve_topology.py : + Translate to POV the curve based geometries + +object_particles.py : + Translate to POV the particle based geometries + +object_gui.py : + Display properties from object_properties.py for user to change them + +shading_properties.py + Initialize properties for translating Blender materials parameters to pov + +shading_nodes.py + Translate node trees to the pov file + +shading_gui.py + Display properties from shading_properties.py for user to change them + shading.py Translate shading properties to declared textures at the top of a pov file -nodes.py - Translate node trees to the pov file +texturing_properties.py + Initialize properties for translating Blender materials /world... texture influences to pov -df3.py - Render smoke to *.df3 files +texturing_gui.py + Display properties from texturing_properties.py for user to change them + +texturing.py + Translate blender texture influences into POV + +render_properties.py : + Initialize properties for render parameters (Blender and POV native) + +render_gui.py : + Display properties from render_properties.py for user to change them render.py : - Translate geometry and UI properties (Blender and POV native) to the POV file + Translate render properties (Blender and POV native) to POV, ini file and bash + +scripting_properties.py : + Initialize properties for scene description language parameters (POV native) + +scripting_gui.py : + Display properties from scripting_properties.py for user to add his custom POV code + +scripting.py : + Insert POV native scene description elements to exported POV file + +df3_library.py + Render smoke to *.df3 files + +update_files.py + Update new variables to values from older API. This file needs an update Along these essential files also coexist a few additional libraries to help make @@ -99,5587 +153,290 @@ Blender stand up to other POV IDEs such as povwin or QTPOV bl_info = { "name": "Persistence of Vision", "author": "Campbell Barton, " - "Maurice Raybaud, " - "Leonid Desyatkov, " - "Bastien Montagne, " - "Constantin Rahn, " - "Silvio Falcinelli", - "version": (0, 1, 1), + "Maurice Raybaud, " + "Leonid Desyatkov, " + "Bastien Montagne, " + "Constantin Rahn, " + "Silvio Falcinelli," + "Paco GarcĂa", + "version": (0, 1, 2), "blender": (2, 81, 0), "location": "Render Properties > Render Engine > Persistence of Vision", "description": "Persistence of Vision integration for blender", "doc_url": "{BLENDER_MANUAL_URL}/addons/render/povray.html", "category": "Render", - "warning": "Under active development, seeking co-maintainer(s)", + "warning": "Co-maintainers welcome", } +# Other occasional contributors, more or less in chronological order: +# Luca Bonavita ; Shane Ambler ; Brendon Murphy ; Doug Hammond ; +# Thomas Dinges ; Jonathan Smith ; Sebastian Nell ; Philipp Oeser ; +# Sybren A. StĂĽvel ; Dalai Felinto ; Sergey Sharybin ; Brecht Van Lommel ; +# Stephen Leger ; Rune Morling ; Aaron Carlisle ; Ankit Meel ; + if "bpy" in locals(): import importlib - importlib.reload(ui) - importlib.reload(nodes) + importlib.reload(base_ui) + importlib.reload(shading_nodes) + importlib.reload(scenography_properties) + importlib.reload(scenography) + importlib.reload(render_properties) + importlib.reload(render_gui) importlib.reload(render) + importlib.reload(shading_properties) + importlib.reload(shading_gui) importlib.reload(shading) - importlib.reload(primitives) + importlib.reload(texturing_properties) + importlib.reload(texturing_gui) + importlib.reload(texturing) + importlib.reload(object_properties) + importlib.reload(object_gui) + importlib.reload(object_mesh_topology) + importlib.reload(object_curve_topology) + importlib.reload(object_primitives) + importlib.reload(scripting_gui) importlib.reload(update_files) else: import bpy from bpy.utils import register_class, unregister_class - # import addon_utils # To use some other addons - import nodeitems_utils # for Nodes - from nodeitems_utils import NodeCategory, NodeItem # for Nodes - from bl_operators.presets import AddPresetBase - from bpy.types import ( - AddonPreferences, - PropertyGroup, - NodeSocket, - ) - - from bpy.props import ( - FloatVectorProperty, - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - PointerProperty, - CollectionProperty, - ) - from . import ui, render, update_files - - -def string_strip_hyphen(name): - - """Remove hyphen characters from a string to avoid POV errors""" - - return name.replace("-", "") - -def pov_context_tex_datablock(context): - """Texture context type recreated as deprecated in blender 2.8""" - - idblock = context.brush - if idblock and context.scene.texture_context == 'OTHER': - return idblock - - # idblock = bpy.context.active_object.active_material - idblock = context.view_layer.objects.active.active_material - if idblock and context.scene.texture_context == 'MATERIAL': - return idblock - - idblock = context.world - if idblock and context.scene.texture_context == 'WORLD': - return idblock - - idblock = context.light - if idblock and context.scene.texture_context == 'LIGHT': - return idblock - - if context.particle_system and context.scene.texture_context == 'PARTICLES': - idblock = context.particle_system.settings - - return idblock - - idblock = context.line_style - if idblock and context.scene.texture_context == 'LINESTYLE': - return idblock - -def brush_texture_update(self, context): - - """Brush texture rolldown must show active slot texture props""" - idblock = pov_context_tex_datablock(context) - if idblock is not None: - #mat = context.view_layer.objects.active.active_material - idblock = pov_context_tex_datablock(context) - slot = idblock.pov_texture_slots[idblock.pov.active_texture_index] - tex = slot.texture - - if tex: - # Switch paint brush to active texture so slot and settings remain contextual - bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex] - bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex] - -def active_texture_name_from_uilist(self, context): - - idblock = pov_context_tex_datablock(context) - #mat = context.view_layer.objects.active.active_material - if idblock is not None: - index = idblock.pov.active_texture_index - name = idblock.pov_texture_slots[index].name - newname = idblock.pov_texture_slots[index].texture - tex = bpy.data.textures[name] - tex.name = newname - idblock.pov_texture_slots[index].name = newname - - -def active_texture_name_from_search(self, context): - """Texture rolldown to change the data linked by an existing texture""" - idblock = pov_context_tex_datablock(context) - #mat = context.view_layer.objects.active.active_material - if idblock is not None: - index = idblock.pov.active_texture_index - slot = idblock.pov_texture_slots[index] - name = slot.texture_search - - try: - tex = bpy.data.textures[name] - slot.name = name - slot.texture = name - # Switch paint brush to this texture so settings remain contextual - #bpy.context.tool_settings.image_paint.brush.texture = tex - #bpy.context.tool_settings.image_paint.brush.mask_texture = tex - except: - pass + from bpy.props import StringProperty, BoolProperty, EnumProperty + from . import ( + base_ui, + render_properties, + scenography_properties, + shading_properties, + texturing_properties, + object_properties, + scripting_properties, + render, + object_primitives, # for import and export of POV specific primitives + update_files, + ) ############################################################################### -# Scene POV properties. +# Auto update. ############################################################################### -class RenderPovSettingsScene(PropertyGroup): - - """Declare scene level properties controllable in UI and translated to POV""" - - # Linux SDL-window enable - sdl_window_enable: BoolProperty( - name="Enable SDL window", - description="Enable the SDL window in Linux OS", - default=True, - ) - # File Options - text_block: StringProperty( - name="Text Scene Name", - description="Name of POV scene to use. " - "Set when clicking Run to render current text only", - maxlen=1024, - ) - tempfiles_enable: BoolProperty( - name="Enable Tempfiles", - description="Enable the OS-Tempfiles. Otherwise set the path where" - " to save the files", - default=True, - ) - pov_editor: BoolProperty( - name="POV editor", - description="Don't Close POV editor after rendering (Overridden" - " by /EXIT command)", - default=False, - ) - deletefiles_enable: BoolProperty( - name="Delete files", - description="Delete files after rendering. " - "Doesn't work with the image", - default=True, - ) - scene_name: StringProperty( - name="Scene Name", - description="Name of POV scene to create. Empty name will use " - "the name of the blend file", - maxlen=1024, - ) - scene_path: StringProperty( - name="Export scene path", - # Bug in POV-Ray RC3 - # description="Path to directory where the exported scene " - # "(POV and INI) is created", - description="Path to directory where the files are created", - maxlen=1024, - subtype="DIR_PATH", - ) - renderimage_path: StringProperty( - name="Rendered image path", - description="Full path to directory where the rendered image is " - "saved", - maxlen=1024, - subtype="DIR_PATH", - ) - list_lf_enable: BoolProperty( - name="LF in lists", - description="Enable line breaks in lists (vectors and indices). " - "Disabled: lists are exported in one line", - default=True, - ) - - # Not a real pov option, just to know if we should write - radio_enable: BoolProperty( - name="Enable Radiosity", - description="Enable POV radiosity calculation", - default=True, - ) - - radio_display_advanced: BoolProperty( - name="Advanced Options", - description="Show advanced options", - default=False, - ) - - media_enable: BoolProperty( - name="Enable Media", - description="Enable POV atmospheric media", - default=False, - ) - - media_samples: IntProperty( - name="Samples", - description="Number of samples taken from camera to first object " - "encountered along ray path for media calculation", - min=1, - max=100, - default=35, - ) - - media_scattering_type: EnumProperty( - name="Scattering Type", - description="Scattering model", - items=( - ( - '1', - "1 Isotropic", - "The simplest form of scattering because" - " it is independent of direction." - ), - ( - '2', - "2 Mie haze ", - "For relatively small particles such as " - "minuscule water droplets of fog, cloud " - "particles, and particles responsible " - "for the polluted sky. In this model the" - " scattering is extremely directional in" - " the forward direction i.e. the amount " - "of scattered light is largest when the " - "incident light is anti-parallel to the " - "viewing direction (the light goes " - "directly to the viewer). It is smallest" - " when the incident light is parallel to" - " the viewing direction. " - ), - ( - '3', - "3 Mie murky", - "Like haze but much more directional" - ), - ( - '4', - "4 Rayleigh", - "For extremely small particles such as " - "molecules of the air. The amount of " - "scattered light depends on the incident" - " light angle. It is largest when the " - "incident light is parallel or " - "anti-parallel to the viewing direction " - "and smallest when the incident light is " - "perpendicular to viewing direction." - ), - ( - '5', - "5 Henyey-Greenstein", - "The default eccentricity value " - "of zero defines isotropic " - "scattering while positive " - "values lead to scattering in " - "the direction of the light and " - "negative values lead to " - "scattering in the opposite " - "direction of the light. Larger " - "values of e (or smaller values " - "in the negative case) increase " - "the directional property of the" - " scattering." +class POV_OT_update_addon(bpy.types.Operator): + """Update this addon to the latest version.""" + + bl_idname = "pov.update_addon" + bl_label = "Update POV addon" + + def execute(self, context): + import os, tempfile, shutil, urllib.request, urllib.error, zipfile + + def recursive_overwrite(src, dest, ignore=None): + if os.path.isdir(src): + if not os.path.isdir(dest): + os.makedirs(dest) + files = os.listdir(src) + if ignore is not None: + ignored = ignore(src, files) + else: + ignored = set() + for f in files: + if f not in ignored: + recursive_overwrite(os.path.join(src, f), os.path.join(dest, f), ignore) + else: + shutil.copyfile(src, dest) + + print('-' * 20) + print('Updating POV addon...') + + with tempfile.TemporaryDirectory() as temp_dir_path: + temp_zip_path = os.path.join(temp_dir_path, 'master.zip') + + # Download zip archive of latest addons master branch commit (So we also get presets) + # switch this URL back to the BF hosted one as soon as gitweb snapshot gets fixed + url = 'https://github.com/blender/blender-addons/archive/refs/heads/master.zip' + try: + print('Downloading', url) + + with urllib.request.urlopen(url, timeout=60) as url_handle, open( + temp_zip_path, 'wb' + ) as file_handle: + file_handle.write(url_handle.read()) + except urllib.error.URLError as err: + self.report({'ERROR'}, 'Could not download: %s' % err) + + # Extract the zip + print('Extracting ZIP archive') + with zipfile.ZipFile(temp_zip_path) as zip: + for member in zip.namelist(): + if 'blender-addons-master/render_povray' in member: + # Remove the first directory and the filename + # e.g. blender-addons-master/render_povray/shading_nodes.py + # becomes render_povray/shading_nodes.py + target_path = os.path.join( + temp_dir_path, os.path.join(*member.split('/')[1:-1]) + ) + + filename = os.path.basename(member) + # Skip directories + if len(filename) == 0: + continue + + # Create the target directory if necessary + if not os.path.exists(target_path): + os.makedirs(target_path) + + source = zip.open(member) + target = open(os.path.join(target_path, filename), "wb") + + with source, target: + shutil.copyfileobj(source, target) + print('copying', source, 'to', target) + + extracted_render_povray_path = os.path.join(temp_dir_path, 'render_povray') + + if not os.path.exists(extracted_render_povray_path): + self.report({'ERROR'}, 'Could not extract ZIP archive! Aborting.') + return {'FINISHED'} + + # Find the old POV addon files + render_povray_dir = os.path.abspath(os.path.dirname(__file__)) + print('POV addon addon folder:', render_povray_dir) + + # TODO: Create backup + + # Delete old POV addon files (only directories and *.py files, the user might have other stuff in there!) + print('Deleting old POV addon files') + # remove __init__.py + os.remove(os.path.join(render_povray_dir, '__init__.py')) + # remove all folders + DIRNAMES = 1 + for dir in next(os.walk(render_povray_dir))[DIRNAMES]: + shutil.rmtree(os.path.join(render_povray_dir, dir)) + + print('Copying new POV addon files') + # copy new POV addon files + # copy __init__.py + shutil.copy2( + os.path.join(extracted_render_povray_path, '__init__.py'), render_povray_dir ) - ), - default='1', - ) - - media_diffusion_scale: FloatProperty( - name="Scale", - description="Scale factor of Media Diffusion Color", - precision=6, step=0.00000001, min=0.000000001, max=1.0, - default=(1.0), - ) - - media_diffusion_color: FloatVectorProperty( - name="Media Diffusion Color", - description="The atmospheric media color", - precision=4, step=0.01, min=0, soft_max=1, - default=(0.001, 0.001, 0.001), - options={'ANIMATABLE'}, - subtype='COLOR', - ) - - media_absorption_scale: FloatProperty( - name="Scale", - description="Scale factor of Media Absorption Color. " - "use 1/depth of media volume in meters", - precision=6, - step=0.000001, - min=0.000000001, - max=1.0, - default=(0.00002), - ) - - media_absorption_color: FloatVectorProperty( - name="Media Absorption Color", - description="The atmospheric media absorption color", - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), - options={'ANIMATABLE'}, - subtype='COLOR', - ) - - media_eccentricity: FloatProperty( - name="Media Eccenticity Factor", - description="Positive values lead" - " to scattering in the direction of the light and negative " - "values lead to scattering in the opposite direction of the " - "light. Larger values of e (or smaller values in the negative" - " case) increase the directional property of the scattering", - precision=2, - step=0.01, - min=-1.0, - max=1.0, - default=(0.0), - options={'ANIMATABLE'}, - ) - - baking_enable: BoolProperty( - name="Enable Baking", - description="Enable POV texture baking", - default=False - ) - - indentation_character: EnumProperty( - name="Indentation", - description="Select the indentation type", - items=( - ('NONE', "None", "No indentation"), - ('TAB', "Tabs", "Indentation with tabs"), - ('SPACE', "Spaces", "Indentation with spaces") - ), - default='SPACE' - ) - - indentation_spaces: IntProperty( - name="Quantity of spaces", - description="The number of spaces for indentation", - min=1, - max=10, - default=4, - ) - - comments_enable: BoolProperty( - name="Enable Comments", - description="Add comments to pov file", - default=True, - ) - - # Real pov options - command_line_switches: StringProperty( - name="Command Line Switches", - description="Command line switches consist of a + (plus) or - " - "(minus) sign, followed by one or more alphabetic " - "characters and possibly a numeric value", - maxlen=500, - ) - - antialias_enable: BoolProperty( - name="Anti-Alias", description="Enable Anti-Aliasing", - default=True, - ) - - antialias_method: EnumProperty( - name="Method", - description="AA-sampling method. Type 1 is an adaptive, " - "non-recursive, super-sampling method. Type 2 is an " - "adaptive and recursive super-sampling method. Type 3 " - "is a stochastic halton based super-sampling method", - items=( - ("0", "non-recursive AA", "Type 1 Sampling in POV"), - ("1", "recursive AA", "Type 2 Sampling in POV"), - ("2", "stochastic AA", "Type 3 Sampling in POV") - ), - default="1", - ) - - antialias_confidence: FloatProperty( - name="Antialias Confidence", - description="how surely the computed color " - "of a given pixel is indeed" - "within the threshold error margin", - min=0.0001, - max=1.0000, - default=0.9900, - precision=4 - ) - - antialias_depth: IntProperty( - name="Antialias Depth", - description="Depth of pixel for sampling", - min=1, - max=9, - default=3 - ) - - antialias_threshold: FloatProperty( - name="Antialias Threshold", - description="Tolerance for sub-pixels", - min=0.0, - max=1.0, - soft_min=0.05, - soft_max=0.5, - default=0.03, - ) - - jitter_enable: BoolProperty( - name="Jitter", - description="Enable Jittering. Adds noise into the sampling " - "process (it should be avoided to use jitter in " - "animation)", - default=False, - ) - - jitter_amount: FloatProperty( - name="Jitter Amount", - description="Amount of jittering", - min=0.0, - max=1.0, - soft_min=0.01, - soft_max=1.0, - default=1.0, - ) - - antialias_gamma: FloatProperty( - name="Antialias Gamma", - description="POV-Ray compares gamma-adjusted values for super " - "sampling. Antialias Gamma sets the Gamma before " - "comparison", - min=0.0, - max=5.0, - soft_min=0.01, - soft_max=2.5, - default=2.5, - ) - - alpha_mode: EnumProperty( - name="Alpha", - description="Representation of alpha information in the RGBA pixels", - items=( - ("SKY", "Sky", "Transparent pixels are filled with sky color"), - ( - "TRANSPARENT", - "Transparent", - "Transparent, World background is transparent with premultiplied alpha", - ), - ), - default="SKY", - ) - - use_shadows: BoolProperty( - name="Shadows", - description="Calculate shadows while rendering", - default=True, - ) - - max_trace_level: IntProperty( - name="Max Trace Level", - description="Number of reflections/refractions allowed on ray " - "path", - min=1, - max=256, - default=5 - ) - - adc_bailout_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - adc_bailout: FloatProperty( - name="ADC Bailout", - description="Adaptive Depth Control (ADC) to stop computing additional" - "reflected or refracted rays when their contribution is insignificant." - "The default value is 1/255, or approximately 0.0039, since a change " - "smaller than that could not be visible in a 24 bit image. Generally " - "this value is fine and should be left alone." - "Setting adc_bailout to 0 will disable ADC, relying completely on " - "max_trace_level to set an upper limit on the number of rays spawned. ", - min=0.0, - max=1000.0, - default=0.00392156862745, - precision=3 - ) - - ambient_light_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - ambient_light: FloatVectorProperty( - name="Ambient Light", - description="Ambient light is used to simulate the effect of inter-diffuse reflection", - precision=4, step=0.01, min=0, soft_max=1, - default=(1, 1, 1), options={'ANIMATABLE'}, subtype='COLOR', - ) - global_settings_advanced: BoolProperty( - name="Advanced", - description="", - default=False, - ) - - irid_wavelength_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - irid_wavelength: FloatVectorProperty( - name="Irid Wavelength", - description=( - "Iridescence calculations depend upon the dominant " - "wavelengths of the primary colors of red, green and blue light" - ), - precision=4, - step=0.01, - min=0, - soft_max=1, - default=(0.25,0.18,0.14), - options={'ANIMATABLE'}, - subtype='COLOR' - ) - # Deprecated (autodetected in pov3.8): - # charset: EnumProperty( - # name="Charset", - # description="This allows you to specify the assumed character set of all text strings", - # items=( - # ("ascii", "ASCII", ""), - # ("utf8", "UTF-8", ""), - # ("sys", "SYS", "") - # ), - # default="utf8", - # ) - - max_intersections_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - max_intersections: IntProperty( - name="Max Intersections", - description="POV-Ray uses a set of internal stacks to collect ray/object intersection points", - min=2, - max=1024, - default=64, - ) - - number_of_waves_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - number_of_waves: IntProperty( - name="Number Waves", - description=( - "The waves and ripples patterns are generated by summing a series of waves, " - "each with a slightly different center and size" - ), - min=1, - max=10, - default=1000, - ) - - noise_generator_enable: BoolProperty( - name="Enable", - description="", - default=False, - ) - - noise_generator: IntProperty( - name="Noise Generator", - description="There are three noise generators implemented", - min=1, - max=3, - default=2, - ) - - ########################### PHOTONS ####################################### - photon_enable: BoolProperty( - name="Photons", - description="Enable global photons", - default=False, - ) - - photon_enable_count: BoolProperty( - name="Spacing / Count", - description="Enable count photons", - default=False, - ) + # copy all folders + recursive_overwrite(extracted_render_povray_path, render_povray_dir) - photon_count: IntProperty( - name="Count", - description="Photons count", - min=1, - max=100000000, - default=20000 - ) + bpy.ops.preferences.addon_refresh() + print('POV addon update finished, restart Blender for the changes to take effect.') + print('-' * 20) + self.report({'WARNING'}, 'Restart Blender!') + return {'FINISHED'} - photon_spacing: FloatProperty( - name="Spacing", - description="Average distance between photons on surfaces. half " - "this get four times as many surface photons", - min=0.001, - max=1.000, - soft_min=0.001, - soft_max=1.000, - precision=3, - default=0.005, - ) - photon_max_trace_level: IntProperty( - name="Max Trace Level", - description="Number of reflections/refractions allowed on ray " - "path", - min=1, - max=256, - default=5 - ) +############################################################################### +# Povray Preferences. +############################################################################### - photon_adc_bailout: FloatProperty( - name="ADC Bailout", - description="The adc_bailout for photons. Use adc_bailout = " - "0.01 / brightest_ambient_object for good results", - min=0.0, - max=1000.0, - soft_min=0.0, - soft_max=1.0, - precision=3, - default=0.1, - ) - photon_gather_min: IntProperty( - name="Gather Min", description="Minimum number of photons gathered" - "for each point", - min=1, max=256, default=20 - ) +class PovrayPreferences(bpy.types.AddonPreferences): + """Declare preference variables to set up POV binary.""" - photon_gather_max: IntProperty( - name="Gather Max", description="Maximum number of photons gathered for each point", - min=1, max=256, default=100 - ) + bl_idname = __name__ - photon_map_file_save_load: EnumProperty( - name="Operation", - description="Load or Save photon map file", + branch_feature_set_povray: EnumProperty( + name="Feature Set", + description="Choose between official (POV-Ray) or (UberPOV) " + "development branch features to write in the pov file", items=( - ("NONE", "None", ""), - ("save", "Save", ""), - ("load", "Load", "") + ("povray", "Official POV-Ray", "", "PLUGIN", 0), + ("uberpov", "Unofficial UberPOV", "", "PLUGIN", 1), ), - default="NONE", - ) - - photon_map_filename: StringProperty( - name="Filename", - description="", - maxlen=1024 - ) - - photon_map_dir: StringProperty( - name="Directory", - description="", - maxlen=1024, - subtype="DIR_PATH", - ) - - photon_map_file: StringProperty( - name="File", - description="", - maxlen=1024, - subtype="FILE_PATH" - ) - - #########RADIOSITY######## - radio_adc_bailout: FloatProperty( - name="ADC Bailout", - description="The adc_bailout for radiosity rays. Use " - "adc_bailout = 0.01 / brightest_ambient_object for good results", - min=0.0, - max=1000.0, - soft_min=0.0, - soft_max=1.0, - default=0.0039, - precision=4, + default="povray", ) - radio_always_sample: BoolProperty( - name="Always Sample", - description="Only use the data from the pretrace step and not gather " - "any new samples during the final radiosity pass", - default=False, - ) - - radio_brightness: FloatProperty( - name="Brightness", - description="Amount objects are brightened before being returned " - "upwards to the rest of the system", - min=0.0, max=1000.0, soft_min=0.0, soft_max=10.0, default=1.0 - ) - - radio_count: IntProperty( - name="Ray Count", - description="Number of rays for each new radiosity value to be calculated " - "(halton sequence over 1600)", - min=1, max=10000, soft_max=1600, default=35 - ) - - radio_error_bound: FloatProperty( - name="Error Bound", - description="One of the two main speed/quality tuning values, " - "lower values are more accurate", - min=0.0, max=1000.0, soft_min=0.1, soft_max=10.0, default=10.0 - ) - - radio_gray_threshold: FloatProperty( - name="Gray Threshold", - description="One of the two main speed/quality tuning values, " - "lower values are more accurate", - min=0.0, max=1.0, soft_min=0, soft_max=1, default=0.0 - ) - - radio_low_error_factor: FloatProperty( - name="Low Error Factor", - description="Just enough samples is slightly blotchy. Low error changes error " - "tolerance for less critical last refining pass", - min=0.000001, max=1.0, soft_min=0.000001, soft_max=1.0, default=0.5 + filepath_povray: StringProperty( + name="Binary Location", description="Path to renderer executable", subtype="FILE_PATH" ) - radio_media: BoolProperty( - name="Media", - description="Radiosity estimation can be affected by media", - default=True, + docpath_povray: StringProperty( + name="Includes Location", description="Path to Insert Menu files", subtype="FILE_PATH" ) - radio_subsurface: BoolProperty( - name="Subsurface", - description="Radiosity estimation can be affected by Subsurface Light Transport", + use_sounds: BoolProperty( + name="Use Sound", + description="Signaling end of the render process at various" + "stages can help if you're away from monitor", default=False, ) - radio_minimum_reuse: FloatProperty( - name="Minimum Reuse", - description="Fraction of the screen width which sets the minimum radius of reuse " - "for each sample point (At values higher than 2% expect errors)", - min=0.0, max=1.0, soft_min=0.1, soft_max=0.1, default=0.015, precision=3 + # TODO: Auto find POV sound directory as it does for binary + # And implement the three cases, left uncommented for a dummy + # interface in case some doc screenshots get made for that area + filepath_complete_sound: StringProperty( + name="Finish Render Sound", + description="Path to finished render sound file", + subtype="FILE_PATH", ) - radio_maximum_reuse: FloatProperty( - name="Maximum Reuse", - description="The maximum reuse parameter works in conjunction with, and is similar to that of minimum reuse, " - "the only difference being that it is an upper bound rather than a lower one", - min=0.0, max=1.0,default=0.2, precision=3 + filepath_parse_error_sound: StringProperty( + name="Parse Error Sound", + description="Path to parsing time error sound file", + subtype="FILE_PATH", ) - radio_nearest_count: IntProperty( - name="Nearest Count", - description="Number of old ambient values blended together to " - "create a new interpolated value", - min=1, max=20, default=1 + filepath_cancel_sound: StringProperty( + name="Cancel Render Sound", + description="Path to cancelled or render time error sound file", + subtype="FILE_PATH", ) - radio_normal: BoolProperty( - name="Normals", - description="Radiosity estimation can be affected by normals", - default=False, - ) + def draw(self, context): + layout = self.layout + layout.prop(self, "branch_feature_set_povray") + layout.prop(self, "filepath_povray") + layout.prop(self, "docpath_povray") + layout.prop(self, "filepath_complete_sound") + layout.prop(self, "filepath_parse_error_sound") + layout.prop(self, "filepath_cancel_sound") + layout.prop(self, "use_sounds", icon="SOUND") + layout.operator("pov.update_addon", icon='FILE_REFRESH') - radio_recursion_limit: IntProperty( - name="Recursion Limit", - description="how many recursion levels are used to calculate " - "the diffuse inter-reflection", - min=1, max=20, default=1 - ) - radio_pretrace_start: FloatProperty( - name="Pretrace Start", - description="Fraction of the screen width which sets the size of the " - "blocks in the mosaic preview first pass", - min=0.005, max=1.00, soft_min=0.02, soft_max=1.0, default=0.04 - ) - # XXX TODO set automatically to pretrace_end = 8 / max (image_width, image_height) - # for non advanced mode - radio_pretrace_end: FloatProperty( - name="Pretrace End", - description="Fraction of the screen width which sets the size of the blocks " - "in the mosaic preview last pass", - min=0.000925, max=1.00, soft_min=0.01, soft_max=1.00, default=0.004, precision=3 - ) +classes = (POV_OT_update_addon, PovrayPreferences) -############################################################################### -# Material POV properties. -############################################################################### -class MaterialTextureSlot(PropertyGroup): - """Declare material texture slot level properties for UI and translated to POV.""" - bl_idname="pov_texture_slots", - bl_description="Texture_slots from Blender-2.79", +def register(): + for cls in classes: + register_class(cls) - # Adding a "real" texture datablock as property is not possible - # (or at least not easy through a dynamically populated EnumProperty). - # That's why we'll use a prop_search() UILayout function in ui.py. - # So we'll assign the name of the needed texture datablock to the below StringProperty. - texture : StringProperty(update=active_texture_name_from_uilist) - # and use another temporary StringProperty to change the linked data - texture_search : StringProperty( - name="", - update = active_texture_name_from_search, - description = "Browse Texture to be linked", - ) + render_properties.register() + scenography_properties.register() + shading_properties.register() + texturing_properties.register() + object_properties.register() + scripting_properties.register() + scenography.register() + render.register() + base_ui.register() + scripting.register() + object_primitives.register() - alpha_factor: FloatProperty( - name="Alpha", - description="Amount texture affects alpha", - default = 1.0, - ) - ambient_factor: FloatProperty( - name="", - description="Amount texture affects ambient", - default = 1.0, - ) +def unregister(): + object_primitives.unregister() + scripting.unregister() + base_ui.unregister() + render.unregister() + scenography.register() + scripting_properties.unregister() + object_properties.unregister() + texturing_properties.unregister() + shading_properties.unregister() + scenography_properties.unregister() + render_properties.unregister() - bump_method: EnumProperty( - name="", - description="Method to use for bump mapping", - items=( - ("BUMP_ORIGINAL", "Bump Original", ""), - ("BUMP_COMPATIBLE", "Bump Compatible", ""), - ("BUMP_DEFAULT", "Bump Default", ""), - ("BUMP_BEST_QUALITY", "Bump Best Quality", "") - ), - default="BUMP_ORIGINAL", - ) + for cls in classes: + unregister_class(cls) - bump_objectspace: EnumProperty( - name="", - description="Space to apply bump mapping in", - items=( - ("BUMP_VIEWSPACE", "Bump Viewspace", ""), - ("BUMP_OBJECTSPACE", "Bump Objectspace", ""), - ("BUMP_TEXTURESPACE", "Bump Texturespace", "") - ), - default="BUMP_VIEWSPACE", - ) - density_factor: FloatProperty( - name="", - description="Amount texture affects density", - default = 1.0, - ) +if __name__ == "__main__": + register() - diffuse_color_factor: FloatProperty( - name="", - description="Amount texture affects diffuse color", - default = 1.0, - ) - - diffuse_factor: FloatProperty( - name="", - description="Amount texture affects diffuse reflectivity", - default = 1.0, - ) - - displacement_factor: FloatProperty( - name="", - description="Amount texture displaces the surface", - default = 0.2, - ) - - emission_color_factor: FloatProperty( - name="", - description="Amount texture affects emission color", - default = 1.0, - ) - - emission_factor: FloatProperty( - name="", - description="Amount texture affects emission", - default = 1.0, - ) - - emit_factor: FloatProperty( - name="", - description="Amount texture affects emission", - default = 1.0, - ) - - hardness_factor: FloatProperty( - name="", - description="Amount texture affects hardness", - default = 1.0, - ) - - mapping: EnumProperty( - name="", - description="", - items=(("FLAT", "Flat", ""), - ("CUBE", "Cube", ""), - ("TUBE", "Tube", ""), - ("SPHERE", "Sphere", "")), - default="FLAT", - ) - - mapping_x: EnumProperty( - name="", - description="", - items=(("NONE", "", ""), - ("X", "", ""), - ("Y", "", ""), - ("Z", "", "")), - default="NONE", - ) - - mapping_y: EnumProperty( - name="", - description="", - items=(("NONE", "", ""), - ("X", "", ""), - ("Y", "", ""), - ("Z", "", "")), - default="NONE", - ) - - mapping_z: EnumProperty( - name="", - description="", - items=(("NONE", "", ""), - ("X", "", ""), - ("Y", "", ""), - ("Z", "", "")), - default="NONE", - ) - - mirror_factor: FloatProperty( - name="", - description="Amount texture affects mirror color", - default = 1.0, - ) - - normal_factor: FloatProperty( - name="", - description="Amount texture affects normal values", - default = 1.0, - ) - - normal_map_space: EnumProperty( - name="", - description="Sets space of normal map image", - items=(("CAMERA", "Camera", ""), - ("WORLD", "World", ""), - ("OBJECT", "Object", ""), - ("TANGENT", "Tangent", "")), - default="CAMERA", - ) - - object: StringProperty( - name="Object", - description="Object to use for mapping with Object texture coordinates", - default ="", - ) - - raymir_factor: FloatProperty( - name="", - description="Amount texture affects ray mirror", - default = 1.0, - ) - - reflection_color_factor: FloatProperty( - name="", - description="Amount texture affects color of out-scattered light", - default = 1.0, - ) - - reflection_factor: FloatProperty( - name="", - description="Amount texture affects brightness of out-scattered light", - default = 1.0, - ) - - scattering_factor: FloatProperty( - name="", - description="Amount texture affects scattering", - default = 1.0, - ) - - specular_color_factor: FloatProperty( - name="", - description="Amount texture affects specular color", - default = 1.0, - ) - - specular_factor: FloatProperty( - name="", - description="Amount texture affects specular reflectivity", - default = 1.0, - ) - - offset: FloatVectorProperty( - name="Offset", - description=("Fine tune of the texture mapping X, Y and Z locations "), - precision=4, - step=0.1, - soft_min= -100.0, - soft_max=100.0, - default=(0.0,0.0,0.0), - options={'ANIMATABLE'}, - subtype='TRANSLATION', - ) - - scale: FloatVectorProperty( - name="Size", - subtype='XYZ', - size=3, - description="Set scaling for the texture’s X, Y and Z sizes ", - precision=4, - step=0.1, - soft_min= -100.0, - soft_max=100.0, - default=(1.0,1.0,1.0), - options={'ANIMATABLE'}, - ) - - - texture_coords: EnumProperty( - name="", - description="", - items=( - ("GLOBAL", "Global", ""), - ("OBJECT", "Object", ""), - ("UV", "UV", ""), - ("ORCO", "Original Coordinates", ""), - ("STRAND", "Strand", ""), - ("STICKY", "Sticky", ""), - ("WINDOW", "Window", ""), - ("NORMAL", "Normal", ""), - ("REFLECTION", "Reflection", ""), - ("STRESS", "Stress", ""), - ("TANGENT", "Tangent", "") - ), - default="GLOBAL", - ) - - translucency_factor: FloatProperty( - name="", - description="Amount texture affects translucency", - default = 1.0, - ) - - transmission_color_factor: FloatProperty( - name="", - description="Amount texture affects result color after light has been scattered/absorbed", - default = 1.0, - ) - - use: BoolProperty( - name="", - description="Enable this material texture slot", - default = True, - ) - - use_from_dupli: BoolProperty( - name="", - description="Dupli’s instanced from verts, faces or particles, inherit texture coordinate from their parent", - default = False, - ) - - use_from_original: BoolProperty( - name="", - description="Dupli’s derive their object coordinates from the original objects transformation", - default = False, - ) - - use_interpolation: BoolProperty( - name="", - description="Interpolates pixels using selected filter ", - default = False, - ) - - use_map_alpha: BoolProperty( - name="", - description="Causes the texture to affect the alpha value", - default = False, - ) - - use_map_ambient: BoolProperty( - name="", - description="Causes the texture to affect the value of ambient", - default = False, - ) - - use_map_color_diffuse: BoolProperty( - name="", - description="Causes the texture to affect basic color of the material", - default = True, - ) - - use_map_color_emission: BoolProperty( - name="", - description="Causes the texture to affect the color of emission", - default = False, - ) - - use_map_color_reflection: BoolProperty( - name="", - description="Causes the texture to affect the color of scattered light", - default = False, - ) - - use_map_color_spec: BoolProperty( - name="", - description="Causes the texture to affect the specularity color", - default = False, - ) - - use_map_color_transmission: BoolProperty( - name="", - description="Causes the texture to affect the result color after other light has been scattered/absorbed", - default = False, - ) - - use_map_density: BoolProperty( - name="", - description="Causes the texture to affect the volume’s density", - default = False, - ) - - use_map_diffuse: BoolProperty( - name="", - description="Causes the texture to affect the value of the materials diffuse reflectivity", - default = False, - ) - - use_map_displacement: BoolProperty( - name="", - description="Let the texture displace the surface", - default = False, - ) - - use_map_emission: BoolProperty( - name="", - description="Causes the texture to affect the volume’s emission", - default = False, - ) - - use_map_emit: BoolProperty( - name="", - description="Causes the texture to affect the emit value", - default = False, - ) - - use_map_hardness: BoolProperty( - name="", - description="Causes the texture to affect the hardness value", - default = False, - ) - - use_map_mirror: BoolProperty( - name="", - description="Causes the texture to affect the mirror color", - default = False, - ) - - use_map_normal: BoolProperty( - name="", - description="Causes the texture to affect the rendered normal", - default = False, - ) - - use_map_raymir: BoolProperty( - name="", - description="Causes the texture to affect the ray-mirror value", - default = False, - ) - - use_map_reflect: BoolProperty( - name="", - description="Causes the texture to affect the reflected light’s brightness", - default = False, - ) - - use_map_scatter: BoolProperty( - name="", - description="Causes the texture to affect the volume’s scattering", - default = False, - ) - - use_map_specular: BoolProperty( - name="", - description="Causes the texture to affect the value of specular reflectivity", - default = False, - ) - - use_map_translucency: BoolProperty( - name="", - description="Causes the texture to affect the translucency value", - default = False, - ) - - use_map_warp: BoolProperty( - name="", - description="Let the texture warp texture coordinates of next channels", - default = False, - ) - - uv_layer: StringProperty( - name="", - description="UV layer to use for mapping with UV texture coordinates", - default = "", - ) - - warp_factor: FloatProperty( - name="", - description="Amount texture affects texture coordinates of next channels", - default = 0.0, - ) - - -####################################### - - blend_factor: FloatProperty( - name="Blend", - description="Amount texture affects color progression of the " - "background", - soft_min=0.0, soft_max=1.0, default=1.0, - ) - - horizon_factor: FloatProperty( - name="Horizon", - description="Amount texture affects color of the horizon" - "", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - object: StringProperty( - name="Object", - description="Object to use for mapping with Object texture coordinates", - default="", - ) - - texture_coords: EnumProperty( - name="Coordinates", - description="Texture coordinates used to map the texture onto the background", - items=( - ("VIEW", "View", "Use view vector for the texture coordinates"), - ("GLOBAL", "Global", "Use global coordinates for the texture coordinates (interior mist)"), - ("ANGMAP", "AngMap", "Use 360 degree angular coordinates, e.g. for spherical light probes"), - ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"), - ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"), - ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"), - ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates") - ), - default="VIEW", - ) - - use_map_blend: BoolProperty( - name="Blend Map", - description="Affect the color progression of the background", - default=True, - ) - - use_map_horizon: BoolProperty( - name="Horizon Map", - description="Affect the color of the horizon", - default=False, - ) - - use_map_zenith_down: BoolProperty( - name="", description="Affect the color of the zenith below", - default=False, - ) - - use_map_zenith_up: BoolProperty( - name="Zenith Up Map", description="Affect the color of the zenith above", - default=False, - ) - - zenith_down_factor: FloatProperty( - name="Zenith Down", - description="Amount texture affects color of the zenith below", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - zenith_up_factor: FloatProperty( - name="Zenith Up", - description="Amount texture affects color of the zenith above", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - -# former Space properties from removed Blender Internal added below at superclass level -# so as to be available in World, Material, Light for texture slots use - -bpy.types.ID.use_limited_texture_context = BoolProperty( - name="", - description="Use the limited version of texture user (for â€old shading’ mode)", - default=True, -) -bpy.types.ID.texture_context = EnumProperty( - name="Texture context", - description="Type of texture data to display and edit", - items=( - ('MATERIAL', "", "Show material textures", "MATERIAL",0), # "Show material textures" - ('WORLD', "", "Show world textures", "WORLD",1), # "Show world textures" - ('LIGHT', "", "Show lamp textures", "LIGHT",2), # "Show lamp textures" - ('PARTICLES', "", "Show particles textures", "PARTICLES",3), # "Show particles textures" - ('LINESTYLE', "", "Show linestyle textures", "LINE_DATA",4), # "Show linestyle textures" - ('OTHER', "", "Show other data textures", "TEXTURE_DATA",5) # "Show other data textures" - ), - default = 'MATERIAL', -) - -# bpy.types.ID.active_texture_index = IntProperty( - # name = "Index for texture_slots", - # default = 0, -# ) - -class RenderPovSettingsMaterial(PropertyGroup): - """Declare material level properties controllable in UI and translated to POV.""" - - ######################Begin Old Blender Internal Props######################### - # former Space properties from removed Blender Internal - use_limited_texture_context: BoolProperty( - name="", - description="Use the limited version of texture user (for â€old shading’ mode)", - default=True, - ) - texture_context: EnumProperty( - name="Texture context", - description="Type of texture data to display and edit", - items=( - ('MATERIAL', "", "Show material textures", "MATERIAL",0), # "Show material textures" - ('WORLD', "", "Show world textures", "WORLD",1), # "Show world textures" - ('LAMP', "", "Show lamp textures", "LIGHT",2), # "Show lamp textures" - ('PARTICLES', "", "Show particles textures", "PARTICLES",3), # "Show particles textures" - ('LINESTYLE', "", "Show linestyle textures", "LINE_DATA",4), # "Show linestyle textures" - ('OTHER', "", "Show other data textures", "TEXTURE_DATA",5) # "Show other data textures" - ), - default = 'MATERIAL', - ) - - active_texture_index: IntProperty( - name = "Index for texture_slots", - default = 0, - update = brush_texture_update - ) - - transparency_method: EnumProperty( - name="Specular Shader Model", - description="Method to use for rendering transparency", - items=( - ("MASK", "Mask", "Mask the background"), - ("Z_TRANSPARENCY", "Z Transparency", "Use alpha buffer for transparent faces"), - ("RAYTRACE", "Raytrace", "Use raytracing for transparent refraction rendering") - ), - default="MASK", - ) - - use_transparency: BoolProperty( - name="Transparency", - description="Render material as transparent", - default=False, - ) - - alpha: FloatProperty( - name="Alpha", - description="Alpha transparency of the material", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3, - ) - - specular_alpha: FloatProperty( - name="Specular alpha", - description="Alpha transparency for specular areas", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3, - ) - - ambient: FloatProperty( - name="Ambient", - description="Amount of global ambient color the material receives", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3, - ) - - diffuse_color: FloatVectorProperty( - name="Diffuse color", - description=("Diffuse color of the material"), - precision=4, step=0.01, min=0, # max=inf, soft_max=1, - default=(0.6,0.6,0.6), options={'ANIMATABLE'}, subtype='COLOR', - ) - - darkness: FloatProperty( - name="Darkness", - description="Minnaert darkness", - min=0.0, max=2.0, soft_min=0.0, soft_max=2.0, default=1.0, precision=3, - ) - - diffuse_fresnel: FloatProperty( - name="Diffuse fresnel", - description="Power of Fresnel", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=1.0, precision=3, - ) - - diffuse_fresnel_factor: FloatProperty( - name="Diffuse fresnel factor", - description="Blending factor of Fresnel", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=0.5, precision=3, - ) - - diffuse_intensity: FloatProperty( - name="Diffuse intensity", - description="Amount of diffuse reflection multiplying color", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.8, precision=3, - ) - - diffuse_ramp_blend: EnumProperty( - name="Diffuse ramp blend", - description="Blending method of the ramp and the diffuse color", - items=( - ("MIX", "Mix", ""), - ("ADD", "Add", ""), - ("MULTIPLY", "Multiply", ""), - ("SUBTRACT", "Subtract", ""), - ("SCREEN", "Screen", ""), - ("DIVIDE", "Divide", ""), - ("DIFFERENCE", "Difference", ""), - ("DARKEN", "Darken", ""), - ("LIGHTEN", "Lighten", ""), - ("OVERLAY", "Overlay", ""), - ("DODGE", "Dodge", ""), - ("BURN", "Burn", ""), - ("HUE", "Hue", ""), - ("SATURATION", "Saturation", ""), - ("VALUE", "Value", ""), - ("COLOR", "Color", ""), - ("SOFT_LIGHT", "Soft light", ""), - ("LINEAR_LIGHT", "Linear light", "") - ), - default="MIX", - ) - - diffuse_ramp_factor: FloatProperty( - name="Factor", - description="Blending factor (also uses alpha in Colorband)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3, - ) - - diffuse_ramp_input: EnumProperty( - name="Input", - description="How the ramp maps on the surface", - items=( - ("SHADER", "Shader", ""), - ("ENERGY", "Energy", ""), - ("NORMAL", "Normal", ""), - ("RESULT", "Result", "") - ), - default="SHADER", - ) - - diffuse_shader: EnumProperty( - name="Diffuse Shader Model", - description="How the ramp maps on the surface", - items=( - ("LAMBERT", "Lambert", "Use a Lambertian shader"), - ("OREN_NAYAR", "Oren-Nayar", "Use an Oren-Nayar shader"), - ("MINNAERT", "Minnaert", "Use a Minnaert shader"), - ("FRESNEL", "Fresnel", "Use a Fresnel shader") - ), - default="LAMBERT", - ) - - diffuse_toon_size: FloatProperty( - name="Size", - description="Size of diffuse toon area", - min=0.0, max=3.14, soft_min=0.0, soft_max=3.14, default=0.5, precision=3, - ) - - diffuse_toon_smooth: FloatProperty( - name="Smooth", - description="Smoothness of diffuse toon area", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.1, precision=3, - ) - - emit: FloatProperty( - name="Emit", - description="Amount of light to emit", - min=0.0, soft_min=0.0, # max=inf, soft_max=inf, - default=0.0, precision=3, - ) - - mirror_color: FloatVectorProperty( - name="Mirror color", - description=("Mirror color of the material"), - precision=4, step=0.01, min=0, # max=inf, soft_max=1, - default=(0.6,0.6,0.6), options={'ANIMATABLE'}, subtype='COLOR' - ) - - roughness: FloatProperty( - name="Roughness", - description="Oren-Nayar Roughness", - min=0.0, max=3.14, soft_min=0.0, soft_max=3.14, - precision=3, - default=0.5, - ) - - halo: BoolProperty( - name="Halo", - description=" Halo settings for the material", - default=False, - ) - # (was readonly in Blender2.79, never None) - - line_color: FloatVectorProperty( - name="Line color", - description=("Line color used for Freestyle line rendering"), - precision=4, step=0.01, min=0, # max=inf, soft_max=1, - default=(0.0,0.0,0.0), options={'ANIMATABLE'}, subtype='COLOR' - ) - - # diffuse_ramp: - ## Color ramp used to affect diffuse shading - ## Type: ColorRamp, (readonly) - - # cr_node = bpy.data.materials['Material'].node_tree.nodes['ColorRamp'] - # layout.template_color_ramp(cr_node, "color_ramp", expand=True) - - # ou - - # class bpy.types.ColorRamp(bpy_struct) - - line_priority: IntProperty( - name="Recursion Limit", - description="The line color of a higher priority is used at material boundaries", - min=0, max=32767, default=0, - ) - - specular_color: FloatVectorProperty( - name="Specular color", - description=("Specular color of the material "), - precision=4, step=0.01, min=0, # max=inf, soft_max=1, - default=(1.0,1.0,1.0), options={'ANIMATABLE'}, subtype='COLOR' - ) - - specular_hardness: IntProperty( - name="Hardness", - description="How hard (sharp) the specular reflection is", - min=1, max=511, default=50, - ) - - specular_intensity: FloatProperty( - name="Intensity", - description="How intense (bright) the specular reflection is", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.5, precision=3 - ) - - specular_ior: FloatProperty( - name="IOR", - description="Specular index of refraction", - min=-10.0, max=10.0, soft_min=0.0, soft_max=10.0, default=1.0, precision=3 - ) - - ior: FloatProperty( - name="IOR", - description="Index of refraction", - min=-10.0, max=10.0, soft_min=0.0, soft_max=10.0, default=1.0, precision=3 - ) - - specular_shader: EnumProperty( - name="Specular Shader Model", - description="How the ramp maps on the surface", - items=( - ("COOKTORR", "CookTorr", "Use a Cook-Torrance shader"), - ("PHONG", "Phong", "Use a Phong shader"), - ("BLINN", "Blinn", "Use a Blinn shader"), - ("TOON", "Toon", "Use a Toon shader"), - ("WARDISO", "WardIso", "Use a Ward anisotropic shader") - ), - default="COOKTORR", - ) - - specular_slope: FloatProperty( - name="Slope", - description="The standard deviation of surface slope", - min=0.0, max=0.4, soft_min=0.0, soft_max=0.4, default=0.1, precision=3 - ) - - specular_toon_size: FloatProperty( - name="Size", - description="Size of specular toon area", - min=0.0, max=0.53, soft_min=0.0, soft_max=0.53, default=0.5, precision=3 - ) - - specular_toon_smooth: FloatProperty( - name="Smooth", - description="Smoothness of specular toon area", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.1, precision=3 - ) - - - translucency: FloatProperty( - name="Translucency", - description="Amount of diffuse shading on the back side", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.0, precision=3 - ) - - transparency_method: EnumProperty( - name="Specular Shader Model", - description="Method to use for rendering transparency", - items=( - ("MASK", "Mask", "Mask the background"), - ("Z_TRANSPARENCY", "Z Transparency", "Use an ior of 1 for transparent faces"), - ("RAYTRACE", "Raytrace", "Use raytracing for transparent refraction rendering") - ), - default="MASK", - ) - - type: EnumProperty( - name="Type", - description="Material type defining how the object is rendered", - items=( - ("SURFACE", "Surface", "Render object as a surface"), - ("WIRE", "Wire", "Render the edges of faces as wires (not supported in raytracing)"),# TO UPDATE > USE MACRO AND CHANGE DESCRIPTION - ("VOLUME", "Volume", "Render object as a volume"), - ("â€HALO’", "Halo", "Render object as halo particles") - ), # TO UPDATE > USE MACRO AND CHANGE DESCRIPTION - default="SURFACE", - ) - - use_cast_shadows: BoolProperty( - name="Cast", - description="Allow this material to cast shadows", - default=True, - ) - - use_cast_shadows_only: BoolProperty( - name="Cast Only", - description="Make objects with this material " - "appear invisible (not rendered), only " - "casting shadows", - default=False, - ) - - use_cubic: BoolProperty( - name="Cubic Interpolation", - description="Use cubic interpolation for diffuse " - "values, for smoother transitions", - default=False, - ) - - use_diffuse_ramp: BoolProperty( - name="Ramp", - description="Toggle diffuse ramp operations", - default=False, - ) - - use_light_group_exclusive: BoolProperty( - name="Exclusive", - description="Material uses the light group exclusively" - "- these lamps are excluded from other " - "scene lighting", - default=False, - ) - - use_light_group_local: BoolProperty( - name="Local", - description="When linked in, material uses local light" - " group with the same name", - default=False, - ) - - use_mist: BoolProperty( - name="Use Mist", - description="Use mist with this material " - "(in world settings)", - default=True, - ) - - use_nodes: BoolProperty( - name="Nodes", - description="Use shader nodes to render the material",# Add Icon in UI or here? icon='NODES' - default=False, - ) - - use_object_color: BoolProperty( - name="Object Color", - description="Modulate the result with a per-object color", - default=False, - ) - - use_only_shadow: BoolProperty( - name="Shadows Only", - description="Render shadows as the material’s alpha " - "value, making the material transparent " - "except for shadowed areas", - default=False, - ) - - use_shadeless: BoolProperty( - name="Shadeless", - description="Make this material insensitive to " - "light or shadow", - default=False, - ) - - use_shadows: BoolProperty( - name="Receive", - description="Allow this material to receive shadows", - default=True, - ) - - use_sky: BoolProperty( - name="Sky", - description="Render this material with zero alpha, " - "with sky background in place (scanline only)", - default=False, - ) - - use_specular_ramp: BoolProperty( - name="Ramp", - description="Toggle specular ramp operations", - default=False, - ) - - use_tangent_shading: BoolProperty( - name="Tangent Shading", - description="Use the material’s tangent vector instead" - "of the normal for shading - for " - "anisotropic shading effects", - default=False, - ) - - use_transparent_shadows: BoolProperty( - name="Receive Transparent", - description="Allow this object to receive transparent " - "shadows cast through other object", - default=False, - ) # linked to fake caustics - - use_vertex_color_light: BoolProperty( - name="Vertex Color Light", - description="Add vertex colors as additional lighting", - default=False, - ) - - use_vertex_color_paint: BoolProperty( - name="Vertex Color Paint", description="Replace object base color with vertex " - "colors (multiply with â€texture face’ " - "face assigned textures)", - default=False, - ) - - - specular_ramp_blend: EnumProperty( - name="Specular ramp blend", - description="Blending method of the ramp and the specular color", - items=( - ("MIX", "Mix", ""), - ("ADD", "Add", ""), - ("MULTIPLY", "Multiply", ""), - ("SUBTRACT", "Subtract", ""), - ("SCREEN", "Screen", ""), - ("DIVIDE", "Divide", ""), - ("DIFFERENCE", "Difference", ""), - ("DARKEN", "Darken", ""), - ("LIGHTEN", "Lighten", ""), - ("OVERLAY", "Overlay", ""), - ("DODGE", "Dodge", ""), - ("BURN", "Burn", ""), - ("HUE", "Hue", ""), - ("SATURATION", "Saturation", ""), - ("VALUE", "Value", ""), - ("COLOR", "Color", ""), - ("SOFT_LIGHT", "Soft light", ""), - ("LINEAR_LIGHT", "Linear light", "") - ), - default="MIX", - ) - - specular_ramp_factor: FloatProperty( - name="Factor", - description="Blending factor (also uses alpha in Colorband)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3, - ) - - specular_ramp_input: EnumProperty( - name="Input", - description="How the ramp maps on the surface", - items=( - ("SHADER", "Shader", ""), - ("ENERGY", "Energy", ""), - ("NORMAL", "Normal", ""), - ("RESULT", "Result", "") - ), - default="SHADER", - ) - - - irid_enable: BoolProperty( - name="Iridescence coating", - description="Newton's thin film interference (like an oil slick on a puddle of " - "water or the rainbow hues of a soap bubble.)", - default=False, - ) - - mirror_use_IOR: BoolProperty( - name="Correct Reflection", - description="Use same IOR as raytrace transparency to calculate mirror reflections. " - "More physically correct", - default=False, - ) - - mirror_metallic: BoolProperty( - name="Metallic Reflection", - description="mirror reflections get colored as diffuse (for metallic materials)", - default=False, - ) - - conserve_energy: BoolProperty( - name="Conserve Energy", - description="Light transmitted is more correctly reduced by mirror reflections, " - "also the sum of diffuse and translucency gets reduced below one ", - default=True, - ) - - irid_amount: FloatProperty( - name="amount", - description="Contribution of the iridescence effect to the overall surface color. " - "As a rule of thumb keep to around 0.25 (25% contribution) or less, " - "but experiment. If the surface is coming out too white, try lowering " - "the diffuse and possibly the ambient values of the surface", - min=0.0, max=1.0, soft_min=0.01, soft_max=1.0, default=0.25, - ) - - irid_thickness: FloatProperty( - name="thickness", - description="A very thin film will have a high frequency of color changes while a " - "thick film will have large areas of color", - min=0.0, max=1000.0, soft_min=0.1, soft_max=10.0, default=1, - ) - - irid_turbulence: FloatProperty( - name="turbulence", - description="This parameter varies the thickness", - min=0.0, max=10.0, soft_min=0.000, soft_max=1.0, default=0 - ) - - interior_fade_color: FloatVectorProperty( - name="Interior Fade Color", - description="Color of filtered attenuation for transparent " - "materials", - precision=4, step=0.01, min=0.0, soft_max=1.0, - default=(0, 0, 0), options={'ANIMATABLE'}, subtype='COLOR' - ) - - caustics_enable: BoolProperty( - name="Caustics", - description="use only fake refractive caustics (default) or photon based " - "reflective/refractive caustics", - default=True, - ) - - fake_caustics: BoolProperty( - name="Fake Caustics", - description="use only (Fast) fake refractive caustics", - default=True, - ) - - fake_caustics_power: FloatProperty( - name="Fake caustics power", - description="Values typically range from 0.0 to 1.0 or higher. Zero is no caustics. " - "Low, non-zero values give broad hot-spots while higher values give " - "tighter, smaller simulated focal points", - min=0.00, max=10.0, soft_min=0.00, soft_max=5.0, default=0.15 - ) - - refraction_caustics: BoolProperty( - name="Refractive Caustics", - description="hotspots of light focused when going through the material", - default=True, - ) - - photons_dispersion: FloatProperty( - name="Chromatic Dispersion", - description="Light passing through will be separated according to wavelength. " - "This ratio of refractive indices for violet to red controls how much " - "the colors are spread out 1 = no dispersion, good values are 1.01 to 1.1", - min=1.0000, max=10.000, soft_min=1.0000, soft_max=1.1000, precision=4, - default=1.0000, - ) - - photons_dispersion_samples: IntProperty( - name="Dispersion Samples", - description="Number of color-steps for dispersion", - min=2, max=128, default=7, - ) - - photons_reflection: BoolProperty( - name="Reflective Photon Caustics", - description="Use this to make your Sauron's ring ;-P", - default=False, - ) - - refraction_type: EnumProperty( - items=[ - ("1", "Z Transparency Fake Caustics", "use fake caustics"), - ("2", "Raytrace Photons Caustics", "use photons for refractive caustics")], - name="Refraction Type:", - description="use fake caustics (fast) or true photons for refractive Caustics", - default="1", - ) - - ##################################CustomPOV Code############################ - replacement_text: StringProperty( - name="Declared name:", - description="Type the declared name in custom POV code or an external " - ".inc it points at. texture {} expected", - default="", - ) - - - # NODES - - def use_material_nodes_callback(self, context): - if hasattr(context.space_data, "tree_type"): - context.space_data.tree_type = 'ObjectNodeTree' - mat=context.object.active_material - if mat.pov.material_use_nodes: - mat.use_nodes=True - tree = mat.node_tree - tree.name=mat.name - links = tree.links - default = True - if len(tree.nodes) == 2: - o = 0 - m = 0 - for node in tree.nodes: - if node.type in {"OUTPUT","MATERIAL"}: - tree.nodes.remove(node) - default = True - for node in tree.nodes: - if node.bl_idname == 'PovrayOutputNode': - o+=1 - if node.bl_idname == 'PovrayTextureNode': - m+=1 - if o == 1 and m == 1: - default = False - elif len(tree.nodes) == 0: - default = True - else: - default = False - if default: - output = tree.nodes.new('PovrayOutputNode') - output.location = 200,200 - tmap = tree.nodes.new('PovrayTextureNode') - tmap.location = 0,200 - links.new(tmap.outputs[0],output.inputs[0]) - tmap.select = True - tree.nodes.active = tmap - else: - mat.use_nodes=False - - - def use_texture_nodes_callback(self, context): - tex=context.object.active_material.active_texture - if tex.pov.texture_use_nodes: - tex.use_nodes=True - if len(tex.node_tree.nodes)==2: - for node in tex.node_tree.nodes: - if node.type in {"OUTPUT","CHECKER"}: - tex.node_tree.nodes.remove(node) - else: - tex.use_nodes=False - - def node_active_callback(self, context): - items = [] - mat=context.material - mat.node_tree.nodes - for node in mat.node_tree.nodes: - node.select=False - for node in mat.node_tree.nodes: - if node.name==mat.pov.material_active_node: - node.select=True - mat.node_tree.nodes.active=node - - return node - - def node_enum_callback(self, context): - items = [] - mat=context.material - nodes=mat.node_tree.nodes - for node in nodes: - items.append(("%s"%node.name,"%s"%node.name,"")) - return items - - def pigment_normal_callback(self, context): - render = context.scene.pov.render - items = [("pigment", "Pigment", ""),("normal", "Normal", "")] - if render == 'hgpovray': - items = [("pigment", "Pigment", ""),("normal", "Normal", ""),("modulation", "Modulation", "")] - return items - - def glow_callback(self, context): - scene = context.scene - ob = context.object - ob.pov.mesh_write_as_old = ob.pov.mesh_write_as - if scene.pov.render == 'uberpov' and ob.pov.glow: - ob.pov.mesh_write_as = 'NONE' - else: - ob.pov.mesh_write_as = ob.pov.mesh_write_as_old - - material_use_nodes: BoolProperty( - name="Use nodes", - description="", - update=use_material_nodes_callback, - default=False, - ) - - material_active_node: EnumProperty( - name="Active node", - description="", - items=node_enum_callback, - update=node_active_callback - ) - - preview_settings: BoolProperty( - name="Preview Settings", - description="", - default=False, - ) - - object_preview_transform: BoolProperty( - name="Transform object", - description="", - default=False, - ) - - object_preview_scale: FloatProperty( - name="XYZ", - min=0.5, - max=2.0, - default=1.0, - ) - - object_preview_rotate: FloatVectorProperty( - name="Rotate", - description="", - min=-180.0, - max=180.0, - default=(0.0,0.0,0.0), - subtype='XYZ', - ) - - object_preview_bgcontrast: FloatProperty( - name="Contrast", - min=0.0, - max=1.0, - default=0.5, - ) - - -class MaterialRaytraceTransparency(PropertyGroup): - """Declare transparency panel properties controllable in UI and translated to POV.""" - - depth: IntProperty( - name="Depth", - description="Maximum allowed number of light inter-refractions", - min=0, max=32767, default=2 - ) - - depth_max: FloatProperty( - name="Depth", - description="Maximum depth for light to travel through the " - "transparent material before becoming fully filtered (0.0 is disabled)", - min=0, max=100, default=0.0, - ) - - falloff: FloatProperty( - name="Falloff", - description="Falloff power for transmissivity filter effect (1.0 is linear)", - min=0.1, max=10.0, default=1.0, precision=3 - ) - - filter: FloatProperty( - name="Filter", - description="Amount to blend in the material’s diffuse color in raytraced " - "transparency (simulating absorption)", - min=0.0, max=1.0, default=0.0, precision=3 - ) - - fresnel: FloatProperty( - name="Fresnel", - description="Power of Fresnel for transparency (Ray or ZTransp)", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=0.0, precision=3 - ) - - fresnel_factor: FloatProperty( - name="Blend", - description="Blending factor for Fresnel", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=1.250, precision=3 - ) - - gloss_factor: FloatProperty( - name="Amount", - description="The clarity of the refraction. " - "(values < 1.0 give diffuse, blurry refractions)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3 - ) - - gloss_samples: IntProperty( - name="Samples", - description="frequency of the noise sample used for blurry refractions", - min=0, max=1024, default=18 - ) - - gloss_threshold: FloatProperty( - name="Threshold", - description="Threshold for adaptive sampling (if a sample " - "contributes less than this amount [as a percentage], " - "sampling is stopped)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.005, precision=3 - ) - - ior: FloatProperty( - name="IOR", - description="Sets angular index of refraction for raytraced refraction", - min=-0.0, max=10.0, soft_min=0.25, soft_max=4.0, default=1.3 - ) - -class MaterialRaytraceMirror(PropertyGroup): - """Declare reflection panel properties controllable in UI and translated to POV.""" - - bl_description = "Raytraced reflection settings for the Material", - - use: BoolProperty( - name="Mirror", - description="Enable raytraced reflections", - default=False, - ) - - - depth: IntProperty( - name="Depth", - description="Maximum allowed number of light inter-reflections", - min=0, max=32767, default=2 - ) - - distance: FloatProperty( - name="Max Dist", - description="Maximum distance of reflected rays " - "(reflections further than this range " - "fade to sky color or material color)", - min=0.0, max=100000.0, soft_min=0.0, soft_max=10000.0, default=0.0, precision=3 - ) - - fade_to: EnumProperty( - items=[ - ("FADE_TO_SKY", "Fade to sky", ""), - ("FADE_TO_MATERIAL", "Fade to material color", "")], - name="Fade-out Color", - description="The color that rays with no intersection within the " - "Max Distance take (material color can be best for " - "indoor scenes, sky color for outdoor)", - default="FADE_TO_SKY", - ) - - fresnel: FloatProperty( - name="Fresnel", - description="Power of Fresnel for mirror reflection", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=0.0, precision=3, - ) - - fresnel_factor: FloatProperty( - name="Blend", - description="Blending factor for Fresnel", - min=0.0, max=5.0, soft_min=0.0, soft_max=5.0, default=1.250, precision=3 - ) - - gloss_anisotropic: FloatProperty( - name="Anisotropic", - description="The shape of the reflection, from 0.0 (circular) " - "to 1.0 (fully stretched along the tangent", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3 - ) - - gloss_factor: FloatProperty( - name="Amount", - description="The shininess of the reflection " - "(values < 1.0 give diffuse, blurry reflections)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3 - ) - - gloss_samples: IntProperty( - name="Noise", - description="Frequency of the noise pattern bumps averaged for blurry reflections", - min=0, max=1024, default=18, - ) - - gloss_threshold: FloatProperty( - name="Threshold", - description="Threshold for adaptive sampling (if a sample " - "contributes less than this amount [as a percentage], " - "sampling is stopped)", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.005, precision=3 - ) - - mirror_color: FloatVectorProperty( - name="Mirror color", - description=("Mirror color of the material"), - precision=4, step=0.01, - default=(1.0,1.0,1.0), options={'ANIMATABLE'}, subtype='COLOR' - ) - - reflect_factor: FloatProperty( - name="Reflectivity", - description="Amount of mirror reflection for raytrace", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3 - ) - - -class MaterialSubsurfaceScattering(PropertyGroup): - r"""Declare SSS/SSTL properties controllable in UI and translated to POV.""" - - bl_description = "Subsurface scattering settings for the material", - - use: BoolProperty( - name="Subsurface Scattering", - description="Enable diffuse subsurface scatting " - "effects in a material", - default=False, - ) - - back: FloatProperty( - name="Back", - description="Back scattering weight", - min=0.0, max=10.0, soft_min=0.0, soft_max=10.0, default=1.0, precision=3 - ) - - color: FloatVectorProperty( - name="Scattering color", - description=("Scattering color"), - precision=4, step=0.01, - default=(0.604,0.604,0.604), options={'ANIMATABLE'}, subtype='COLOR' - ) - - color_factor: FloatProperty( - name="Color", - description="Blend factor for SSS colors", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=1.0, precision=3 - ) - - error_threshold: FloatProperty( - name="Error", - description="Error tolerance (low values are slower and higher quality)", - default=0.050, precision=3 - ) - - front: FloatProperty( - name="Front", - description="Front scattering weight", - min=0.0, max=2.0, soft_min=0.0, soft_max=2.0, default=1.0, precision=3 - ) - - ior: FloatProperty( - name="IOR", - description="Index of refraction (higher values are denser)", - min=-0.0, max=10.0, soft_min=0.1, soft_max=2.0, default=1.3 - ) - - radius: FloatVectorProperty( - name="RGB Radius", - description=("Mean red/green/blue scattering path length"), - precision=4, step=0.01, min=0.001, - default=(1.0,1.0,1.0), options={'ANIMATABLE'} - ) - - scale: FloatProperty( - name="Scale", - description="Object scale factor", - default=0.100, precision=3 - ) - - texture_factor: FloatProperty( - name="Texture", - description="Texture scattering blend factor", - min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, default=0.0, precision=3 - ) - - -class MaterialStrandSettings(PropertyGroup): - """Declare strand properties controllable in UI and translated to POV.""" - - bl_description = "Strand settings for the material", - - blend_distance: FloatProperty( - name="Distance", - description="Worldspace distance over which to blend in the surface normal", - min=0.0, max=10.0, soft_min=0.0, soft_max=10.0, default=0.0, precision=3 - ) - - root_size: FloatProperty( - name="Root", - description="Start size of strands in pixels or Blender units", - min=0.25, default=1.0, precision=5 - ) - - shape: FloatProperty( - name="Shape", - description="Positive values make strands rounder, negative ones make strands spiky", - min=-0.9, max=0.9, default=0.0, precision=3 - ) - - size_min: FloatProperty( - name="Minimum", - description="Minimum size of strands in pixels", - min=0.001, max=10.0, default=1.0, precision=3 - ) - - tip_size: FloatProperty( - name="Tip", - description="End size of strands in pixels or Blender units", - min=0.0, default=1.0, precision=5 - ) - - use_blender_units: BoolProperty( - name="Blender Units", - description="Use Blender units for widths instead of pixels", - default=False, - ) - - use_surface_diffuse: BoolProperty( - name="Surface diffuse", - description="Make diffuse shading more similar to shading the surface", - default=False, - ) - - use_tangent_shading: BoolProperty( - name="Tangent Shading", - description="Use direction of strands as normal for tangent-shading", - default=True, - ) - - uv_layer: StringProperty( - name="UV Layer", - # icon="GROUP_UVS", - description="Name of UV map to override", - default="", - ) - - width_fade: FloatProperty( - name="Width Fade", - description="Transparency along the width of the strand", - min=0.0, max=2.0, default=0.0, precision=3 - ) - - # halo - - # Halo settings for the material - # Type: MaterialHalo, (readonly, never None) - - # ambient - - # Amount of global ambient color the material receives - # Type: float in [0, 1], default 0.0 - - # darkness - - # Minnaert darkness - # Type: float in [0, 2], default 0.0 - - # diffuse_color - - # Diffuse color of the material - # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) - - # diffuse_fresnel - - # Power of Fresnel - # Type: float in [0, 5], default 0.0 - - # diffuse_fresnel_factor - - # Blending factor of Fresnel - # Type: float in [0, 5], default 0.0 - - # diffuse_intensity - - # Amount of diffuse reflection - # Type: float in [0, 1], default 0.0 - - # diffuse_ramp - - # Color ramp used to affect diffuse shading - # Type: ColorRamp, (readonly) - - # diffuse_ramp_blend - - # Blending method of the ramp and the diffuse color - # Type: enum in [â€MIX’, â€ADD’, â€MULTIPLY’, â€SUBTRACT’, â€SCREEN’, â€DIVIDE’, â€DIFFERENCE’, â€DARKEN’, â€LIGHTEN’, â€OVERLAY’, â€DODGE’, â€BURN’, â€HUE’, â€SATURATION’, â€VALUE’, â€COLOR’, â€SOFT_LIGHT’, â€LINEAR_LIGHT’], default â€MIX’ - - # diffuse_ramp_factor - - # Blending factor (also uses alpha in Colorband) - # Type: float in [0, 1], default 0.0 - - # diffuse_ramp_input - - # How the ramp maps on the surface - # Type: enum in [â€SHADER’, â€ENERGY’, â€NORMAL’, â€RESULT’], default â€SHADER’ - - # diffuse_shader - - # LAMBERT Lambert, Use a Lambertian shader. - # OREN_NAYAR Oren-Nayar, Use an Oren-Nayar shader. - # TOON Toon, Use a toon shader. - # MINNAERT Minnaert, Use a Minnaert shader. - # FRESNEL Fresnel, Use a Fresnel shader. - - # Type: enum in [â€LAMBERT’, â€OREN_NAYAR’, â€TOON’, â€MINNAERT’, â€FRESNEL’], default â€LAMBERT’ - - # diffuse_toon_size - - # Size of diffuse toon area - # Type: float in [0, 3.14], default 0.0 - - # diffuse_toon_smooth - - # Smoothness of diffuse toon area - # Type: float in [0, 1], default 0.0 - - # emit - - # Amount of light to emit - # Type: float in [0, inf], default 0.0 - - # game_settings - - # Game material settings - # Type: MaterialGameSettings, (readonly, never None) - - # halo - - # Halo settings for the material - # Type: MaterialHalo, (readonly, never None) - - # invert_z - - # Render material’s faces with an inverted Z buffer (scanline only) - # Type: boolean, default False - - # light_group - - # Limit lighting to lamps in this Group - # Type: Group - - # line_color - - # Line color used for Freestyle line rendering - # Type: float array of 4 items in [0, inf], default (0.0, 0.0, 0.0, 0.0) - - # line_priority - - # The line color of a higher priority is used at material boundaries - # Type: int in [0, 32767], default 0 - - # mirror_color - - # Mirror color of the material - # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) - - # node_tree - - # Node tree for node based materials - # Type: NodeTree, (readonly) - - # offset_z - - # Give faces an artificial offset in the Z buffer for Z transparency - # Type: float in [-inf, inf], default 0.0 - - # paint_active_slot - - # Index of active texture paint slot - # Type: int in [0, 32767], default 0 - - # paint_clone_slot - - # Index of clone texture paint slot - # Type: int in [0, 32767], default 0 - - # pass_index - - # Index number for the “Material Index” render pass - # Type: int in [0, 32767], default 0 - - # physics - - # Game physics settings - # Type: MaterialPhysics, (readonly, never None) - - # preview_render_type - - # Type of preview render - - # FLAT Flat, Flat XY plane. - # SPHERE Sphere, Sphere. - # CUBE Cube, Cube. - # MONKEY Monkey, Monkey. - # HAIR Hair, Hair strands. - # SPHERE_A World Sphere, Large sphere with sky. - - # Type: enum in [â€FLAT’, â€SPHERE’, â€CUBE’, â€MONKEY’, â€HAIR’, â€SPHERE_A’], default â€FLAT’ - - # raytrace_mirror - - # Raytraced reflection settings for the material - # Type: MaterialRaytraceMirror, (readonly, never None) - - # raytrace_transparency - - # Raytraced transparency settings for the material - # Type: MaterialRaytraceTransparency, (readonly, never None) - - # roughness - - # Oren-Nayar Roughness - # Type: float in [0, 3.14], default 0.0 - - # shadow_buffer_bias - - # Factor to multiply shadow buffer bias with (0 is ignore) - # Type: float in [0, 10], default 0.0 - - # shadow_cast_alpha - - # Shadow casting alpha, in use for Irregular and Deep shadow buffer - # Type: float in [0.001, 1], default 0.0 - - # shadow_only_type - - # How to draw shadows - - # SHADOW_ONLY_OLD Shadow and Distance, Old shadow only method. - # SHADOW_ONLY Shadow Only, Improved shadow only method. - # SHADOW_ONLY_SHADED Shadow and Shading, Improved shadow only method which also renders lightless areas as shadows. - - # Type: enum in [â€SHADOW_ONLY_OLD’, â€SHADOW_ONLY’, â€SHADOW_ONLY_SHADED’], default â€SHADOW_ONLY_OLD’ - - # shadow_ray_bias - - # Shadow raytracing bias to prevent terminator problems on shadow boundary - # Type: float in [0, 0.25], default 0.0 - - - # specular_color - - # Specular color of the material - # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) - - # specular_hardness - - # How hard (sharp) the specular reflection is - # Type: int in [1, 511], default 0 - - # specular_intensity - - # How intense (bright) the specular reflection is - # Type: float in [0, 1], default 0.0 - - # specular_ior - - # Specular index of refraction - # Type: float in [1, 10], default 0.0 - - # specular_ramp - - # Color ramp used to affect specular shading - # Type: ColorRamp, (readonly) - - # specular_ramp_blend - - # Blending method of the ramp and the specular color - # Type: enum in [â€MIX’, â€ADD’, â€MULTIPLY’, â€SUBTRACT’, â€SCREEN’, â€DIVIDE’, â€DIFFERENCE’, â€DARKEN’, â€LIGHTEN’, â€OVERLAY’, â€DODGE’, â€BURN’, â€HUE’, â€SATURATION’, â€VALUE’, â€COLOR’, â€SOFT_LIGHT’, â€LINEAR_LIGHT’], default â€MIX’ - - # specular_ramp_factor - - # Blending factor (also uses alpha in Colorband) - # Type: float in [0, 1], default 0.0 - - # specular_ramp_input - - # How the ramp maps on the surface - # Type: enum in [â€SHADER’, â€ENERGY’, â€NORMAL’, â€RESULT’], default â€SHADER’ - # specular_shader - - # COOKTORR CookTorr, Use a Cook-Torrance shader. - # PHONG Phong, Use a Phong shader. - # BLINN Blinn, Use a Blinn shader. - # TOON Toon, Use a toon shader. - # WARDISO WardIso, Use a Ward anisotropic shader. - - # Type: enum in [â€COOKTORR’, â€PHONG’, â€BLINN’, â€TOON’, â€WARDISO’], default â€COOKTORR’ - - # specular_slope - - # The standard deviation of surface slope - # Type: float in [0, 0.4], default 0.0 - - # specular_toon_size - - # Size of specular toon area - # Type: float in [0, 1.53], default 0.0 - - # specular_toon_smooth - - # Smoothness of specular toon area - # Type: float in [0, 1], default 0.0 - - # strand - - # Strand settings for the material - # Type: MaterialStrand, (readonly, never None) - - # subsurface_scattering - - # Subsurface scattering settings for the material - # Type: MaterialSubsurfaceScattering, (readonly, never None) - - # texture_paint_images - - # Texture images used for texture painting - # Type: bpy_prop_collection of Image, (readonly) - - # texture_paint_slots - - # Texture slots defining the mapping and influence of textures - # Type: bpy_prop_collection of TexPaintSlot, (readonly) - - # texture_slots - - # Texture slots defining the mapping and influence of textures - # Type: MaterialTextureSlots bpy_prop_collection of MaterialTextureSlot, (readonly) - - # translucency - - # Amount of diffuse shading on the back side - # Type: float in [0, 1], default 0.0 - - # transparency_method - - # Method to use for rendering transparency - - # MASK Mask, Mask the background. - # Z_TRANSPARENCY Z Transparency, Use alpha buffer for transparent faces. - # RAYTRACE Raytrace, Use raytracing for transparent refraction rendering. - - # Type: enum in [â€MASK’, â€Z_TRANSPARENCY’, â€RAYTRACE’], default â€MASK’ - - # type - - # Material type defining how the object is rendered - - # SURFACE Surface, Render object as a surface. - # WIRE Wire, Render the edges of faces as wires (not supported in raytracing). - # VOLUME Volume, Render object as a volume. - # HALO Halo, Render object as halo particles. - - # Type: enum in [â€SURFACE’, â€WIRE’, â€VOLUME’, â€HALO’], default â€SURFACE’ - - # use_cast_approximate - - # Allow this material to cast shadows when using approximate ambient occlusion - # Type: boolean, default False - - # use_cast_buffer_shadows - - # Allow this material to cast shadows from shadow buffer lamps - # Type: boolean, default False - - # use_cast_shadows - - # Allow this material to cast shadows - # Type: boolean, default False - - # use_cast_shadows_only - - # Make objects with this material appear invisible (not rendered), only casting shadows - # Type: boolean, default False - - # use_cubic - - # Use cubic interpolation for diffuse values, for smoother transitions - # Type: boolean, default False - - # use_diffuse_ramp - - # Toggle diffuse ramp operations - # Type: boolean, default False - - # use_face_texture - - # Replace the object’s base color with color from UV map image textures - # Type: boolean, default False - - # use_face_texture_alpha - - # Replace the object’s base alpha value with alpha from UV map image textures - # Type: boolean, default False - - # use_full_oversampling - - # Force this material to render full shading/textures for all anti-aliasing samples - # Type: boolean, default False - - # use_light_group_exclusive - - # Material uses the light group exclusively - these lamps are excluded from other scene lighting - # Type: boolean, default False - - # use_light_group_local - - # When linked in, material uses local light group with the same name - # Type: boolean, default False - - # use_mist - - # Use mist with this material (in world settings) - # Type: boolean, default False - - # use_nodes - - # Use shader nodes to render the material - # Type: boolean, default False - - # use_object_color - - # Modulate the result with a per-object color - # Type: boolean, default False - - # use_only_shadow - - # Render shadows as the material’s alpha value, making the material transparent except for shadowed areas - # Type: boolean, default False - - # use_ray_shadow_bias - - # Prevent raytraced shadow errors on surfaces with smooth shaded normals (terminator problem) - # Type: boolean, default False - - # use_raytrace - - # Include this material and geometry that uses it in raytracing calculations - # Type: boolean, default False - - # use_shadeless - - # Make this material insensitive to light or shadow - # Type: boolean, default False - - # use_shadows - - # Allow this material to receive shadows - # Type: boolean, default False - - # use_sky - - # Render this material with zero alpha, with sky background in place (scanline only) - # Type: boolean, default False - - # use_specular_ramp - - # Toggle specular ramp operations - # Type: boolean, default False - - # use_tangent_shading - - # Use the material’s tangent vector instead of the normal for shading - for anisotropic shading effects - # Type: boolean, default False - - # use_textures - - # Enable/Disable each texture - # Type: boolean array of 18 items, default (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) - - # use_transparency - - # Render material as transparent - # Type: boolean, default False - - # use_transparent_shadows - - # Allow this object to receive transparent shadows cast through other objects - # Type: boolean, default False - - # use_uv_project - - # Use to ensure UV interpolation is correct for camera projections (use with UV project modifier) - # Type: boolean, default False - - # use_vertex_color_light - - # Add vertex colors as additional lighting - # Type: boolean, default False - - # use_vertex_color_paint - - # Replace object base color with vertex colors (multiply with â€texture face’ face assigned textures) - # Type: boolean, default False - - # volume - - # Volume settings for the material - # Type: MaterialVolume, (readonly, never None) - ''' - (mat.type in {'SURFACE', 'WIRE', 'VOLUME'}) - "use_transparency") - - - - mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY' - - - - - col.prop(mat, "use_raytrace") - col.prop(mat, "use_full_oversampling") - - sub.prop(mat, "use_sky") - - - col.prop(mat, "use_cast_shadows", text="Cast") - col.prop(mat, "use_cast_shadows_only", text="Cast Only") - col.prop(mat, "use_cast_buffer_shadows") - - sub.active = mat.use_cast_buffer_shadows - sub.prop(mat, "shadow_cast_alpha", text="Casting Alpha") - col.prop(mat, "use_cast_approximate") - - - - col.prop(mat, "diffuse_color", text="") - - sub.active = (not mat.use_shadeless) - - sub.prop(mat, "diffuse_intensity", text="Intensity") - - - col.prop(mat, "diffuse_shader", text="") - col.prop(mat, "use_diffuse_ramp", text="Ramp") - - - if mat.diffuse_shader == 'OREN_NAYAR': - col.prop(mat, "roughness") - elif mat.diffuse_shader == 'MINNAERT': - col.prop(mat, "darkness") - elif mat.diffuse_shader == 'TOON': - - row.prop(mat, "diffuse_toon_size", text="Size") - row.prop(mat, "diffuse_toon_smooth", text="Smooth") - elif mat.diffuse_shader == 'FRESNEL': - - row.prop(mat, "diffuse_fresnel", text="Fresnel") - row.prop(mat, "diffuse_fresnel_factor", text="Factor") - - if mat.use_diffuse_ramp: - - col.template_color_ramp(mat, "diffuse_ramp", expand=True) - - - - row.prop(mat, "diffuse_ramp_input", text="Input") - row.prop(mat, "diffuse_ramp_blend", text="Blend") - - col.prop(mat, "diffuse_ramp_factor", text="Factor") - - - - - col.prop(mat, "specular_color", text="") - col.prop(mat, "specular_intensity", text="Intensity") - - col.prop(mat, "specular_shader", text="") - col.prop(mat, "use_specular_ramp", text="Ramp") - - if mat.specular_shader in {'COOKTORR', 'PHONG'}: - col.prop(mat, "specular_hardness", text="Hardness") - elif mat.specular_shader == 'BLINN': - - row.prop(mat, "specular_hardness", text="Hardness") - row.prop(mat, "specular_ior", text="IOR") - elif mat.specular_shader == 'WARDISO': - col.prop(mat, "specular_slope", text="Slope") - elif mat.specular_shader == 'TOON': - - row.prop(mat, "specular_toon_size", text="Size") - row.prop(mat, "specular_toon_smooth", text="Smooth") - - if mat.use_specular_ramp: - layout.separator() - layout.template_color_ramp(mat, "specular_ramp", expand=True) - layout.separator() - - row = layout.row() - row.prop(mat, "specular_ramp_input", text="Input") - row.prop(mat, "specular_ramp_blend", text="Blend") - - layout.prop(mat, "specular_ramp_factor", text="Factor") - - - class MATERIAL_PT_shading(MaterialButtonsPanel, Panel): - bl_label = "Shading" - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - mat = active_node_mat(context.material) - - if mat.type in {'SURFACE', 'WIRE'}: - split = layout.split() - - col = split.column() - sub = col.column() - sub.active = not mat.use_shadeless - sub.prop(mat, "emit") - sub.prop(mat, "ambient") - sub = col.column() - sub.prop(mat, "translucency") - - col = split.column() - col.prop(mat, "use_shadeless") - sub = col.column() - sub.active = not mat.use_shadeless - sub.prop(mat, "use_tangent_shading") - sub.prop(mat, "use_cubic") - - - class MATERIAL_PT_transp(MaterialButtonsPanel, Panel): - bl_label = "Transparency" - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - mat = context.material - - if simple_material(mat): - self.layout.prop(mat, "use_transparency", text="") - - def draw(self, context): - layout = self.layout - - base_mat = context.material - mat = active_node_mat(context.material) - rayt = mat.raytrace_transparency - - if simple_material(base_mat): - row = layout.row() - row.active = mat.use_transparency - row.prop(mat, "transparency_method", expand=True) - - split = layout.split() - split.active = base_mat.use_transparency - - col = split.column() - col.prop(mat, "alpha") - row = col.row() - row.active = (base_mat.transparency_method != 'MASK') and (not mat.use_shadeless) - row.prop(mat, "specular_alpha", text="Specular") - - col = split.column() - col.active = (not mat.use_shadeless) - col.prop(rayt, "fresnel") - sub = col.column() - sub.active = (rayt.fresnel > 0.0) - sub.prop(rayt, "fresnel_factor", text="Blend") - - if base_mat.transparency_method == 'RAYTRACE': - layout.separator() - split = layout.split() - split.active = base_mat.use_transparency - - col = split.column() - col.prop(rayt, "ior") - col.prop(rayt, "filter") - col.prop(rayt, "falloff") - col.prop(rayt, "depth_max") - col.prop(rayt, "depth") - - col = split.column() - col.label(text="Gloss:") - col.prop(rayt, "gloss_factor", text="Amount") - sub = col.column() - sub.active = rayt.gloss_factor < 1.0 - sub.prop(rayt, "gloss_threshold", text="Threshold") - sub.prop(rayt, "gloss_samples", text="Samples") - - - class MATERIAL_PT_mirror(MaterialButtonsPanel, Panel): - bl_label = "Mirror" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - raym = active_node_mat(context.material).raytrace_mirror - - self.layout.prop(raym, "use", text="") - - def draw(self, context): - layout = self.layout - - mat = active_node_mat(context.material) - raym = mat.raytrace_mirror - - layout.active = raym.use - - split = layout.split() - - col = split.column() - col.prop(raym, "reflect_factor") - col.prop(mat, "mirror_color", text="") - - col = split.column() - col.prop(raym, "fresnel") - sub = col.column() - sub.active = (raym.fresnel > 0.0) - sub.prop(raym, "fresnel_factor", text="Blend") - - split = layout.split() - - col = split.column() - col.separator() - col.prop(raym, "depth") - col.prop(raym, "distance", text="Max Dist") - col.separator() - sub = col.split(percentage=0.4) - sub.active = (raym.distance > 0.0) - sub.label(text="Fade To:") - sub.prop(raym, "fade_to", text="") - - col = split.column() - col.label(text="Gloss:") - col.prop(raym, "gloss_factor", text="Amount") - sub = col.column() - sub.active = (raym.gloss_factor < 1.0) - sub.prop(raym, "gloss_threshold", text="Threshold") - sub.prop(raym, "gloss_samples", text="Samples") - sub.prop(raym, "gloss_anisotropic", text="Anisotropic") - - - class MATERIAL_PT_sss(MaterialButtonsPanel, Panel): - bl_label = "Subsurface Scattering" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - mat = active_node_mat(context.material) - sss = mat.subsurface_scattering - - self.layout.active = (not mat.use_shadeless) - self.layout.prop(sss, "use", text="") - - def draw(self, context): - layout = self.layout - - mat = active_node_mat(context.material) - sss = mat.subsurface_scattering - - layout.active = (sss.use) and (not mat.use_shadeless) - - row = layout.row().split() - sub = row.row(align=True).split(align=True, percentage=0.75) - sub.menu("MATERIAL_MT_sss_presets", text=bpy.types.MATERIAL_MT_sss_presets.bl_label) - sub.operator("material.sss_preset_add", text="", icon='ZOOMIN') - sub.operator("material.sss_preset_add", text="", icon='ZOOMOUT').remove_active = True - - split = layout.split() - - col = split.column() - col.prop(sss, "ior") - col.prop(sss, "scale") - col.prop(sss, "color", text="") - col.prop(sss, "radius", text="RGB Radius", expand=True) - - col = split.column() - sub = col.column(align=True) - sub.label(text="Blend:") - sub.prop(sss, "color_factor", text="Color") - sub.prop(sss, "texture_factor", text="Texture") - sub.label(text="Scattering Weight:") - sub.prop(sss, "front") - sub.prop(sss, "back") - col.separator() - col.prop(sss, "error_threshold", text="Error") - - - class MATERIAL_PT_halo(MaterialButtonsPanel, Panel): - bl_label = "Halo" - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - mat = context.material # don't use node material - halo = mat.halo - - def number_but(layout, toggle, number, name, color): - row = layout.row(align=True) - row.prop(halo, toggle, text="") - sub = row.column(align=True) - sub.active = getattr(halo, toggle) - sub.prop(halo, number, text=name, translate=False) - if not color == "": - sub.prop(mat, color, text="") - - split = layout.split() - - col = split.column() - col.prop(mat, "alpha") - col.prop(mat, "diffuse_color", text="") - col.prop(halo, "seed") - - col = split.column() - col.prop(halo, "size") - col.prop(halo, "hardness") - col.prop(halo, "add") - - layout.label(text="Options:") - - split = layout.split() - col = split.column() - col.prop(halo, "use_texture") - col.prop(halo, "use_vertex_normal") - col.prop(halo, "use_extreme_alpha") - col.prop(halo, "use_shaded") - col.prop(halo, "use_soft") - - col = split.column() - number_but(col, "use_ring", "ring_count", iface_("Rings"), "mirror_color") - number_but(col, "use_lines", "line_count", iface_("Lines"), "specular_color") - number_but(col, "use_star", "star_tip_count", iface_("Star Tips"), "") - - - class MATERIAL_PT_flare(MaterialButtonsPanel, Panel): - bl_label = "Flare" - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - halo = context.material.halo - - self.layout.prop(halo, "use_flare_mode", text="") - - def draw(self, context): - layout = self.layout - - mat = context.material # don't use node material - halo = mat.halo - - layout.active = halo.use_flare_mode - - split = layout.split() - - col = split.column() - col.prop(halo, "flare_size", text="Size") - col.prop(halo, "flare_boost", text="Boost") - col.prop(halo, "flare_seed", text="Seed") - - col = split.column() - col.prop(halo, "flare_subflare_count", text="Subflares") - col.prop(halo, "flare_subflare_size", text="Subsize") - - ''' -#######################End Old Blender Internal Props########################## -############################################################################### -# Povray Nodes -############################################################################### - -class PovraySocketUniversal(NodeSocket): - bl_idname = 'PovraySocketUniversal' - bl_label = 'Povray Socket' - value_unlimited: bpy.props.FloatProperty(default=0.0) - value_0_1: bpy.props.FloatProperty(min=0.0,max=1.0,default=0.0) - value_0_10: bpy.props.FloatProperty(min=0.0,max=10.0,default=0.0) - value_000001_10: bpy.props.FloatProperty(min=0.000001,max=10.0,default=0.0) - value_1_9: bpy.props.IntProperty(min=1,max=9,default=1) - value_0_255: bpy.props.IntProperty(min=0,max=255,default=0) - percent: bpy.props.FloatProperty(min=0.0,max=100.0,default=0.0) - def draw(self, context, layout, node, text): - space = context.space_data - tree = space.edit_tree - links=tree.links - if self.is_linked: - value=[] - for link in links: - if link.from_node==node: - inps=link.to_node.inputs - for inp in inps: - if inp.bl_idname=="PovraySocketFloat_0_1" and inp.is_linked: - prop="value_0_1" - if prop not in value: - value.append(prop) - if inp.bl_idname=="PovraySocketFloat_000001_10" and inp.is_linked: - prop="value_000001_10" - if prop not in value: - value.append(prop) - if inp.bl_idname=="PovraySocketFloat_0_10" and inp.is_linked: - prop="value_0_10" - if prop not in value: - value.append(prop) - if inp.bl_idname=="PovraySocketInt_1_9" and inp.is_linked: - prop="value_1_9" - if prop not in value: - value.append(prop) - if inp.bl_idname=="PovraySocketInt_0_255" and inp.is_linked: - prop="value_0_255" - if prop not in value: - value.append(prop) - if inp.bl_idname=="PovraySocketFloatUnlimited" and inp.is_linked: - prop="value_unlimited" - if prop not in value: - value.append(prop) - if len(value)==1: - layout.prop(self, "%s"%value[0], text=text) - else: - layout.prop(self, "percent", text="Percent") - else: - layout.prop(self, "percent", text=text) - def draw_color(self, context, node): - return (1, 0, 0, 1) - -class PovraySocketFloat_0_1(NodeSocket): - bl_idname = 'PovraySocketFloat_0_1' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(description="Input node Value_0_1",min=0,max=1,default=0) - def draw(self, context, layout, node, text): - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - - def draw_color(self, context, node): - return (0.5, 0.7, 0.7, 1) - -class PovraySocketFloat_0_10(NodeSocket): - bl_idname = 'PovraySocketFloat_0_10' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(description="Input node Value_0_10",min=0,max=10,default=0) - def draw(self, context, layout, node, text): - if node.bl_idname == 'ShaderNormalMapNode' and node.inputs[2].is_linked: - layout.label(text='') - self.hide_value=True - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - def draw_color(self, context, node): - return (0.65, 0.65, 0.65, 1) - -class PovraySocketFloat_10(NodeSocket): - bl_idname = 'PovraySocketFloat_10' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(description="Input node Value_10",min=-10,max=10,default=0) - def draw(self, context, layout, node, text): - if node.bl_idname == 'ShaderNormalMapNode' and node.inputs[2].is_linked: - layout.label(text='') - self.hide_value=True - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - def draw_color(self, context, node): - return (0.65, 0.65, 0.65, 1) - -class PovraySocketFloatPositive(NodeSocket): - bl_idname = 'PovraySocketFloatPositive' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(description="Input Node Value Positive", min=0.0, default=0) - def draw(self, context, layout, node, text): - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - def draw_color(self, context, node): - return (0.045, 0.005, 0.136, 1) - -class PovraySocketFloat_000001_10(NodeSocket): - bl_idname = 'PovraySocketFloat_000001_10' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(min=0.000001,max=10,default=0.000001) - def draw(self, context, layout, node, text): - if self.is_output or self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - def draw_color(self, context, node): - return (1, 0, 0, 1) - -class PovraySocketFloatUnlimited(NodeSocket): - bl_idname = 'PovraySocketFloatUnlimited' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(default = 0.0) - def draw(self, context, layout, node, text): - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text, slider=True) - def draw_color(self, context, node): - return (0.7, 0.7, 1, 1) - -class PovraySocketInt_1_9(NodeSocket): - bl_idname = 'PovraySocketInt_1_9' - bl_label = 'Povray Socket' - default_value: bpy.props.IntProperty(description="Input node Value_1_9",min=1,max=9,default=6) - def draw(self, context, layout, node, text): - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text) - def draw_color(self, context, node): - return (1, 0.7, 0.7, 1) - -class PovraySocketInt_0_256(NodeSocket): - bl_idname = 'PovraySocketInt_0_256' - bl_label = 'Povray Socket' - default_value: bpy.props.IntProperty(min=0,max=255,default=0) - def draw(self, context, layout, node, text): - if self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text) - def draw_color(self, context, node): - return (0.5, 0.5, 0.5, 1) - - -class PovraySocketPattern(NodeSocket): - bl_idname = 'PovraySocketPattern' - bl_label = 'Povray Socket' - - default_value: bpy.props.EnumProperty( - name="Pattern", - description="Select the pattern", - items=( - ('boxed', "Boxed", ""), - ('brick', "Brick", ""), - ('cells', "Cells", ""), - ('checker', "Checker", ""), - ('granite', "Granite", ""), - ('leopard', "Leopard", ""), - ('marble', "Marble", ""), - ('onion', "Onion", ""), - ('planar', "Planar", ""), - ('quilted', "Quilted", ""), - ('ripples', "Ripples", ""), - ('radial', "Radial", ""), - ('spherical', "Spherical", ""), - ('spotted', "Spotted", ""), - ('waves', "Waves", ""), - ('wood', "Wood", ""), - ('wrinkles', "Wrinkles", "") - ), - default='granite') - - def draw(self, context, layout, node, text): - if self.is_output or self.is_linked: - layout.label(text="Pattern") - else: - layout.prop(self, "default_value", text=text) - - def draw_color(self, context, node): - return (1, 1, 1, 1) - -class PovraySocketColor(NodeSocket): - bl_idname = 'PovraySocketColor' - bl_label = 'Povray Socket' - - default_value: bpy.props.FloatVectorProperty( - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), options={'ANIMATABLE'}, subtype='COLOR') - - def draw(self, context, layout, node, text): - if self.is_output or self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text) - - def draw_color(self, context, node): - return (1, 1, 0, 1) - -class PovraySocketColorRGBFT(NodeSocket): - bl_idname = 'PovraySocketColorRGBFT' - bl_label = 'Povray Socket' - - default_value: bpy.props.FloatVectorProperty( - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), options={'ANIMATABLE'}, subtype='COLOR') - f: bpy.props.FloatProperty(default = 0.0,min=0.0,max=1.0) - t: bpy.props.FloatProperty(default = 0.0,min=0.0,max=1.0) - def draw(self, context, layout, node, text): - if self.is_output or self.is_linked: - layout.label(text) - else: - layout.prop(self, "default_value", text=text) - - def draw_color(self, context, node): - return (1, 1, 0, 1) - -class PovraySocketTexture(NodeSocket): - bl_idname = 'PovraySocketTexture' - bl_label = 'Povray Socket' - default_value: bpy.props.IntProperty() - def draw(self, context, layout, node, text): - layout.label(text) - - def draw_color(self, context, node): - return (0, 1, 0, 1) - - - -class PovraySocketTransform(NodeSocket): - bl_idname = 'PovraySocketTransform' - bl_label = 'Povray Socket' - default_value: bpy.props.IntProperty(min=0,max=255,default=0) - def draw(self, context, layout, node, text): - layout.label(text) - - def draw_color(self, context, node): - return (99/255, 99/255, 199/255, 1) - -class PovraySocketNormal(NodeSocket): - bl_idname = 'PovraySocketNormal' - bl_label = 'Povray Socket' - default_value: bpy.props.IntProperty(min=0,max=255,default=0) - def draw(self, context, layout, node, text): - layout.label(text) - - def draw_color(self, context, node): - return (0.65, 0.65, 0.65, 1) - -class PovraySocketSlope(NodeSocket): - bl_idname = 'PovraySocketSlope' - bl_label = 'Povray Socket' - default_value: bpy.props.FloatProperty(min = 0.0, max = 1.0) - height: bpy.props.FloatProperty(min = 0.0, max = 10.0) - slope: bpy.props.FloatProperty(min = -10.0, max = 10.0) - def draw(self, context, layout, node, text): - if self.is_output or self.is_linked: - layout.label(text) - else: - layout.prop(self,'default_value',text='') - layout.prop(self,'height',text='') - layout.prop(self,'slope',text='') - def draw_color(self, context, node): - return (0, 0, 0, 1) - -class PovraySocketMap(NodeSocket): - bl_idname = 'PovraySocketMap' - bl_label = 'Povray Socket' - default_value: bpy.props.StringProperty() - def draw(self, context, layout, node, text): - layout.label(text) - def draw_color(self, context, node): - return (0.2, 0, 0.2, 1) - -class PovrayShaderNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'ObjectNodeTree' - -class PovrayTextureNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'TextureNodeTree' - -class PovraySceneNodeCategory(NodeCategory): - @classmethod - def poll(cls, context): - return context.space_data.tree_type == 'CompositorNodeTree' - -node_categories = [ - - PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items=[ - NodeItem("PovrayOutputNode"), - ]), - - PovrayShaderNodeCategory("SIMPLE", "Simple texture", items=[ - NodeItem("PovrayTextureNode"), - ]), - - PovrayShaderNodeCategory("MAPS", "Maps", items=[ - NodeItem("PovrayBumpMapNode"), - NodeItem("PovrayColorImageNode"), - NodeItem("ShaderNormalMapNode"), - NodeItem("PovraySlopeNode"), - NodeItem("ShaderTextureMapNode"), - NodeItem("ShaderNodeValToRGB"), - ]), - - PovrayShaderNodeCategory("OTHER", "Other patterns", items=[ - NodeItem("PovrayImagePatternNode"), - NodeItem("ShaderPatternNode"), - ]), - - PovrayShaderNodeCategory("COLOR", "Color", items=[ - NodeItem("PovrayPigmentNode"), - ]), - - PovrayShaderNodeCategory("TRANSFORM", "Transform", items=[ - NodeItem("PovrayMappingNode"), - NodeItem("PovrayMultiplyNode"), - NodeItem("PovrayModifierNode"), - NodeItem("PovrayTransformNode"), - NodeItem("PovrayValueNode"), - ]), - - PovrayShaderNodeCategory("FINISH", "Finish", items=[ - NodeItem("PovrayFinishNode"), - NodeItem("PovrayDiffuseNode"), - NodeItem("PovraySpecularNode"), - NodeItem("PovrayPhongNode"), - NodeItem("PovrayAmbientNode"), - NodeItem("PovrayMirrorNode"), - NodeItem("PovrayIridescenceNode"), - NodeItem("PovraySubsurfaceNode"), - ]), - - PovrayShaderNodeCategory("CYCLES", "Cycles", items=[ - NodeItem("ShaderNodeAddShader"), - NodeItem("ShaderNodeAmbientOcclusion"), - NodeItem("ShaderNodeAttribute"), - NodeItem("ShaderNodeBackground"), - NodeItem("ShaderNodeBlackbody"), - NodeItem("ShaderNodeBrightContrast"), - NodeItem("ShaderNodeBsdfAnisotropic"), - NodeItem("ShaderNodeBsdfDiffuse"), - NodeItem("ShaderNodeBsdfGlass"), - NodeItem("ShaderNodeBsdfGlossy"), - NodeItem("ShaderNodeBsdfHair"), - NodeItem("ShaderNodeBsdfRefraction"), - NodeItem("ShaderNodeBsdfToon"), - NodeItem("ShaderNodeBsdfTranslucent"), - NodeItem("ShaderNodeBsdfTransparent"), - NodeItem("ShaderNodeBsdfVelvet"), - NodeItem("ShaderNodeBump"), - NodeItem("ShaderNodeCameraData"), - NodeItem("ShaderNodeCombineHSV"), - NodeItem("ShaderNodeCombineRGB"), - NodeItem("ShaderNodeCombineXYZ"), - NodeItem("ShaderNodeEmission"), - NodeItem("ShaderNodeExtendedMaterial"), - NodeItem("ShaderNodeFresnel"), - NodeItem("ShaderNodeGamma"), - NodeItem("ShaderNodeGeometry"), - NodeItem("ShaderNodeGroup"), - NodeItem("ShaderNodeHairInfo"), - NodeItem("ShaderNodeHoldout"), - NodeItem("ShaderNodeHueSaturation"), - NodeItem("ShaderNodeInvert"), - NodeItem("ShaderNodeLampData"), - NodeItem("ShaderNodeLayerWeight"), - NodeItem("ShaderNodeLightFalloff"), - NodeItem("ShaderNodeLightPath"), - NodeItem("ShaderNodeMapping"), - NodeItem("ShaderNodeMaterial"), - NodeItem("ShaderNodeMath"), - NodeItem("ShaderNodeMixRGB"), - NodeItem("ShaderNodeMixShader"), - NodeItem("ShaderNodeNewGeometry"), - NodeItem("ShaderNodeNormal"), - NodeItem("ShaderNodeNormalMap"), - NodeItem("ShaderNodeObjectInfo"), - NodeItem("ShaderNodeOutput"), - NodeItem("ShaderNodeOutputLamp"), - NodeItem("ShaderNodeOutputLineStyle"), - NodeItem("ShaderNodeOutputMaterial"), - NodeItem("ShaderNodeOutputWorld"), - NodeItem("ShaderNodeParticleInfo"), - NodeItem("ShaderNodeRGB"), - NodeItem("ShaderNodeRGBCurve"), - NodeItem("ShaderNodeRGBToBW"), - NodeItem("ShaderNodeScript"), - NodeItem("ShaderNodeSeparateHSV"), - NodeItem("ShaderNodeSeparateRGB"), - NodeItem("ShaderNodeSeparateXYZ"), - NodeItem("ShaderNodeSqueeze"), - NodeItem("ShaderNodeSubsurfaceScattering"), - NodeItem("ShaderNodeTangent"), - NodeItem("ShaderNodeTexBrick"), - NodeItem("ShaderNodeTexChecker"), - NodeItem("ShaderNodeTexCoord"), - NodeItem("ShaderNodeTexEnvironment"), - NodeItem("ShaderNodeTexGradient"), - NodeItem("ShaderNodeTexImage"), - NodeItem("ShaderNodeTexMagic"), - NodeItem("ShaderNodeTexMusgrave"), - NodeItem("ShaderNodeTexNoise"), - NodeItem("ShaderNodeTexPointDensity"), - NodeItem("ShaderNodeTexSky"), - NodeItem("ShaderNodeTexVoronoi"), - NodeItem("ShaderNodeTexWave"), - NodeItem("ShaderNodeTexture"), - NodeItem("ShaderNodeUVAlongStroke"), - NodeItem("ShaderNodeUVMap"), - NodeItem("ShaderNodeValToRGB"), - NodeItem("ShaderNodeValue"), - NodeItem("ShaderNodeVectorCurve"), - NodeItem("ShaderNodeVectorMath"), - NodeItem("ShaderNodeVectorTransform"), - NodeItem("ShaderNodeVolumeAbsorption"), - NodeItem("ShaderNodeVolumeScatter"), - NodeItem("ShaderNodeWavelength"), - NodeItem("ShaderNodeWireframe"), - ]), - - PovrayTextureNodeCategory("TEXTUREOUTPUT", "Output", items=[ - NodeItem("TextureNodeValToRGB"), - NodeItem("TextureOutputNode"), - ]), - - PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items=[ - NodeItem("IsoPropsNode"), - ]), - - PovraySceneNodeCategory("FOG", "Fog", items=[ - NodeItem("PovrayFogNode"), - - ]), - ] -############### end nodes - -############################################################################### -# Texture POV properties. -############################################################################### - -class RenderPovSettingsTexture(PropertyGroup): - """Declare texture level properties controllable in UI and translated to POV.""" - - # former Space properties from removed Blender Internal - active_texture_index: IntProperty( - name = "Index for texture_slots", - min=0, - max=17, - default=0, - ) - - use_limited_texture_context: BoolProperty( - name="", - description="Use the limited version of texture user (for â€old shading’ mode)", - default=True, - ) - - texture_context: EnumProperty( - name="Texture context", - description="Type of texture data to display and edit", - items=( - ('MATERIAL', "", "Show material textures", "MATERIAL",0), # "Show material textures" - ('WORLD', "", "Show world textures", "WORLD",1), # "Show world textures" - ('LAMP', "", "Show lamp textures", "LIGHT",2), # "Show lamp textures" - ('PARTICLES', "", "Show particles textures", "PARTICLES",3), # "Show particles textures" - ('LINESTYLE', "", "Show linestyle textures", "LINE_DATA",4), # "Show linestyle textures" - ('OTHER', "", "Show other data textures", "TEXTURE_DATA",5), # "Show other data textures" - ), - default = 'MATERIAL', - ) - - # Custom texture gamma - tex_gamma_enable: BoolProperty( - name="Enable custom texture gamma", - description="Notify some custom gamma for which texture has been precorrected " - "without the file format carrying it and only if it differs from your " - "OS expected standard (see pov doc)", - default=False, - ) - - tex_gamma_value: FloatProperty( - name="Custom texture gamma", - description="value for which the file was issued e.g. a Raw photo is gamma 1.0", - min=0.45, max=5.00, soft_min=1.00, soft_max=2.50, default=1.00 - ) - - ##################################CustomPOV Code############################ - # commented out below if we wanted custom pov code in texture only, inside exported material: - # replacement_text = StringProperty( - # name="Declared name:", - # description="Type the declared name in custom POV code or an external .inc " - # "it points at. pigment {} expected", - # default="") - - tex_pattern_type: EnumProperty( - name="Texture_Type", - description="Choose between Blender or POV parameters to specify texture", - items= ( - ('agate', 'Agate', '','PLUGIN', 0), - ('aoi', 'Aoi', '', 'PLUGIN', 1), - ('average', 'Average', '', 'PLUGIN', 2), - ('boxed', 'Boxed', '', 'PLUGIN', 3), - ('bozo', 'Bozo', '', 'PLUGIN', 4), - ('bumps', 'Bumps', '', 'PLUGIN', 5), - ('cells', 'Cells', '', 'PLUGIN', 6), - ('crackle', 'Crackle', '', 'PLUGIN', 7), - ('cubic', 'Cubic', '', 'PLUGIN', 8), - ('cylindrical', 'Cylindrical', '', 'PLUGIN', 9), - ('density_file', 'Density', '(.df3)', 'PLUGIN', 10), - ('dents', 'Dents', '', 'PLUGIN', 11), - ('fractal', 'Fractal', '', 'PLUGIN', 12), - ('function', 'Function', '', 'PLUGIN', 13), - ('gradient', 'Gradient', '', 'PLUGIN', 14), - ('granite', 'Granite', '', 'PLUGIN', 15), - ('image_pattern', 'Image pattern', '', 'PLUGIN', 16), - ('leopard', 'Leopard', '', 'PLUGIN', 17), - ('marble', 'Marble', '', 'PLUGIN', 18), - ('onion', 'Onion', '', 'PLUGIN', 19), - ('pigment_pattern', 'pigment pattern', '', 'PLUGIN', 20), - ('planar', 'Planar', '', 'PLUGIN', 21), - ('quilted', 'Quilted', '', 'PLUGIN', 22), - ('radial', 'Radial', '', 'PLUGIN', 23), - ('ripples', 'Ripples', '', 'PLUGIN', 24), - ('slope', 'Slope', '', 'PLUGIN', 25), - ('spherical', 'Spherical', '', 'PLUGIN', 26), - ('spiral1', 'Spiral1', '', 'PLUGIN', 27), - ('spiral2', 'Spiral2', '', 'PLUGIN', 28), - ('spotted', 'Spotted', '', 'PLUGIN', 29), - ('waves', 'Waves', '', 'PLUGIN', 30), - ('wood', 'Wood', '', 'PLUGIN', 31), - ('wrinkles', 'Wrinkles', '', 'PLUGIN', 32), - ('brick', "Brick", "", 'PLUGIN', 33), - ('checker', "Checker", "", 'PLUGIN', 34), - ('hexagon', "Hexagon", "", 'PLUGIN', 35), - ('object', "Mesh", "", 'PLUGIN', 36), - ('emulator', "Blender Type Emulator", "", 'SCRIPTPLUGINS', 37) - ), - default='emulator', - ) - - magnet_style: EnumProperty( - name="Magnet style", - description="magnet or julia", - items=(('mandel', "Mandelbrot", ""),('julia', "Julia", "")), - default='julia', - ) - - magnet_type: IntProperty( - name="Magnet_type", - description="1 or 2", - min=1, - max=2, - default=2, - ) - - warp_types: EnumProperty( - name="Warp Types", - description="Select the type of warp", - items=(('PLANAR', "Planar", ""), ('CUBIC', "Cubic", ""), - ('SPHERICAL', "Spherical", ""), ('TOROIDAL', "Toroidal", ""), - ('CYLINDRICAL', "Cylindrical", ""), ('NONE', "None", "No indentation")), - default='NONE' - ) - - warp_orientation: EnumProperty( - name="Warp Orientation", - description="Select the orientation of warp", - items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")), - default='y', - ) - - wave_type: EnumProperty( - name="Waves type", - description="Select the type of waves", - items=(('ramp', "Ramp", ""), ('sine', "Sine", ""), ('scallop', "Scallop", ""), - ('cubic', "Cubic", ""), ('poly', "Poly", ""), ('triangle', 'Triangle', "")), - default='ramp', - ) - - gen_noise: IntProperty( - name="Noise Generators", - description="Noise Generators", - min=1, max=3, default=1, - ) - - warp_dist_exp: FloatProperty( - name="Distance exponent", - description="Distance exponent", - min=0.0, max=100.0, default=1.0, - ) - - warp_tor_major_radius: FloatProperty( - name="Major radius", - description="Torus is distance from major radius", - min=0.0, max=5.0, default=1.0, - ) - - warp_turbulence_x: FloatProperty( - name="Turbulence X", - description="Turbulence X", - min=0.0, max=5.0, default=0.0, - ) - - warp_turbulence_y: FloatProperty( - name="Turbulence Y", - description="Turbulence Y", - min=0.0, max=5.0, default=0.0 - ) - - warp_turbulence_z: FloatProperty( - name="Turbulence Z", - description="Turbulence Z", - min=0.0, max=5.0, default=0.0 - ) - - modifier_octaves: IntProperty( - name="Turbulence octaves", - description="Turbulence octaves", - min=1, max=10, default=1 - ) - - modifier_lambda: FloatProperty( - name="Turbulence lambda", - description="Turbulence lambda", - min=0.0, max=5.0, default=1.00 - ) - - modifier_omega: FloatProperty( - name="Turbulence omega", - description="Turbulence omega", - min=0.0, max=10.0, default=1.00 - ) - - modifier_phase: FloatProperty( - name="Phase", - description="The phase value causes the map entries to be shifted so that the map " - "starts and ends at a different place", - min=0.0, max=2.0, default=0.0 - ) - - modifier_frequency: FloatProperty( - name="Frequency", - description="The frequency keyword adjusts the number of times that a color map " - "repeats over one cycle of a pattern", - min=0.0, max=25.0, default=2.0 - ) - - modifier_turbulence: FloatProperty( - name="Turbulence", - description="Turbulence", - min=0.0, max=5.0, default=2.0 - ) - - modifier_numbers: IntProperty( - name="Numbers", - description="Numbers", - min=1, max=27, default=2 - ) - - modifier_control0: IntProperty( - name="Control0", - description="Control0", - min=0, max=100, default=1 - ) - - modifier_control1: IntProperty( - name="Control1", - description="Control1", - min=0, max=100, default=1 - ) - - brick_size_x: FloatProperty( - name="Brick size x", - description="", - min=0.0000, max=1.0000, default=0.2500 - ) - - brick_size_y: FloatProperty( - name="Brick size y", - description="", - min=0.0000, max=1.0000, default=0.0525 - ) - - brick_size_z: FloatProperty( - name="Brick size z", - description="", - min=0.0000, max=1.0000, default=0.1250 - ) - - brick_mortar: FloatProperty( - name="Mortar", - description="Mortar", - min=0.000, max=1.500, default=0.01 - ) - - julia_complex_1: FloatProperty( - name="Julia Complex 1", - description="", - min=0.000, max=1.500, default=0.360 - ) - - julia_complex_2: FloatProperty( - name="Julia Complex 2", - description="", - min=0.000, max=1.500, default=0.250 - ) - - f_iter: IntProperty( - name="Fractal Iteration", - description="", - min=0, max=100, default=20 - ) - - f_exponent: IntProperty( - name="Fractal Exponent", - description="", - min=2, max=33, default=2 - ) - - f_ior: IntProperty( - name="Fractal Interior", - description="", - min=1, max=6, default=1 - ) - - f_ior_fac: FloatProperty( - name="Fractal Interior Factor", - description="", - min=0.0, max=10.0, default=1.0 - ) - - f_eor: IntProperty( - name="Fractal Exterior", - description="", - min=1, max=8, default=1 - ) - - f_eor_fac: FloatProperty( - name="Fractal Exterior Factor", - description="", - min=0.0, max=10.0, default=1.0 - ) - - grad_orient_x: IntProperty( - name="Gradient orientation X", - description="", - min=0, max=1, default=0 - ) - - grad_orient_y: IntProperty( - name="Gradient orientation Y", - description="", - min=0, max=1, default=1 - ) - - grad_orient_z: IntProperty( - name="Gradient orientation Z", - description="", - min=0, max=1, default=0 - ) - - pave_sides: EnumProperty( - name="Pavement sides", - description="", - items=( - ('3', "3", ""), - ('4', "4", ""), - ('6', "6", "") - ), - default='3' - ) - - pave_pat_2: IntProperty( - name="Pavement pattern 2", - description="maximum: 2", - min=1, max=2, default=2 - ) - - pave_pat_3: IntProperty( - name="Pavement pattern 3", - description="maximum: 3", - min=1, max=3, default=3 - ) - - pave_pat_4: IntProperty( - name="Pavement pattern 4", - description="maximum: 4", - min=1, max=4, default=4 - ) - - pave_pat_5: IntProperty( - name="Pavement pattern 5", - description="maximum: 5", - min=1, max=5, default=5 - ) - - pave_pat_7: IntProperty( - name="Pavement pattern 7", - description="maximum: 7", - min=1, max=7, default=7 - ) - - pave_pat_12: IntProperty( - name="Pavement pattern 12", - description="maximum: 12", - min=1, max=12, default=12 - ) - - pave_pat_22: IntProperty( - name="Pavement pattern 22", - description="maximum: 22", - min=1, max=22, default=22 - ) - - pave_pat_35: IntProperty( - name="Pavement pattern 35", - description="maximum: 35", - min=1, max=35, default=35, - ) - - pave_tiles: IntProperty( - name="Pavement tiles", - description="If sides = 6, maximum tiles 5!!!", - min=1, max=6, default=1 - ) - - pave_form: IntProperty( - name="Pavement form", - description="", - min=0, max=4, default=0 - ) - - #########FUNCTIONS############################################################################# - #########FUNCTIONS############################################################################# - - func_list: EnumProperty( - name="Functions", - description="Select the function for create pattern", - items=( - ('NONE', "None", "No indentation"), - ("f_algbr_cyl1","Algbr cyl1",""), ("f_algbr_cyl2","Algbr cyl2",""), - ("f_algbr_cyl3","Algbr cyl3",""), ("f_algbr_cyl4","Algbr cyl4",""), - ("f_bicorn","Bicorn",""), ("f_bifolia","Bifolia",""), - ("f_blob","Blob",""), ("f_blob2","Blob2",""), - ("f_boy_surface","Boy surface",""), ("f_comma","Comma",""), - ("f_cross_ellipsoids","Cross ellipsoids",""), - ("f_crossed_trough","Crossed trough",""), ("f_cubic_saddle","Cubic saddle",""), - ("f_cushion","Cushion",""), ("f_devils_curve","Devils curve",""), - ("f_devils_curve_2d","Devils curve 2d",""), - ("f_dupin_cyclid","Dupin cyclid",""), ("f_ellipsoid","Ellipsoid",""), - ("f_enneper","Enneper",""), ("f_flange_cover","Flange cover",""), - ("f_folium_surface","Folium surface",""), - ("f_folium_surface_2d","Folium surface 2d",""), ("f_glob","Glob",""), - ("f_heart","Heart",""), ("f_helical_torus","Helical torus",""), - ("f_helix1","Helix1",""), ("f_helix2","Helix2",""), ("f_hex_x","Hex x",""), - ("f_hex_y","Hex y",""), ("f_hetero_mf","Hetero mf",""), - ("f_hunt_surface","Hunt surface",""), - ("f_hyperbolic_torus","Hyperbolic torus",""), - ("f_isect_ellipsoids","Isect ellipsoids",""), - ("f_kampyle_of_eudoxus","Kampyle of eudoxus",""), - ("f_kampyle_of_eudoxus_2d","Kampyle of eudoxus 2d",""), - ("f_klein_bottle","Klein bottle",""), - ("f_kummer_surface_v1","Kummer surface v1",""), - ("f_kummer_surface_v2","Kummer surface v2",""), - ("f_lemniscate_of_gerono","Lemniscate of gerono",""), - ("f_lemniscate_of_gerono_2d","Lemniscate of gerono 2d",""), - ("f_mesh1","Mesh1",""), ("f_mitre","Mitre",""), - ("f_nodal_cubic","Nodal cubic",""), ("f_noise3d","Noise3d",""), - ("f_noise_generator","Noise generator",""), ("f_odd","Odd",""), - ("f_ovals_of_cassini","Ovals of cassini",""), ("f_paraboloid","Paraboloid",""), - ("f_parabolic_torus","Parabolic torus",""), ("f_ph","Ph",""), - ("f_pillow","Pillow",""), ("f_piriform","Piriform",""), - ("f_piriform_2d","Piriform 2d",""), ("f_poly4","Poly4",""), - ("f_polytubes","Polytubes",""), ("f_quantum","Quantum",""), - ("f_quartic_paraboloid","Quartic paraboloid",""), - ("f_quartic_saddle","Quartic saddle",""), - ("f_quartic_cylinder","Quartic cylinder",""), ("f_r","R",""), - ("f_ridge","Ridge",""), ("f_ridged_mf","Ridged mf",""), - ("f_rounded_box","Rounded box",""), ("f_sphere","Sphere",""), - ("f_spikes","Spikes",""), ("f_spikes_2d","Spikes 2d",""), - ("f_spiral","Spiral",""), ("f_steiners_roman","Steiners roman",""), - ("f_strophoid","Strophoid",""), ("f_strophoid_2d","Strophoid 2d",""), - ("f_superellipsoid","Superellipsoid",""), ("f_th","Th",""), - ("f_torus","Torus",""), ("f_torus2","Torus2",""), - ("f_torus_gumdrop","Torus gumdrop",""), ("f_umbrella","Umbrella",""), - ("f_witch_of_agnesi","Witch of agnesi",""), - ("f_witch_of_agnesi_2d","Witch of agnesi 2d","") - ), - - default='NONE', - ) - - func_x: FloatProperty( - name="FX", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_plus_x: EnumProperty( - name="Func plus x", - description="", - items=(('NONE', "None", ""), ('increase', "*", ""), ('plus', "+", "")), - default='NONE', - ) - - func_y: FloatProperty( - name="FY", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_plus_y: EnumProperty( - name="Func plus y", - description="", - items=(('NONE', "None", ""), ('increase', "*", ""), ('plus', "+", "")), - default='NONE', - ) - - func_z: FloatProperty( - name="FZ", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_plus_z: EnumProperty( - name="Func plus z", - description="", - items=( - ('NONE', "None", ""), - ('increase', "*", ""), - ('plus', "+", "") - ), - default='NONE', - ) - - func_P0: FloatProperty( - name="P0", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P1: FloatProperty( - name="P1", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P2: FloatProperty( - name="P2", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P3: FloatProperty( - name="P3", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P4: FloatProperty( - name="P4", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P5: FloatProperty( - name="P5", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P6: FloatProperty( - name="P6", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P7: FloatProperty( - name="P7", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P8: FloatProperty( - name="P8", - description="", - min=0.0, max=25.0, default=1.0 - ) - - func_P9: FloatProperty( - name="P9", - description="", - min=0.0, max=25.0, default=1.0 - ) - - ######################################### - tex_rot_x: FloatProperty( - name="Rotate X", - description="", - min=-180.0, max=180.0, default=0.0 - ) - - tex_rot_y: FloatProperty( - name="Rotate Y", - description="", - min=-180.0, max=180.0, default=0.0 - ) - - tex_rot_z: FloatProperty( - name="Rotate Z", - description="", - min=-180.0, max=180.0, default=0.0 - ) - - tex_mov_x: FloatProperty( - name="Move X", - description="", - min=-100000.0, max=100000.0, default=0.0 - ) - - tex_mov_y: FloatProperty( - name="Move Y", - description="", - min=-100000.0, - max=100000.0, - default=0.0, - ) - - tex_mov_z: FloatProperty( - name="Move Z", - description="", - min=-100000.0, - max=100000.0, - default=0.0, - ) - - tex_scale_x: FloatProperty( - name="Scale X", - description="", - min=0.0, - max=10000.0, - default=1.0, - ) - - tex_scale_y: FloatProperty( - name="Scale Y", - description="", - min=0.0, - max=10000.0, - default=1.0, - ) - - tex_scale_z: FloatProperty( - name="Scale Z", - description="", - min=0.0, - max=10000.0, - default=1.0, - ) - -############################################################################### -# Object POV properties. -############################################################################### - -class RenderPovSettingsObject(PropertyGroup): - """Declare object and primitives level properties controllable in UI and translated to POV.""" - - # Pov inside_vector used for CSG - inside_vector: FloatVectorProperty( - name="CSG Inside Vector", - description="Direction to shoot CSG inside test rays at", - precision=4, - step=0.01, - min=0, - soft_max=1, - default=(0.001, 0.001, 0.5), - options={'ANIMATABLE'}, - subtype='XYZ' - ) - - # Importance sampling - importance_value: FloatProperty( - name="Radiosity Importance", - description="Priority value relative to other objects for sampling radiosity rays. " - "Increase to get more radiosity rays at comparatively small yet " - "bright objects", - min=0.01, max=1.00, default=0.50 - ) - - # Collect photons - collect_photons: BoolProperty( - name="Receive Photon Caustics", - description="Enable object to collect photons from other objects caustics. Turn " - "off for objects that don't really need to receive caustics (e.g. objects" - " that generate caustics often don't need to show any on themselves)", - default=True, - ) - - # Photons spacing_multiplier - spacing_multiplier: FloatProperty( - name="Photons Spacing Multiplier", - description="Multiplier value relative to global spacing of photons. " - "Decrease by half to get 4x more photons at surface of " - "this object (or 8x media photons than specified in the globals", - min=0.01, max=1.00, default=1.00) - - ##################################CustomPOV Code############################ - # Only DUMMIES below for now: - replacement_text: StringProperty( - name="Declared name:", - description="Type the declared name in custom POV code or an external .inc " - "it points at. Any POV shape expected e.g: isosurface {}", - default="", - ) - - #############POV specific object properties.############################ - object_as: StringProperty(maxlen=1024) - - imported_loc: FloatVectorProperty( - name="Imported Pov location", - precision=6, - default=(0.0, 0.0, 0.0), - ) - - imported_loc_cap: FloatVectorProperty( - name="Imported Pov location", - precision=6, - default=(0.0, 0.0, 2.0), - ) - - unlock_parameters: BoolProperty(name="Lock",default = False) - - # not in UI yet but used for sor (lathe) / prism... pov primitives - curveshape: EnumProperty( - name="Povray Shape Type", - items=(("birail", "Birail", ""), - ("cairo", "Cairo", ""), - ("lathe", "Lathe", ""), - ("loft", "Loft", ""), - ("prism", "Prism", ""), - ("sphere_sweep", "Sphere Sweep", "")), - default="sphere_sweep", - ) - - mesh_write_as: EnumProperty( - name="Mesh Write As", - items=(("blobgrid", "Blob Grid", ""), - ("grid", "Grid", ""), - ("mesh", "Mesh", "")), - default="mesh", - ) - - object_ior: FloatProperty( - name="IOR", description="IOR", - min=1.0, max=10.0,default=1.0 - ) - - # shape_as_light = StringProperty(name="Light",maxlen=1024) - # fake_caustics_power = FloatProperty( - # name="Power", description="Fake caustics power", - # min=0.0, max=10.0,default=0.0) - # target = BoolProperty(name="Target",description="",default=False) - # target_value = FloatProperty( - # name="Value", description="", - # min=0.0, max=1.0,default=1.0) - # refraction = BoolProperty(name="Refraction",description="",default=False) - # dispersion = BoolProperty(name="Dispersion",description="",default=False) - # dispersion_value = FloatProperty( - # name="Dispersion", description="Good values are 1.01 to 1.1. ", - # min=1.0, max=1.2,default=1.01) - # dispersion_samples = IntProperty(name="Samples",min=2, max=100,default=7) - # reflection = BoolProperty(name="Reflection",description="",default=False) - # pass_through = BoolProperty(name="Pass through",description="",default=False) - no_shadow: BoolProperty(name="No Shadow",default=False) - - no_image: BoolProperty(name="No Image",default=False) - - no_reflection: BoolProperty(name="No Reflection",default=False) - - no_radiosity: BoolProperty(name="No Radiosity",default=False) - - inverse: BoolProperty(name="Inverse",default=False) - - sturm: BoolProperty(name="Sturm",default=False) - - double_illuminate: BoolProperty(name="Double Illuminate",default=False) - - hierarchy: BoolProperty(name="Hierarchy",default=False) - - hollow: BoolProperty(name="Hollow",default=False) - - boundorclip: EnumProperty( - name="Boundorclip", - items=(("none", "None", ""), - ("bounded_by", "Bounded_by", ""), - ("clipped_by", "Clipped_by", "")), - default="none", - ) - - boundorclipob: StringProperty(maxlen=1024) - - addboundorclip: BoolProperty(description="",default=False) - - blob_threshold: FloatProperty(name="Threshold",min=0.00, max=10.0, default=0.6) - - blob_strength: FloatProperty(name="Strength",min=-10.00, max=10.0, default=1.00) - - res_u: IntProperty(name="U",min=100, max=1000, default=500) - - res_v: IntProperty(name="V",min=100, max=1000, default=500) - - contained_by: EnumProperty( - name="Contained by", - items=(("box", "Box", ""), - ("sphere", "Sphere", "")), - default="box", - ) - - container_scale: FloatProperty(name="Container Scale",min=0.0, max=10.0, default=1.00) - - threshold: FloatProperty(name="Threshold",min=0.0, max=10.0, default=0.00) - - accuracy: FloatProperty(name="Accuracy",min=0.0001, max=0.1, default=0.001) - - max_gradient: FloatProperty(name="Max Gradient",min=0.0, max=100.0, default=5.0) - - all_intersections: BoolProperty(name="All Intersections",default=False) - - max_trace: IntProperty(name="Max Trace",min=1, max=100,default=1) - - - def prop_update_cylinder(self, context): - """Update POV cylinder primitive parameters not only at creation but anytime they are changed in UI.""" - if bpy.ops.pov.cylinder_update.poll(): - bpy.ops.pov.cylinder_update() - - cylinder_radius: FloatProperty( - name="Cylinder R",min=0.00, max=10.0, default=0.04, update=prop_update_cylinder) - - cylinder_location_cap: FloatVectorProperty( - name="Cylinder Cap Location", subtype='TRANSLATION', - description="The position of the 'other' end of the cylinder (relative to object location)", - default=(0.0, 0.0, 2.0), update=prop_update_cylinder - ) - - imported_cyl_loc: FloatVectorProperty( - name="Imported Pov location", - precision=6, - default=(0.0, 0.0, 0.0) - ) - - imported_cyl_loc_cap: FloatVectorProperty( - name="Imported Pov location", - precision=6, - default=(0.0, 0.0, 2.0) - ) - - def prop_update_sphere(self, context): - - """Update POV sphere primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.sphere_update() - - sphere_radius: FloatProperty( - name="Sphere radius",min=0.00, max=10.0, default=0.5, update=prop_update_sphere) - - def prop_update_cone(self, context): - - """Update POV cone primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.cone_update() - - cone_base_radius: FloatProperty( - name = "Base radius", - description = "The first radius of the cone", - default = 1.0, min = 0.01, max = 100.0, update=prop_update_cone - ) - - cone_cap_radius: FloatProperty( - name = "Cap radius", - description = "The second radius of the cone", - default = 0.3, min = 0.0, max = 100.0, update=prop_update_cone - ) - - cone_segments: IntProperty( - name = "Segments", - description = "Radial segmentation of proxy mesh", - default = 16, min = 3, max = 265, update=prop_update_cone - ) - - cone_height: FloatProperty( - name = "Height", - description = "Height of the cone", - default = 2.0, min = 0.01, max = 100.0, update=prop_update_cone - ) - - cone_base_z: FloatProperty() - - cone_cap_z: FloatProperty() - -###########Parametric - def prop_update_parametric(self, context): - - """Update POV parametric surface primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.parametric_update() - - u_min: FloatProperty( - name = "U Min", - description = "", - default = 0.0, update=prop_update_parametric - ) - - v_min: FloatProperty( - name = "V Min", - description = "", - default = 0.0, - update=prop_update_parametric - ) - - u_max: FloatProperty( - name = "U Max", - description = "", - default = 6.28, - update=prop_update_parametric - ) - - v_max: FloatProperty( - name = "V Max", - description = "", - default = 12.57, update=prop_update_parametric - ) - - x_eq: StringProperty( - maxlen=1024, - default = "cos(v)*(1+cos(u))*sin(v/8)", - update=prop_update_parametric - ) - - y_eq: StringProperty( - maxlen=1024, - default = "sin(u)*sin(v/8)+cos(v/8)*1.5", - update=prop_update_parametric - ) - - z_eq: StringProperty( - maxlen=1024, - default = "sin(v)*(1+cos(u))*sin(v/8)", - update=prop_update_parametric - ) - - ###########Torus - - def prop_update_torus(self, context): - - """Update POV torus primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.torus_update() - - torus_major_segments: IntProperty( - name = "Segments", - description = "Radial segmentation of proxy mesh", - default = 48, min = 3, max = 720, update=prop_update_torus - ) - - torus_minor_segments: IntProperty( - name = "Segments", - description = "Cross-section segmentation of proxy mesh", - default = 12, min = 3, max = 720, update=prop_update_torus - ) - - torus_major_radius: FloatProperty( - name="Major radius", - description="Major radius", - min=0.00, max=100.00, default=1.0, update=prop_update_torus - ) - - torus_minor_radius: FloatProperty( - name="Minor radius", - description="Minor radius", - min=0.00, max=100.00, default=0.25, update=prop_update_torus - ) - - - ###########Rainbow - arc_angle: FloatProperty( - name = "Arc angle", - description = "The angle of the raynbow arc in degrees", - default = 360, min = 0.01, max = 360.0 - ) - - falloff_angle: FloatProperty( - name = "Falloff angle", - description = "The angle after which rainbow dissolves into background", - default = 360, min = 0.0, max = 360 - ) - - ###########HeightFields - - quality: IntProperty( - name = "Quality", - description = "", - default = 100, min = 1, max = 100 - ) - - hf_filename: StringProperty(maxlen = 1024) - - hf_gamma: FloatProperty( - name="Gamma", - description="Gamma", - min=0.0001, max=20.0, default=1.0 - ) - - hf_premultiplied: BoolProperty( - name="Premultiplied", - description="Premultiplied", - default=True, - ) - - hf_smooth: BoolProperty( - name="Smooth", - description="Smooth", - default=False, - ) - - hf_water: FloatProperty( - name="Water Level", - description="Wather Level", - min=0.00, max=1.00, default=0.0, - ) - - hf_hierarchy: BoolProperty( - name="Hierarchy", - description="Height field hierarchy", - default=True, - ) - - ##############Superellipsoid - def prop_update_superellipsoid(self, context): - - """Update POV superellipsoid primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.superellipsoid_update() - - se_param1: FloatProperty( - name="Parameter 1", - description="", - min=0.00, max=10.0, default=0.04 - ) - - se_param2: FloatProperty( - name="Parameter 2", - description="", - min=0.00, max=10.0, default=0.04 - ) - - se_u: IntProperty( - name = "U-segments", - description = "radial segmentation", - default = 20, min = 4, max = 265, - update=prop_update_superellipsoid - ) - - se_v: IntProperty( - name = "V-segments", - description = "lateral segmentation", - default = 20, min = 4, max = 265, - update=prop_update_superellipsoid - ) - - se_n1: FloatProperty( - name = "Ring manipulator", - description = "Manipulates the shape of the Ring", - default = 1.0, min = 0.01, max = 100.0, - update=prop_update_superellipsoid - ) - - se_n2: FloatProperty(name = "Cross manipulator", - description = "Manipulates the shape of the cross-section", - default = 1.0, min = 0.01, max = 100.0, - update=prop_update_superellipsoid - ) - - se_edit: EnumProperty(items=[("NOTHING", "Nothing", ""), - ("NGONS", "N-Gons", ""), - ("TRIANGLES", "Triangles", "")], - name="Fill up and down", - description="", - default='TRIANGLES', - update=prop_update_superellipsoid - ) - - #############Used for loft and Superellipsoid, etc. - curveshape: EnumProperty( - name="Povray Shape Type", - items=( - ("birail", "Birail", ""), - ("cairo", "Cairo", ""), - ("lathe", "Lathe", ""), - ("loft", "Loft", ""), - ("prism", "Prism", ""), - ("sphere_sweep", "Sphere Sweep", ""), - ("sor", "Surface of Revolution", "") - ), - default="sphere_sweep", - ) - -#############Supertorus - def prop_update_supertorus(self, context): - - """Update POV supertorus primitive parameters not only at creation but anytime they are changed in UI.""" - - bpy.ops.pov.supertorus_update() - - st_major_radius: FloatProperty( - name="Major radius", - description="Major radius", - min=0.00, - max=100.00, - default=1.0, - update=prop_update_supertorus - ) - - st_minor_radius: FloatProperty( - name="Minor radius", - description="Minor radius", - min=0.00, max=100.00, default=0.25, - update=prop_update_supertorus - ) - - st_ring: FloatProperty( - name="Ring", - description="Ring manipulator", - min=0.0001, max=100.00, default=1.00, - update=prop_update_supertorus - ) - - st_cross: FloatProperty( - name="Cross", - description="Cross manipulator", - min=0.0001, max=100.00, default=1.00, - update=prop_update_supertorus - ) - - st_accuracy: FloatProperty( - name="Accuracy", - description="Supertorus accuracy", - min=0.00001, max=1.00, default=0.001 - ) - - st_max_gradient: FloatProperty( - name="Gradient", - description="Max gradient", - min=0.0001, max=100.00, default=10.00, - update=prop_update_supertorus - ) - - st_R: FloatProperty( - name = "big radius", - description = "The radius inside the tube", - default = 1.0, min = 0.01, max = 100.0, - update=prop_update_supertorus - ) - - st_r: FloatProperty( - name = "small radius", - description = "The radius of the tube", - default = 0.3, min = 0.01, max = 100.0, - update=prop_update_supertorus - ) - - st_u: IntProperty( - name = "U-segments", - description = "radial segmentation", - default = 16, min = 3, max = 265, - update=prop_update_supertorus - ) - - st_v: IntProperty( - name = "V-segments", - description = "lateral segmentation", - default = 8, min = 3, max = 265, - update=prop_update_supertorus - ) - - st_n1: FloatProperty( - name = "Ring manipulator", - description = "Manipulates the shape of the Ring", - default = 1.0, min = 0.01, max = 100.0, - update=prop_update_supertorus - ) - - st_n2: FloatProperty( - name = "Cross manipulator", - description = "Manipulates the shape of the cross-section", - default = 1.0, min = 0.01, max = 100.0, - update=prop_update_supertorus - ) - - st_ie: BoolProperty( - name = "Use Int.+Ext. radii", - description = "Use internal and external radii", - default = False, - update=prop_update_supertorus - ) - - st_edit: BoolProperty( - name="", - description="", - default=False, - options={'HIDDEN'}, - update=prop_update_supertorus - ) - - ########################Loft - loft_n: IntProperty( - name = "Segments", - description = "Vertical segments", - default = 16, min = 3, max = 720 - ) - - loft_rings_bottom: IntProperty( - name = "Bottom", - description = "Bottom rings", - default = 5, min = 2, max = 100 - ) - - loft_rings_side: IntProperty( - name = "Side", - description = "Side rings", - default = 10, min = 2, max = 100 - ) - - loft_thick: FloatProperty( - name = "Thickness", - description = "Manipulates the shape of the Ring", - default = 0.3, min = 0.01, max = 1.0 - ) - - loft_r: FloatProperty( - name = "Radius", - description = "Radius", - default = 1, min = 0.01, max = 10 - ) - - loft_height: FloatProperty( - name = "Height", - description = "Manipulates the shape of the Ring", - default = 2, min = 0.01, max = 10.0 - ) - - ###################Prism - prism_n: IntProperty( - name = "Sides", - description = "Number of sides", - default = 5, min = 3, max = 720 - ) - - prism_r: FloatProperty( - name = "Radius", - description = "Radius", - default = 1.0 - ) - - ##################Isosurface - iso_function_text: StringProperty( - name="Function Text", - maxlen=1024 - ) #,update=iso_props_update_callback) - - ##################PolygonToCircle - polytocircle_resolution: IntProperty( - name = "Resolution", - description = "", - default = 3, min = 0, max = 256 - ) - - polytocircle_ngon: IntProperty( - name = "NGon", - description = "", - min = 3, max = 64,default = 5 - ) - - polytocircle_ngonR: FloatProperty( - name = "NGon Radius", - description = "", - default = 0.3 - ) - - polytocircle_circleR: FloatProperty( - name = "Circle Radius", - description = "", - default = 1.0 - ) - - -############################################################################### -# Modifiers POV properties. -############################################################################### -# class RenderPovSettingsModifier(PropertyGroup): - boolean_mod: EnumProperty( - name="Operation", - description="Choose the type of calculation for Boolean modifier", - items=( - ("BMESH", "Use the BMesh Boolean Solver", ""), - ("CARVE", "Use the Carve Boolean Solver", ""), - ("POV", "Use POV Constructive Solid Geometry", "") - ), - default="BMESH", - ) - - #################Avogadro - # filename_ext = ".png" - - # filter_glob = StringProperty( - # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF", - # options={'HIDDEN'}, - # ) - -############################################################################### -# Camera POV properties. -############################################################################### -class RenderPovSettingsCamera(PropertyGroup): - - """Declare camera properties controllable in UI and translated to POV.""" - - # DOF Toggle - dof_enable: BoolProperty( - name="Depth Of Field", - description="Enable POV Depth Of Field ", - default=False, - ) - - # Aperture (Intensity of the Blur) - dof_aperture: FloatProperty( - name="Aperture", - description="Similar to a real camera's aperture effect over focal blur (though not " - "in physical units and independent of focal length). " - "Increase to get more blur", - min=0.01, max=1.00, default=0.50 - ) - - # Aperture adaptive sampling - dof_samples_min: IntProperty( - name="Samples Min", - description="Minimum number of rays to use for each pixel", - min=1, max=128, default=3 - ) - - dof_samples_max: IntProperty( - name="Samples Max", - description="Maximum number of rays to use for each pixel", - min=1, max=128, default=9 - ) - - dof_variance: IntProperty( - name="Variance", - description="Minimum threshold (fractional value) for adaptive DOF sampling (up " - "increases quality and render time). The value for the variance should " - "be in the range of the smallest displayable color difference", - min=1, max=100000, soft_max=10000, default=8192 - ) - - dof_confidence: FloatProperty( - name="Confidence", - description="Probability to reach the real color value. Larger confidence values " - "will lead to more samples, slower traces and better images", - min=0.01, max=0.99, default=0.20 - ) - - normal_enable: BoolProperty( - name="Perturbated Camera", - default=False, - ) - - cam_normal: FloatProperty( - name="Normal Strength", - min=0.0, - max=1.0, - default=0.0, - ) - - normal_patterns: EnumProperty( - name="Pattern", - description="", - items=( - ('agate', "Agate", ""), - ('boxed', "Boxed", ""), - ('bumps', "Bumps", ""), - ('cells', "Cells", ""), - ('crackle', "Crackle", ""), - ('dents', "Dents", ""), - ('granite', "Granite", ""), - ('leopard', "Leopard", ""), - ('marble', "Marble", ""), - ('onion', "Onion", ""), - ('pavement', "Pavement", ""), - ('planar', "Planar", ""), - ('quilted', "Quilted", ""), - ('ripples', "Ripples", ""), - ('radial', "Radial", ""), - ('spherical', "Spherical", ""), - ('spiral1', "Spiral1", ""), - ('spiral2', "Spiral2", ""), - ('spotted', "Spotted", ""), - ('square', "Square", ""), - ('tiling', "Tiling", ""), - ('waves', "Waves", ""), - ('wood', "Wood", ""), - ('wrinkles', "Wrinkles", "") - ), - default='agate', - ) - - turbulence: FloatProperty(name="Turbulence", min=0.0, max=100.0, default=0.1) - - scale: FloatProperty(name="Scale", min=0.0,default=1.0) - - ##################################CustomPOV Code############################ - # Only DUMMIES below for now: - replacement_text: StringProperty( - name="Texts in blend file", - description="Type the declared name in custom POV code or an external .inc " - "it points at. camera {} expected", - default="", - ) -############################################################################### -# Light POV properties. -############################################################################### -class RenderPovSettingsLight(PropertyGroup): - - """Declare light properties controllable in UI and translated to POV.""" - - # former Space properties from removed Blender Internal - use_limited_texture_context: BoolProperty( - name="", - description="Use the limited version of texture user (for â€old shading’ mode)", - default=True, - ) - - texture_context: EnumProperty( - name="Texture context", - description="Type of texture data to display and edit", - items=( - ('MATERIAL', "", "Show material textures", "MATERIAL",0), # "Show material textures" - ('WORLD', "", "Show world textures", "WORLD",1), # "Show world textures" - ('LAMP', "", "Show lamp textures", "LIGHT",2), # "Show lamp textures" - ('PARTICLES', "", "Show particles textures", "PARTICLES",3), # "Show particles textures" - ('LINESTYLE', "", "Show linestyle textures", "LINE_DATA",4), # "Show linestyle textures" - ('OTHER', "", "Show other data textures", "TEXTURE_DATA",5), # "Show other data textures" - ), - default = 'MATERIAL', - ) - - shadow_method: EnumProperty( - name="Shadow", - description="", - items=(("NOSHADOW", "No Shadow", "No Shadow"), - ("RAY_SHADOW", "Ray Shadow", "Ray Shadow, Use ray tracing for shadow")), - default="RAY_SHADOW", - ) - - active_texture_index: IntProperty( - name = "Index for texture_slots", - default = 0 - ) - - use_halo: BoolProperty( - name="Halo", - description="Render spotlight with a volumetric halo", - default=False, - ) - - halo_intensity: FloatProperty( - name="Halo intensity", - description="Brightness of the spotlight halo cone", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - shadow_ray_samples_x: IntProperty( - name = "Number of samples taken extra (samples x samples)", - min=1, soft_max=64, - default = 1, - ) - - shadow_ray_samples_y: IntProperty( - name = "Number of samples taken extra (samples x samples)", - min=1, soft_max=64, - default = 1, - ) - - shadow_ray_sample_method: EnumProperty( - name="", - description="Method for generating shadow samples: Adaptive QMC is fastest, Constant QMC is less noisy but slower", - items=( - ('ADAPTIVE_QMC', "", "Halton samples distribution", "",0), - ('CONSTANT_QMC', "", "QMC samples distribution", "",1), - ('CONSTANT_JITTERED', "", "Uses POV jitter keyword", "",2) # "Show other data textures" - ), - default = 'CONSTANT_JITTERED', - ) - - use_jitter: BoolProperty( - name="Jitter", - description="Use noise for sampling (Constant Jittered sampling)", - default=False, - ) - -############################################################################### -# World POV properties. -############################################################################### -class RenderPovSettingsWorld(PropertyGroup): - - """Declare world properties controllable in UI and translated to POV.""" - - # former Space properties from removed Blender Internal - use_limited_texture_context: BoolProperty( - name="", - description="Use the limited version of texture user (for â€old shading’ mode)", - default=True, - ) - - texture_context: EnumProperty( - name="Texture context", - description="Type of texture data to display and edit", - items=( - ('MATERIAL', "", "Show material textures", "MATERIAL",0), # "Show material textures" - ('WORLD', "", "Show world textures", "WORLD",1), # "Show world textures" - ('LIGHT', "", "Show lamp textures", "LIGHT",2), # "Show lamp textures" - ('PARTICLES', "", "Show particles textures", "PARTICLES",3), # "Show particles textures" - ('LINESTYLE', "", "Show linestyle textures", "LINE_DATA",4), # "Show linestyle textures" - ('OTHER', "", "Show other data textures", "TEXTURE_DATA",5), # "Show other data textures" - ), - default = 'MATERIAL', - ) - - use_sky_blend: BoolProperty( - name="Blend Sky", - description="Render background with natural progression from horizon to zenith", - default=False, - ) - - use_sky_paper: BoolProperty( - name="Paper Sky", - description="Flatten blend or texture coordinates", - default=False, - ) - - use_sky_real: BoolProperty( - name="Real Sky", - description="Render background with a real horizon, relative to the camera angle", - default=False, - ) - - horizon_color: FloatVectorProperty( - name="Horizon Color", - description="Color at the horizon", - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), options={'ANIMATABLE'}, subtype='COLOR', - ) - - zenith_color: FloatVectorProperty( - name="Zenith Color", - description="Color at the zenith", - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), options={'ANIMATABLE'}, subtype='COLOR', - ) - - ambient_color: FloatVectorProperty( - name="Ambient Color", - description="Ambient color of the world", - precision=4, step=0.01, min=0, soft_max=1, - default=(0.0, 0.0, 0.0), options={'ANIMATABLE'}, subtype='COLOR', - ) - active_texture_index: IntProperty( - name = "Index for texture_slots", - default = 0, - update = brush_texture_update - ) - -class WorldTextureSlot(PropertyGroup): - """Declare world texture slot level properties for UI and translated to POV.""" - - bl_idname="pov_texture_slots", - bl_description="Texture_slots from Blender-2.79", - - # Adding a "real" texture datablock as property is not possible - # (or at least not easy through a dynamically populated EnumProperty). - # That's why we'll use a prop_search() UILayout function in ui.py. - # So we'll assign the name of the needed texture datablock to the below StringProperty. - texture : StringProperty(update=active_texture_name_from_uilist) - # and use another temporary StringProperty to change the linked data - texture_search : StringProperty( - name="", - update = active_texture_name_from_search, - description = "Browse Texture to be linked", - ) - - blend_factor: FloatProperty( - name="Blend", - description="Amount texture affects color progression of the " - "background", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - horizon_factor: FloatProperty( - name="Horizon", - description="Amount texture affects color of the horizon", - soft_min=0.0, soft_max=1.0, default=1.0, - ) - - object: StringProperty( - name="Object", - description="Object to use for mapping with Object texture coordinates", - default="", - ) - - offset: FloatVectorProperty( - name="Offset", - description=("Fine tune of the texture mapping X, Y and Z locations "), - precision=4, - step=0.1, - soft_min= -100.0, - soft_max=100.0, - default=(0.0,0.0,0.0), - options={'ANIMATABLE'}, - subtype='TRANSLATION', - ) - - scale: FloatVectorProperty( - name="Size", - subtype='XYZ', - size=3, - description="Set scaling for the texture’s X, Y and Z sizes ", - precision=4, - step=0.1, - soft_min= -100.0, - soft_max=100.0, - default=(1.0,1.0,1.0), - options={'ANIMATABLE'}, - ) - - texture_coords: EnumProperty( - name="Coordinates", - description="Texture coordinates used to map the texture onto the background", - items=( - ("VIEW", "View", "Use view vector for the texture coordinates"), - ("GLOBAL", "Global", "Use global coordinates for the texture coordinates (interior mist)"), - ("ANGMAP", "AngMap", "Use 360 degree angular coordinates, e.g. for spherical light probes"), - ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"), - ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"), - ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"), - ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates") - ), - default="VIEW", - ) - - use_map_blend: BoolProperty( - name="Blend Map", - description="Affect the color progression of the background", - default=True, - ) - - use_map_horizon: BoolProperty( - name="Horizon Map", - description="Affect the color of the horizon", - default=False, - ) - - use_map_zenith_down: BoolProperty( - name="", - description="Affect the color of the zenith below", - default=False, - ) - - use_map_zenith_up: BoolProperty( - name="Zenith Up Map", - description="Affect the color of the zenith above", - default=False, - ) - - zenith_down_factor: FloatProperty( - name="Zenith Down", - description="Amount texture affects color of the zenith below", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - - zenith_up_factor: FloatProperty( - name="Zenith Up", - description="Amount texture affects color of the zenith above", - soft_min=0.0, soft_max=1.0, default=1.0 - ) - -''' -# class WORLD_TEXTURE_SLOTS_UL_layerlist(bpy.types.UIList): -# texture_slots: - -class WorldTextureSlots(bpy.props.PropertyGroup): - index = bpy.prop.PropertyInt(name='index') - # foo = random prop - -bpy.types.World.texture_slots = bpy.props.CollectionProperty(type=PropertyGroup) - -for i in range(18): # length of world texture slots - world.texture_slots.add() -''' - -class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(bpy.types.UIList): - # texture_slots: - #index: bpy.props.IntProperty(name='index') - # foo = random prop - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - ob = data - slot = item - # ma = slot.name - # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. - if self.layout_type in {'DEFAULT', 'COMPACT'}: - # You should always start your row layout by a label (icon + text), or a non-embossed text field, - # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. - # We use icon_value of label, as our given icon is an integer value, not an enum ID. - # Note "data" names should never be translated! - if slot: - layout.prop(item, "texture", text="", emboss=False, icon='TEXTURE') - else: - layout.label(text="New", translate=False, icon_value=icon) - # 'GRID' layout type should be as compact as possible (typically a single icon!). - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - -############################################################################### -# Text POV properties. -############################################################################### - - -class RenderPovSettingsText(PropertyGroup): - - """Declare text properties to use UI as an IDE or render text snippets to POV.""" - - custom_code: EnumProperty( - name="Custom Code", - description="rendered source: Both adds text at the " - "top of the exported POV file", - items=( - ("3dview", "View", ""), - ("text", "Text", ""), - ("both", "Both", "") - ), - default="text", - ) - -############################################################################### -# Povray Preferences. -############################################################################### - - -class PovrayPreferences(AddonPreferences): - - """Declare preference variables to set up POV binary.""" - - bl_idname = __name__ - - branch_feature_set_povray: EnumProperty( - name="Feature Set", - description="Choose between official (POV-Ray) or (UberPOV) " - "development branch features to write in the pov file", - items= ( - ('povray', 'Official POV-Ray', '','PLUGIN', 0), - ('uberpov', 'Unofficial UberPOV', '', 'PLUGIN', 1) - ), - default='povray', - ) - - filepath_povray: StringProperty( - name="Binary Location", - description="Path to renderer executable", - subtype='FILE_PATH', - ) - - docpath_povray: StringProperty( - name="Includes Location", - description="Path to Insert Menu files", - subtype='FILE_PATH', - ) - - use_sounds: BoolProperty( - name="Use Sound", - description="Signaling end of the render process at various" - "stages can help if you're away from monitor", - default=False, - ) - - # TODO: Auto find POV sound directory as it does for binary - # And implement the three cases, left uncommented for a dummy - # interface in case some doc screenshots get made for that area - filepath_complete_sound: StringProperty( - name="Finish Render Sound", - description="Path to finished render sound file", - subtype='FILE_PATH', - ) - - filepath_parse_error_sound: StringProperty( - name="Parse Error Sound", - description="Path to parsing time error sound file", - subtype='FILE_PATH', - ) - - filepath_cancel_sound: StringProperty( - name="Cancel Render Sound", - description="Path to cancelled or render time error sound file", - subtype='FILE_PATH', - ) - - #shall we not move this to UI file? - def draw(self, context): - layout = self.layout - layout.prop(self, "branch_feature_set_povray") - layout.prop(self, "filepath_povray") - layout.prop(self, "docpath_povray") - layout.prop(self, "filepath_complete_sound") - layout.prop(self, "filepath_parse_error_sound") - layout.prop(self, "filepath_cancel_sound") - layout.prop(self, "use_sounds", icon='SOUND') - - -classes = ( - PovrayPreferences, - RenderPovSettingsCamera, - RenderPovSettingsLight, - RenderPovSettingsWorld, - MaterialTextureSlot, - WorldTextureSlot, - RenderPovSettingsMaterial, - MaterialRaytraceTransparency, - MaterialRaytraceMirror, - MaterialSubsurfaceScattering, - MaterialStrandSettings, - RenderPovSettingsObject, - RenderPovSettingsScene, - RenderPovSettingsText, - RenderPovSettingsTexture, -) - - -def register(): - - # bpy.utils.register_module(__name__) # DEPRECATED Now imported from bpy.utils import register_class - - for cls in classes: - register_class(cls) - - render.register() - ui.register() - primitives.register() - nodes.register() - - ''' - bpy.types.VIEW3D_MT_add.prepend(ui.menu_func_add) - bpy.types.TOPBAR_MT_file_import.append(ui.menu_func_import) - bpy.types.TEXT_MT_templates.append(ui.menu_func_templates) - bpy.types.RENDER_PT_POV_radiosity.prepend(ui.rad_panel_func) - bpy.types.LIGHT_PT_POV_light.prepend(ui.light_panel_func) - bpy.types.WORLD_PT_world.prepend(ui.world_panel_func) - # was used for parametric objects but made the other addon unreachable on - # unregister for other tools to use created a user action call instead - # addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True) - - # bpy.types.TEXTURE_PT_context_texture.prepend(TEXTURE_PT_povray_type) - ''' - bpy.types.NODE_HT_header.append(ui.menu_func_nodes) - nodeitems_utils.register_node_categories("POVRAYNODES", node_categories) - bpy.types.Scene.pov = PointerProperty(type=RenderPovSettingsScene) - # bpy.types.Modifier.pov = PointerProperty(type=RenderPovSettingsModifier) - bpy.types.Material.pov_raytrace_transparency = PointerProperty(type=MaterialRaytraceTransparency) - bpy.types.Material.pov = PointerProperty(type=RenderPovSettingsMaterial) - bpy.types.Material.pov_subsurface_scattering = PointerProperty(type=MaterialSubsurfaceScattering) - bpy.types.Material.strand = PointerProperty(type=MaterialStrandSettings) - bpy.types.Material.pov_raytrace_mirror = PointerProperty(type=MaterialRaytraceMirror) - bpy.types.Texture.pov = PointerProperty(type=RenderPovSettingsTexture) - bpy.types.Object.pov = PointerProperty(type=RenderPovSettingsObject) - bpy.types.Camera.pov = PointerProperty(type=RenderPovSettingsCamera) - bpy.types.Light.pov = PointerProperty(type=RenderPovSettingsLight) - bpy.types.World.pov = PointerProperty(type=RenderPovSettingsWorld) - bpy.types.Material.pov_texture_slots = CollectionProperty(type=MaterialTextureSlot) - bpy.types.World.pov_texture_slots = CollectionProperty(type=WorldTextureSlot) - bpy.types.Text.pov = PointerProperty(type=RenderPovSettingsText) - - -def unregister(): - del bpy.types.Scene.pov - del bpy.types.Material.pov - del bpy.types.Material.pov_subsurface_scattering - del bpy.types.Material.strand - del bpy.types.Material.pov_raytrace_mirror - del bpy.types.Material.pov_raytrace_transparency - # del bpy.types.Modifier.pov - del bpy.types.Texture.pov - del bpy.types.Object.pov - del bpy.types.Camera.pov - del bpy.types.Light.pov - del bpy.types.World.pov - del bpy.types.World.pov_texture_slots - del bpy.types.Material.pov_texture_slots - del bpy.types.Text.pov - - nodeitems_utils.unregister_node_categories("POVRAYNODES") - bpy.types.NODE_HT_header.remove(ui.menu_func_nodes) - ''' - # bpy.types.TEXTURE_PT_context_texture.remove(TEXTURE_PT_povray_type) - # addon_utils.disable("add_mesh_extra_objects", default_set=False) - bpy.types.WORLD_PT_POV_world.remove(ui.world_panel_func) - bpy.types.LIGHT_PT_POV_light.remove(ui.light_panel_func) - bpy.types.RENDER_PT_POV_radiosity.remove(ui.rad_panel_func) - bpy.types.TEXT_MT_templates.remove(ui.menu_func_templates) - bpy.types.TOPBAR_MT_file_import.remove(ui.menu_func_import) - bpy.types.VIEW3D_MT_add.remove(ui.menu_func_add) - ''' - # bpy.utils.unregister_module(__name__) - - nodes.unregister() - primitives.unregister() - ui.unregister() - render.unregister() - - for cls in reversed(classes): - unregister_class(cls) - - - -if __name__ == "__main__": - register() +# ------------8<---------[ BREAKPOINT ]--------------8<-----------# Move this snippet around +# __import__('code').interact(local=dict(globals(), **locals())) # < and uncomment this line +# ----------------------------------------------------------------# to inspect from Terminal diff --git a/render_povray/base_ui.py b/render_povray/base_ui.py new file mode 100755 index 000000000..f0e2cb2f0 --- /dev/null +++ b/render_povray/base_ui.py @@ -0,0 +1,307 @@ +# ##### 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 ##### + +# <pep8 compliant> + +"""User interface imports and preferences for the addon.""" + +# import addon_utils +# from time import sleep +import bpy + + +from bpy.app.handlers import persistent + +# from bpy.utils import register_class, unregister_class +# from bpy.types import ( +# Operator, +# Menu, +# UIList, +# Panel, +# Brush, +# Material, +# Light, +# World, +# ParticleSettings, +# FreestyleLineStyle, +# ) + +# from bl_operators.presets import AddPresetBase + +from . import ( + render_gui, + scenography_gui, + object_gui, + shading_gui, + texturing_gui, + shading_nodes, # for POV specific nodes + scripting_gui, +) + + +############# POV-Centric WORKSPACE ############# +@persistent +def povCentricWorkspace(dummy): + """Set up a POV centric Workspace if addon was activated and saved as default renderer. + + This would bring a ’_RestrictData’ error because UI needs to be fully loaded before + workspace changes so registering this function in bpy.app.handlers is needed. + By default handlers are freed when loading new files, but here we want the handler + to stay running across multiple files as part of this add-on. That is why the + bpy.app.handlers.persistent decorator is used (@persistent) above. + """ + # Scripting workspace may have been altered from factory though, so should + # we put all within a Try... Except AttributeErrors ? Any better solution ? + # Should it simply not run when opening existing file? be a preferences operator to create + # Moray like workspace + if 'Scripting' in bpy.data.workspaces: + + wsp = bpy.data.workspaces.get('Scripting') + context = bpy.context + if context.scene.render.engine == 'POVRAY_RENDER' and wsp is not None: + bpy.ops.workspace.duplicate({'workspace': wsp}) + bpy.data.workspaces['Scripting.001'].name = 'POV' + # Already done it would seem, but explicitly make this workspace the active one + context.window.workspace = bpy.data.workspaces['POV'] + pov_screen = bpy.data.workspaces['POV'].screens[0] + pov_workspace = pov_screen.areas + pov_window = context.window + try: + # Already outliners but invert both types + pov_workspace[1].spaces[0].display_mode = 'LIBRARIES' + pov_workspace[3].spaces[0].display_mode = 'VIEW_LAYER' + except AttributeError: + # But not necessarily outliners in existing blend files + pass + override = bpy.context.copy() + + for area in pov_workspace: + if area.type == 'VIEW_3D': + for region in [r for r in area.regions if r.type == 'WINDOW']: + for space in area.spaces: + if space.type == 'VIEW_3D': + # override['screen'] = pov_screen + override['area'] = area + override['region'] = region + # bpy.data.workspaces['POV'].screens[0].areas[6].spaces[0].width = 333 # Read only, + # how do we set ? + # This has a glitch: + # bpy.ops.screen.area_move(override, x=(area.x + area.width), y=(area.y + 5), delta=100) + # bpy.ops.screen.area_move(override, x=(area.x + 5), y=area.y, delta=-100) + + bpy.ops.screen.space_type_set_or_cycle( + override, space_type='TEXT_EDITOR' + ) + space.show_region_ui = True + # bpy.ops.screen.region_scale(override) + # bpy.ops.screen.region_scale() + break + + elif area.type == 'CONSOLE': + for region in [r for r in area.regions if r.type == 'WINDOW']: + for space in area.spaces: + if space.type == 'CONSOLE': + override['screen'] = pov_screen + override['window'] = pov_window + override['area'] = area + override['region'] = region + + area_x = area.x + (area.width / 2) + area_y = area.y + area.height + bpy.ops.screen.space_type_set_or_cycle(override, space_type='INFO') + try: + if area == pov_workspace[6] and bpy.ops.screen.area_move.poll( + override + ): + # bpy.ops.screen.area_move(override, x = area_x, y = area_y, delta = -300) + pass + # pov_window.cursor_warp(area_x, area_y-300) # Is manual move emulation necessary + # despite the delta? + except IndexError: + # Not necessarily so many areas in existing blend files + pass + + break + + elif area.type == 'INFO': + for region in [r for r in area.regions if r.type == 'WINDOW']: + for space in area.spaces: + if space.type == 'INFO': + # override['screen'] = pov_screen + override['area'] = area + override['region'] = region + bpy.ops.screen.space_type_set_or_cycle( + override, space_type='CONSOLE' + ) + + break + + elif area.type == 'TEXT_EDITOR': + for region in [r for r in area.regions if r.type == 'WINDOW']: + for space in area.spaces: + if space.type == 'TEXT_EDITOR': + # override['screen'] = pov_screen + override['area'] = area + override['region'] = region + # bpy.ops.screen.space_type_set_or_cycle(space_type='VIEW_3D') + # space.type = 'VIEW_3D' + bpy.ops.screen.space_type_set_or_cycle( + override, space_type='VIEW_3D' + ) + + # bpy.ops.screen.area_join(override, cursor=(area.x, area.y + area.height)) + + break + + if area.type == 'VIEW_3D': + for region in [r for r in area.regions if r.type == 'WINDOW']: + for space in area.spaces: + if space.type == 'VIEW_3D': + # override['screen'] = pov_screen + override['area'] = area + override['region'] = region + bpy.ops.screen.region_quadview(override) + space.region_3d.view_perspective = 'CAMERA' + # bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'TEXT_EDITOR') + # bpy.ops.screen.region_quadview(override) + + elif area.type == 'OUTLINER': + for region in [ + r for r in area.regions if r.type == 'HEADER' and (r.y - area.y) + ]: + for space in area.spaces: + if space.display_mode == 'LIBRARIES': + override['area'] = area + override['region'] = region + override['window'] = pov_window + bpy.ops.screen.region_flip(override) + + bpy.data.workspaces.update() + + ''' + for window in bpy.context.window_manager.windows: + for area in [a for a in window.screen.areas if a.type == 'VIEW_3D']: + for region in [r for r in area.regions if r.type == 'WINDOW']: + context_override = { + 'window': window, + 'screen': window.screen, + 'area': area, + 'region': region, + 'space_data': area.spaces.active, + 'scene': bpy.context.scene + } + bpy.ops.view3d.camera_to_view(context_override) + ''' + + else: + print( + "\nPOV centric workspace available if you set render option\n" + "and save it in default file with CTRL+U" + ) + + else: + print( + "\nThe factory 'Scripting' workspace is needed before POV centric " + "\nworkspace may activate when POV is set as your default renderer" + ) + ####################################UTF-8################################### + # Check and fix all strings in current .blend file to be valid UTF-8 Unicode + # sometimes needed for old, 2.4x / 2.6x area files + bpy.ops.wm.blend_strings_utf8_validate() + + +def check_material(mat): + """Allow use of material properties buttons rather than nodes.""" + if mat is not None: + if mat.use_nodes: + if not mat.node_tree: # FORMERLY : #mat.active_node_material is not None: + return True + return False + return True + return False + + +def simple_material(mat): + """Test if a material uses nodes.""" + if (mat is not None) and (not mat.use_nodes): + return True + return False + + +def pov_context_tex_datablock(context): + """Recreate texture context type as deprecated in blender 2.8.""" + idblock = context.brush + if idblock and context.scene.texture_context == 'OTHER': + return idblock + + # idblock = bpy.context.active_object.active_material + idblock = context.view_layer.objects.active.active_material + if idblock and context.scene.texture_context == 'MATERIAL': + return idblock + + idblock = context.scene.world + if idblock and context.scene.texture_context == 'WORLD': + return idblock + + idblock = context.light + if idblock and context.scene.texture_context == 'LIGHT': + return idblock + + if context.particle_system and context.scene.texture_context == 'PARTICLES': + idblock = context.particle_system.settings + + return idblock + + idblock = context.line_style + if idblock and context.scene.texture_context == 'LINESTYLE': + return idblock + + +# class TextureTypePanel(TextureButtonsPanel): + +# @classmethod +# def poll(cls, context): +# tex = context.texture +# engine = context.scene.render.engine +# return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES)) + + +def register(): + render_gui.register() + scenography_gui.register() + object_gui.register() + shading_gui.register() + texturing_gui.register() + shading_nodes.register() + scripting_gui.register() + + if not povCentricWorkspace in bpy.app.handlers.load_post: + bpy.app.handlers.load_post.append(povCentricWorkspace) + + +def unregister(): + if povCentricWorkspace in bpy.app.handlers.load_post: + bpy.app.handlers.load_post.remove(povCentricWorkspace) + + scripting_gui.unregister() + shading_nodes.unregister() + texturing_gui.unregister() + shading_gui.unregister() + object_gui.unregister() + scenography_gui.unregister() + render_gui.register() diff --git a/render_povray/df3.py b/render_povray/df3_library.py old mode 100644 new mode 100755 similarity index 57% rename from render_povray/df3.py rename to render_povray/df3_library.py index b84342875..f802fb2dd --- a/render_povray/df3.py +++ b/render_povray/df3_library.py @@ -52,18 +52,19 @@ import sys # -+-+-+- Start df3 Class -+-+-+- + class df3: __version__ = '0.2' __arraytype__ = 'f' - __struct4byte__ = '>I' - __struct2byte__ = '>H' + __struct4byte__ = '>I' + __struct2byte__ = '>H' __struct2byte3__ = '>HHH' - __struct1byte__ = '>B' - __array4byte__ = 'I' - __array2byte__ = 'H' - __array1byte__ = 'B' + __struct1byte__ = '>B' + __array4byte__ = 'I' + __array2byte__ = 'H' + __array1byte__ = 'B' def __init__(self, x=1, y=1, z=1): self.maxX = x @@ -73,7 +74,7 @@ class df3: def clone(self, indf3): self.voxel = array.array(self.__arraytype__) - for i in range(indf3.sizeX()*indf3.sizeY()*indf3.sizeZ()): + for i in range(indf3.sizeX() * indf3.sizeY() * indf3.sizeZ()): self.voxel[i] = indf3.voxel[i] return self @@ -98,35 +99,41 @@ class df3: #### Voxel Access Functions def get(self, x, y, z): - return self.voxel[self.__voxa__(x,y,z)] + return self.voxel[self.__voxa__(x, y, z)] def getB(self, x, y, z): - if (x > self.sizeX() or x < 0): return 0 - if (y > self.sizeX() or y < 0): return 0 - if (z > self.sizeX() or z < 0): return 0 + if x > self.sizeX() or x < 0: + return 0 + if y > self.sizeX() or y < 0: + return 0 + if z > self.sizeX() or z < 0: + return 0 - return self.voxel[self.__voxa__(x,y,z)] + return self.voxel[self.__voxa__(x, y, z)] def set(self, x, y, z, val): - self.voxel[self.__voxa__(x,y,z)] = val + self.voxel[self.__voxa__(x, y, z)] = val def setB(self, x, y, z, val): - if (x > self.sizeX() or x < 0): return - if (y > self.sizeX() or y < 0): return - if (z > self.sizeX() or z < 0): return + if x > self.sizeX() or x < 0: + return + if y > self.sizeX() or y < 0: + return + if z > self.sizeX() or z < 0: + return - self.voxel[self.__voxa__(x,y,z)] = val + self.voxel[self.__voxa__(x, y, z)] = val #### Scalar Functions def mult(self, val): - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): self.voxel[i] = self.voxel[i] * val return self def add(self, val): - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): self.voxel[i] = self.voxel[i] + val return self @@ -134,8 +141,8 @@ class df3: def max(self): tmp = self.voxel[0] - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): - if (self.voxel[i] > tmp): + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): + if self.voxel[i] > tmp: tmp = self.voxel[i] return tmp @@ -143,8 +150,8 @@ class df3: def min(self): tmp = self.voxel[0] - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): - if (self.voxel[i] < tmp): + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): + if self.voxel[i] < tmp: tmp = self.voxel[i] return tmp @@ -152,30 +159,31 @@ class df3: #### Vector Functions def compare(self, indf3): - if (self.__samesize__(indf3) == 0): return 0 + if self.__samesize__(indf3) == 0: + return 0 - if (self.voxel == indf3.voxel): + if self.voxel == indf3.voxel: return 1 return 0 def multV(self, indf3): - if (self.__samesize__(indf3) == 0): + if self.__samesize__(indf3) == 0: print("Cannot multiply voxels - not same size") return - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): - self.voxel[i] = self.voxel[i]*indf3.voxel[i] + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): + self.voxel[i] = self.voxel[i] * indf3.voxel[i] return self def addV(self, indf3): - if (self.__samesize__(indf3) == 0): + if self.__samesize__(indf3) == 0: print("Cannot add voxels - not same size") return - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): - self.voxel[i] = self.voxel[i]+indf3.voxel[i] + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): + self.voxel[i] = self.voxel[i] + indf3.voxel[i] return self @@ -183,31 +191,31 @@ class df3: fx = filt.sizeX() fy = filt.sizeY() fz = filt.sizeZ() - if (fx % 2 != 1): + if fx % 2 != 1: print("Incompatible filter - must be odd number of X") return self - if (fy % 2 != 1): + if fy % 2 != 1: print("Incompatible filter - must be odd number of Y") return self - if (fz % 2 != 1): + if fz % 2 != 1: print("Incompatible filter - must be odd number of Z") return self - fdx = (fx-1)/2 - fdy = (fy-1)/2 - fdz = (fz-1)/2 - flen = fx*fy*fz + fdx = (fx - 1) / 2 + fdy = (fy - 1) / 2 + fdz = (fz - 1) / 2 + flen = fx * fy * fz - newV = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ()); + newV = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ()) for x in range(self.sizeX()): for y in range(self.sizeY()): for z in range(self.sizeZ()): - rip = self.__rip__(x-fdx, x+fdx, y-fdy, y+fdy, z-fdz, z+fdz) + rip = self.__rip__(x - fdx, x + fdx, y - fdy, y + fdy, z - fdz, z + fdz) tmp = 0.0 for i in range(flen): - tmp += rip[i]*filt.voxel[i] - newV[self.__voxa__(x,y,z)] = tmp + tmp += rip[i] * filt.voxel[i] + newV[self.__voxa__(x, y, z)] = tmp self.voxel = newV @@ -221,64 +229,67 @@ class df3: z = self.sizeZ() try: - f = open(file, 'wb'); + f = open(file, 'wb') except: - print("Could not open " + file + " for write"); + print("Could not open " + file + " for write") return - f.write(struct.pack(self.__struct2byte3__, x, y, z)); + f.write(struct.pack(self.__struct2byte3__, x, y, z)) - tmp = self.__toInteger__(pow(2,depth)-1, rescale) + tmp = self.__toInteger__(pow(2, depth) - 1, rescale) - if (depth > 16): # 32-bit - for i in range( x*y*z ): + if depth > 16: # 32-bit + for i in range(x * y * z): f.write(struct.pack(self.__struct4byte__, tmp[i])) - elif (depth > 8): # 16-bit - for i in range( x*y*z ): + elif depth > 8: # 16-bit + for i in range(x * y * z): f.write(struct.pack(self.__struct2byte__, tmp[i])) else: - for i in range( x*y*z ): + for i in range(x * y * z): f.write(struct.pack(self.__struct1byte__, tmp[i])) def importDF3(self, file, scale=1): try: - f = open(file, 'rb'); + f = open(file, 'rb') size = os.stat(file)[stat.ST_SIZE] except: - print("Could not open " + file + " for read"); + print("Could not open " + file + " for read") return [] - (x, y, z) = struct.unpack(self.__struct2byte3__, f.read(6) ) + (x, y, z) = struct.unpack(self.__struct2byte3__, f.read(6)) self.voxel = self.__create__(x, y, z) self.maxX = x self.maxY = y self.maxZ = z - size = size-6 - if (size == x*y*z): format = 8 - elif (size == 2*x*y*z): format = 16 - elif (size == 4*x*y*z): format = 32 - - if (format == 32): - for i in range(x*y*z): - self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4) )[0]) - elif (format == 16): - for i in range(x*y*z): - self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2) )[0]) - elif (format == 8): - for i in range(x*y*z): - self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1) )[0]) + size = size - 6 + if size == x * y * z: + format = 8 + elif size == 2 * x * y * z: + format = 16 + elif size == 4 * x * y * z: + format = 32 + + if format == 32: + for i in range(x * y * z): + self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4))[0]) + elif format == 16: + for i in range(x * y * z): + self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2))[0]) + elif format == 8: + for i in range(x * y * z): + self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1))[0]) return self #### Local classes not intended for user use def __rip__(self, minX, maxX, minY, maxY, minZ, maxZ): - sizeX = maxX-minX+1 - sizeY = maxY-minY+1 - sizeZ = maxZ-minZ+1 + sizeX = maxX - minX + 1 + sizeY = maxY - minY + 1 + sizeZ = maxZ - minZ + 1 tmpV = self.__create__(sizeX, sizeY, sizeZ) @@ -286,95 +297,99 @@ class df3: for y in range(sizeY): for z in range(sizeZ): # Check X - if ((minX + x) < 0): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 - elif ((minX + x) > self.sizeX()-1): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 + if (minX + x) < 0: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 + elif (minX + x) > self.sizeX() - 1: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 # Check Y - elif ((minY + y) < 0): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 - elif ((minY + y) > self.sizeY()-1): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 + elif (minY + y) < 0: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 + elif (minY + y) > self.sizeY() - 1: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 # Check Z - elif ((minZ + z) < 0): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 - elif ((minZ + z) > self.sizeZ()-1): - tmpV[(z*sizeZ+y)*sizeY+x] = 0.0 + elif (minZ + z) < 0: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 + elif (minZ + z) > self.sizeZ() - 1: + tmpV[(z * sizeZ + y) * sizeY + x] = 0.0 else: - tmpV[(z*sizeZ+y)*sizeY+x] = self.get(minX+x,minY+y,minZ+z) + tmpV[(z * sizeZ + y) * sizeY + x] = self.get(minX + x, minY + y, minZ + z) return tmpV def __samesize__(self, indf3): - if (self.sizeX() != indf3.sizeX()): return 0 - if (self.sizeY() != indf3.sizeY()): return 0 - if (self.sizeZ() != indf3.sizeZ()): return 0 + if self.sizeX() != indf3.sizeX(): + return 0 + if self.sizeY() != indf3.sizeY(): + return 0 + if self.sizeZ() != indf3.sizeZ(): + return 0 return 1 def __voxa__(self, x, y, z): - return ((z*self.sizeY()+y)*self.sizeX()+x) + return (z * self.sizeY() + y) * self.sizeX() + x def __create__(self, x, y, z, atype='0', init=1): - if (atype == '0'): + if atype == '0': tmp = self.__arraytype__ else: tmp = atype - if (init == 1): - if tmp in ('f','d'): - voxel = array.array(tmp, [0.0 for i in range(x*y*z)]) + if init == 1: + if tmp in ('f', 'd'): + voxel = array.array(tmp, [0.0 for i in range(x * y * z)]) else: - voxel = array.array(tmp, [0 for i in range(x*y*z)]) + voxel = array.array(tmp, [0 for i in range(x * y * z)]) else: voxel = array.array(tmp) return voxel def __toInteger__(self, scale, rescale=1): - if (scale < pow(2,8)): # 8-bit + if scale < pow(2, 8): # 8-bit tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array1byte__) - elif (scale < pow(2,16)): # 16-bit + elif scale < pow(2, 16): # 16-bit tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array2byte__) - else: # 32-bit + else: # 32-bit tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array4byte__) maxVal = self.max() print(scale) - for i in range(self.sizeX()*self.sizeY()*self.sizeZ()): - if (rescale == 1): - tmp[i] = max(0,int(round(scale*self.voxel[i]/maxVal))) + for i in range(self.sizeX() * self.sizeY() * self.sizeZ()): + if rescale == 1: + tmp[i] = max(0, int(round(scale * self.voxel[i] / maxVal))) else: - tmp[i] = max(0,min(scale,int(round(self.voxel[i])))) + tmp[i] = max(0, min(scale, int(round(self.voxel[i])))) return tmp + # -=-=-=- End df3 Class -=-=-=- ##########DEFAULT EXAMPLES # if __name__ == '__main__': - # localX = 80 - # localY = 90 - # localZ = 100 - ## Generate an output - # temp = df3(localX, localY, localZ) - - # for i in range(localX): - # for j in range(localY): - # for k in range(localZ): - # if (i >= (localX/2)): - # temp.set(i, j, k, 1.0) - - # temp.exportDF3('temp.df3', 16) +# localX = 80 +# localY = 90 +# localZ = 100 +## Generate an output +# temp = df3(localX, localY, localZ) + +# for i in range(localX): +# for j in range(localY): +# for k in range(localZ): +# if (i >= (localX/2)): +# temp.set(i, j, k, 1.0) + +# temp.exportDF3('temp.df3', 16) ############################################################################### - ## Import - # temp2 = df3().importDF3('temp.df3') - # temp2.mult(1/temp2.max()) +## Import +# temp2 = df3().importDF3('temp.df3') +# temp2.mult(1/temp2.max()) - ## Compare - # print(temp2.size()) +## Compare +# print(temp2.size()) - # if (temp.compare(temp2) == 0): print("DF3's Do Not Match") +# if (temp.compare(temp2) == 0): print("DF3's Do Not Match") ############################################################################### # ChangeLog diff --git a/render_povray/nodes.py b/render_povray/nodes.py deleted file mode 100644 index bbdb97545..000000000 --- a/render_povray/nodes.py +++ /dev/null @@ -1,1369 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -import bpy - -from bpy.utils import register_class -from bpy.types import ( - Node, - ShaderNodeTree, - CompositorNodeTree, - TextureNodeTree, - #NodeSocket, - Operator, - ) -from bpy.props import ( - StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - FloatVectorProperty, - EnumProperty, - #PointerProperty, - #CollectionProperty, - ) - - - -############### object - -class ObjectNodeTree(bpy.types.NodeTree): - '''Povray Material Nodes''' - - bl_idname = 'ObjectNodeTree' - bl_label = 'Povray Object Nodes' - bl_icon = 'PLUGIN' - - @classmethod - def poll(cls, context): - return context.scene.render.engine == 'POVRAY_RENDER' - - @classmethod - def get_from_context(cls, context): - ob = context.active_object - if ob and ob.type not in {'LIGHT'}: - ma = ob.active_material - if ma is not None: - nt_name = ma.node_tree - if nt_name != '': - return nt_name, ma, ma - return (None, None, None) - - def update(self): - self.refresh = True -################### output ############################################################################################# - -class PovrayOutputNode(Node, ObjectNodeTree): - '''Output''' - bl_idname = 'PovrayOutputNode' - bl_label = 'Output' - bl_icon = 'SOUND' - - def init(self, context): - - self.inputs.new('PovraySocketTexture', "Texture") - - def draw_buttons(self, context, layout): - - ob=context.object - layout.prop(ob.pov, "object_ior",slider=True) - - def draw_buttons_ext(self, context, layout): - - ob=context.object - layout.prop(ob.pov, "object_ior",slider=True) - - def draw_label(self): - return "Output" - - - -################### material ########################################################################################### -class PovrayTextureNode(Node, ObjectNodeTree): - '''Texture''' - bl_idname = 'PovrayTextureNode' - bl_label = 'Simple texture' - bl_icon = 'SOUND' - - def init(self, context): - - color=self.inputs.new('PovraySocketColor', "Pigment") - color.default_value=(1,1,1) - normal=self.inputs.new('NodeSocketFloat', "Normal") - normal.hide_value=True - finish=self.inputs.new('NodeSocketVector', "Finish") - finish.hide_value=True - - self.outputs.new('PovraySocketTexture', "Texture") - - def draw_label(self): - return "Simple texture" - -class PovrayFinishNode(Node, ObjectNodeTree): - '''Finish''' - bl_idname = 'PovrayFinishNode' - bl_label = 'Finish' - bl_icon = 'SOUND' - - def init(self, context): - - self.inputs.new('PovraySocketFloat_0_1', "Emission") - ambient=self.inputs.new('NodeSocketVector', "Ambient") - ambient.hide_value=True - diffuse=self.inputs.new('NodeSocketVector', "Diffuse") - diffuse.hide_value=True - specular=self.inputs.new('NodeSocketVector', "Highlight") - specular.hide_value=True - mirror=self.inputs.new('NodeSocketVector', "Mirror") - mirror.hide_value=True - iridescence=self.inputs.new('NodeSocketVector', "Iridescence") - iridescence.hide_value=True - subsurface=self.inputs.new('NodeSocketVector', "Translucency") - subsurface.hide_value=True - self.outputs.new('NodeSocketVector', "Finish") - - def draw_label(self): - return "Finish" - -class PovrayDiffuseNode(Node, ObjectNodeTree): - '''Diffuse''' - bl_idname = 'PovrayDiffuseNode' - bl_label = 'Diffuse' - bl_icon = 'SOUND' - - def init(self, context): - - intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity") - intensity.default_value=0.8 - albedo=self.inputs.new('NodeSocketBool', "Albedo") - albedo.default_value=False - brilliance=self.inputs.new('PovraySocketFloat_0_10', "Brilliance") - brilliance.default_value=1.8 - self.inputs.new('PovraySocketFloat_0_1', "Crand") - self.outputs.new('NodeSocketVector', "Diffuse") - - def draw_label(self): - return "Diffuse" - -class PovrayPhongNode(Node, ObjectNodeTree): - '''Phong''' - bl_idname = 'PovrayPhongNode' - bl_label = 'Phong' - bl_icon = 'SOUND' - - def init(self, context): - - albedo=self.inputs.new('NodeSocketBool', "Albedo") - intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity") - intensity.default_value=0.8 - phong_size=self.inputs.new('PovraySocketInt_0_256', "Size") - phong_size.default_value=60 - metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic") - - self.outputs.new('NodeSocketVector', "Phong") - - def draw_label(self): - return "Phong" - -class PovraySpecularNode(Node, ObjectNodeTree): - '''Specular''' - bl_idname = 'PovraySpecularNode' - bl_label = 'Specular' - bl_icon = 'SOUND' - - def init(self, context): - - albedo=self.inputs.new('NodeSocketBool', "Albedo") - intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity") - intensity.default_value=0.8 - roughness=self.inputs.new('PovraySocketFloat_0_1', "Roughness") - roughness.default_value=0.02 - metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic") - - self.outputs.new('NodeSocketVector', "Specular") - - def draw_label(self): - return "Specular" - -class PovrayMirrorNode(Node, ObjectNodeTree): - '''Mirror''' - bl_idname = 'PovrayMirrorNode' - bl_label = 'Mirror' - bl_icon = 'SOUND' - - def init(self, context): - - color=self.inputs.new('PovraySocketColor', "Color") - color.default_value=(1,1,1) - metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic") - metallic.default_value=1.0 - exponent=self.inputs.new('PovraySocketFloat_0_1', "Exponent") - exponent.default_value=1.0 - self.inputs.new('PovraySocketFloat_0_1', "Falloff") - self.inputs.new('NodeSocketBool', "Fresnel") - self.inputs.new('NodeSocketBool', "Conserve energy") - self.outputs.new('NodeSocketVector', "Mirror") - - def draw_label(self): - return "Mirror" - -class PovrayAmbientNode(Node, ObjectNodeTree): - '''Ambient''' - bl_idname = 'PovrayAmbientNode' - bl_label = 'Ambient' - bl_icon = 'SOUND' - - def init(self, context): - - self.inputs.new('PovraySocketColor', "Ambient") - - self.outputs.new('NodeSocketVector', "Ambient") - - def draw_label(self): - return "Ambient" - -class PovrayIridescenceNode(Node, ObjectNodeTree): - '''Iridescence''' - bl_idname = 'PovrayIridescenceNode' - bl_label = 'Iridescence' - bl_icon = 'SOUND' - - def init(self, context): - - amount=self.inputs.new('NodeSocketFloat', "Amount") - amount.default_value=0.25 - thickness=self.inputs.new('NodeSocketFloat', "Thickness") - thickness.default_value=1 - self.inputs.new('NodeSocketFloat', "Turbulence") - - self.outputs.new('NodeSocketVector', "Iridescence") - - def draw_label(self): - return "Iridescence" - -class PovraySubsurfaceNode(Node, ObjectNodeTree): - '''Subsurface''' - bl_idname = 'PovraySubsurfaceNode' - bl_label = 'Subsurface' - bl_icon = 'SOUND' - - def init(self, context): - - translucency=self.inputs.new('NodeSocketColor', "Translucency") - translucency.default_value=(0,0,0,1) - energy=self.inputs.new('PovraySocketInt_0_256', "Energy") - energy.default_value=20 - self.outputs.new('NodeSocketVector', "Translucency") - - def draw_buttons(self, context, layout): - scene=context.scene - layout.prop(scene.pov, "sslt_enable",text="SSLT") - - - def draw_buttons_ext(self, context, layout): - scene=context.scene - layout.prop(scene.pov, "sslt_enable",text="SSLT") - - def draw_label(self): - return "Subsurface" - -##################################################################################################### - -class PovrayMappingNode(Node, ObjectNodeTree): - '''Mapping''' - bl_idname = 'PovrayMappingNode' - bl_label = 'Mapping' - bl_icon = 'SOUND' - - warp_type: EnumProperty( - name="Warp Types", - description="Select the type of warp", - items=( ('cubic', "Cubic", ""), ('cylindrical', "Cylindrical", ""),('planar', "Planar", ""), - ('spherical', "Spherical", ""),('toroidal', "Toroidal", ""), - ('uv_mapping', "UV", ""), - ('NONE', "None", "No indentation")), - default='NONE') - - warp_orientation: EnumProperty( - name="Warp Orientation", - description="Select the orientation of warp", - items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")), - default='y') - - warp_dist_exp: FloatProperty( - name="Distance exponent", - description="Distance exponent", - min=0.0, max=100.0, default=1.0) - - warp_tor_major_radius: FloatProperty( - name="Major radius", - description="Torus is distance from major radius", - min=0.0, max=5.0, default=1.0) - - def init(self, context): - self.outputs.new('NodeSocketVector', "Mapping") - - def draw_buttons(self, context, layout): - - column=layout.column() - column.prop(self,"warp_type",text="Warp type") - if self.warp_type in {'toroidal','spherical','cylindrical','planar'}: - column.prop(self,"warp_orientation",text="Orientation") - column.prop(self,"warp_dist_exp",text="Exponent") - if self.warp_type=='toroidal': - column.prop(self,"warp_tor_major_radius",text="Major R") - - def draw_buttons_ext(self, context, layout): - - column=layout.column() - column.prop(self,"warp_type",text="Warp type") - if self.warp_type in {'toroidal','spherical','cylindrical','planar'}: - column.prop(self,"warp_orientation",text="Orientation") - column.prop(self,"warp_dist_exp",text="Exponent") - if self.warp_type=='toroidal': - column.prop(self,"warp_tor_major_radius",text="Major R") - - def draw_label(self): - return "Mapping" - -class PovrayMultiplyNode(Node, ObjectNodeTree): - '''Multiply''' - bl_idname = 'PovrayMultiplyNode' - bl_label = 'Multiply' - bl_icon = 'SOUND' - - amount_x : FloatProperty( - name="X", - description="Number of repeats", - min=1.0, max=10000.0, default=1.0) - - amount_y : FloatProperty( - name="Y", - description="Number of repeats", - min=1.0, max=10000.0, default=1.0) - - amount_z : FloatProperty( - name="Z", - description="Number of repeats", - min=1.0, max=10000.0, default=1.0) - - - def init(self, context): - self.outputs.new('NodeSocketVector', "Amount") - - def draw_buttons(self, context, layout): - - column=layout.column() - column.label(text="Amount") - row=column.row(align=True) - row.prop(self,"amount_x") - row.prop(self,"amount_y") - row.prop(self,"amount_z") - - def draw_buttons_ext(self, context, layout): - - column=layout.column() - column.label(text="Amount") - row=column.row(align=True) - row.prop(self,"amount_x") - row.prop(self,"amount_y") - row.prop(self,"amount_z") - - def draw_label(self): - return "Multiply" - -class PovrayTransformNode(Node, ObjectNodeTree): - '''Transform''' - bl_idname = 'PovrayTransformNode' - bl_label = 'Transform' - bl_icon = 'SOUND' - - def init(self, context): - - self.inputs.new('PovraySocketFloatUnlimited', "Translate x") - self.inputs.new('PovraySocketFloatUnlimited', "Translate y") - self.inputs.new('PovraySocketFloatUnlimited', "Translate z") - self.inputs.new('PovraySocketFloatUnlimited', "Rotate x") - self.inputs.new('PovraySocketFloatUnlimited', "Rotate y") - self.inputs.new('PovraySocketFloatUnlimited', "Rotate z") - sX = self.inputs.new('PovraySocketFloatUnlimited', "Scale x") - sX.default_value = 1.0 - sY = self.inputs.new('PovraySocketFloatUnlimited', "Scale y") - sY.default_value = 1.0 - sZ = self.inputs.new('PovraySocketFloatUnlimited', "Scale z") - sZ.default_value = 1.0 - - self.outputs.new('NodeSocketVector', "Transform") - - def draw_label(self): - return "Transform" - -class PovrayValueNode(Node, ObjectNodeTree): - '''Value''' - bl_idname = 'PovrayValueNode' - bl_label = 'Value' - bl_icon = 'SOUND' - - def init(self, context): - - self.outputs.new('PovraySocketUniversal', "Value") - - def draw_label(self): - return "Value" - -class PovrayModifierNode(Node, ObjectNodeTree): - '''Modifier''' - bl_idname = 'PovrayModifierNode' - bl_label = 'Modifier' - bl_icon = 'SOUND' - - def init(self, context): - - turb_x=self.inputs.new('PovraySocketFloat_0_10', "Turb X") - turb_x.default_value=0.1 - turb_y=self.inputs.new('PovraySocketFloat_0_10', "Turb Y") - turb_y.default_value=0.1 - turb_z=self.inputs.new('PovraySocketFloat_0_10', "Turb Z") - turb_z.default_value=0.1 - octaves=self.inputs.new('PovraySocketInt_1_9', "Octaves") - octaves.default_value=1 - lambat=self.inputs.new('PovraySocketFloat_0_10', "Lambda") - lambat.default_value=2.0 - omega=self.inputs.new('PovraySocketFloat_0_10', "Omega") - omega.default_value=0.5 - freq=self.inputs.new('PovraySocketFloat_0_10', "Frequency") - freq.default_value=2.0 - self.inputs.new('PovraySocketFloat_0_10', "Phase") - - self.outputs.new('NodeSocketVector', "Modifier") - - def draw_label(self): - return "Modifier" - -class PovrayPigmentNode(Node, ObjectNodeTree): - '''Pigment''' - bl_idname = 'PovrayPigmentNode' - bl_label = 'Color' - - def init(self, context): - - color = self.inputs.new('PovraySocketColor', "Color") - color.default_value = (1,1,1) - povfilter = self.inputs.new('PovraySocketFloat_0_1', "Filter") - transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit") - self.outputs.new('NodeSocketColor', "Pigment") - - def draw_label(self): - return "Color" - -class PovrayColorImageNode(Node, ObjectNodeTree): - '''ColorImage''' - bl_idname = 'PovrayColorImageNode' - bl_label = 'Image map' - - map_type: bpy.props.EnumProperty( - name="Map type", - description="", - items=( ('uv_mapping', "UV", ""), - ('0', "Planar", "Default planar mapping"), - ('1', "Spherical", "Spherical mapping"), - ('2', "Cylindrical", "Cylindrical mapping"), - ('5', "Torroidal", "Torus or donut shaped mapping")), - default='0') - image: StringProperty(maxlen=1024) # , subtype="FILE_PATH" - interpolate: EnumProperty( - name="Interpolate", - description="Adding the interpolate keyword can smooth the jagged look of a bitmap", - items=( - ('2', "Bilinear", "Gives bilinear interpolation"), - ('4', "Normalized", "Gives normalized distance"), - ), - default='2') - premultiplied: BoolProperty(default=False) - once: BoolProperty(description="Not to repeat", default=False) - - def init(self, context): - - gamma=self.inputs.new('PovraySocketFloat_000001_10', "Gamma") - gamma.default_value=2.0 - transmit=self.inputs.new('PovraySocketFloat_0_1', "Transmit") - povfilter=self.inputs.new('PovraySocketFloat_0_1', "Filter") - mapping=self.inputs.new('NodeSocketVector', "Mapping") - mapping.hide_value=True - transform=self.inputs.new('NodeSocketVector', "Transform") - transform.hide_value=True - modifier=self.inputs.new('NodeSocketVector', "Modifier") - modifier.hide_value=True - - self.outputs.new('NodeSocketColor', "Pigment") - - def draw_buttons(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(factor=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - row=column.row() - row.prop(self,"premultiplied",text="Premul") - row.prop(self,"once",text="Once") - - def draw_buttons_ext(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(factor=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - row=column.row() - row.prop(self,"premultiplied",text="Premul") - row.prop(self,"once",text="Once") - - def draw_label(self): - return "Image map" - -class PovrayBumpMapNode(Node, ObjectNodeTree): - '''BumpMap''' - bl_idname = 'PovrayBumpMapNode' - bl_label = 'Bump map' - bl_icon = 'SOUND' - - map_type : bpy.props.EnumProperty( - name="Map type", - description="", - items=( - ('uv_mapping', "UV", ""), - ('0', "Planar", "Default planar mapping"), - ('1', "Spherical", "Spherical mapping"), - ('2', "Cylindrical", "Cylindrical mapping"), - ('5', "Torroidal", "Torus or donut shaped mapping") - ), - default='0') - image : StringProperty(maxlen=1024) # , subtype="FILE_PATH" - interpolate : EnumProperty( - name="Interpolate", - description="Adding the interpolate keyword can smooth the jagged look of a bitmap", - items=( - ('2', "Bilinear", "Gives bilinear interpolation"), - ('4', "Normalized", "Gives normalized distance"), - ), - default='2') - once : BoolProperty(description="Not to repeat", default=False) - - def init(self, context): - - self.inputs.new('PovraySocketFloat_0_10', "Normal") - mapping=self.inputs.new('NodeSocketVector', "Mapping") - mapping.hide_value=True - transform=self.inputs.new('NodeSocketVector', "Transform") - transform.hide_value=True - modifier=self.inputs.new('NodeSocketVector', "Modifier") - modifier.hide_value=True - - normal=self.outputs.new('NodeSocketFloat', "Normal") - normal.hide_value=True - - def draw_buttons(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(percentage=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - column.prop(self,"once",text="Once") - - def draw_buttons_ext(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(percentage=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - column.prop(self,"once",text="Once") - - def draw_label(self): - return "Bump Map" - -class PovrayImagePatternNode(Node, ObjectNodeTree): - '''ImagePattern''' - bl_idname = 'PovrayImagePatternNode' - bl_label = 'Image pattern' - bl_icon = 'SOUND' - - map_type: bpy.props.EnumProperty( - name="Map type", - description="", - items=( - ('uv_mapping', "UV", ""), - ('0', "Planar", "Default planar mapping"), - ('1', "Spherical", "Spherical mapping"), - ('2', "Cylindrical", "Cylindrical mapping"), - ('5', "Torroidal", "Torus or donut shaped mapping"), - ), - default='0') - image: StringProperty(maxlen=1024) # , subtype="FILE_PATH" - interpolate: EnumProperty( - name="Interpolate", - description="Adding the interpolate keyword can smooth the jagged look of a bitmap", - items=( - ('2', "Bilinear", "Gives bilinear interpolation"), - ('4', "Normalized", "Gives normalized distance"), - ), - default='2') - premultiplied: BoolProperty(default=False) - once: BoolProperty(description="Not to repeat", default=False) - use_alpha: BoolProperty(default=True) - def init(self, context): - - gamma=self.inputs.new('PovraySocketFloat_000001_10', "Gamma") - gamma.default_value=2.0 - - self.outputs.new('PovraySocketPattern', "Pattern") - - def draw_buttons(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(factor=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - row=column.row() - row.prop(self,"premultiplied",text="Premul") - row.prop(self,"once",text="Once") - column.prop(self,"use_alpha",text="Use alpha") - - def draw_buttons_ext(self, context, layout): - - column=layout.column() - im=None - for image in bpy.data.images: - if image.name == self.image: - im=image - split = column.split(factor=0.8,align=True) - split.prop_search(self,"image",context.blend_data,"images",text="") - split.operator("pov.imageopen",text="",icon="FILEBROWSER") - if im is not None: - column.prop(im,"source",text="") - column.prop(self,"map_type",text="") - column.prop(self,"interpolate",text="") - row=column.row() - row.prop(self,"premultiplied",text="Premul") - row.prop(self,"once",text="Once") - - def draw_label(self): - return "Image pattern" - -class ShaderPatternNode(Node, ObjectNodeTree): - '''Pattern''' - bl_idname = 'ShaderPatternNode' - bl_label = 'Other patterns' - - pattern : EnumProperty( - name="Pattern", - description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling", - items=(('agate', "Agate", ""),('crackle', "Crackle", ""),('gradient', "Gradient", ""), - ('pavement', "Pavement", ""), - ('spiral1', "Spiral 1", ""), - ('spiral2', "Spiral 2", ""), - ('tiling', "Tiling", "")), - default='agate') - - agate_turb : FloatProperty( - name="Agate turb", - description="Agate turbulence", - min=0.0, max=100.0, default=0.5) - - crackle_form_x : FloatProperty( - name="X", - description="Form vector X", - min=-150.0, max=150.0, default=-1) - - crackle_form_y : FloatProperty( - name="Y", - description="Form vector Y", - min=-150.0, max=150.0, default=1) - - crackle_form_z : FloatProperty( - name="Z", - description="Form vector Z", - min=-150.0, max=150.0, default=0) - - crackle_metric : FloatProperty( - name="Metric", - description="Crackle metric", - min=0.0, max=150.0, default=1) - - crackle_solid : BoolProperty( - name="Solid", - description="Crackle solid", - default=False) - - spiral_arms : FloatProperty( - name="Number", - description="", - min=0.0, max=256.0, default=2.0) - - tiling_number : IntProperty( - name="Number", - description="", - min=1, max=27, default=1) - - gradient_orient : EnumProperty( - name="Orient", - description="", - items=(('x', "X", ""), - ('y', "Y", ""), - ('z', "Z", "")), - default='x') - - def init(self, context): - - pat = self.outputs.new('PovraySocketPattern', "Pattern") - - def draw_buttons(self, context, layout): - - layout.prop(self, "pattern",text="") - if self.pattern=='agate': - layout.prop(self, "agate_turb") - if self.pattern=='crackle': - layout.prop(self, "crackle_metric") - layout.prop(self, "crackle_solid") - layout.label(text="Form:") - layout.prop(self, "crackle_form_x") - layout.prop(self, "crackle_form_y") - layout.prop(self, "crackle_form_z") - if self.pattern in {"spiral1","spiral2"}: - layout.prop(self, "spiral_arms") - if self.pattern in {'tiling'}: - layout.prop(self, "tiling_number") - if self.pattern in {'gradient'}: - layout.prop(self, "gradient_orient") - def draw_buttons_ext(self, context, layout): - pass - - def draw_label(self): - return "Other patterns" - -class ShaderTextureMapNode(Node, ObjectNodeTree): - '''Texture Map''' - bl_idname = 'ShaderTextureMapNode' - bl_label = 'Texture map' - - brick_size_x: FloatProperty( - name="X", - description="", - min=0.0000, max=1.0000, default=0.2500) - - brick_size_y: FloatProperty( - name="Y", - description="", - min=0.0000, max=1.0000, default=0.0525) - - brick_size_z: FloatProperty( - name="Z", - description="", - min=0.0000, max=1.0000, default=0.1250) - - brick_mortar: FloatProperty( - name="Mortar", - description="Mortar", - min=0.000, max=1.500, default=0.01) - - def init(self, context): - mat = bpy.context.object.active_material - self.inputs.new('PovraySocketPattern', "") - color = self.inputs.new('NodeSocketColor', "Color ramp") - color.hide_value = True - for i in range(0,4): - transform=self.inputs.new('PovraySocketTransform', "Transform") - transform.hide_value=True - number = mat.pov.inputs_number - for i in range(number): - self.inputs.new('PovraySocketTexture', "%s"%i) - - - self.outputs.new('PovraySocketTexture', "Texture") - - def draw_buttons(self, context, layout): - - if self.inputs[0].default_value =='brick': - layout.prop(self, "brick_mortar") - layout.label(text="Brick size:") - layout.prop(self, "brick_size_x") - layout.prop(self, "brick_size_y") - layout.prop(self, "brick_size_z") - - def draw_buttons_ext(self, context, layout): - - if self.inputs[0].default_value =='brick': - layout.prop(self, "brick_mortar") - layout.label(text="Brick size:") - layout.prop(self, "brick_size_x") - layout.prop(self, "brick_size_y") - layout.prop(self, "brick_size_z") - - def draw_label(self): - return "Texture map" - - -class ShaderNormalMapNode(Node, ObjectNodeTree): - '''Normal Map''' - bl_idname = 'ShaderNormalMapNode' - bl_label = 'Normal map' - - brick_size_x : FloatProperty( - name="X", - description="", - min=0.0000, max=1.0000, default=0.2500) - - brick_size_y : FloatProperty( - name="Y", - description="", - min=0.0000, max=1.0000, default=0.0525) - - brick_size_z : FloatProperty( - name="Z", - description="", - min=0.0000, max=1.0000, default=0.1250) - - brick_mortar : FloatProperty( - name="Mortar", - description="Mortar", - min=0.000, max=1.500, default=0.01) - - def init(self, context): - self.inputs.new('PovraySocketPattern', "") - normal = self.inputs.new('PovraySocketFloat_10', "Normal") - slope = self.inputs.new('PovraySocketMap', "Slope map") - for i in range(0,4): - transform=self.inputs.new('PovraySocketTransform', "Transform") - transform.hide_value=True - self.outputs.new('PovraySocketNormal', "Normal") - - def draw_buttons(self, context, layout): - #for i, inp in enumerate(self.inputs): - - if self.inputs[0].default_value =='brick': - layout.prop(self, "brick_mortar") - layout.label(text="Brick size:") - layout.prop(self, "brick_size_x") - layout.prop(self, "brick_size_y") - layout.prop(self, "brick_size_z") - - def draw_buttons_ext(self, context, layout): - - if self.inputs[0].default_value =='brick': - layout.prop(self, "brick_mortar") - layout.label(text="Brick size:") - layout.prop(self, "brick_size_x") - layout.prop(self, "brick_size_y") - layout.prop(self, "brick_size_z") - - def draw_label(self): - return "Normal map" - -class ShaderNormalMapEntryNode(Node, ObjectNodeTree): - '''Normal Map Entry''' - bl_idname = 'ShaderNormalMapEntryNode' - bl_label = 'Normal map entry' - - def init(self, context): - self.inputs.new('PovraySocketFloat_0_1', "Stop") - self.inputs.new('PovraySocketFloat_0_1', "Gray") - def draw_label(self): - return "Normal map entry" - -class IsoPropsNode(Node, CompositorNodeTree): - '''ISO Props''' - bl_idname = 'IsoPropsNode' - bl_label = 'Iso' - node_label : StringProperty(maxlen=1024) - def init(self, context): - ob = bpy.context.object - self.node_label = ob.name - textName = ob.pov.function_text - if textName: - text = bpy.data.texts[textName] - for line in text.lines: - split = line.body.split() - if split[0] == "#declare": - socket = self.inputs.new('NodeSocketFloat', "%s"%split[1]) - value = split[3].split(";") - value = value[0] - socket.default_value=float(value) - def draw_label(self): - return self.node_label - -class PovrayFogNode(Node, CompositorNodeTree): - '''Fog settings''' - bl_idname = 'PovrayFogNode' - bl_label = 'Fog' - def init(self, context): - color=self.inputs.new('NodeSocketColor', "Color") - color.default_value=(0.7,0.7,0.7,0.25) - self.inputs.new('PovraySocketFloat_0_1', "Filter") - distance = self.inputs.new('NodeSocketInt', "Distance") - distance.default_value=150 - self.inputs.new('NodeSocketBool', "Ground") - fog_offset=self.inputs.new('NodeSocketFloat', "Offset") - fog_alt=self.inputs.new('NodeSocketFloat', "Altitude") - turb = self.inputs.new('NodeSocketVector', "Turbulence") - turb_depth=self.inputs.new('PovraySocketFloat_0_10', "Depth") - turb_depth.default_value=0.5 - octaves=self.inputs.new('PovraySocketInt_1_9', "Octaves") - octaves.default_value=5 - lambdat=self.inputs.new('PovraySocketFloat_0_10', "Lambda") - lambdat.default_value=1.25 - omega=self.inputs.new('PovraySocketFloat_0_10', "Omega") - omega.default_value=0.35 - translate = self.inputs.new('NodeSocketVector', "Translate") - rotate = self.inputs.new('NodeSocketVector', "Rotate") - scale = self.inputs.new('NodeSocketVector', "Scale") - scale.default_value=(1,1,1) - def draw_label(self): - return "Fog" - -class PovraySlopeNode(Node, TextureNodeTree): - '''Output''' - bl_idname = 'PovraySlopeNode' - bl_label = 'Slope Map' - - def init(self, context): - self.use_custom_color = True - self.color = (0,0.2,0) - slope = self.inputs.new('PovraySocketSlope', "0") - slope = self.inputs.new('PovraySocketSlope', "1") - slopemap = self.outputs.new('PovraySocketMap', "Slope map") - output.hide_value = True - def draw_buttons(self, context, layout): - - layout.operator("pov.nodeinputadd") - row = layout.row() - row.label(text='Value') - row.label(text='Height') - row.label(text='Slope') - - def draw_buttons_ext(self, context, layout): - - layout.operator("pov.nodeinputadd") - row = layout.row() - row.label(text='Value') - row.label(text='Height') - row.label(text='Slope') - - def draw_label(self): - return "Slope Map" - -######################################## Texture nodes ############################### -class TextureOutputNode(Node, TextureNodeTree): - '''Output''' - bl_idname = 'TextureOutputNode' - bl_label = 'Color Map' - - def init(self, context): - tex = bpy.context.object.active_material.active_texture - num_sockets = int(tex.pov.density_lines/32) - for i in range(num_sockets): - color = self.inputs.new('NodeSocketColor', "%s"%i) - color.hide_value = True - - def draw_buttons(self, context, layout): - - layout.label(text="Color Ramps:") - - def draw_label(self): - return "Color Map" - - -################################################################################## -#################################Operators######################################## -################################################################################## - - -class NODE_OT_iso_add(Operator): - bl_idname = "pov.nodeisoadd" - bl_label = "Create iso props" - - def execute(self, context): - ob = bpy.context.object - if bpy.context.scene.use_nodes == False: - bpy.context.scene.use_nodes = True - tree = bpy.context.scene.node_tree - for node in tree.nodes: - if node.bl_idname == "IsoPropsNode" and node.label == ob.name: - tree.nodes.remove(node) - isonode = tree.nodes.new('IsoPropsNode') - isonode.location = (0,0) - isonode.label = ob.name - return {'FINISHED'} - -class NODE_OT_map_create(Operator): - bl_idname = "node.map_create" - bl_label = "Create map" - - def execute(self, context): - x = y = 0 - space = context.space_data - tree = space.edit_tree - for node in tree.nodes: - if node.select == True: - x,y = node.location - node.select=False - tmap = tree.nodes.new('ShaderTextureMapNode') - tmap.location = (x - 200,y) - return {'FINISHED'} - - def invoke(self, context, event): - wm = context.window_manager - return wm.invoke_props_dialog(self) - - def draw(self, context): - layout = self.layout - mat = context.object.active_material - layout.prop(mat.pov,"inputs_number") - -class NODE_OT_povray_node_texture_map_add(Operator): - bl_idname = "pov.nodetexmapadd" - bl_label = "Texture map" - - def execute(self, context): - tree=bpy.context.object.active_material.node_tree - tmap = tree.nodes.active - bpy.context.object.active_material.node_tree.nodes.active=tmap - el=tmap.color_ramp.elements.new(0.5) - for el in tmap.color_ramp.elements: - el.color=(0,0,0,1) - for inp in tmap.inputs: - tmap.inputs.remove(inp) - for outp in tmap.outputs: - tmap.outputs.remove(outp) - pattern=tmap.inputs.new('NodeSocketVector', "Pattern") - pattern.hide_value=True - for i in range(0,3): - tmap.inputs.new('NodeSocketColor', "Shader") - tmap.outputs.new('NodeSocketShader', "BSDF") - tmap.label="Texture Map" - return {'FINISHED'} - - -class NODE_OT_povray_node_output_add(Operator): - bl_idname = "pov.nodeoutputadd" - bl_label = "Output" - - def execute(self, context): - tree=bpy.context.object.active_material.node_tree - tmap = tree.nodes.new('ShaderNodeOutputMaterial') - bpy.context.object.active_material.node_tree.nodes.active=tmap - for inp in tmap.inputs: - tmap.inputs.remove(inp) - tmap.inputs.new('NodeSocketShader', "Surface") - tmap.label="Output" - return {'FINISHED'} - -class NODE_OT_povray_node_layered_add(Operator): - bl_idname = "pov.nodelayeredadd" - bl_label = "Layered material" - - def execute(self, context): - tree=bpy.context.object.active_material.node_tree - tmap = tree.nodes.new('ShaderNodeAddShader') - bpy.context.object.active_material.node_tree.nodes.active=tmap - tmap.label="Layered material" - return {'FINISHED'} - -class NODE_OT_povray_input_add(Operator): - bl_idname = "pov.nodeinputadd" - bl_label = "Add entry" - - def execute(self, context): - node=bpy.context.object.active_material.node_tree.nodes.active - if node.type in {'VALTORGB'}: - number=1 - for inp in node.inputs: - if inp.type=='SHADER': - number+=1 - node.inputs.new('NodeSocketShader', "%s"%number) - els=node.color_ramp.elements - pos1=els[len(els)-1].position - pos2=els[len(els)-2].position - pos=(pos1-pos2)/2+pos2 - el=els.new(pos) - - if node.bl_idname == 'PovraySlopeNode': - number=len(node.inputs) - node.inputs.new('PovraySocketSlope', "%s"%number) - - - return {'FINISHED'} - -class NODE_OT_povray_input_remove(Operator): - bl_idname = "pov.nodeinputremove" - bl_label = "Remove input" - - def execute(self, context): - node=bpy.context.object.active_material.node_tree.nodes.active - if node.type in {'VALTORGB','ADD_SHADER'}: - number=len(node.inputs)-1 - if number > 5: - inp=node.inputs[number] - node.inputs.remove(inp) - if node.type in {'VALTORGB'}: - els=node.color_ramp.elements - number=len(els)-2 - el=els[number] - els.remove(el) - return {'FINISHED'} - -class NODE_OT_povray_image_open(Operator): - bl_idname = "pov.imageopen" - bl_label = "Open" - - filepath: StringProperty( - name="File Path", - description="Open image", - maxlen=1024, - subtype='FILE_PATH', - ) - - def invoke(self, context, event): - context.window_manager.fileselect_add(self) - return {'RUNNING_MODAL'} - - def execute(self, context): - im=bpy.data.images.load(self.filepath) - node=context.object.active_material.node_tree.nodes.active - node.image = im.name - return {'FINISHED'} - - -# class TEXTURE_OT_povray_open_image(Operator): - # bl_idname = "pov.openimage" - # bl_label = "Open Image" - - # filepath = StringProperty( - # name="File Path", - # description="Open image", - # maxlen=1024, - # subtype='FILE_PATH', - # ) - - # def invoke(self, context, event): - # context.window_manager.fileselect_add(self) - # return {'RUNNING_MODAL'} - - # def execute(self, context): - # im=bpy.data.images.load(self.filepath) - # tex = context.texture - # tex.pov.image = im.name - # view_layer = context.view_layer - # view_layer.update() - # return {'FINISHED'} - -class PovrayPatternNode(Operator): - bl_idname = "pov.patternnode" - bl_label = "Pattern" - - add=True - - def execute(self, context): - space = context.space_data - tree = space.edit_tree - for node in tree.nodes: - node.select=False - if self.add==True: - tmap = tree.nodes.new('ShaderNodeValToRGB') - tmap.label="Pattern" - for inp in tmap.inputs: - tmap.inputs.remove(inp) - for outp in tmap.outputs: - tmap.outputs.remove(outp) - pattern = tmap.inputs.new('PovraySocketPattern', "Pattern") - pattern.hide_value = True - mapping=tmap.inputs.new('NodeSocketVector', "Mapping") - mapping.hide_value=True - transform=tmap.inputs.new('NodeSocketVector', "Transform") - transform.hide_value=True - modifier=tmap.inputs.new('NodeSocketVector', "Modifier") - modifier.hide_value=True - for i in range(0,2): - tmap.inputs.new('NodeSocketShader', "%s"%(i+1)) - tmap.outputs.new('NodeSocketShader', "Material") - tmap.outputs.new('NodeSocketColor', "Color") - tree.nodes.active=tmap - self.add=False - aNode=tree.nodes.active - aNode.select=True - v2d = context.region.view2d - x, y = v2d.region_to_view(self.x, self.y) - aNode.location = (x, y) - - def modal(self, context, event): - if event.type == 'MOUSEMOVE': - self.x = event.mouse_region_x - self.y = event.mouse_region_y - self.execute(context) - return {'RUNNING_MODAL'} - elif event.type == 'LEFTMOUSE': - return {'FINISHED'} - elif event.type in ('RIGHTMOUSE', 'ESC'): - return {'CANCELLED'} - - return {'RUNNING_MODAL'} - - def invoke(self, context, event): - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - -class UpdatePreviewMaterial(Operator): - '''Operator update preview material''' - bl_idname = "node.updatepreview" - bl_label = "Update preview" - - def execute(self, context): - scene=context.view_layer - ob=context.object - for obj in scene.objects: - if obj != ob: - scene.objects.active = ob - break - scene.objects.active = ob - - def modal(self, context, event): - if event.type == 'RIGHTMOUSE': - self.execute(context) - return {'FINISHED'} - return {'PASS_THROUGH'} - - def invoke(self, context, event): - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - -class UpdatePreviewKey(Operator): - '''Operator update preview keymap''' - bl_idname = "wm.updatepreviewkey" - bl_label = "Activate RMB" - @classmethod - def poll(cls, context): - conf = context.window_manager.keyconfigs.active - mapstr = "Node Editor" - map = conf.keymaps[mapstr] - try: - map.keymap_items["node.updatepreview"] - return False - except: - return True - - def execute(self, context): - conf = context.window_manager.keyconfigs.active - mapstr = "Node Editor" - map = conf.keymaps[mapstr] - map.keymap_items.new("node.updatepreview",type='RIGHTMOUSE',value="PRESS") - return {'FINISHED'} - -classes = ( - ObjectNodeTree, - PovrayOutputNode, - PovrayTextureNode, - PovrayFinishNode, - PovrayDiffuseNode, - PovrayPhongNode, - PovraySpecularNode, - PovrayMirrorNode, - PovrayAmbientNode, - PovrayIridescenceNode, - PovraySubsurfaceNode, - PovrayMappingNode, - PovrayMultiplyNode, - PovrayTransformNode, - PovrayValueNode, - PovrayModifierNode, - PovrayPigmentNode, - PovrayColorImageNode, - PovrayBumpMapNode, - PovrayImagePatternNode, - ShaderPatternNode, - ShaderTextureMapNode, - ShaderNormalMapNode, - ShaderNormalMapEntryNode, - IsoPropsNode, - PovrayFogNode, - PovraySlopeNode, - TextureOutputNode, - NODE_OT_iso_add, - NODE_OT_map_create, - NODE_OT_povray_node_texture_map_add, - NODE_OT_povray_node_output_add, - NODE_OT_povray_node_layered_add, - NODE_OT_povray_input_add, - NODE_OT_povray_input_remove, - NODE_OT_povray_image_open, - PovrayPatternNode, - UpdatePreviewMaterial, - UpdatePreviewKey, -) - - -def register(): - #from bpy.utils import register_class - - for cls in classes: - register_class(cls) - - -def unregister(): - from bpy.utils import unregister_class - - for cls in classes: - unregister_class(cls) diff --git a/render_povray/object_curve_topology.py b/render_povray/object_curve_topology.py new file mode 100755 index 000000000..5fa2b277b --- /dev/null +++ b/render_povray/object_curve_topology.py @@ -0,0 +1,974 @@ +# ***** 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 #**** + +# <pep8 compliant> + +"""Translate to POV the control point compounded geometries like polygon + +meshes or curve based shapes. +""" + +import bpy + +from .shading import write_object_material + +################################ LOFT, ETC. +def export_curves(ob, string_strip_hyphen, global_matrix, tab_write): + """write all curves based POV primitives to exported file """ + name_orig = "OB" + ob.name + dataname_orig = "DATA" + ob.data.name + + name = string_strip_hyphen(bpy.path.clean_name(name_orig)) + dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig)) + + matrix = global_matrix @ ob.matrix_world + bezier_sweep = False + if ob.pov.curveshape == 'sphere_sweep': + # inlined spheresweep macro, which itself calls Shapes.inc: + file.write(' #include "shapes.inc"\n') + + file.write( + ' #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n' + ) + file.write(' //input adjusting and inspection\n') + file.write(' #if(_resolution <= 1)\n') + file.write(' #local res = 1;\n') + file.write(' #else\n') + file.write(' #local res = int(_resolution);\n') + file.write(' #end\n') + file.write(' #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n') + file.write(' #error ""\n') + file.write( + ' #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n' + ) + file.write(' #error ""\n') + file.write( + ' #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n' + ) + file.write(' #error ""\n') + file.write(' #else\n') + file.write(' #local n_of_seg = div(dimension_size(_points_array,1), 4);\n') + file.write(' #local ctrl_pts_array = array[n_of_seg]\n') + file.write(' #local ctrl_rs_array = array[n_of_seg]\n') + file.write(' #for(i, 0, n_of_seg-1)\n') + file.write( + ' #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n' + ) + file.write( + ' #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n' + ) + file.write(' #end\n') + file.write(' #end\n') + + file.write(' //drawing\n') + file.write(' #local mockup1 =\n') + file.write(' #if(_merge_shape) merge{ #else union{ #end\n') + file.write(' #for(i, 0, n_of_seg-1)\n') + file.write(' #local has_head = true;\n') + file.write(' #if(i = 0)\n') + file.write( + ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n' + ) + file.write(' #local has_head = false;\n') + file.write(' #end\n') + file.write(' #else\n') + file.write( + ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n' + ) + file.write(' #local has_head = false;\n') + file.write(' #end\n') + file.write(' #end\n') + file.write(' #if(has_head = true)\n') + file.write(' sphere{\n') + file.write(' ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n') + file.write(' }\n') + file.write(' #end\n') + file.write(' #local para_t = (1/2)/res;\n') + file.write( + ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n' + ) + file.write( + ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n' + ) + file.write( + ' #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n' + ) + file.write(' object{\n') + file.write( + ' Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n' + ) + file.write(' }\n') + file.write(' #end\n') + file.write(' sphere{\n') + file.write(' this_point, this_radius\n') + file.write(' }\n') + file.write(' #for(j, 1, res-1)\n') + file.write(' #local last_point = this_point;\n') + file.write(' #local last_radius = this_radius;\n') + file.write(' #local para_t = (1/2+j)/res;\n') + file.write( + ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n' + ) + file.write( + ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n' + ) + file.write( + ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n' + ) + file.write(' object{\n') + file.write( + ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n' + ) + file.write(' }\n') + file.write(' #end\n') + file.write(' sphere{\n') + file.write(' this_point, this_radius\n') + file.write(' }\n') + file.write(' #end\n') + file.write(' #local last_point = this_point;\n') + file.write(' #local last_radius = this_radius;\n') + file.write(' #local this_point = ctrl_pts_array[i][3];\n') + file.write(' #local this_radius = ctrl_rs_array[i][3];\n') + file.write( + ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n' + ) + file.write(' object{\n') + file.write( + ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n' + ) + file.write(' }\n') + file.write(' #end\n') + file.write(' sphere{\n') + file.write(' this_point, this_radius\n') + file.write(' }\n') + file.write(' #end\n') + file.write(' }\n') + file.write(' mockup1\n') + file.write(' #end\n') + + for spl in ob.data.splines: + if spl.type == "BEZIER": + bezier_sweep = True + if ob.pov.curveshape in {'loft', 'birail'}: + n = 0 + for spline in ob.data.splines: + n += 1 + tab_write('#declare %s%s=spline {\n' % (dataname, n)) + tab_write('cubic_spline\n') + lp = len(spline.points) + delta = 1 / (lp) + d = -delta + point = spline.points[lp - 1] + x, y, z, w = point.co[:] + tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) + d += delta + for point in spline.points: + x, y, z, w = point.co[:] + tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) + d += delta + for i in range(2): + point = spline.points[i] + x, y, z, w = point.co[:] + tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) + d += delta + tab_write('}\n') + if ob.pov.curveshape in {'loft'}: + n = len(ob.data.splines) + tab_write('#declare %s = array[%s]{\n' % (dataname, (n + 3))) + tab_write('spline{%s%s},\n' % (dataname, n)) + for i in range(n): + tab_write('spline{%s%s},\n' % (dataname, (i + 1))) + tab_write('spline{%s1},\n' % (dataname)) + tab_write('spline{%s2}\n' % (dataname)) + tab_write('}\n') + # Use some of the Meshmaker.inc macro, here inlined + file.write('#macro CheckFileName(FileName)\n') + file.write(' #local Len=strlen(FileName);\n') + file.write(' #if(Len>0)\n') + file.write(' #if(file_exists(FileName))\n') + file.write(' #if(Len>=4)\n') + file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n') + file.write( + ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n' + ) + file.write(' #local Return=99;\n') + file.write(' #else\n') + file.write(' #local Return=0;\n') + file.write(' #end\n') + file.write(' #else\n') + file.write(' #local Return=0;\n') + file.write(' #end\n') + file.write(' #else\n') + file.write(' #if(Len>=4)\n') + file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n') + file.write( + ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n' + ) + file.write(' #if (strcmp(Ext,".obj")=0)\n') + file.write(' #local Return=2;\n') + file.write(' #end\n') + file.write(' #if (strcmp(Ext,".pcm")=0)\n') + file.write(' #local Return=3;\n') + file.write(' #end\n') + file.write(' #if (strcmp(Ext,".arr")=0)\n') + file.write(' #local Return=4;\n') + file.write(' #end\n') + file.write(' #else\n') + file.write(' #local Return=1;\n') + file.write(' #end\n') + file.write(' #else\n') + file.write(' #local Return=1;\n') + file.write(' #end\n') + file.write(' #end\n') + file.write(' #else\n') + file.write(' #local Return=1;\n') + file.write(' #end\n') + file.write(' (Return)\n') + file.write('#end\n') + + file.write('#macro BuildSpline(Arr, SplType)\n') + file.write(' #local Ds=dimension_size(Arr,1);\n') + file.write(' #local Asc=asc(strupr(SplType));\n') + file.write(' #if(Asc!=67 & Asc!=76 & Asc!=81) \n') + file.write(' #local Asc=76;\n') + file.write( + ' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n' + ) + file.write(' #end\n') + file.write(' spline {\n') + file.write(' #switch (Asc)\n') + file.write(' #case (67) //C cubic_spline\n') + file.write(' cubic_spline\n') + file.write(' #break\n') + file.write(' #case (76) //L linear_spline\n') + file.write(' linear_spline\n') + file.write(' #break\n') + file.write(' #case (78) //N linear_spline\n') + file.write(' natural_spline\n') + file.write(' #break\n') + file.write(' #case (81) //Q Quadratic_spline\n') + file.write(' quadratic_spline\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' #local Add=1/((Ds-2)-1);\n') + file.write(' #local J=0-Add;\n') + file.write(' #local I=0;\n') + file.write(' #while (I<Ds)\n') + file.write(' J\n') + file.write(' Arr[I]\n') + file.write(' #local I=I+1;\n') + file.write(' #local J=J+Add;\n') + file.write(' #end\n') + file.write(' }\n') + file.write('#end\n') + + file.write('#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n') + # suppressed some file checking from original macro because no more separate files + file.write(' #local Write=0;\n') + file.write(' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n') + file.write(' #local NumVertices=dimension_size(VecArr,1);\n') + file.write(' #switch (Write)\n') + file.write(' #case(1)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' " vertex_vectors {\\n",\n') + file.write(' " ", str(NumVertices,0,0),"\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' str(2*NumVertices,0,0),",\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' mesh2 {\n') + file.write(' vertex_vectors {\n') + file.write(' NumVertices\n') + file.write(' #local I=0;\n') + file.write(' #while (I<NumVertices)\n') + file.write(' VecArr[I]\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile, VecArr[I])\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write( + ' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n' + ) + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile, VecArr[I])\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' #local I=I+1;\n') + file.write(' #if(Write=1 | Write=4)\n') + file.write(' #if(mod(I,3)=0)\n') + file.write(' #write(MeshFile,"\\n ")\n') + file.write(' #end\n') + file.write(' #end \n') + file.write(' #end\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile,"\\n }\\n")\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(MeshFile,"\\n")\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' // do nothing\n') + file.write(' #break\n') + file.write(' #case(4) \n') + file.write(' #write(MeshFile,"\\n}\\n")\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' }\n') + + file.write(' #debug concat(" - normal_vectors\\n") \n') + file.write(' #local NumVertices=dimension_size(NormArr,1);\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' " normal_vectors {\\n",\n') + file.write(' " ", str(NumVertices,0,0),"\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "# Normals: ",str(NumVertices,0,0),"\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' // do nothing\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write( + ' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n' + ) + file.write(' )\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' normal_vectors {\n') + file.write(' NumVertices\n') + file.write(' #local I=0;\n') + file.write(' #while (I<NumVertices)\n') + file.write(' NormArr[I]\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile NormArr[I])\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write( + ' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n' + ) + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile NormArr[I])\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' #local I=I+1;\n') + file.write(' #if(Write=1 | Write=4) \n') + file.write(' #if(mod(I,3)=0)\n') + file.write(' #write(MeshFile,"\\n ")\n') + file.write(' #end\n') + file.write(' #end\n') + file.write(' #end\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile,"\\n }\\n")\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(MeshFile,"\\n")\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' //do nothing\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile,"\\n}\\n")\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' }\n') + + file.write(' #debug concat(" - uv_vectors\\n") \n') + file.write(' #local NumVertices=dimension_size(UVArr,1);\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(\n') + file.write(' MeshFile, \n') + file.write(' " uv_vectors {\\n",\n') + file.write(' " ", str(NumVertices,0,0),"\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' // do nothing, *.pcm does not support uv-vectors\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' uv_vectors {\n') + file.write(' NumVertices\n') + file.write(' #local I=0;\n') + file.write(' #while (I<NumVertices)\n') + file.write(' UVArr[I]\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile UVArr[I])\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' //do nothing\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile UVArr[I])\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' #local I=I+1; \n') + file.write(' #if(Write=1 | Write=4)\n') + file.write(' #if(mod(I,3)=0)\n') + file.write(' #write(MeshFile,"\\n ")\n') + file.write(' #end \n') + file.write(' #end\n') + file.write(' #end \n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile,"\\n }\\n")\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(MeshFile,"\\n")\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' //do nothing\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile,"\\n}\\n")\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' }\n') + file.write('\n') + file.write(' #debug concat(" - face_indices\\n") \n') + file.write(' #declare NumFaces=U*V*2;\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' " face_indices {\\n"\n') + file.write(' " ", str(NumFaces,0,0),"\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write (\n') + file.write(' MeshFile,\n') + file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #write (\n') + file.write(' MeshFile,\n') + file.write(' "0,",str(NumFaces,0,0),",\\n"\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' face_indices {\n') + file.write(' NumFaces\n') + file.write(' #local I=0;\n') + file.write(' #local H=0;\n') + file.write(' #local NumVertices=dimension_size(VecArr,1);\n') + file.write(' #while (I<V)\n') + file.write(' #local J=0;\n') + file.write(' #while (J<U)\n') + file.write(' #local Ind=(I*U)+I+J;\n') + file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write( + ' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n' + ) + file.write( + ' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n' + ) + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write( + ' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n' + ) + file.write( + ' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n' + ) + file.write(' )\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(\n') + file.write(' MeshFile,\n') + file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n') + file.write(' )\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' #local J=J+1;\n') + file.write(' #local H=H+1;\n') + file.write(' #if(Write=1 | Write=4)\n') + file.write(' #if(mod(H,3)=0)\n') + file.write(' #write(MeshFile,"\\n ")\n') + file.write(' #end \n') + file.write(' #end\n') + file.write(' #end\n') + file.write(' #local I=I+1;\n') + file.write(' #end\n') + file.write(' }\n') + file.write(' #switch(Write)\n') + file.write(' #case(1)\n') + file.write(' #write(MeshFile, "\\n }\\n}")\n') + file.write(' #fclose MeshFile\n') + file.write(' #debug concat(" Done writing\\n")\n') + file.write(' #break\n') + file.write(' #case(2)\n') + file.write(' #fclose MeshFile\n') + file.write(' #debug concat(" Done writing\\n")\n') + file.write(' #break\n') + file.write(' #case(3)\n') + file.write(' #fclose MeshFile\n') + file.write(' #debug concat(" Done writing\\n")\n') + file.write(' #break\n') + file.write(' #case(4)\n') + file.write(' #write(MeshFile, "\\n}\\n}")\n') + file.write(' #fclose MeshFile\n') + file.write(' #debug concat(" Done writing\\n")\n') + file.write(' #break\n') + file.write(' #end\n') + file.write(' }\n') + file.write('#end\n') + + file.write('#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n') + file.write(' #declare Build=CheckFileName(FileName);\n') + file.write(' #if(Build=0)\n') + file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n') + file.write(' #include FileName\n') + file.write(' object{Surface}\n') + file.write(' #else\n') + file.write(' #local NumVertices=(SplRes+1)*(InterpRes+1);\n') + file.write(' #local NumFaces=SplRes*InterpRes*2;\n') + file.write( + ' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n' + ) + file.write(' #local VecArr=array[NumVertices]\n') + file.write(' #local NormArr=array[NumVertices]\n') + file.write(' #local UVArr=array[NumVertices]\n') + file.write(' #local N=dimension_size(SplineArray,1);\n') + file.write(' #local TempSplArr0=array[N];\n') + file.write(' #local TempSplArr1=array[N];\n') + file.write(' #local TempSplArr2=array[N];\n') + file.write(' #local PosStep=1/SplRes;\n') + file.write(' #local InterpStep=1/InterpRes;\n') + file.write(' #local Count=0;\n') + file.write(' #local Pos=0;\n') + file.write(' #while(Pos<=1)\n') + file.write(' #local I=0;\n') + file.write(' #if (Pos=0)\n') + file.write(' #while (I<N)\n') + file.write(' #local Spl=spline{SplineArray[I]}\n') + file.write(' #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n') + file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n') + file.write(' #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n') + file.write(' #local I=I+1;\n') + file.write(' #end\n') + file.write(' #local S0=BuildSpline(TempSplArr0, Interp_type)\n') + file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n') + file.write(' #local S2=BuildSpline(TempSplArr2, Interp_type)\n') + file.write(' #else\n') + file.write(' #while (I<N)\n') + file.write(' #local Spl=spline{SplineArray[I]}\n') + file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n') + file.write(' #local I=I+1;\n') + file.write(' #end\n') + file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n') + file.write(' #end\n') + file.write(' #local J=0;\n') + file.write(' #while (J<=1)\n') + file.write(' #local P0=<0,0,0>+S0(J);\n') + file.write(' #local P1=<0,0,0>+S1(J);\n') + file.write(' #local P2=<0,0,0>+S2(J);\n') + file.write(' #local P3=<0,0,0>+S0(J+InterpStep);\n') + file.write(' #local P4=<0,0,0>+S0(J-InterpStep);\n') + file.write(' #local B1=P4-P0;\n') + file.write(' #local B2=P2-P0;\n') + file.write(' #local B3=P3-P0;\n') + file.write(' #local B4=P1-P0;\n') + file.write(' #local N1=vcross(B1,B2);\n') + file.write(' #local N2=vcross(B2,B3);\n') + file.write(' #local N3=vcross(B3,B4);\n') + file.write(' #local N4=vcross(B4,B1);\n') + file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n') + file.write(' #local VecArr[Count]=P0;\n') + file.write(' #local NormArr[Count]=Norm;\n') + file.write(' #local UVArr[Count]=<J,Pos>;\n') + file.write(' #local J=J+InterpStep;\n') + file.write(' #local Count=Count+1;\n') + file.write(' #end\n') + file.write(' #local S2=spline{S0}\n') + file.write(' #local S0=spline{S1}\n') + file.write( + ' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n' + ) + file.write(' #local Pos=Pos+PosStep;\n') + file.write(' #end\n') + file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n') + file.write(' #end\n') + file.write('#end\n\n') + + file.write('#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n') + file.write(' #declare Build=CheckFileName(FileName);\n') + file.write(' #if(Build=0)\n') + file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n') + file.write(' #include FileName\n') + file.write(' object{Surface}\n') + file.write(' #else\n') + file.write(' #local NumVertices=(Iter_U+1)*(Iter_V+1);\n') + file.write(' #local NumFaces=Iter_U*Iter_V*2;\n') + file.write( + ' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n' + ) + file.write(' #declare VecArr=array[NumVertices] \n') + file.write(' #declare NormArr=array[NumVertices] \n') + file.write(' #local UVArr=array[NumVertices] \n') + file.write(' #local Spl1_0=Spl1(0);\n') + file.write(' #local Spl2_0=Spl2(0);\n') + file.write(' #local Spl3_0=Spl3(0);\n') + file.write(' #local Spl4_0=Spl4(0);\n') + file.write(' #local UStep=1/Iter_U;\n') + file.write(' #local VStep=1/Iter_V;\n') + file.write(' #local Count=0;\n') + file.write(' #local I=0;\n') + file.write(' #while (I<=1)\n') + file.write(' #local Im=1-I;\n') + file.write(' #local J=0;\n') + file.write(' #while (J<=1)\n') + file.write(' #local Jm=1-J;\n') + file.write( + ' #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n' + ) + file.write(' #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n') + file.write(' LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n') + file.write(' #declare VecArr[Count]=P0;\n') + file.write(' #local UVArr[Count]=<J,I>;\n') + file.write(' #local J=J+UStep;\n') + file.write(' #local Count=Count+1;\n') + file.write(' #end\n') + file.write(' #debug concat(\n') + file.write(' "\r Done ", str(Count,0,0)," vertices : ",\n') + file.write(' str(100*Count/NumVertices,0,2)," %"\n') + file.write(' )\n') + file.write(' #local I=I+VStep;\n') + file.write(' #end\n') + file.write(' #debug "\r Normals "\n') + file.write(' #local Count=0;\n') + file.write(' #local I=0;\n') + file.write(' #while (I<=Iter_V)\n') + file.write(' #local J=0;\n') + file.write(' #while (J<=Iter_U)\n') + file.write(' #local Ind=(I*Iter_U)+I+J;\n') + file.write(' #local P0=VecArr[Ind];\n') + file.write(' #if(J=0)\n') + file.write(' #local P1=P0+(P0-VecArr[Ind+1]);\n') + file.write(' #else\n') + file.write(' #local P1=VecArr[Ind-1];\n') + file.write(' #end\n') + file.write(' #if (J=Iter_U)\n') + file.write(' #local P2=P0+(P0-VecArr[Ind-1]);\n') + file.write(' #else\n') + file.write(' #local P2=VecArr[Ind+1];\n') + file.write(' #end\n') + file.write(' #if (I=0)\n') + file.write(' #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n') + file.write(' #else\n') + file.write(' #local P3=VecArr[Ind-Iter_U-1];\n') + file.write(' #end\n') + file.write(' #if (I=Iter_V)\n') + file.write(' #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n') + file.write(' #else\n') + file.write(' #local P4=VecArr[Ind+Iter_U+1];\n') + file.write(' #end\n') + file.write(' #local B1=P4-P0;\n') + file.write(' #local B2=P2-P0;\n') + file.write(' #local B3=P3-P0;\n') + file.write(' #local B4=P1-P0;\n') + file.write(' #local N1=vcross(B1,B2);\n') + file.write(' #local N2=vcross(B2,B3);\n') + file.write(' #local N3=vcross(B3,B4);\n') + file.write(' #local N4=vcross(B4,B1);\n') + file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n') + file.write(' #declare NormArr[Count]=Norm;\n') + file.write(' #local J=J+1;\n') + file.write(' #local Count=Count+1;\n') + file.write(' #end\n') + file.write( + ' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n' + ) + file.write(' #local I=I+1;\n') + file.write(' #end\n') + file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n') + file.write(' #end\n') + file.write('#end\n\n') + # Empty curves + if len(ob.data.splines) == 0: + tab_write("\n//dummy sphere to represent empty curve location\n") + tab_write("#declare %s =\n" % dataname) + tab_write( + "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n" + % (ob.location.x, ob.location.y, ob.location.z) + ) # ob.name > povdataname) + # And non empty curves + else: + if not bezier_sweep: + tab_write("#declare %s =\n" % dataname) + if ob.pov.curveshape == 'sphere_sweep' and not bezier_sweep: + tab_write("union {\n") + for spl in ob.data.splines: + if spl.type != "BEZIER": + spl_type = "linear" + if spl.type == "NURBS": + spl_type = "cubic" + points = spl.points + num_points = len(points) + if spl.use_cyclic_u: + num_points += 3 + + tab_write("sphere_sweep { %s_spline %s,\n" % (spl_type, num_points)) + if spl.use_cyclic_u: + pt1 = points[len(points) - 1] + wpt1 = pt1.co + tab_write( + "<%.4g,%.4g,%.4g>,%.4g\n" + % (wpt1[0], wpt1[1], wpt1[2], pt1.radius * ob.data.bevel_depth) + ) + for pt in points: + wpt = pt.co + tab_write( + "<%.4g,%.4g,%.4g>,%.4g\n" + % (wpt[0], wpt[1], wpt[2], pt.radius * ob.data.bevel_depth) + ) + if spl.use_cyclic_u: + for i in range(0, 2): + end_pt = points[i] + wpt = end_pt.co + tab_write( + "<%.4g,%.4g,%.4g>,%.4g\n" + % (wpt[0], wpt[1], wpt[2], end_pt.radius * ob.data.bevel_depth) + ) + + tab_write("}\n") + # below not used yet? + if ob.pov.curveshape == 'sor': + for spl in ob.data.splines: + if spl.type in {'POLY', 'NURBS'}: + points = spl.points + num_points = len(points) + tab_write("sor { %s,\n" % num_points) + for pt in points: + wpt = pt.co + tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) + else: + tab_write("box { 0,0\n") + if ob.pov.curveshape in {'lathe', 'prism'}: + spl = ob.data.splines[0] + if spl.type == "BEZIER": + points = spl.bezier_points + len_cur = len(points) - 1 + len_pts = len_cur * 4 + ifprism = '' + if ob.pov.curveshape in {'prism'}: + height = ob.data.extrude + ifprism = '-%s, %s,' % (height, height) + len_cur += 1 + len_pts += 4 + tab_write("%s { bezier_spline %s %s,\n" % (ob.pov.curveshape, ifprism, len_pts)) + for i in range(0, len_cur): + p1 = points[i].co + pR = points[i].handle_right + end = i + 1 + if i == len_cur - 1 and ob.pov.curveshape in {'prism'}: + end = 0 + pL = points[end].handle_left + p2 = points[end].co + line = "<%.4g,%.4g>" % (p1[0], p1[1]) + line += "<%.4g,%.4g>" % (pR[0], pR[1]) + line += "<%.4g,%.4g>" % (pL[0], pL[1]) + line += "<%.4g,%.4g>" % (p2[0], p2[1]) + tab_write("%s\n" % line) + else: + points = spl.points + len_cur = len(points) + len_pts = len_cur + ifprism = '' + if ob.pov.curveshape in {'prism'}: + height = ob.data.extrude + ifprism = '-%s, %s,' % (height, height) + len_pts += 3 + spl_type = 'quadratic' + if spl.type == 'POLY': + spl_type = 'linear' + tab_write( + "%s { %s_spline %s %s,\n" % (ob.pov.curveshape, spl_type, ifprism, len_pts) + ) + if ob.pov.curveshape in {'prism'}: + pt = points[len(points) - 1] + wpt = pt.co + tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) + for pt in points: + wpt = pt.co + tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) + if ob.pov.curveshape in {'prism'}: + for i in range(2): + pt = points[i] + wpt = pt.co + tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) + if bezier_sweep: + for p in range(len(ob.data.splines)): + br = [] + depth = ob.data.bevel_depth + spl = ob.data.splines[p] + points = spl.bezier_points + len_cur = len(points) - 1 + num_points = len_cur * 4 + if spl.use_cyclic_u: + len_cur += 1 + num_points += 4 + tab_write("#declare %s_points_%s = array[%s]{\n" % (dataname, p, num_points)) + for i in range(len_cur): + p1 = points[i].co + pR = points[i].handle_right + end = i + 1 + if spl.use_cyclic_u and i == (len_cur - 1): + end = 0 + pL = points[end].handle_left + p2 = points[end].co + r3 = points[end].radius * depth + r0 = points[i].radius * depth + r1 = 2 / 3 * r0 + 1 / 3 * r3 + r2 = 1 / 3 * r0 + 2 / 3 * r3 + br.append((r0, r1, r2, r3)) + line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2]) + line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2]) + line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2]) + line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2]) + tab_write("%s\n" % line) + tab_write("}\n") + tab_write("#declare %s_radii_%s = array[%s]{\n" % (dataname, p, len(br) * 4)) + for rad_tuple in br: + tab_write( + '%.4f,%.4f,%.4f,%.4f\n' + % (rad_tuple[0], rad_tuple[1], rad_tuple[2], rad_tuple[3]) + ) + tab_write("}\n") + if len(ob.data.splines) == 1: + tab_write('#declare %s = object{\n' % dataname) + tab_write( + ' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n' + % (ob.data.resolution_u, dataname, p, dataname, p) + ) + else: + tab_write('#declare %s = union{\n' % dataname) + for p in range(len(ob.data.splines)): + tab_write( + ' object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n' + % (ob.data.resolution_u, dataname, p, dataname, p) + ) + # tab_write('#include "bezier_spheresweep.inc"\n') #now inlined + # tab_write('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth)) + if ob.pov.curveshape in {'loft'}: + tab_write('object {MSM(%s,%s,"c",%s,"")\n' % (dataname, ob.pov.res_u, ob.pov.res_v)) + if ob.pov.curveshape in {'birail'}: + splines = '%s1,%s2,%s3,%s4' % (dataname, dataname, dataname, dataname) + tab_write('object {Coons(%s, %s, %s, "")\n' % (splines, ob.pov.res_u, ob.pov.res_v)) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(ob.data) + # tab_write("texture {%s}\n"%pov_mat_name) + if ob.pov.curveshape in {'prism'}: + tab_write("rotate <90,0,0>\n") + tab_write("scale y*-1\n") + tab_write("}\n") diff --git a/render_povray/object_gui.py b/render_povray/object_gui.py new file mode 100755 index 000000000..bc7df9f44 --- /dev/null +++ b/render_povray/object_gui.py @@ -0,0 +1,731 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""User interface for the POV tools""" + +import bpy + +from bpy.utils import register_class, unregister_class +from bpy.types import ( + # Operator, + Menu, + Panel, +) + + +# Example of wrapping every class 'as is' +from bl_ui import properties_data_modifier + +for member in dir(properties_data_modifier): + subclass = getattr(properties_data_modifier, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) +del properties_data_modifier + + +from bl_ui import properties_data_mesh + +# These panels are kept +properties_data_mesh.DATA_PT_custom_props_mesh.COMPAT_ENGINES.add('POVRAY_RENDER') +properties_data_mesh.DATA_PT_context_mesh.COMPAT_ENGINES.add('POVRAY_RENDER') + +## make some native panels contextual to some object variable +## by recreating custom panels inheriting their properties + + +from .scripting_gui import VIEW_MT_POV_import + + +class ModifierButtonsPanel: + """Use this class to define buttons from the modifier tab of + properties window.""" + + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "modifier" + # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here + + @classmethod + def poll(cls, context): + mods = context.object.modifiers + rd = context.scene.render + return mods and (rd.engine in cls.COMPAT_ENGINES) + + +class ObjectButtonsPanel: + """Use this class to define buttons from the object tab of + properties window.""" + + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here + + @classmethod + def poll(cls, context): + obj = context.object + rd = context.scene.render + return obj and (rd.engine in cls.COMPAT_ENGINES) + + +class PovDataButtonsPanel(properties_data_mesh.MeshButtonsPanel): + """Use this class to define buttons from the edit data tab of + properties window.""" + + COMPAT_ENGINES = {'POVRAY_RENDER'} + POV_OBJECT_TYPES = { + 'PLANE', + 'BOX', + 'SPHERE', + 'CYLINDER', + 'CONE', + 'TORUS', + 'BLOB', + 'ISOSURFACE', + 'SUPERELLIPSOID', + 'SUPERTORUS', + 'HEIGHT_FIELD', + 'PARAMETRIC', + 'POLYCIRCLE', + } + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + obj = context.object + # We use our parent class poll func too, avoids to re-define too much things... + return ( + super(PovDataButtonsPanel, 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. ;) +class DATA_PT_POV_normals(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_normals.bl_label + + draw = properties_data_mesh.DATA_PT_normals.draw + + +class DATA_PT_POV_texture_space(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_texture_space.bl_label + bl_options = properties_data_mesh.DATA_PT_texture_space.bl_options + + draw = properties_data_mesh.DATA_PT_texture_space.draw + + +class DATA_PT_POV_vertex_groups(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_vertex_groups.bl_label + + draw = properties_data_mesh.DATA_PT_vertex_groups.draw + + +class DATA_PT_POV_shape_keys(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_shape_keys.bl_label + + draw = properties_data_mesh.DATA_PT_shape_keys.draw + + +class DATA_PT_POV_uv_texture(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_uv_texture.bl_label + + draw = properties_data_mesh.DATA_PT_uv_texture.draw + + +class DATA_PT_POV_vertex_colors(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_vertex_colors.bl_label + + draw = properties_data_mesh.DATA_PT_vertex_colors.draw + + +class DATA_PT_POV_customdata(PovDataButtonsPanel, Panel): + bl_label = properties_data_mesh.DATA_PT_customdata.bl_label + bl_options = properties_data_mesh.DATA_PT_customdata.bl_options + draw = properties_data_mesh.DATA_PT_customdata.draw + + +del properties_data_mesh + + +class MODIFIERS_PT_POV_modifiers(ModifierButtonsPanel, Panel): + """Use this class to define pov modifier buttons. (For booleans)""" + + bl_label = "POV-Ray" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + # def draw_header(self, context): + # scene = context.scene + # self.layout.prop(scene.pov, "boolean_mod", text="") + + def draw(self, context): + # scene = context.scene + layout = self.layout + ob = context.object + mod = ob.modifiers + col = layout.column() + # Find Boolean Modifiers for displaying CSG option + onceCSG = 0 + for mod in ob.modifiers: + if onceCSG == 0: + if mod: + if mod.type == 'BOOLEAN': + col.prop(ob.pov, "boolean_mod") + onceCSG = 1 + + if ob.pov.boolean_mod == "POV": + split = layout.split() + col = layout.column() + # Inside Vector for CSG + col.prop(ob.pov, "inside_vector") + + +class OBJECT_PT_POV_obj_parameters(ObjectButtonsPanel, Panel): + """Use this class to define pov specific object level options buttons.""" + + bl_label = "POV" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + + engine = context.scene.render.engine + return engine in cls.COMPAT_ENGINES + + def draw(self, context): + layout = self.layout + + obj = context.object + + split = layout.split() + + col = split.column(align=True) + + col.label(text="Radiosity:") + col.prop(obj.pov, "importance_value", text="Importance") + col.label(text="Photons:") + col.prop(obj.pov, "collect_photons", text="Receive Photon Caustics") + if obj.pov.collect_photons: + col.prop(obj.pov, "spacing_multiplier", text="Photons Spacing Multiplier") + + split = layout.split() + + col = split.column() + col.prop(obj.pov, "hollow") + col.prop(obj.pov, "double_illuminate") + + if obj.type == 'META' or obj.pov.curveshape == 'lathe': + # if obj.pov.curveshape == 'sor' + col.prop(obj.pov, "sturm") + col.prop(obj.pov, "no_shadow") + col.prop(obj.pov, "no_image") + col.prop(obj.pov, "no_reflection") + col.prop(obj.pov, "no_radiosity") + col.prop(obj.pov, "inverse") + col.prop(obj.pov, "hierarchy") + # col.prop(obj.pov,"boundorclip",text="Bound / Clip") + # if obj.pov.boundorclip != "none": + # col.prop_search(obj.pov,"boundorclipob",context.blend_data,"objects",text="Object") + # text = "Clipped by" + # if obj.pov.boundorclip == "clipped_by": + # text = "Bounded by" + # col.prop(obj.pov,"addboundorclip",text=text) + + +class OBJECT_PT_POV_obj_sphere(PovDataButtonsPanel, Panel): + """Use this class to define pov sphere primitive parameters buttons.""" + + bl_label = "POV Sphere" + 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 == 'SPHERE' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'SPHERE': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Sphere radius: " + str(obj.pov.sphere_radius)) + + 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.sphere_update", text="Update", icon="SHADING_RENDERED") + + # col.label(text="Parameters:") + col.prop(obj.pov, "sphere_radius", text="Radius of Sphere") + + +class OBJECT_PT_POV_obj_cylinder(PovDataButtonsPanel, Panel): + """Use this class to define pov cylinder primitive parameters buttons.""" + + bl_label = "POV Cylinder" + 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 == 'CYLINDER' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'CYLINDER': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Cylinder radius: " + str(obj.pov.cylinder_radius)) + col.label(text="Cylinder cap location: " + str(obj.pov.cylinder_location_cap)) + + 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.cylinder_update", text="Update", icon="MESH_CYLINDER") + + # col.label(text="Parameters:") + col.prop(obj.pov, "cylinder_radius") + col.prop(obj.pov, "cylinder_location_cap") + + +class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel): + """Use this class to define pov cone primitive parameters buttons.""" + + bl_label = "POV Cone" + 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 == 'CONE' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'CONE': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Cone base radius: " + str(obj.pov.cone_base_radius)) + col.label(text="Cone cap radius: " + str(obj.pov.cone_cap_radius)) + col.label(text="Cone proxy segments: " + str(obj.pov.cone_segments)) + col.label(text="Cone height: " + str(obj.pov.cone_height)) + 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.pov, "cone_base_radius", text="Radius of Cone Base") + col.prop(obj.pov, "cone_cap_radius", text="Radius of Cone Cap") + col.prop(obj.pov, "cone_segments", text="Segmentation of Cone proxy") + col.prop(obj.pov, "cone_height", text="Height of the cone") + + +class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel): + """Use this class to define pov superellipsoid primitive parameters buttons.""" + + bl_label = "POV Superquadric ellipsoid" + 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 == 'SUPERELLIPSOID' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'SUPERELLIPSOID': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Radial segmentation: " + str(obj.pov.se_u)) + col.label(text="Lateral segmentation: " + str(obj.pov.se_v)) + col.label(text="Ring shape: " + str(obj.pov.se_n1)) + col.label(text="Cross-section shape: " + str(obj.pov.se_n2)) + col.label(text="Fill up and down: " + str(obj.pov.se_edit)) + 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.superellipsoid_update", text="Update", icon="MOD_SUBSURF") + + # col.label(text="Parameters:") + col.prop(obj.pov, "se_u") + col.prop(obj.pov, "se_v") + col.prop(obj.pov, "se_n1") + col.prop(obj.pov, "se_n2") + col.prop(obj.pov, "se_edit") + + +class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel): + """Use this class to define pov torus primitive parameters buttons.""" + + bl_label = "POV Torus" + 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 == 'TORUS' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'TORUS': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Torus major radius: " + str(obj.pov.torus_major_radius)) + col.label(text="Torus minor radius: " + str(obj.pov.torus_minor_radius)) + col.label(text="Torus major segments: " + str(obj.pov.torus_major_segments)) + col.label(text="Torus minor segments: " + str(obj.pov.torus_minor_segments)) + 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.torus_update", text="Update", icon="MESH_TORUS") + + # col.label(text="Parameters:") + col.prop(obj.pov, "torus_major_radius") + col.prop(obj.pov, "torus_minor_radius") + col.prop(obj.pov, "torus_major_segments") + col.prop(obj.pov, "torus_minor_segments") + + +class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel): + """Use this class to define pov supertorus primitive parameters buttons.""" + + bl_label = "POV SuperTorus" + 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 == 'SUPERTORUS' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'SUPERTORUS': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="SuperTorus major radius: " + str(obj.pov.st_major_radius)) + col.label(text="SuperTorus minor radius: " + str(obj.pov.st_minor_radius)) + col.label(text="SuperTorus major segments: " + str(obj.pov.st_u)) + col.label(text="SuperTorus minor segments: " + str(obj.pov.st_v)) + + col.label(text="SuperTorus Ring Manipulator: " + str(obj.pov.st_ring)) + col.label(text="SuperTorus Cross Manipulator: " + str(obj.pov.st_cross)) + col.label(text="SuperTorus Internal And External radii: " + str(obj.pov.st_ie)) + + col.label(text="SuperTorus accuracy: " + str(obj.pov.st_accuracy)) + col.label(text="SuperTorus max gradient: " + str(obj.pov.st_max_gradient)) + + 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.supertorus_update", text="Update", icon="MESH_TORUS") + + # col.label(text="Parameters:") + col.prop(obj.pov, "st_major_radius") + col.prop(obj.pov, "st_minor_radius") + col.prop(obj.pov, "st_u") + col.prop(obj.pov, "st_v") + col.prop(obj.pov, "st_ring") + col.prop(obj.pov, "st_cross") + col.prop(obj.pov, "st_ie") + # col.prop(obj.pov, "st_edit") #? + col.prop(obj.pov, "st_accuracy") + col.prop(obj.pov, "st_max_gradient") + + +class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel): + """Use this class to define pov parametric surface primitive parameters buttons.""" + + bl_label = "POV Parametric surface" + 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 == 'PARAMETRIC' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'PARAMETRIC': + if not obj.pov.unlock_parameters: + col.prop( + obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED' + ) + col.label(text="Minimum U: " + str(obj.pov.u_min)) + col.label(text="Minimum V: " + str(obj.pov.v_min)) + col.label(text="Maximum U: " + str(obj.pov.u_max)) + col.label(text="Minimum V: " + str(obj.pov.v_min)) + col.label(text="X Function: " + str(obj.pov.x_eq)) + col.label(text="Y Function: " + str(obj.pov.y_eq)) + col.label(text="Z Function: " + str(obj.pov.x_eq)) + + 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.parametric_update", text="Update", icon="SCRIPTPLUGINS") + + col.prop(obj.pov, "u_min", text="Minimum U") + col.prop(obj.pov, "v_min", text="Minimum V") + col.prop(obj.pov, "u_max", text="Maximum U") + col.prop(obj.pov, "v_max", text="Minimum V") + col.prop(obj.pov, "x_eq", text="X Function") + col.prop(obj.pov, "y_eq", text="Y Function") + col.prop(obj.pov, "z_eq", text="Z Function") + + +class OBJECT_PT_povray_replacement_text(ObjectButtonsPanel, Panel): + """Use this class to define pov object replacement field.""" + + bl_label = "Custom POV Code" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + col.label(text="Replace properties with:") + col.prop(obj.pov, "replacement_text", text="") + + +############################################################################### +# Add Povray Objects +############################################################################### +def check_add_mesh_extra_objects(): + """Test if Add mesh extra objects addon is activated + + This addon is currently used to generate the proxy for POV parametric + surface which is almost the same priciple as its Math xyz surface + """ + if "add_mesh_extra_objects" in bpy.context.preferences.addons.keys(): + return True + return False + + +def menu_func_add(self, context): + """Append the POV primitives submenu to blender add objects menu""" + engine = context.scene.render.engine + if engine == 'POVRAY_RENDER': + self.layout.menu("VIEW_MT_POV_primitives_add", icon="PLUGIN") + + +class VIEW_MT_POV_primitives_add(Menu): + """Define the primitives menu with presets""" + + bl_idname = "VIEW_MT_POV_primitives_add" + bl_label = "Povray" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return engine == 'POVRAY_RENDER' + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.menu(VIEW_MT_POV_Basic_Shapes.bl_idname, text="Primitives", icon="GROUP") + layout.menu(VIEW_MT_POV_import.bl_idname, text="Import", icon="IMPORT") + + +class VIEW_MT_POV_Basic_Shapes(Menu): + """Use this class to sort simple primitives menu entries.""" + + bl_idname = "POVRAY_MT_basic_shape_tools" + bl_label = "Basic_shapes" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("pov.addplane", text="Infinite Plane", icon='MESH_PLANE') + layout.operator("pov.addbox", text="Box", icon='MESH_CUBE') + layout.operator("pov.addsphere", text="Sphere", icon='SHADING_RENDERED') + layout.operator("pov.addcylinder", text="Cylinder", icon="MESH_CYLINDER") + layout.operator("pov.cone_add", text="Cone", icon="MESH_CONE") + layout.operator("pov.addtorus", text="Torus", icon='MESH_TORUS') + layout.separator() + layout.operator("pov.addrainbow", text="Rainbow", icon="COLOR") + layout.operator("pov.addlathe", text="Lathe", icon='MOD_SCREW') + layout.operator("pov.addprism", text="Prism", icon='MOD_SOLIDIFY') + layout.operator("pov.addsuperellipsoid", text="Superquadric Ellipsoid", icon='MOD_SUBSURF') + layout.operator("pov.addheightfield", text="Height Field", icon="RNDCURVE") + layout.operator("pov.addspheresweep", text="Sphere Sweep", icon='FORCE_CURVE') + layout.separator() + layout.operator("pov.addblobsphere", text="Blob Sphere", icon='META_DATA') + layout.separator() + layout.label(text="Isosurfaces") + layout.operator("pov.addisosurfacebox", text="Isosurface Box", icon="META_CUBE") + layout.operator("pov.addisosurfacesphere", text="Isosurface Sphere", icon="META_BALL") + layout.operator("pov.addsupertorus", text="Supertorus", icon="SURFACE_NTORUS") + layout.separator() + layout.label(text="Macro based") + layout.operator( + "pov.addpolygontocircle", text="Polygon To Circle Blending", icon="MOD_CAST" + ) + layout.operator("pov.addloft", text="Loft", icon="SURFACE_NSURFACE") + layout.separator() + # Warning if the Add Advanced Objects addon containing + # Add mesh extra objects is not enabled + if not check_add_mesh_extra_objects(): + # col = box.column() + layout.label(text="Please enable Add Mesh: Extra Objects addon", icon="INFO") + # layout.separator() + layout.operator( + "preferences.addon_show", + text="Go to Add Mesh: Extra Objects addon", + icon="PREFERENCES", + ).module = "add_mesh_extra_objects" + + # layout.separator() + return + layout.operator("pov.addparametric", text="Parametric", icon='SCRIPTPLUGINS') + + +classes = ( + # ObjectButtonsPanel, + # PovDataButtonsPanel, + DATA_PT_POV_normals, + DATA_PT_POV_texture_space, + DATA_PT_POV_vertex_groups, + DATA_PT_POV_shape_keys, + DATA_PT_POV_uv_texture, + DATA_PT_POV_vertex_colors, + DATA_PT_POV_customdata, + MODIFIERS_PT_POV_modifiers, + OBJECT_PT_POV_obj_parameters, + OBJECT_PT_POV_obj_sphere, + OBJECT_PT_POV_obj_cylinder, + OBJECT_PT_POV_obj_cone, + OBJECT_PT_POV_obj_superellipsoid, + OBJECT_PT_POV_obj_torus, + OBJECT_PT_POV_obj_supertorus, + OBJECT_PT_POV_obj_parametric, + OBJECT_PT_povray_replacement_text, + VIEW_MT_POV_primitives_add, + VIEW_MT_POV_Basic_Shapes, +) + + +def register(): + # from bpy.utils import register_class + + for cls in classes: + register_class(cls) + + bpy.types.VIEW3D_MT_add.prepend(menu_func_add) + + # was used for parametric objects but made the other addon unreachable on + # unregister for other tools to use created a user action call instead + # addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True) + + +def unregister(): + # addon_utils.disable("add_mesh_extra_objects", default_set=False) + + bpy.types.VIEW3D_MT_add.remove(menu_func_add) + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/object_mesh_topology.py b/render_povray/object_mesh_topology.py new file mode 100755 index 000000000..2b0ce008a --- /dev/null +++ b/render_povray/object_mesh_topology.py @@ -0,0 +1,1540 @@ +# ***** 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 #**** + +# <pep8 compliant> + +"""Translate to POV the control point compounded geometries like polygon + +meshes or curve based shapes.""" + +#################### +## Faster mesh export +import numpy as np + +#################### +import random # used for hair +import bpy +from . import texturing # for how textures influence shaders +from .scenography import export_smoke + + +def matrix_as_pov_string(matrix): + """Translate some tranform matrix from Blender UI + to POV syntax and return that string """ + matrix_string = ( + "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n" + % ( + matrix[0][0], + matrix[1][0], + matrix[2][0], + matrix[0][1], + matrix[1][1], + matrix[2][1], + matrix[0][2], + matrix[1][2], + matrix[2][2], + matrix[0][3], + matrix[1][3], + matrix[2][3], + ) + ) + return matrix_string + + +# objectNames = {} +DEF_OBJ_NAME = "Default" + + +def export_meshes( + preview_dir, + file, + scene, + sel, + csg, + string_strip_hyphen, + safety, + write_object_modifiers, + material_names_dictionary, + write_object_material, + exported_lights_count, + unpacked_images, + image_format, + img_map, + img_map_transforms, + path_image, + smoke_path, + global_matrix, + write_matrix, + using_uberpov, + comments, + linebreaksinlists, + tab, + tab_level, + tab_write, + info_callback, +): + """write all meshes as POV mesh2{} syntax to exported file """ + # some numpy functions to speed up mesh export NOT IN USE YET + + # TODO: also write a numpy function to read matrices at object level? + # feed below with mesh object.data, but only after doing data.calc_loop_triangles() + def read_verts_co(self, mesh): + #'float64' would be a slower 64-bit floating-point number numpy datatype + # using 'float32' vert coordinates for now until any issue is reported + mverts_co = np.zeros((len(mesh.vertices) * 3), dtype=np.float32) + mesh.vertices.foreach_get("co", mverts_co) + return np.reshape(mverts_co, (len(mesh.vertices), 3)) + + def read_verts_idx(self, mesh): + mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64) + mesh.vertices.foreach_get("index", mverts_idx) + return np.reshape(mverts_idx, (len(mesh.vertices), 1)) + + def read_verts_norms(self, mesh): + #'float64' would be a slower 64-bit floating-point number numpy datatype + # using less accurate 'float16' normals for now until any issue is reported + mverts_no = np.zeros((len(mesh.vertices) * 3), dtype=np.float16) + mesh.vertices.foreach_get("normal", mverts_no) + return np.reshape(mverts_no, (len(mesh.vertices), 3)) + + def read_faces_idx(self, mesh): + mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64) + mesh.loop_triangles.foreach_get("index", mfaces_idx) + return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1)) + + def read_faces_verts_indices(self, mesh): + mfaces_verts_idx = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64) + mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx) + return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3)) + + # Why is below different from vertex indices? + def read_faces_verts_loops(self, mesh): + mfaces_verts_loops = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64) + mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops) + return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3)) + + def read_faces_norms(self, mesh): + #'float64' would be a slower 64-bit floating-point number numpy datatype + # using less accurate 'float16' normals for now until any issue is reported + mfaces_no = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.float16) + mesh.loop_triangles.foreach_get("normal", mfaces_no) + return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3)) + + def read_faces_smooth(self, mesh): + mfaces_smth = np.zeros((len(mesh.loop_triangles) * 1), dtype=np.bool) + mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth) + return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1)) + + def read_faces_material_indices(self, mesh): + mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16) + mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx) + return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1)) + + # obmatslist = [] + # def hasUniqueMaterial(): + # # Grab materials attached to object instances ... + # if hasattr(ob, 'material_slots'): + # for ms in ob.material_slots: + # if ms.material is not None and ms.link == 'OBJECT': + # if ms.material in obmatslist: + # return False + # else: + # obmatslist.append(ms.material) + # return True + # def hasObjectMaterial(ob): + # # Grab materials attached to object instances ... + # if hasattr(ob, 'material_slots'): + # for ms in ob.material_slots: + # if ms.material is not None and ms.link == 'OBJECT': + # # If there is at least one material slot linked to the object + # # and not the data (mesh), always create a new, "private" data instance. + # return True + # return False + # For objects using local material(s) only! + # This is a mapping between a tuple (dataname, material_names_dictionary, ...), and the POV dataname. + # As only objects using: + # * The same data. + # * EXACTLY the same materials, in EXACTLY the same sockets. + # ... can share a same instance in POV export. + obmats2data = {} + + def check_object_materials(ob, name, dataname): + """Compare other objects exported material slots to avoid rewriting duplicates""" + if hasattr(ob, 'material_slots'): + has_local_mats = False + key = [dataname] + for ms in ob.material_slots: + if ms.material is not None: + key.append(ms.material.name) + if ms.link == 'OBJECT' and not has_local_mats: + has_local_mats = True + else: + # Even if the slot is empty, it is important to grab it... + key.append("") + if has_local_mats: + # If this object uses local material(s), lets find if another object + # using the same data and exactly the same list of materials + # (in the same slots) has already been processed... + # Note that here also, we use object name as new, unique dataname for Pov. + key = tuple(key) # Lists are not hashable... + if key not in obmats2data: + obmats2data[key] = name + return obmats2data[key] + return None + + data_ref = {} + + def store(scene, ob, name, dataname, matrix): + # The Object needs to be written at least once but if its data is + # already in data_ref this has already been done. + # This func returns the "povray" name of the data, or None + # if no writing is needed. + if ob.is_modified(scene, 'RENDER'): + # Data modified. + # Create unique entry in data_ref by using object name + # (always unique in Blender) as data name. + data_ref[name] = [(name, matrix_as_pov_string(matrix))] + return name + # Here, we replace dataname by the value returned by check_object_materials, only if + # it is not evaluated to False (i.e. only if the object uses some local material(s)). + dataname = check_object_materials(ob, name, dataname) or dataname + if dataname in data_ref: + # Data already known, just add the object instance. + data_ref[dataname].append((name, matrix_as_pov_string(matrix))) + # No need to write data + return None + # Else (no return yet): Data not yet processed, create a new entry in data_ref. + data_ref[dataname] = [(name, matrix_as_pov_string(matrix))] + return dataname + + # XXX TODO : Too many nested blocks in this object loop, split hair (+particles?) to their function in own file, + ob_num = 0 + for ob in sel: + # Using depsgraph + depsgraph = bpy.context.evaluated_depsgraph_get() + ob = bpy.data.objects[ob.name].evaluated_get(depsgraph) + + # subtract original from the count of their instances as were not counted before 2.8 + if not (ob.is_instancer and ob.original != ob): + ob_num += 1 + + # XXX I moved all those checks here, as there is no need to compute names + # for object we won't export here! + if ob.type in { + 'LIGHT', + 'CAMERA', #'EMPTY', #empties can bear dupligroups + 'META', + 'ARMATURE', + 'LATTICE', + }: + continue + fluid_flag = False + for mod in ob.modifiers: + if mod and hasattr(mod, 'fluid_type'): + fluid_flag = True + if mod.fluid_type == 'DOMAIN': + if mod.domain_settings.domain_type == 'GAS': + export_smoke( + file, ob.name, smoke_path, comments, global_matrix, write_matrix + ) + break # don't render domain mesh, skip to next object. + if mod.fluid_type == 'FLOW': # The domain contains all the smoke. so that's it. + if mod.flow_settings.flow_type == 'SMOKE': # Check how liquids behave + break # don't render smoke flow emitter mesh either, skip to next object. + if not fluid_flag: + # Export Hair + # importing here rather than at the top recommended for addons startup footprint + from .object_particles import export_hair + + render_emitter = True + if hasattr(ob, 'particle_systems'): + render_emitter = False + if ob.show_instancer_for_render: + render_emitter = True + for p_sys in ob.particle_systems: + for mod in [ + m + for m in ob.modifiers + if (m is not None) and (m.type == 'PARTICLE_SYSTEM') + ]: + if ( + (p_sys.settings.render_type == 'PATH') + and mod.show_render + and (p_sys.name == mod.particle_system.name) + ): + export_hair(file, ob, p_sys, global_matrix, write_matrix) + if not render_emitter: + continue # don't render mesh, skip to next object. + + ############################################# + # Generating a name for object just like materials to be able to use it + # (baking for now or anything else). + # XXX I don't understand that if we are here, sel if a non-empty iterable, + # so this condition is always True, IMO -- mont29 + if ob.data: + name_orig = "OB" + ob.name + dataname_orig = "DATA" + ob.data.name + elif ob.is_instancer: + if ob.instance_type == 'COLLECTION': + name_orig = "OB" + ob.name + dataname_orig = "DATA" + ob.instance_collection.name + else: + # hoping only dupligroups have several source datablocks + # ob_dupli_list_create(scene) #deprecated in 2.8 + depsgraph = bpy.context.evaluated_depsgraph_get() + for eachduplicate in depsgraph.object_instances: + # Real dupli instance filtered because + # original included in list since 2.8 + if eachduplicate.is_instance: + dataname_orig = "DATA" + eachduplicate.object.name + # ob.dupli_list_clear() #just don't store any reference to instance since 2.8 + elif ob.type == 'EMPTY': + name_orig = "OB" + ob.name + dataname_orig = "DATA" + ob.name + else: + name_orig = DEF_OBJ_NAME + dataname_orig = DEF_OBJ_NAME + name = string_strip_hyphen(bpy.path.clean_name(name_orig)) + dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig)) + ## for slot in ob.material_slots: + ## if slot.material is not None and slot.link == 'OBJECT': + ## obmaterial = slot.material + + ############################################# + + if info_callback: + info_callback("Object %2.d of %2.d (%s)" % (ob_num, len(sel), ob.name)) + + # if ob.type != 'MESH': + # continue + # me = ob.data + + matrix = global_matrix @ ob.matrix_world + povdataname = store(scene, ob, name, dataname, matrix) + if povdataname is None: + print("This is an instance of " + name) + continue + + print("Writing Down First Occurrence of " + name) + + ############################################Povray Primitives + # special export_curves() function takes care of writing + # lathe, sphere_sweep, birail, and loft except with modifiers + # converted to mesh + if not ob.is_modified(scene, 'RENDER'): + if ob.type == 'CURVE' and ( + ob.pov.curveshape in {'lathe', 'sphere_sweep', 'loft'} + ): + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'ISOSURFACE': + tab_write("#declare %s = isosurface{ \n" % povdataname) + tab_write("function{ \n") + text_name = ob.pov.iso_function_text + if text_name: + node_tree = bpy.context.scene.node_tree + for node in node_tree.nodes: + if node.bl_idname == "IsoPropsNode" and node.label == ob.name: + for inp in node.inputs: + if inp: + tab_write( + "#declare %s = %.6g;\n" % (inp.name, inp.default_value) + ) + + text = bpy.data.texts[text_name] + for line in text.lines: + split = line.body.split() + if split[0] != "#declare": + tab_write("%s\n" % line.body) + else: + tab_write("abs(x) - 2 + y") + tab_write("}\n") + tab_write("threshold %.6g\n" % ob.pov.threshold) + tab_write("max_gradient %.6g\n" % ob.pov.max_gradient) + tab_write("accuracy %.6g\n" % ob.pov.accuracy) + tab_write("contained_by { ") + if ob.pov.contained_by == "sphere": + tab_write("sphere {0,%.6g}}\n" % ob.pov.container_scale) + else: + tab_write( + "box {-%.6g,%.6g}}\n" % (ob.pov.container_scale, ob.pov.container_scale) + ) + if ob.pov.all_intersections: + tab_write("all_intersections\n") + else: + if ob.pov.max_trace > 1: + tab_write("max_trace %.6g\n" % ob.pov.max_trace) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + tab_write("scale %.6g\n" % (1 / ob.pov.container_scale)) + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'SUPERELLIPSOID': + tab_write( + "#declare %s = superellipsoid{ <%.4f,%.4f>\n" + % (povdataname, ob.pov.se_n2, ob.pov.se_n1) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'SUPERTORUS': + rad_maj = ob.pov.st_major_radius + rad_min = ob.pov.st_minor_radius + ring = ob.pov.st_ring + cross = ob.pov.st_cross + accuracy = ob.pov.st_accuracy + gradient = ob.pov.st_max_gradient + ############Inline Supertorus macro + file.write( + "#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)\n" + ) + file.write(" #local CP = 2/MinorControl;\n") + file.write(" #local RP = 2/MajorControl;\n") + file.write(" isosurface {\n") + file.write( + " function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }\n" + ) + file.write(" threshold 0\n") + file.write( + " contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}\n" + ) + file.write(" #if(MaxGradient >= 1)\n") + file.write(" max_gradient MaxGradient\n") + file.write(" #else\n") + file.write(" evaluate 1, 10, 0.1\n") + file.write(" #end\n") + file.write(" accuracy Accuracy\n") + file.write(" }\n") + file.write("#end\n") + ############ + tab_write( + "#declare %s = object{ Supertorus( %.4g,%.4g,%.4g,%.4g,%.4g,%.4g)\n" + % (povdataname, rad_maj, rad_min, ring, cross, accuracy, gradient) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'PLANE': + tab_write("#declare %s = plane{ <0,0,1>,1\n" % povdataname) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'BOX': + tab_write("#declare %s = box { -1,1\n" % povdataname) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'CONE': + br = ob.pov.cone_base_radius + cr = ob.pov.cone_cap_radius + bz = ob.pov.cone_base_z + cz = ob.pov.cone_cap_z + tab_write( + "#declare %s = cone { <0,0,%.4f>,%.4f,<0,0,%.4f>,%.4f\n" + % (povdataname, bz, br, cz, cr) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'CYLINDER': + r = ob.pov.cylinder_radius + x2 = ob.pov.cylinder_location_cap[0] + y2 = ob.pov.cylinder_location_cap[1] + z2 = ob.pov.cylinder_location_cap[2] + tab_write( + "#declare %s = cylinder { <0,0,0>,<%6f,%6f,%6f>,%6f\n" + % (povdataname, x2, y2, z2, r) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + # cylinders written at origin, translated below + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'HEIGHT_FIELD': + data = "" + filename = ob.pov.hf_filename + data += '"%s"' % filename + gamma = ' gamma %.4f' % ob.pov.hf_gamma + data += gamma + if ob.pov.hf_premultiplied: + data += ' premultiplied on' + if ob.pov.hf_smooth: + data += ' smooth' + if ob.pov.hf_water > 0: + data += ' water_level %.4f' % ob.pov.hf_water + # hierarchy = ob.pov.hf_hierarchy + tab_write('#declare %s = height_field { %s\n' % (povdataname, data)) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + tab_write("rotate x*90\n") + tab_write("translate <-0.5,0.5,0>\n") + tab_write("scale <0,-1,0>\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'SPHERE': + + tab_write( + "#declare %s = sphere { 0,%6f\n" % (povdataname, ob.pov.sphere_radius) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'TORUS': + tab_write( + "#declare %s = torus { %.4f,%.4f\n" + % (povdataname, ob.pov.torus_major_radius, ob.pov.torus_minor_radius) + ) + pov_mat_name = "Default_texture" + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + try: + material = ob.active_material + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + tab_write("rotate x*90\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'PARAMETRIC': + tab_write("#declare %s = parametric {\n" % povdataname) + tab_write("function { %s }\n" % ob.pov.x_eq) + tab_write("function { %s }\n" % ob.pov.y_eq) + tab_write("function { %s }\n" % ob.pov.z_eq) + tab_write( + "<%.4f,%.4f>, <%.4f,%.4f>\n" + % (ob.pov.u_min, ob.pov.v_min, ob.pov.u_max, ob.pov.v_max) + ) + # Previous to 3.8 default max_gradient 1.0 was too slow + tab_write("max_gradient 0.001\n") + if ob.pov.contained_by == "sphere": + tab_write("contained_by { sphere{0, 2} }\n") + else: + tab_write("contained_by { box{-2, 2} }\n") + tab_write("max_gradient %.6f\n" % ob.pov.max_gradient) + tab_write("accuracy %.6f\n" % ob.pov.accuracy) + tab_write("precompute 10 x,y,z\n") + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + if ob.pov.object_as == 'POLYCIRCLE': + # TODO write below macro Once: + # if write_polytocircle_macro_once == 0: + file.write("/****************************\n") + file.write("This macro was written by 'And'.\n") + file.write("Link:(http://news.povray.org/povray.binaries.scene-files/)\n") + file.write("****************************/\n") + file.write("//from math.inc:\n") + file.write("#macro VPerp_Adjust(V, Axis)\n") + file.write(" vnormalize(vcross(vcross(Axis, V), Axis))\n") + file.write("#end\n") + file.write("//Then for the actual macro\n") + file.write("#macro Shape_Slice_Plane_2P_1V(point1, point2, clip_direct)\n") + file.write("#local p1 = point1 + <0,0,0>;\n") + file.write("#local p2 = point2 + <0,0,0>;\n") + file.write("#local clip_v = vnormalize(clip_direct + <0,0,0>);\n") + file.write("#local direct_v1 = vnormalize(p2 - p1);\n") + file.write("#if(vdot(direct_v1, clip_v) = 1)\n") + file.write(' #error "Shape_Slice_Plane_2P_1V error: Can\'t decide plane"\n') + file.write("#end\n\n") + file.write( + "#local norm = -vnormalize(clip_v - direct_v1*vdot(direct_v1,clip_v));\n" + ) + file.write("#local d = vdot(norm, p1);\n") + file.write("plane{\n") + file.write("norm, d\n") + file.write("}\n") + file.write("#end\n\n") + file.write("//polygon to circle\n") + file.write( + "#macro Shape_Polygon_To_Circle_Blending(_polygon_n, _side_face, _polygon_circumscribed_radius, _circle_radius, _height)\n" + ) + file.write("#local n = int(_polygon_n);\n") + file.write("#if(n < 3)\n") + file.write(" #error " "\n") + file.write("#end\n\n") + file.write("#local front_v = VPerp_Adjust(_side_face, z);\n") + file.write("#if(vdot(front_v, x) >= 0)\n") + file.write(" #local face_ang = acos(vdot(-y, front_v));\n") + file.write("#else\n") + file.write(" #local face_ang = -acos(vdot(-y, front_v));\n") + file.write("#end\n") + file.write("#local polyg_ext_ang = 2*pi/n;\n") + file.write("#local polyg_outer_r = _polygon_circumscribed_radius;\n") + file.write("#local polyg_inner_r = polyg_outer_r*cos(polyg_ext_ang/2);\n") + file.write("#local cycle_r = _circle_radius;\n") + file.write("#local h = _height;\n") + file.write("#if(polyg_outer_r < 0 | cycle_r < 0 | h <= 0)\n") + file.write(' #error "error: each side length must be positive"\n') + file.write("#end\n\n") + file.write("#local multi = 1000;\n") + file.write("#local poly_obj =\n") + file.write("polynomial{\n") + file.write("4,\n") + file.write("xyz(0,2,2): multi*1,\n") + file.write("xyz(2,0,1): multi*2*h,\n") + file.write("xyz(1,0,2): multi*2*(polyg_inner_r-cycle_r),\n") + file.write("xyz(2,0,0): multi*(-h*h),\n") + file.write("xyz(0,0,2): multi*(-pow(cycle_r - polyg_inner_r, 2)),\n") + file.write("xyz(1,0,1): multi*2*h*(-2*polyg_inner_r + cycle_r),\n") + file.write("xyz(1,0,0): multi*2*h*h*polyg_inner_r,\n") + file.write("xyz(0,0,1): multi*2*h*polyg_inner_r*(polyg_inner_r - cycle_r),\n") + file.write("xyz(0,0,0): multi*(-pow(polyg_inner_r*h, 2))\n") + file.write("sturm\n") + file.write("}\n\n") + file.write("#local mockup1 =\n") + file.write("difference{\n") + file.write(" cylinder{\n") + file.write(" <0,0,0.0>,<0,0,h>, max(polyg_outer_r, cycle_r)\n") + file.write(" }\n\n") + file.write(" #for(i, 0, n-1)\n") + file.write(" object{\n") + file.write(" poly_obj\n") + file.write(" inverse\n") + file.write(" rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n") + file.write(" }\n") + file.write(" object{\n") + file.write( + " Shape_Slice_Plane_2P_1V(<polyg_inner_r,0,0>,<cycle_r,0,h>,x)\n" + ) + file.write(" rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n") + file.write(" }\n") + file.write(" #end\n") + file.write("}\n\n") + file.write("object{\n") + file.write("mockup1\n") + file.write("rotate <0, 0, degrees(face_ang)>\n") + file.write("}\n") + file.write("#end\n") + # Use the macro + ngon = ob.pov.polytocircle_ngon + ngonR = ob.pov.polytocircle_ngonR + circleR = ob.pov.polytocircle_circleR + tab_write( + "#declare %s = object { Shape_Polygon_To_Circle_Blending(%s, z, %.4f, %.4f, 2) rotate x*180 translate z*1\n" + % (povdataname, ngon, ngonR, circleR) + ) + tab_write("}\n") + continue # Don't render proxy mesh, skip to next object + + ## In remaining cases; keep at end so no "elif" is needed, + # (as not skipped by any previous "continue") + # and for originals not their instances, + # attempt to export mesh: + if not ob.is_instancer: + # except duplis which should be instances groups for now but all duplis later + if ob.type == 'EMPTY': + # XXX Should we only write this once and instanciate the same for every + # empty in the final matrix writing, or even no marix and just a comment + # with empty object transforms ? + tab_write("\n//dummy sphere to represent Empty location\n") + tab_write( + "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" + % povdataname + ) + continue # Don't render empty object but this is later addition, watch it. + + depsgraph = bpy.context.evaluated_depsgraph_get() + ob_eval = ob.evaluated_get(depsgraph) + try: + me = ob_eval.to_mesh() + + # Here identify the exception for mesh object with no data: Runtime-Error ? + # So we can write something for the dataname or maybe treated "if not me" below + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + # also happens when curves cant be made into meshes because of no-data + continue + + importance = ob.pov.importance_value + if me: + me.calc_loop_triangles() + me_materials = me.materials + me_faces = me.loop_triangles[:] + ## numpytest + # me_looptris = me.loops + + ## otypes = ['int32'] is a 32-bit signed integer number numpy datatype + # get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True) + # faces_verts_idx = get_v_index(me_looptris) + + # if len(me_faces)==0: + # tab_write("\n//dummy sphere to represent empty mesh location\n") + # tab_write("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname) + + if not me or not me_faces: + tab_write("\n//dummy sphere to represent empty mesh location\n") + tab_write( + "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" + % povdataname + ) + continue + + uv_layers = me.uv_layers + if len(uv_layers) > 0: + if me.uv_layers.active and uv_layers.active.data: + uv_layer = uv_layers.active.data + else: + uv_layer = None + + try: + # vcol_layer = me.vertex_colors.active.data + vcol_layer = me.vertex_colors.active.data + except AttributeError: + vcol_layer = None + + faces_verts = [f.vertices[:] for f in me_faces] + faces_normals = [f.normal[:] for f in me_faces] + verts_normals = [v.normal[:] for v in me.vertices] + + # Use named declaration to allow reference e.g. for baking. MR + file.write("\n") + tab_write("#declare %s =\n" % povdataname) + tab_write("mesh2 {\n") + tab_write("vertex_vectors {\n") + tab_write("%d" % len(me.vertices)) # vert count + + tab_str = tab * tab_level + for v in me.vertices: + if linebreaksinlists: + file.write(",\n") + file.write(tab_str + "<%.6f, %.6f, %.6f>" % v.co[:]) # vert count + else: + file.write(", ") + file.write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count + # tab_write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count + file.write("\n") + tab_write("}\n") + + # Build unique Normal list + uniqueNormals = {} + for fi, f in enumerate(me_faces): + fv = faces_verts[fi] + # [-1] is a dummy index, use a list so we can modify in place + if f.use_smooth: # Use vertex normals + for v in fv: + key = verts_normals[v] + uniqueNormals[key] = [-1] + else: # Use face normal + key = faces_normals[fi] + uniqueNormals[key] = [-1] + + tab_write("normal_vectors {\n") + tab_write("%d" % len(uniqueNormals)) # vert count + idx = 0 + tab_str = tab * tab_level + for no, index in uniqueNormals.items(): + if linebreaksinlists: + file.write(",\n") + file.write(tab_str + "<%.6f, %.6f, %.6f>" % no) # vert count + else: + file.write(", ") + file.write("<%.6f, %.6f, %.6f>" % no) # vert count + index[0] = idx + idx += 1 + file.write("\n") + tab_write("}\n") + + # Vertex colors + vertCols = {} # Use for material colors also. + + if uv_layer: + # Generate unique UV's + uniqueUVs = {} + # n = 0 + for f in me_faces: # me.faces in 2.7 + uvs = [uv_layer[l].uv[:] for l in f.loops] + + for uv in uvs: + uniqueUVs[uv[:]] = [-1] + + tab_write("uv_vectors {\n") + # print unique_uvs + tab_write("%d" % len(uniqueUVs)) # vert count + idx = 0 + tab_str = tab * tab_level + for uv, index in uniqueUVs.items(): + if linebreaksinlists: + file.write(",\n") + file.write(tab_str + "<%.6f, %.6f>" % uv) + else: + file.write(", ") + file.write("<%.6f, %.6f>" % uv) + index[0] = idx + idx += 1 + ''' + else: + # Just add 1 dummy vector, no real UV's + tab_write('1') # vert count + file.write(',\n\t\t<0.0, 0.0>') + ''' + file.write("\n") + tab_write("}\n") + + if me.vertex_colors: + # Write down vertex colors as a texture for each vertex + tab_write("texture_list {\n") + tab_write("%d\n" % (len(me_faces) * 3)) # assumes we have only triangles + VcolIdx = 0 + if comments: + file.write( + "\n //Vertex colors: one simple pigment texture per vertex\n" + ) + for fi, f in enumerate(me_faces): + # annoying, index may be invalid + material_index = f.material_index + try: + material = me_materials[material_index] + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + material = None + if ( + material + ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now + + cols = [vcol_layer[l].color[:] for l in f.loops] + + for col in cols: + key = ( + col[0], + col[1], + col[2], + material_index, + ) # Material index! + VcolIdx += 1 + vertCols[key] = [VcolIdx] + if linebreaksinlists: + tab_write( + "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n" + % (col[0], col[1], col[2]) + ) + else: + tab_write( + "texture {pigment{ color srgb <%6f,%6f,%6f> }}" + % (col[0], col[1], col[2]) + ) + tab_str = tab * tab_level + else: + if material: + # Multiply diffuse with SSS Color + if material.pov_subsurface_scattering.use: + diffuse_color = [ + i * j + for i, j in zip( + material.pov_subsurface_scattering.color[:], + material.diffuse_color[:], + ) + ] + key = ( + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + material_index, + ) + vertCols[key] = [-1] + else: + diffuse_color = material.diffuse_color[:] + key = ( + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + material_index, + ) + vertCols[key] = [-1] + + tab_write("\n}\n") + # Face indices + tab_write("\nface_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + + for fi, f in enumerate(me_faces): + fv = faces_verts[fi] + material_index = f.material_index + + if vcol_layer: + cols = [vcol_layer[l].color[:] for l in f.loops] + + if ( + not me_materials or me_materials[material_index] is None + ): # No materials + if linebreaksinlists: + file.write(",\n") + # vert count + file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2])) + else: + file.write(", ") + file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count + else: + material = me_materials[material_index] + if me.vertex_colors: # and material.use_vertex_color_paint: + # Color per vertex - vertex color + + col1 = cols[0] + col2 = cols[1] + col3 = cols[2] + + ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0] + ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0] + ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0] + else: + # Color per material - flat material color + if material.pov_subsurface_scattering.use: + diffuse_color = [ + i * j + for i, j in zip( + material.pov_subsurface_scattering.color[:], + material.diffuse_color[:], + ) + ] + else: + diffuse_color = material.diffuse_color[:] + ci1 = ci2 = ci3 = vertCols[ + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + f.material_index, + ][0] + # ci are zero based index so we'll subtract 1 from them + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>, %d,%d,%d" + % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1) + ) # vert count + else: + file.write(", ") + file.write( + "<%d,%d,%d>, %d,%d,%d" + % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1) + ) # vert count + + file.write("\n") + tab_write("}\n") + + # normal_indices indices + tab_write("normal_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + for fi, fv in enumerate(faces_verts): + + if me_faces[fi].use_smooth: + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>" + % ( + uniqueNormals[verts_normals[fv[0]]][0], + uniqueNormals[verts_normals[fv[1]]][0], + uniqueNormals[verts_normals[fv[2]]][0], + ) + ) # vert count + else: + file.write(", ") + file.write( + "<%d,%d,%d>" + % ( + uniqueNormals[verts_normals[fv[0]]][0], + uniqueNormals[verts_normals[fv[1]]][0], + uniqueNormals[verts_normals[fv[2]]][0], + ) + ) # vert count + else: + idx = uniqueNormals[faces_normals[fi]][0] + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + "<%d,%d,%d>" % (idx, idx, idx) + ) # vert count + else: + file.write(", ") + file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count + + file.write("\n") + tab_write("}\n") + + if uv_layer: + tab_write("uv_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + for f in me_faces: + uvs = [uv_layer[l].uv[:] for l in f.loops] + + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>" + % ( + uniqueUVs[uvs[0]][0], + uniqueUVs[uvs[1]][0], + uniqueUVs[uvs[2]][0], + ) + ) + else: + file.write(", ") + file.write( + "<%d,%d,%d>" + % ( + uniqueUVs[uvs[0]][0], + uniqueUVs[uvs[1]][0], + uniqueUVs[uvs[2]][0], + ) + ) + + file.write("\n") + tab_write("}\n") + + # XXX BOOLEAN + onceCSG = 0 + for mod in ob.modifiers: + if onceCSG == 0: + if mod: + if mod.type == 'BOOLEAN': + if ob.pov.boolean_mod == "POV": + file.write( + "\tinside_vector <%.6g, %.6g, %.6g>\n" + % ( + ob.pov.inside_vector[0], + ob.pov.inside_vector[1], + ob.pov.inside_vector[2], + ) + ) + onceCSG = 1 + + if me.materials: + try: + material = me.materials[0] # dodgy + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + + # POV object modifiers such as + # hollow / sturm / double_illuminate etc. + write_object_modifiers(scene, ob, file) + + # Importance for radiosity sampling added here: + tab_write("radiosity { \n") + tab_write("importance %3g \n" % importance) + tab_write("}\n") + + tab_write("}\n") # End of mesh block + else: + facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!! + if me_materials: + for f in me_faces: + if f.material_index not in facesMaterials: + facesMaterials.append(f.material_index) + # No vertex colors, so write material colors as vertex colors + for i, material in enumerate(me_materials): + + if ( + material and material.pov.material_use_nodes == False + ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # Multiply diffuse with SSS Color + if material.pov_subsurface_scattering.use: + diffuse_color = [ + i * j + for i, j in zip( + material.pov_subsurface_scattering.color[:], + material.diffuse_color[:], + ) + ] + key = ( + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + i, + ) # i == f.mat + vertCols[key] = [-1] + else: + diffuse_color = material.diffuse_color[:] + key = ( + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + i, + ) # i == f.mat + vertCols[key] = [-1] + + idx = 0 + local_material_names = [] + for col, index in vertCols.items(): + # if me_materials: + mater = me_materials[col[3]] + if me_materials is None: # XXX working? + material_finish = DEF_MAT_NAME # not working properly, + trans = 0.0 + + else: + texturing.write_texture_influence( + using_uberpov, + mater, + material_names_dictionary, + local_material_names, + path_image, + exported_lights_count, + image_format, + img_map, + img_map_transforms, + tab_write, + comments, + string_strip_hyphen, + safety, + col, + preview_dir, + unpacked_images, + ) + ################################################################### + index[0] = idx + idx += 1 + + # Vert Colors + tab_write("texture_list {\n") + # In case there's is no material slot, give at least one texture + # (an empty one so it uses pov default) + if len(vertCols) == 0: + file.write(tab_str + "1") + else: + file.write(tab_str + "%s" % (len(vertCols))) # vert count + + # below "material" alias, added check ob.active_material + # to avoid variable referenced before assignment error + try: + material = ob.active_material + except IndexError: + # when no material slot exists, + material = None + + # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if ( + material + and ob.active_material is not None + and material.pov.material_use_nodes == False + ): + if material.pov.replacement_text != "": + file.write("\n") + file.write(" texture{%s}\n" % material.pov.replacement_text) + + else: + # Loop through declared materials list + for cMN in local_material_names: + if material != "Default": + file.write("\n texture{MAT_%s}\n" % cMN) + # use string_strip_hyphen(material_names_dictionary[material])) + # or Something like that to clean up the above? + elif material and material.pov.material_use_nodes: + for index in facesMaterials: + faceMaterial = string_strip_hyphen( + bpy.path.clean_name(me_materials[index].name) + ) + file.write("\n texture{%s}\n" % faceMaterial) + # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + else: + file.write(" texture{}\n") + tab_write("}\n") + + # Face indices + tab_write("face_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + + for fi, f in enumerate(me_faces): + fv = faces_verts[fi] + material_index = f.material_index + + if vcol_layer: + cols = [vcol_layer[l].color[:] for l in f.loops] + + if ( + not me_materials or me_materials[material_index] is None + ): # No materials + if linebreaksinlists: + file.write(",\n") + # vert count + file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2])) + else: + file.write(", ") + file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count + else: + material = me_materials[material_index] + ci1 = ci2 = ci3 = f.material_index + if me.vertex_colors: # and material.use_vertex_color_paint: + # Color per vertex - vertex color + + col1 = cols[0] + col2 = cols[1] + col3 = cols[2] + + ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0] + ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0] + ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0] + elif material.pov.material_use_nodes: + ci1 = ci2 = ci3 = 0 + else: + # Color per material - flat material color + if material.pov_subsurface_scattering.use: + diffuse_color = [ + i * j + for i, j in zip( + material.pov_subsurface_scattering.color[:], + material.diffuse_color[:], + ) + ] + else: + diffuse_color = material.diffuse_color[:] + ci1 = ci2 = ci3 = vertCols[ + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + f.material_index, + ][0] + + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>, %d,%d,%d" + % (fv[0], fv[1], fv[2], ci1, ci2, ci3) + ) # vert count + else: + file.write(", ") + file.write( + "<%d,%d,%d>, %d,%d,%d" + % (fv[0], fv[1], fv[2], ci1, ci2, ci3) + ) # vert count + + file.write("\n") + tab_write("}\n") + + # normal_indices indices + tab_write("normal_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + for fi, fv in enumerate(faces_verts): + if me_faces[fi].use_smooth: + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>" + % ( + uniqueNormals[verts_normals[fv[0]]][0], + uniqueNormals[verts_normals[fv[1]]][0], + uniqueNormals[verts_normals[fv[2]]][0], + ) + ) # vert count + else: + file.write(", ") + file.write( + "<%d,%d,%d>" + % ( + uniqueNormals[verts_normals[fv[0]]][0], + uniqueNormals[verts_normals[fv[1]]][0], + uniqueNormals[verts_normals[fv[2]]][0], + ) + ) # vert count + else: + idx = uniqueNormals[faces_normals[fi]][0] + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + "<%d,%d,%d>" % (idx, idx, idx) + ) # vertcount + else: + file.write(", ") + file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count + + file.write("\n") + tab_write("}\n") + + if uv_layer: + tab_write("uv_indices {\n") + tab_write("%d" % (len(me_faces))) # faces count + tab_str = tab * tab_level + for f in me_faces: + uvs = [uv_layer[l].uv[:] for l in f.loops] + + if linebreaksinlists: + file.write(",\n") + file.write( + tab_str + + "<%d,%d,%d>" + % ( + uniqueUVs[uvs[0]][0], + uniqueUVs[uvs[1]][0], + uniqueUVs[uvs[2]][0], + ) + ) + else: + file.write(", ") + file.write( + "<%d,%d,%d>" + % ( + uniqueUVs[uvs[0]][0], + uniqueUVs[uvs[1]][0], + uniqueUVs[uvs[2]][0], + ) + ) + + file.write("\n") + tab_write("}\n") + + # XXX BOOLEAN + onceCSG = 0 + for mod in ob.modifiers: + if onceCSG == 0: + if mod: + if mod.type == 'BOOLEAN': + if ob.pov.boolean_mod == "POV": + file.write( + "\tinside_vector <%.6g, %.6g, %.6g>\n" + % ( + ob.pov.inside_vector[0], + ob.pov.inside_vector[1], + ob.pov.inside_vector[2], + ) + ) + onceCSG = 1 + + if me.materials: + try: + material = me.materials[0] # dodgy + write_object_material(material, ob, tab_write) + except IndexError: + print(me) + + # POV object modifiers such as + # hollow / sturm / double_illuminate etc. + write_object_modifiers(scene, ob, file) + + # Importance for radiosity sampling added here: + tab_write("radiosity { \n") + tab_write("importance %3g \n" % importance) + tab_write("}\n") + + tab_write("}\n") # End of mesh block + + ob_eval.to_mesh_clear() + + if csg: + duplidata_ref = [] + _dupnames_seen = dict() # avoid duplicate output during introspection + for ob in sel: + # matrix = global_matrix @ ob.matrix_world + if ob.is_instancer: + tab_write("\n//--DupliObjects in %s--\n\n" % ob.name) + # ob.dupli_list_create(scene) #deprecated in 2.8 + depsgraph = bpy.context.evaluated_depsgraph_get() + dup = "" + if ob.is_modified(scene, 'RENDER'): + # modified object always unique so using object name rather than data name + dup = "#declare OB%s = union{\n" % ( + string_strip_hyphen(bpy.path.clean_name(ob.name)) + ) + else: + dup = "#declare DATA%s = union{\n" % ( + string_strip_hyphen(bpy.path.clean_name(ob.name)) + ) + for eachduplicate in depsgraph.object_instances: + if ( + eachduplicate.is_instance + ): # Real dupli instance filtered because original included in list since 2.8 + _dupname = eachduplicate.object.name + _dupobj = bpy.data.objects[_dupname] + # BEGIN introspection for troubleshooting purposes + if not "name" in dir(_dupobj.data): + if _dupname not in _dupnames_seen: + print( + "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute" + % (_dupname, type(_dupobj.data)) + ) + for _thing in dir(_dupobj): + print( + "|| %s.%s = %s" + % (_dupname, _thing, getattr(_dupobj, _thing)) + ) + _dupnames_seen[_dupname] = 1 + print("''=> Unparseable objects so far: %s" % (_dupnames_seen)) + else: + _dupnames_seen[_dupname] += 1 + continue # don't try to parse data objects with no name attribute + # END introspection for troubleshooting purposes + duplidataname = "OB" + string_strip_hyphen( + bpy.path.clean_name(_dupobj.data.name) + ) + dupmatrix = ( + eachduplicate.matrix_world.copy() + ) # has to be copied to not store instance since 2.8 + dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % ( + string_strip_hyphen(bpy.path.clean_name(_dupobj.data.name)), + matrix_as_pov_string(ob.matrix_world.inverted() @ dupmatrix), + ) + # add object to a list so that it is not rendered for some instance_types + if ( + ob.instance_type not in {'COLLECTION'} + and duplidataname not in duplidata_ref + ): + duplidata_ref.append( + duplidataname + ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))] + dup += "}\n" + # ob.dupli_list_clear()# just do not store any reference to instance since 2.8 + tab_write(dup) + else: + continue + print("WARNING: Unparseable objects in current .blend file:\n''=> %s" % (_dupnames_seen)) + print("duplidata_ref = %s" % (duplidata_ref)) + for data_name, inst in data_ref.items(): + for ob_name, matrix_str in inst: + if ob_name not in duplidata_ref: # .items() for a dictionary + tab_write("\n//----Blender Object Name:%s----\n" % ob_name) + if ob.pov.object_as == '': + tab_write("object { \n") + tab_write("%s\n" % data_name) + tab_write("%s\n" % matrix_str) + tab_write("}\n") + else: + no_boolean = True + for mod in ob.modifiers: + if mod.type == 'BOOLEAN': + operation = None + no_boolean = False + if mod.operation == 'INTERSECT': + operation = 'intersection' + else: + operation = mod.operation.lower() + mod_ob_name = string_strip_hyphen( + bpy.path.clean_name(mod.object.name) + ) + mod_matrix = global_matrix @ mod.object.matrix_world + mod_ob_matrix = matrix_as_pov_string(mod_matrix) + tab_write("%s { \n" % operation) + tab_write("object { \n") + tab_write("%s\n" % data_name) + tab_write("%s\n" % matrix_str) + tab_write("}\n") + tab_write("object { \n") + tab_write("%s\n" % ('DATA' + mod_ob_name)) + tab_write("%s\n" % mod_ob_matrix) + tab_write("}\n") + tab_write("}\n") + break + if no_boolean: + tab_write("object { \n") + tab_write("%s\n" % data_name) + tab_write("%s\n" % matrix_str) + tab_write("}\n") diff --git a/render_povray/object_particles.py b/render_povray/object_particles.py new file mode 100755 index 000000000..2d4f0dccc --- /dev/null +++ b/render_povray/object_particles.py @@ -0,0 +1,255 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Get some Blender particle objects translated to POV.""" + +import bpy + + +def export_hair(file, ob, p_sys, global_matrix, write_matrix): + """Get Blender path particles (hair strands) objects translated to POV sphere_sweep unions.""" + # tstart = time.time() + textured_hair = 0 + if ob.material_slots[p_sys.settings.material - 1].material and ob.active_material is not None: + pmaterial = ob.material_slots[p_sys.settings.material - 1].material + # XXX Todo: replace by pov_(Particles?)_texture_slot + for th in pmaterial.pov_texture_slots: + povtex = th.texture # slot.name + tex = bpy.data.textures[povtex] + + if th and th.use: + if (tex.type == 'IMAGE' and tex.image) or tex.type != 'IMAGE': + if th.use_map_color_diffuse: + textured_hair = 1 + if pmaterial.strand.use_blender_units: + strand_start = pmaterial.strand.root_size + strand_end = pmaterial.strand.tip_size + strand_shape = pmaterial.strand.shape + else: # Blender unit conversion + strand_start = pmaterial.strand.root_size / 200.0 + strand_end = pmaterial.strand.tip_size / 200.0 + strand_shape = pmaterial.strand.shape + else: + pmaterial = "default" # No material assigned in blender, use default one + strand_start = 0.01 + strand_end = 0.01 + strand_shape = 0.0 + # Set the number of particles to render count rather than 3d view display + # p_sys.set_resolution(scene, ob, 'RENDER') # DEPRECATED + # When you render, the entire dependency graph will be + # evaluated at render resolution, including the particles. + # In the viewport it will be at viewport resolution. + # So there is no need fo render engines to use this function anymore, + # it's automatic now. + steps = p_sys.settings.display_step + steps = 2 ** steps # or + 1 # Formerly : len(particle.hair_keys) + + total_number_of_strands = p_sys.settings.count + p_sys.settings.rendered_child_count + # hairCounter = 0 + file.write('#declare HairArray = array[%i] {\n' % total_number_of_strands) + for pindex in range(0, total_number_of_strands): + + # if particle.is_exist and particle.is_visible: + # hairCounter += 1 + # controlPointCounter = 0 + # Each hair is represented as a separate sphere_sweep in POV-Ray. + + file.write('sphere_sweep{') + if p_sys.settings.use_hair_bspline: + file.write('b_spline ') + file.write( + '%i,\n' % (steps + 2) + ) # +2 because the first point needs tripling to be more than a handle in POV + else: + file.write('linear_spline ') + file.write('%i,\n' % (steps)) + # changing world coordinates to object local coordinates by + # multiplying with inverted matrix + init_coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=0)) + if ( + ob.material_slots[p_sys.settings.material - 1].material + and ob.active_material is not None + ): + pmaterial = ob.material_slots[p_sys.settings.material - 1].material + for th in pmaterial.pov_texture_slots: + if th and th.use and th.use_map_color_diffuse: + povtex = th.texture # slot.name + tex = bpy.data.textures[povtex] + # treat POV textures as bitmaps + if ( + tex.type == 'IMAGE' + and tex.image + and th.texture_coords == 'UV' + and ob.data.uv_textures is not None + ): + # or ( + # tex.pov.tex_pattern_type != 'emulator' + # and th.texture_coords == 'UV' + # and ob.data.uv_textures is not None + # ): + image = tex.image + image_width = image.size[0] + image_height = image.size[1] + image_pixels = image.pixels[:] + uv_co = p_sys.uv_on_emitter(mod, p_sys.particles[pindex], pindex, 0) + x_co = round(uv_co[0] * (image_width - 1)) + y_co = round(uv_co[1] * (image_height - 1)) + pixelnumber = (image_width * y_co) + x_co + r = image_pixels[pixelnumber * 4] + g = image_pixels[pixelnumber * 4 + 1] + b = image_pixels[pixelnumber * 4 + 2] + a = image_pixels[pixelnumber * 4 + 3] + init_color = (r, g, b, a) + else: + # only overwrite variable for each competing texture for now + init_color = tex.evaluate((init_coord[0], init_coord[1], init_coord[2])) + for step in range(0, steps): + coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=step)) + # for controlPoint in particle.hair_keys: + if p_sys.settings.clump_factor != 0: + hair_strand_diameter = p_sys.settings.clump_factor / 200.0 * random.uniform(0.5, 1) + elif step == 0: + hair_strand_diameter = strand_start + else: + hair_strand_diameter += (strand_end - strand_start) / ( + p_sys.settings.display_step + 1 + ) # XXX +1 or not? # XXX use strand_shape in formula + if step == 0 and p_sys.settings.use_hair_bspline: + # Write three times the first point to compensate pov Bezier handling + file.write( + '<%.6g,%.6g,%.6g>,%.7g,\n' + % (coord[0], coord[1], coord[2], abs(hair_strand_diameter)) + ) + file.write( + '<%.6g,%.6g,%.6g>,%.7g,\n' + % (coord[0], coord[1], coord[2], abs(hair_strand_diameter)) + ) + # Useless because particle location is the tip, not the root: + # file.write( + # '<%.6g,%.6g,%.6g>,%.7g' + # % ( + # particle.location[0], + # particle.location[1], + # particle.location[2], + # abs(hair_strand_diameter) + # ) + # ) + # file.write(',\n') + # controlPointCounter += 1 + # total_number_of_strands += len(p_sys.particles)# len(particle.hair_keys) + + # Each control point is written out, along with the radius of the + # hair at that point. + file.write( + '<%.6g,%.6g,%.6g>,%.7g' % (coord[0], coord[1], coord[2], abs(hair_strand_diameter)) + ) + + # All coordinates except the last need a following comma. + + if step != steps - 1: + file.write(',\n') + else: + if textured_hair: + # Write pigment and alpha (between Pov and Blender, + # alpha 0 and 1 are reversed) + file.write( + '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n' + % (init_color[0], init_color[1], init_color[2], 1.0 - init_color[3]) + ) + # End the sphere_sweep declaration for this hair + file.write('}\n') + + # All but the final sphere_sweep (each array element) needs a terminating comma. + if pindex != total_number_of_strands: + file.write(',\n') + else: + file.write('\n') + + # End the array declaration. + + file.write('}\n') + file.write('\n') + + if not textured_hair: + # Pick up the hair material diffuse color and create a default POV-Ray hair texture. + + file.write('#ifndef (HairTexture)\n') + file.write(' #declare HairTexture = texture {\n') + file.write( + ' pigment {srgbt <%s,%s,%s,%s>}\n' + % ( + pmaterial.diffuse_color[0], + pmaterial.diffuse_color[1], + pmaterial.diffuse_color[2], + (pmaterial.strand.width_fade + 0.05), + ) + ) + file.write(' }\n') + file.write('#end\n') + file.write('\n') + + # Dynamically create a union of the hairstrands (or a subset of them). + # By default use every hairstrand, commented line is for hand tweaking test renders. + file.write('//Increasing HairStep divides the amount of hair for test renders.\n') + file.write('#ifndef(HairStep) #declare HairStep = 1; #end\n') + file.write('union{\n') + file.write(' #local I = 0;\n') + file.write(' #while (I < %i)\n' % total_number_of_strands) + file.write(' object {HairArray[I]') + if not textured_hair: + file.write(' texture{HairTexture}\n') + else: + file.write('\n') + # Translucency of the hair: + file.write(' hollow\n') + file.write(' double_illuminate\n') + file.write(' interior {\n') + file.write(' ior 1.45\n') + file.write(' media {\n') + file.write(' scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n') + file.write(' absorption 10/<0.83, 0.75, 0.15>\n') + file.write(' samples 1\n') + file.write(' method 2\n') + file.write(' density {cylindrical\n') + file.write(' color_map {\n') + file.write(' [0.0 rgb <0.83, 0.45, 0.35>]\n') + file.write(' [0.5 rgb <0.8, 0.8, 0.4>]\n') + file.write(' [1.0 rgb <1,1,1>]\n') + file.write(' }\n') + file.write(' }\n') + file.write(' }\n') + file.write(' }\n') + file.write(' }\n') + + file.write(' #local I = I + HairStep;\n') + file.write(' #end\n') + + write_matrix(global_matrix @ ob.matrix_world) + + file.write('}') + print('Totals hairstrands written: %i' % total_number_of_strands) + print('Number of tufts (particle systems)', len(ob.particle_systems)) + + # Set back the displayed number of particles to preview count + # p_sys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED + # When you render, the entire dependency graph will be + # evaluated at render resolution, including the particles. + # In the viewport it will be at viewport resolution. + # So there is no need fo render engines to use this function anymore, + # it's automatic now. diff --git a/render_povray/primitives.py b/render_povray/object_primitives.py old mode 100644 new mode 100755 similarity index 58% rename from render_povray/primitives.py rename to render_povray/object_primitives.py index 6d8642203..4556f2df5 --- a/render_povray/primitives.py +++ b/render_povray/object_primitives.py @@ -19,13 +19,12 @@ # <pep8 compliant> """ Get POV-Ray specific objects In and Out of Blender """ - -import bpy +from math import pi, cos, sin import os.path +import bpy +from bpy_extras.object_utils import object_data_add from bpy_extras.io_utils import ImportHelper -from bpy_extras import object_utils -from bpy.utils import register_class -from math import atan, pi, degrees, sqrt, cos, sin +from bpy.utils import register_class, unregister_class from bpy.types import Operator from bpy.props import ( @@ -35,8 +34,6 @@ from bpy.props import ( FloatProperty, FloatVectorProperty, EnumProperty, - PointerProperty, - CollectionProperty, ) from mathutils import Vector, Matrix @@ -45,6 +42,69 @@ from mathutils import Vector, Matrix # import collections +def write_object_modifiers(scene, ob, File): + """Translate some object level POV statements from Blender UI + to POV syntax and write to exported file """ + + # Maybe return that string to be added instead of directly written. + + '''XXX WIP + onceCSG = 0 + for mod in ob.modifiers: + if onceCSG == 0: + if mod : + if mod.type == 'BOOLEAN': + if ob.pov.boolean_mod == "POV": + File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" % + (ob.pov.inside_vector[0], + ob.pov.inside_vector[1], + ob.pov.inside_vector[2])) + onceCSG = 1 + ''' + + if ob.pov.hollow: + File.write("\thollow\n") + if ob.pov.double_illuminate: + File.write("\tdouble_illuminate\n") + if ob.pov.sturm: + File.write("\tsturm\n") + if ob.pov.no_shadow: + File.write("\tno_shadow\n") + if ob.pov.no_image: + File.write("\tno_image\n") + if ob.pov.no_reflection: + File.write("\tno_reflection\n") + if ob.pov.no_radiosity: + File.write("\tno_radiosity\n") + if ob.pov.inverse: + File.write("\tinverse\n") + if ob.pov.hierarchy: + File.write("\thierarchy\n") + + # XXX, Commented definitions + ''' + if scene.pov.photon_enable: + File.write("photons {\n") + if ob.pov.target: + File.write("target %.4g\n"%ob.pov.target_value) + if ob.pov.refraction: + File.write("refraction on\n") + if ob.pov.reflection: + File.write("reflection on\n") + if ob.pov.pass_through: + File.write("pass_through\n") + File.write("}\n") + if ob.pov.object_ior > 1: + File.write("interior {\n") + File.write("ior %.4g\n"%ob.pov.object_ior) + if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion: + File.write("ior %.4g\n"%ob.pov.dispersion_value) + File.write("ior %s\n"%ob.pov.dispersion_samples) + if scene.pov.photon_enable == False: + File.write("caustics %.4g\n"%ob.pov.fake_caustics_power) + ''' + + def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True): """Generate proxy mesh.""" if mesh is None: @@ -83,7 +143,9 @@ class POVRAY_OT_lathe_add(Operator): ob_data.dimensions = '2D' ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, 'Z')) ob.pov.object_as = 'LATHE' - self.report({'INFO'}, "This native POV-Ray primitive") + self.report( + {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode" + ) ob.pov.curveshape = "lathe" bpy.ops.object.modifier_add(type='SCREW') mod = ob.modifiers[-1] @@ -194,7 +256,7 @@ def pov_superellipsoid_define(context, op, ob): mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid") if not ob: - ob = object_utils.object_data_add(context, mesh, operator=None) + ob = object_data_add(context, mesh, operator=None) # engine = context.scene.render.engine what for? ob = context.object ob.name = ob.data.name = "PovSuperellipsoid" @@ -222,29 +284,18 @@ class POVRAY_OT_superellipsoid_add(Operator): bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} - # XXX Keep it in sync with __init__'s RenderPovSettingsConePrimitive + # Keep in sync within object_properties.py section Superellipsoid + # as this allows interactive update # If someone knows how to define operators' props from a func, I'd be delighted to learn it! - se_param1: FloatProperty( - name="Parameter 1", description="", min=0.00, max=10.0, default=0.04 - ) + se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04) - se_param2: FloatProperty( - name="Parameter 2", description="", min=0.00, max=10.0, default=0.04 - ) + se_param2: FloatProperty(name="Parameter 2", description="", min=0.00, max=10.0, default=0.04) se_u: IntProperty( - name="U-segments", - description="radial segmentation", - default=20, - min=4, - max=265, + name="U-segments", description="radial segmentation", default=20, min=4, max=265 ) se_v: IntProperty( - name="V-segments", - description="lateral segmentation", - default=20, - min=4, - max=265, + name="V-segments", description="lateral segmentation", default=20, min=4, max=265 ) se_n1: FloatProperty( name="Ring manipulator", @@ -261,11 +312,7 @@ class POVRAY_OT_superellipsoid_add(Operator): max=100.0, ) se_edit: EnumProperty( - items=[ - ("NOTHING", "Nothing", ""), - ("NGONS", "N-Gons", ""), - ("TRIANGLES", "Triangles", ""), - ], + items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")], name="Fill up and down", description="", default='TRIANGLES', @@ -280,8 +327,7 @@ class POVRAY_OT_superellipsoid_add(Operator): pov_superellipsoid_define(context, self, None) self.report( - {'INFO'}, - "This native POV-Ray primitive won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode" ) return {'FINISHED'} @@ -303,12 +349,7 @@ class POVRAY_OT_superellipsoid_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): bpy.ops.object.mode_set(mode="EDIT") @@ -322,60 +363,52 @@ class POVRAY_OT_superellipsoid_update(Operator): return {'FINISHED'} -def createFaces(vertIdx1, vertIdx2, closed=False, flipped=False): +def create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False): + """Generate viewport proxy mesh data for some pov primitives""" faces = [] - if not vertIdx1 or not vertIdx2: + if not vert_idx_1 or not vert_idx_2: return None - if len(vertIdx1) < 2 and len(vertIdx2) < 2: + if len(vert_idx_1) < 2 and len(vert_idx_2) < 2: return None fan = False - if len(vertIdx1) != len(vertIdx2): - if len(vertIdx1) == 1 and len(vertIdx2) > 1: + if len(vert_idx_1) != len(vert_idx_2): + if len(vert_idx_1) == 1 and len(vert_idx_2) > 1: fan = True else: return None - total = len(vertIdx2) + total = len(vert_idx_2) if closed: if flipped: - face = [vertIdx1[0], vertIdx2[0], vertIdx2[total - 1]] + face = [vert_idx_1[0], vert_idx_2[0], vert_idx_2[total - 1]] if not fan: - face.append(vertIdx1[total - 1]) + face.append(vert_idx_1[total - 1]) faces.append(face) else: - face = [vertIdx2[0], vertIdx1[0]] + face = [vert_idx_2[0], vert_idx_1[0]] if not fan: - face.append(vertIdx1[total - 1]) - face.append(vertIdx2[total - 1]) + face.append(vert_idx_1[total - 1]) + face.append(vert_idx_2[total - 1]) faces.append(face) for num in range(total - 1): if flipped: if fan: - face = [vertIdx2[num], vertIdx1[0], vertIdx2[num + 1]] + face = [vert_idx_2[num], vert_idx_1[0], vert_idx_2[num + 1]] else: - face = [ - vertIdx2[num], - vertIdx1[num], - vertIdx1[num + 1], - vertIdx2[num + 1], - ] + face = [vert_idx_2[num], vert_idx_1[num], vert_idx_1[num + 1], vert_idx_2[num + 1]] faces.append(face) else: if fan: - face = [vertIdx1[0], vertIdx2[num], vertIdx2[num + 1]] + face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]] else: - face = [ - vertIdx1[num], - vertIdx2[num], - vertIdx2[num + 1], - vertIdx1[num + 1], - ] + face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]] faces.append(face) return faces def power(a, b): + """Workaround to negative a, where the math.pow() method would return a ValueError.""" if a < 0: return -((-a) ** b) return a ** b @@ -392,22 +425,17 @@ def supertoroid(R, r, u, v, n1, n2): for j in range(v): c2 = R + r * power(cos(j * b), n2) s2 = r * power(sin(j * b), n2) - verts.append( - (c * c2, s * c2, s2) - ) # type as a (mathutils.Vector(c*c2,s*c2,s2))? + verts.append((c * c2, s * c2, s2)) # type as a (mathutils.Vector(c*c2,s*c2,s2))? if i > 0: - f = createFaces( - range((i - 1) * v, i * v), - range(i * v, (i + 1) * v), - closed=True, - ) + f = create_faces(range((i - 1) * v, i * v), range(i * v, (i + 1) * v), closed=True) faces.extend(f) - f = createFaces(range((u - 1) * v, u * v), range(v), closed=True) + f = create_faces(range((u - 1) * v, u * v), range(v), closed=True) faces.extend(f) return verts, faces def pov_supertorus_define(context, op, ob): + """Pick POV supertorus properties either from operator (object creation/import) or data updating """ if op: mesh = None st_R = op.st_R @@ -444,7 +472,7 @@ def pov_supertorus_define(context, op, ob): verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2) mesh = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True) if not ob: - ob = object_utils.object_data_add(context, mesh, operator=None) + ob = object_data_add(context, mesh, operator=None) ob.pov.object_as = 'SUPERTORUS' ob.pov.st_major_radius = st_R ob.pov.st_minor_radius = st_r @@ -473,25 +501,13 @@ class POVRAY_OT_supertorus_add(Operator): max=100.0, ) st_r: FloatProperty( - name="small radius", - description="The radius of the tube", - default=0.3, - min=0.01, - max=100.0, + name="small radius", description="The radius of the tube", default=0.3, min=0.01, max=100.0 ) st_u: IntProperty( - name="U-segments", - description="radial segmentation", - default=16, - min=3, - max=265, + name="U-segments", description="radial segmentation", default=16, min=3, max=265 ) st_v: IntProperty( - name="V-segments", - description="lateral segmentation", - default=8, - min=3, - max=265, + name="V-segments", description="lateral segmentation", default=8, min=3, max=265 ) st_n1: FloatProperty( name="Ring manipulator", @@ -508,13 +524,9 @@ class POVRAY_OT_supertorus_add(Operator): max=100.0, ) st_ie: BoolProperty( - name="Use Int.+Ext. radii", - description="Use internal and external radii", - default=False, - ) - st_edit: BoolProperty( - name="", description="", default=False, options={'HIDDEN'} + name="Use Int.+Ext. radii", description="Use internal and external radii", default=False ) + st_edit: BoolProperty(name="", description="", default=False, options={'HIDDEN'}) @classmethod def poll(cls, context): @@ -525,8 +537,7 @@ class POVRAY_OT_supertorus_add(Operator): pov_supertorus_define(context, self, None) self.report( - {'INFO'}, - "This native POV-Ray primitive won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode" ) return {'FINISHED'} @@ -547,12 +558,7 @@ class POVRAY_OT_supertorus_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): bpy.ops.object.mode_set(mode="EDIT") @@ -577,18 +583,12 @@ class POVRAY_OT_loft_add(Operator): COMPAT_ENGINES = {'POVRAY_RENDER'} loft_n: IntProperty( - name="Segments", - description="Vertical segments", - default=16, - min=3, - max=720, + name="Segments", description="Vertical segments", default=16, min=3, max=720 ) loft_rings_bottom: IntProperty( name="Bottom", description="Bottom rings", default=5, min=2, max=100 ) - loft_rings_side: IntProperty( - name="Side", description="Side rings", default=10, min=2, max=100 - ) + loft_rings_side: IntProperty(name="Side", description="Side rings", default=10, min=2, max=100) loft_thick: FloatProperty( name="Thickness", description="Manipulates the shape of the Ring", @@ -596,9 +596,7 @@ class POVRAY_OT_loft_add(Operator): min=0.01, max=1.0, ) - loft_r: FloatProperty( - name="Radius", description="Radius", default=1, min=0.01, max=10 - ) + loft_r: FloatProperty(name="Radius", description="Radius", default=1, min=0.01, max=10) loft_height: FloatProperty( name="Height", description="Manipulates the shape of the Ring", @@ -610,10 +608,10 @@ class POVRAY_OT_loft_add(Operator): def execute(self, context): props = self.properties - loftData = bpy.data.curves.new('Loft', type='CURVE') - loftData.dimensions = '3D' - loftData.resolution_u = 2 - # loftData.show_normal_face = False # deprecated in 2.8 + loft_data = bpy.data.curves.new('Loft', type='CURVE') + loft_data.dimensions = '3D' + loft_data.resolution_u = 2 + # loft_data.show_normal_face = False # deprecated in 2.8 n = props.loft_n thick = props.loft_thick side = props.loft_rings_side @@ -633,11 +631,11 @@ class POVRAY_OT_loft_add(Operator): coords.append((x, y, z)) angle += pi * 2 / n r0 += distB - nurbs = loftData.splines.new('NURBS') + nurbs = loft_data.splines.new('NURBS') nurbs.points.add(len(coords) - 1) - for i, coord in enumerate(coords): + for c, coord in enumerate(coords): x, y, z = coord - nurbs.points[i].co = (x, y, z, 1) + nurbs.points[c].co = (x, y, z, 1) nurbs.use_cyclic_u = True for i in range(side): z += h / side @@ -648,11 +646,11 @@ class POVRAY_OT_loft_add(Operator): y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n - nurbs = loftData.splines.new('NURBS') + nurbs = loft_data.splines.new('NURBS') nurbs.points.add(len(coords) - 1) - for i, coord in enumerate(coords): + for c, coord in enumerate(coords): x, y, z = coord - nurbs.points[i].co = (x, y, z, 1) + nurbs.points[c].co = (x, y, z, 1) nurbs.use_cyclic_u = True r -= thick for i in range(side): @@ -663,11 +661,11 @@ class POVRAY_OT_loft_add(Operator): y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n - nurbs = loftData.splines.new('NURBS') + nurbs = loft_data.splines.new('NURBS') nurbs.points.add(len(coords) - 1) - for i, coord in enumerate(coords): + for c, coord in enumerate(coords): x, y, z = coord - nurbs.points[i].co = (x, y, z, 1) + nurbs.points[c].co = (x, y, z, 1) nurbs.use_cyclic_u = True z -= h / side z = (-h / 2) + thick @@ -681,13 +679,13 @@ class POVRAY_OT_loft_add(Operator): coords.append((x, y, z)) angle += pi * 2 / n r -= distB - nurbs = loftData.splines.new('NURBS') + nurbs = loft_data.splines.new('NURBS') nurbs.points.add(len(coords) - 1) - for i, coord in enumerate(coords): + for c, coord in enumerate(coords): x, y, z = coord - nurbs.points[i].co = (x, y, z, 1) + nurbs.points[c].co = (x, y, z, 1) nurbs.use_cyclic_u = True - ob = bpy.data.objects.new('Loft_shape', loftData) + ob = bpy.data.objects.new('Loft_shape', loft_data) scn = bpy.context.scene scn.collection.objects.link(ob) context.view_layer.objects.active = ob @@ -715,9 +713,7 @@ class POVRAY_OT_plane_add(Operator): ob.name = ob.data.name = 'PovInfinitePlane' bpy.ops.object.mode_set(mode="EDIT") self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") @@ -745,9 +741,7 @@ class POVRAY_OT_box_add(Operator): ob.name = ob.data.name = 'PovBox' bpy.ops.object.mode_set(mode="EDIT") self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") @@ -756,6 +750,7 @@ class POVRAY_OT_box_add(Operator): def pov_cylinder_define(context, op, ob, radius, loc, loc_cap): + """Pick POV cylinder properties either from creation operator, import, or data update """ if op: R = op.R loc = bpy.context.scene.cursor.location @@ -784,11 +779,7 @@ def pov_cylinder_define(context, op, ob, radius, loc, loc_cap): bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.delete(type='VERT') bpy.ops.mesh.primitive_cylinder_add( - radius=radius, - depth=depth, - location=loc, - rotation=roteuler, - end_fill_type='NGON', + radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type='NGON' ) #'NOTHING' bpy.ops.transform.translate(value=trans) @@ -807,7 +798,8 @@ class POVRAY_OT_cylinder_add(Operator): bl_description = "Add Cylinder" bl_options = {'REGISTER', 'UNDO'} - # XXX Keep it in sync with __init__'s cylinder Primitive + # Keep in sync within object_properties.py section Cylinder + # as this allows interactive update R: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0) imported_cyl_loc: FloatVectorProperty( @@ -838,8 +830,7 @@ class POVRAY_OT_cylinder_add(Operator): LOC_CAP = props.imported_cyl_loc_cap self.report( {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + "This native POV-Ray primitive " "won't have any vertex to show in edit mode", ) pov_cylinder_define(context, self, None, self.R, LOC, LOC_CAP) @@ -906,10 +897,7 @@ def pov_sphere_define(context, op, ob, loc): bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.delete(type='VERT') bpy.ops.mesh.primitive_ico_sphere_add( - subdivisions=4, - radius=ob.pov.sphere_radius, - location=loc, - rotation=obrot, + subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot ) # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') bpy.ops.transform.resize(value=obscale) @@ -921,9 +909,7 @@ def pov_sphere_define(context, op, ob, loc): # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') if not ob: - bpy.ops.mesh.primitive_ico_sphere_add( - subdivisions=4, radius=R, location=loc - ) + bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=R, location=loc) ob = context.object ob.name = ob.data.name = "PovSphere" ob.pov.object_as = "SPHERE" @@ -943,7 +929,8 @@ class POVRAY_OT_sphere_add(Operator): bl_description = "Add Sphere Shape" bl_options = {'REGISTER', 'UNDO'} - # XXX Keep it in sync with __init__'s torus Primitive + # Keep in sync within object_properties.py section Sphere + # as this allows interactive update R: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5) imported_loc: FloatVectorProperty( @@ -966,8 +953,7 @@ class POVRAY_OT_sphere_add(Operator): LOC = props.imported_loc self.report( {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + "This native POV-Ray primitive " "won't have any vertex to show in edit mode", ) pov_sphere_define(context, self, None, LOC) @@ -1006,18 +992,11 @@ class POVRAY_OT_sphere_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): - pov_sphere_define( - context, None, context.object, context.object.location - ) + pov_sphere_define(context, None, context.object, context.object.location) return {'FINISHED'} @@ -1076,7 +1055,7 @@ def pov_cone_define(context, op, ob): mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True) if not ob: - ob = object_utils.object_data_add(context, mesh, operator=None) + ob = object_data_add(context, mesh, operator=None) ob.pov.object_as = "CONE" ob.pov.cone_base_radius = base ob.pov.cone_cap_radius = cap @@ -1094,7 +1073,7 @@ class POVRAY_OT_cone_add(Operator): bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} - # XXX Keep it in sync with __init__.py's RenderPovSettingsConePrimitive + # Keep in sync within object_properties.py section Cone # If someone knows how to define operators' props from a func, I'd be delighted to learn it! base: FloatProperty( name="Base radius", @@ -1118,11 +1097,7 @@ class POVRAY_OT_cone_add(Operator): max=265, ) height: FloatProperty( - name="Height", - description="Height of the cone", - default=2.0, - min=0.01, - max=100.0, + name="Height", description="Height of the cone", default=2.0, min=0.01, max=100.0 ) @classmethod @@ -1134,8 +1109,7 @@ class POVRAY_OT_cone_add(Operator): pov_cone_define(context, self, None) self.report( - {'INFO'}, - "This native POV-Ray primitive won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode" ) return {'FINISHED'} @@ -1156,12 +1130,7 @@ class POVRAY_OT_cone_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): bpy.ops.object.mode_set(mode="EDIT") @@ -1196,9 +1165,7 @@ class POVRAY_OT_isosurface_box_add(Operator): ob = context.object bpy.ops.object.mode_set(mode="EDIT") self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") @@ -1226,9 +1193,7 @@ class POVRAY_OT_isosurface_sphere_add(Operator): ob = context.object bpy.ops.object.mode_set(mode="EDIT") self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") @@ -1338,39 +1303,29 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): bl_description = "Add Height Field" bl_options = {'REGISTER', 'UNDO'} - # XXX Keep it in sync with __init__'s hf Primitive + # Keep in sync within object_properties.py section HeightFields + # as this allows interactive update + # filename_ext = ".png" # filter_glob = StringProperty( # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF", # options={'HIDDEN'}, # ) - quality: IntProperty( - name="Quality", description="", default=100, min=1, max=100 - ) + quality: IntProperty(name="Quality", description="", default=100, min=1, max=100) hf_filename: StringProperty(maxlen=1024) - hf_gamma: FloatProperty( - name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0 - ) + hf_gamma: FloatProperty(name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0) - hf_premultiplied: BoolProperty( - name="Premultiplied", description="Premultiplied", default=True - ) + hf_premultiplied: BoolProperty(name="Premultiplied", description="Premultiplied", default=True) hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False) hf_water: FloatProperty( - name="Water Level", - description="Wather Level", - min=0.00, - max=1.00, - default=0.0, + name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0 ) - hf_hierarchy: BoolProperty( - name="Hierarchy", description="Height field hierarchy", default=True - ) + hf_hierarchy: BoolProperty(name="Hierarchy", description="Height field hierarchy", default=True) def execute(self, context): props = self.properties @@ -1390,9 +1345,7 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): w, h = hf_tex.image.size[:] w = int(w / res) h = int(h / res) - bpy.ops.mesh.primitive_grid_add( - x_subdivisions=w, y_subdivisions=h, size=0.5 - ) + bpy.ops.mesh.primitive_grid_add(x_subdivisions=w, y_subdivisions=h, size=0.5) ob = context.object ob.name = ob.data.name = '%s' % im_name ob.data.materials.append(mat) @@ -1409,7 +1362,9 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") ob.pov.object_as = 'HEIGHT_FIELD' - ob.pov.hf_filename = impath + # POV-Ray will soon use only forwards slashes on every OS and already can + forward_impath = impath.replace(os.sep, '/') + ob.pov.hf_filename = forward_impath return {'FINISHED'} @@ -1417,6 +1372,7 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): def pov_torus_define(context, op, ob): """Add the representation of POV torus using just a Blender torus. + Picking properties either from creation operator, import, or data update. But flag its primitive type with a specific pov.object_as attribute and lock edit mode to keep proxy consistency by hiding edit geometry.""" @@ -1454,10 +1410,7 @@ def pov_torus_define(context, op, ob): if not ob: bpy.ops.mesh.primitive_torus_add( - major_segments=mas, - minor_segments=mis, - major_radius=mar, - minor_radius=mir, + major_segments=mas, minor_segments=mis, major_radius=mar, minor_radius=mir ) ob = context.object ob.name = ob.data.name = "PovTorus" @@ -1479,13 +1432,10 @@ class POVRAY_OT_torus_add(Operator): bl_description = "Add Torus" bl_options = {'REGISTER', 'UNDO'} - # XXX Keep it in sync with __init__'s torus Primitive - mas: IntProperty( - name="Major Segments", description="", default=48, min=3, max=720 - ) - mis: IntProperty( - name="Minor Segments", description="", default=12, min=3, max=720 - ) + # Keep in sync within object_properties.py section Torus + # as this allows interactive update + mas: IntProperty(name="Major Segments", description="", default=48, min=3, max=720) + mis: IntProperty(name="Minor Segments", description="", default=12, min=3, max=720) mar: FloatProperty(name="Major Radius", description="", default=1.0) mir: FloatProperty(name="Minor Radius", description="", default=0.25) @@ -1497,9 +1447,7 @@ class POVRAY_OT_torus_add(Operator): mis = props.mis pov_torus_define(context, self, None) self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) return {'FINISHED'} @@ -1520,12 +1468,7 @@ class POVRAY_OT_torus_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): @@ -1545,19 +1488,17 @@ class POVRAY_OT_prism_add(Operator): bl_description = "Create Prism" bl_options = {'REGISTER', 'UNDO'} - prism_n: IntProperty( - name="Sides", description="Number of sides", default=5, min=3, max=720 - ) + prism_n: IntProperty(name="Sides", description="Number of sides", default=5, min=3, max=720) prism_r: FloatProperty(name="Radius", description="Radius", default=1.0) def execute(self, context): props = self.properties - loftData = bpy.data.curves.new('Prism', type='CURVE') - loftData.dimensions = '2D' - loftData.resolution_u = 2 - # loftData.show_normal_face = False - loftData.extrude = 2 + loft_data = bpy.data.curves.new('Prism', type='CURVE') + loft_data.dimensions = '2D' + loft_data.resolution_u = 2 + # loft_data.show_normal_face = False + loft_data.extrude = 2 n = props.prism_n r = props.prism_r coords = [] @@ -1568,14 +1509,14 @@ class POVRAY_OT_prism_add(Operator): y = r * sin(angle) coords.append((x, y, z)) angle += pi * 2 / n - poly = loftData.splines.new('POLY') + poly = loft_data.splines.new('POLY') poly.points.add(len(coords) - 1) for i, coord in enumerate(coords): x, y, z = coord poly.points[i].co = (x, y, z, 1) poly.use_cyclic_u = True - ob = bpy.data.objects.new('Prism_shape', loftData) + ob = bpy.data.objects.new('Prism_shape', loft_data) scn = bpy.context.scene scn.collection.objects.link(ob) context.view_layer.objects.active = ob @@ -1587,8 +1528,11 @@ class POVRAY_OT_prism_add(Operator): ##############################PARAMETRIC###################################### def pov_parametric_define(context, op, ob): - """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon.""" + """Add the representation of POV parametric surfaces by math surface from add mesh extra objects addon. + Picking properties either from creation operator, import, or data update. + But flag its primitive type with a specific pov.object_as attribute and lock edit mode + to keep proxy consistency by hiding edit geometry.""" if op: u_min = op.u_min u_max = op.u_max @@ -1631,7 +1575,10 @@ def pov_parametric_define(context, op, ob): bpy.ops.mesh.select_all(action='SELECT') # extra work: bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1) - bpy.ops.transform.rotate(axis=obrot, proportional_size=1) + # XXX TODO : https://devtalk.blender.org/t/bpy-ops-transform-rotate-option-axis/6235/7 + # to complete necessary extra work rotation, after updating from blender version > 2.92 + # update and uncomment below, but simple axis deprecated since 2.8 + # bpy.ops.transform.rotate(axis=obrot, proportional_size=1) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") @@ -1671,7 +1618,8 @@ class POVRAY_OT_parametric_add(Operator): bl_description = "Add Paramertic" bl_options = {'REGISTER', 'UNDO'} - # XXX Keep it in sync with __init__'s Parametric primitive + # Keep in sync within object_properties.py section Parametric primitive + # as this allows interactive update u_min: FloatProperty(name="U Min", description="", default=0.0) v_min: FloatProperty(name="V Min", description="", default=0.0) u_max: FloatProperty(name="U Max", description="", default=6.28) @@ -1692,9 +1640,7 @@ class POVRAY_OT_parametric_add(Operator): pov_parametric_define(context, self, None) self.report( - {'INFO'}, - "This native POV-Ray primitive " - "won't have any vertex to show in edit mode", + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" ) return {'FINISHED'} @@ -1715,12 +1661,7 @@ class POVRAY_OT_parametric_update(Operator): def poll(cls, context): engine = context.scene.render.engine ob = context.object - return ( - ob - and ob.data - and ob.type == 'MESH' - and engine in cls.COMPAT_ENGINES - ) + return ob and ob.data and ob.type == 'MESH' and engine in cls.COMPAT_ENGINES def execute(self, context): @@ -1741,19 +1682,14 @@ class POVRAY_OT_shape_polygon_to_circle_add(Operator): bl_options = {'REGISTER', 'UNDO'} COMPAT_ENGINES = {'POVRAY_RENDER'} - # XXX Keep it in sync with __init__'s polytocircle properties + # Keep in sync within object_properties.py section PolygonToCircle properties + # as this allows interactive update polytocircle_resolution: IntProperty( name="Resolution", description="", default=3, min=0, max=256 ) - polytocircle_ngon: IntProperty( - name="NGon", description="", min=3, max=64, default=5 - ) - polytocircle_ngonR: FloatProperty( - name="NGon Radius", description="", default=0.3 - ) - polytocircle_circleR: FloatProperty( - name="Circle Radius", description="", default=1.0 - ) + polytocircle_ngon: IntProperty(name="NGon", description="", min=3, max=64, default=5) + polytocircle_ngonR: FloatProperty(name="NGon Radius", description="", default=0.3) + polytocircle_circleR: FloatProperty(name="Circle Radius", description="", default=1.0) def execute(self, context): props = self.properties @@ -1771,10 +1707,7 @@ class POVRAY_OT_shape_polygon_to_circle_add(Operator): numCircleVerts = ngon + (ngon * resolution) bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.primitive_circle_add( - vertices=numCircleVerts, - radius=circleR, - fill_type='NGON', - enter_editmode=True, + vertices=numCircleVerts, radius=circleR, fill_type='NGON', enter_editmode=True ) bpy.ops.transform.translate(value=(0, 0, -1)) bpy.ops.mesh.select_all(action='SELECT') @@ -1782,10 +1715,7 @@ class POVRAY_OT_shape_polygon_to_circle_add(Operator): if ngon < 5: bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.primitive_circle_add( - vertices=ngon, - radius=ngonR, - fill_type='TRIFAN', - enter_editmode=True, + vertices=ngon, radius=ngonR, fill_type='TRIFAN', enter_editmode=True ) bpy.ops.transform.translate(value=(0, 0, 1)) bpy.ops.mesh.select_all(action='SELECT') @@ -1803,547 +1733,6 @@ class POVRAY_OT_shape_polygon_to_circle_add(Operator): return {'FINISHED'} -#############################IMPORT - - -class ImportPOV(bpy.types.Operator, ImportHelper): - """Load Povray files""" - - bl_idname = "import_scene.pov" - bl_label = "POV-Ray files (.pov/.inc)" - bl_options = {'PRESET', 'UNDO'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - # ----------- - # File props. - files: CollectionProperty( - type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'} - ) - directory: StringProperty( - maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'} - ) - - filename_ext = {".pov", ".inc"} - filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'}) - - import_at_cur: BoolProperty( - name="Import at Cursor Location", - description="Ignore Object Matrix", - default=False, - ) - - def execute(self, context): - from mathutils import Matrix - - verts = [] - faces = [] - materials = [] - blendMats = [] ############## - povMats = [] ############## - colors = [] - matNames = [] - lenverts = None - lenfaces = None - suffix = -1 - name = 'Mesh2_%s' % suffix - name_search = False - verts_search = False - faces_search = False - plane_search = False - box_search = False - cylinder_search = False - sphere_search = False - cone_search = False - tex_search = False ################## - cache = [] - matrixes = {} - writematrix = False - index = None - value = None - # filepov = bpy.path.abspath(self.filepath) #was used for single files - - def mat_search(cache): - r = g = b = 0.5 - f = t = 0 - color = None - - for item, value in enumerate(cache): - - if value == 'texture': - pass - - if value == 'pigment': - - if cache[item + 2] in {'rgb', 'srgb'}: - pass - - elif cache[item + 2] in {'rgbf', 'srgbf'}: - pass - - elif cache[item + 2] in {'rgbt', 'srgbt'}: - try: - r, g, b, t = ( - float(cache[item + 3]), - float(cache[item + 4]), - float(cache[item + 5]), - float(cache[item + 6]), - ) - except: - r = g = b = t = float(cache[item + 2]) - color = (r, g, b, t) - - elif cache[item + 2] in {'rgbft', 'srgbft'}: - pass - - else: - pass - - if colors == [] or (colors != [] and color not in colors): - colors.append(color) - name = ob.name + "_mat" - matNames.append(name) - mat = bpy.data.materials.new(name) - mat.diffuse_color = (r, g, b) - mat.alpha = 1 - t - if mat.alpha != 1: - mat.use_transparency = True - ob.data.materials.append(mat) - - else: - for i, value in enumerate(colors): - if color == value: - ob.data.materials.append( - bpy.data.materials[matNames[i]] - ) - - for file in self.files: - print("Importing file: " + file.name) - filepov = self.directory + file.name - for line in open(filepov): - string = line.replace("{", " ") - string = string.replace("}", " ") - string = string.replace("<", " ") - string = string.replace(">", " ") - string = string.replace(",", " ") - lw = string.split() - lenwords = len(lw) - if lw: - if lw[0] == "object": - writematrix = True - if writematrix: - if lw[0] not in {"object", "matrix"}: - index = lw[0] - if lw[0] in {"matrix"}: - value = [ - float(lw[1]), - float(lw[2]), - float(lw[3]), - float(lw[4]), - float(lw[5]), - float(lw[6]), - float(lw[7]), - float(lw[8]), - float(lw[9]), - float(lw[10]), - float(lw[11]), - float(lw[12]), - ] - matrixes[index] = value - writematrix = False - for line in open(filepov): - S = line.replace("{", " { ") - S = S.replace("}", " } ") - S = S.replace(",", " ") - S = S.replace("<", "") - S = S.replace(">", " ") - S = S.replace("=", " = ") - S = S.replace(";", " ; ") - S = S.split() - lenS = len(S) - for i, word in enumerate(S): - ##################Primitives Import################## - if word == 'cone': - cone_search = True - name_search = False - if cone_search: - cache.append(word) - if cache[-1] == '}': - try: - x0 = float(cache[2]) - y0 = float(cache[3]) - z0 = float(cache[4]) - r0 = float(cache[5]) - x1 = float(cache[6]) - y1 = float(cache[7]) - z1 = float(cache[8]) - r1 = float(cache[9]) - # Y is height in most pov files, not z - bpy.ops.pov.cone_add( - base=r0, cap=r1, height=(y1 - y0) - ) - ob = context.object - ob.location = (x0, y0, z0) - # ob.scale = (r,r,r) - mat_search(cache) - except (ValueError): - pass - cache = [] - cone_search = False - if word == 'plane': - plane_search = True - name_search = False - if plane_search: - cache.append(word) - if cache[-1] == '}': - try: - bpy.ops.pov.addplane() - ob = context.object - mat_search(cache) - except (ValueError): - pass - cache = [] - plane_search = False - if word == 'box': - box_search = True - name_search = False - if box_search: - cache.append(word) - if cache[-1] == '}': - try: - x0 = float(cache[2]) - y0 = float(cache[3]) - z0 = float(cache[4]) - x1 = float(cache[5]) - y1 = float(cache[6]) - z1 = float(cache[7]) - # imported_corner_1=(x0, y0, z0) - # imported_corner_2 =(x1, y1, z1) - center = ( - (x0 + x1) / 2, - (y0 + y1) / 2, - (z0 + z1) / 2, - ) - bpy.ops.pov.addbox() - ob = context.object - ob.location = center - mat_search(cache) - - except (ValueError): - pass - cache = [] - box_search = False - if word == 'cylinder': - cylinder_search = True - name_search = False - if cylinder_search: - cache.append(word) - if cache[-1] == '}': - try: - x0 = float(cache[2]) - y0 = float(cache[3]) - z0 = float(cache[4]) - x1 = float(cache[5]) - y1 = float(cache[6]) - z1 = float(cache[7]) - imported_cyl_loc = (x0, y0, z0) - imported_cyl_loc_cap = (x1, y1, z1) - - r = float(cache[8]) - - vec = Vector(imported_cyl_loc_cap) - Vector( - imported_cyl_loc - ) - depth = vec.length - rot = Vector((0, 0, 1)).rotation_difference( - vec - ) # Rotation from Z axis. - trans = rot @ Vector( - (0, 0, depth / 2) - ) # Such that origin is at center of the base of the cylinder. - # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2) - scaleZ = ( - sqrt( - (x1 - x0) ** 2 - + (y1 - y0) ** 2 - + (z1 - z0) ** 2 - ) - / 2 - ) - bpy.ops.pov.addcylinder( - R=r, - imported_cyl_loc=imported_cyl_loc, - imported_cyl_loc_cap=imported_cyl_loc_cap, - ) - ob = context.object - ob.location = (x0, y0, z0) - ob.rotation_euler = rot.to_euler() - ob.scale = (1, 1, scaleZ) - - # scale data rather than obj? - # bpy.ops.object.mode_set(mode='EDIT') - # bpy.ops.mesh.reveal() - # bpy.ops.mesh.select_all(action='SELECT') - # bpy.ops.transform.resize(value=(1,1,scaleZ), orient_type='LOCAL') - # bpy.ops.mesh.hide(unselected=False) - # bpy.ops.object.mode_set(mode='OBJECT') - - mat_search(cache) - - except (ValueError): - pass - cache = [] - cylinder_search = False - if word == 'sphere': - sphere_search = True - name_search = False - if sphere_search: - cache.append(word) - if cache[-1] == '}': - x = y = z = r = 0 - try: - x = float(cache[2]) - y = float(cache[3]) - z = float(cache[4]) - r = float(cache[5]) - - except (ValueError): - pass - except: - x = y = z = float(cache[2]) - r = float(cache[3]) - bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z)) - ob = context.object - ob.location = (x, y, z) - ob.scale = (r, r, r) - mat_search(cache) - cache = [] - sphere_search = False - ##################End Primitives Import################## - if word == '#declare': - name_search = True - if name_search: - cache.append(word) - if word == 'mesh2': - name_search = False - if cache[-2] == '=': - name = cache[-3] - else: - suffix += 1 - cache = [] - if word in {'texture', ';'}: - name_search = False - cache = [] - if word == 'vertex_vectors': - verts_search = True - if verts_search: - cache.append(word) - if word == '}': - verts_search = False - lenverts = cache[2] - cache.pop() - cache.pop(0) - cache.pop(0) - cache.pop(0) - for i in range(int(lenverts)): - x = i * 3 - y = (i * 3) + 1 - z = (i * 3) + 2 - verts.append( - ( - float(cache[x]), - float(cache[y]), - float(cache[z]), - ) - ) - cache = [] - # if word == 'face_indices': - # faces_search = True - if word == 'texture_list': ######## - tex_search = True ####### - if tex_search: ######### - if ( - word - not in { - 'texture_list', - 'texture', - '{', - '}', - 'face_indices', - } - and word.isdigit() == False - ): ############## - povMats.append(word) ################# - if word == 'face_indices': - tex_search = False ################ - faces_search = True - if faces_search: - cache.append(word) - if word == '}': - faces_search = False - lenfaces = cache[2] - cache.pop() - cache.pop(0) - cache.pop(0) - cache.pop(0) - lf = int(lenfaces) - var = int(len(cache) / lf) - for i in range(lf): - if var == 3: - v0 = i * 3 - v1 = i * 3 + 1 - v2 = i * 3 + 2 - faces.append( - ( - int(cache[v0]), - int(cache[v1]), - int(cache[v2]), - ) - ) - if var == 4: - v0 = i * 4 - v1 = i * 4 + 1 - v2 = i * 4 + 2 - m = i * 4 + 3 - materials.append((int(cache[m]))) - faces.append( - ( - int(cache[v0]), - int(cache[v1]), - int(cache[v2]), - ) - ) - if var == 6: - v0 = i * 6 - v1 = i * 6 + 1 - v2 = i * 6 + 2 - m0 = i * 6 + 3 - m1 = i * 6 + 4 - m2 = i * 6 + 5 - materials.append( - ( - int(cache[m0]), - int(cache[m1]), - int(cache[m2]), - ) - ) - faces.append( - ( - int(cache[v0]), - int(cache[v1]), - int(cache[v2]), - ) - ) - # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False) - # ob = object_utils.object_data_add(context, mesh, operator=None) - - me = bpy.data.meshes.new(name) ######## - ob = bpy.data.objects.new(name, me) ########## - bpy.context.collection.objects.link(ob) ######### - me.from_pydata(verts, [], faces) ############ - - for mat in bpy.data.materials: ############## - blendMats.append(mat.name) ############# - for mName in povMats: ##################### - if mName not in blendMats: ########### - povMat = bpy.data.materials.new( - mName - ) ################# - mat_search(cache) - ob.data.materials.append( - bpy.data.materials[mName] - ) ################### - if materials: ################## - for i, val in enumerate( - materials - ): #################### - try: ################### - ob.data.polygons[ - i - ].material_index = ( - val - ) #################### - except TypeError: ################### - ob.data.polygons[ - i - ].material_index = int( - val[0] - ) ################## - - blendMats = [] ######################### - povMats = [] ######################### - materials = [] ######################### - cache = [] - name_search = True - if name in matrixes and self.import_at_cur == False: - global_matrix = Matrix.Rotation( - pi / 2.0, 4, 'X' - ) - ob = bpy.context.object - matrix = ob.matrix_world - v = matrixes[name] - matrix[0][0] = v[0] - matrix[1][0] = v[1] - matrix[2][0] = v[2] - matrix[0][1] = v[3] - matrix[1][1] = v[4] - matrix[2][1] = v[5] - matrix[0][2] = v[6] - matrix[1][2] = v[7] - matrix[2][2] = v[8] - matrix[0][3] = v[9] - matrix[1][3] = v[10] - matrix[2][3] = v[11] - matrix = global_matrix * ob.matrix_world - ob.matrix_world = matrix - verts = [] - faces = [] - - # if word == 'pigment': - # try: - # #all indices have been incremented once to fit a bad test file - # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5]) - # color = (r,g,b,t) - - # except (IndexError): - # #all indices have been incremented once to fit alternate test file - # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6]) - # color = (r,g,b,t) - # except UnboundLocalError: - # # In case no transmit is specified ? put it to 0 - # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0) - # color = (r,g,b,t) - - # except (ValueError): - # color = (0.8,0.8,0.8,0) - # pass - - # if colors == [] or (colors != [] and color not in colors): - # colors.append(color) - # name = ob.name+"_mat" - # matNames.append(name) - # mat = bpy.data.materials.new(name) - # mat.diffuse_color = (r,g,b) - # mat.alpha = 1-t - # if mat.alpha != 1: - # mat.use_transparency=True - # ob.data.materials.append(mat) - # print (colors) - # else: - # for i in range(len(colors)): - # if color == colors[i]: - # ob.data.materials.append(bpy.data.materials[matNames[i]]) - - ##To keep Avogadro Camera angle: - # for obj in bpy.context.view_layer.objects: - # if obj.type == "CAMERA": - # track = obj.constraints.new(type = "TRACK_TO") - # track.target = ob - # track.track_axis ="TRACK_NEGATIVE_Z" - # track.up_axis = "UP_Y" - # obj.location = (0,0,0) - return {'FINISHED'} - - classes = ( POVRAY_OT_lathe_add, POVRAY_OT_superellipsoid_add, @@ -2371,19 +1760,14 @@ classes = ( POVRAY_OT_parametric_add, POVRAY_OT_parametric_update, POVRAY_OT_shape_polygon_to_circle_add, - ImportPOV, ) def register(): - # from bpy.utils import register_class - for cls in classes: register_class(cls) def unregister(): - from bpy.utils import unregister_class - for cls in classes: unregister_class(cls) diff --git a/render_povray/object_properties.py b/render_povray/object_properties.py new file mode 100755 index 000000000..8cea49af8 --- /dev/null +++ b/render_povray/object_properties.py @@ -0,0 +1,670 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Declare object level properties controllable in UI and translated to POV""" +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import ( + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + StringProperty, + EnumProperty, + PointerProperty, +) + + +############################################################################### +# Object POV properties. +############################################################################### + + +class RenderPovSettingsObject(PropertyGroup): + """Declare object and primitives level properties controllable in UI and translated to POV.""" + + # Pov inside_vector used for CSG + inside_vector: FloatVectorProperty( + name="CSG Inside Vector", + description="Direction to shoot CSG inside test rays at", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.001, 0.001, 0.5), + options={"ANIMATABLE"}, + subtype="XYZ", + ) + + # Importance sampling + importance_value: FloatProperty( + name="Radiosity Importance", + description="Priority value relative to other objects for sampling radiosity rays. " + "Increase to get more radiosity rays at comparatively small yet " + "bright objects", + min=0.01, + max=1.00, + default=0.50, + ) + + # Collect photons + collect_photons: BoolProperty( + name="Receive Photon Caustics", + description="Enable object to collect photons from other objects caustics. Turn " + "off for objects that don't really need to receive caustics (e.g. objects" + " that generate caustics often don't need to show any on themselves)", + default=True, + ) + + # Photons spacing_multiplier + spacing_multiplier: FloatProperty( + name="Photons Spacing Multiplier", + description="Multiplier value relative to global spacing of photons. " + "Decrease by half to get 4x more photons at surface of " + "this object (or 8x media photons than specified in the globals", + min=0.01, + max=1.00, + default=1.00, + ) + + ##################################CustomPOV Code############################ + # Only DUMMIES below for now: + replacement_text: StringProperty( + name="Declared name:", + description="Type the declared name in custom POV code or an external .inc " + "it points at. Any POV shape expected e.g: isosurface {}", + default="", + ) + + #############POV specific object properties.############################ + object_as: StringProperty(maxlen=1024) + + imported_loc: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0) + ) + + imported_loc_cap: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 2.0) + ) + + unlock_parameters: BoolProperty(name="Lock", default=False) + + # not in UI yet but used for sor (lathe) / prism... pov primitives + curveshape: EnumProperty( + name="Povray Shape Type", + items=( + ("birail", "Birail", ""), + ("cairo", "Cairo", ""), + ("lathe", "Lathe", ""), + ("loft", "Loft", ""), + ("prism", "Prism", ""), + ("sphere_sweep", "Sphere Sweep", ""), + ), + default="sphere_sweep", + ) + + mesh_write_as: EnumProperty( + name="Mesh Write As", + items=(("blobgrid", "Blob Grid", ""), ("grid", "Grid", ""), ("mesh", "Mesh", "")), + default="mesh", + ) + + object_ior: FloatProperty(name="IOR", description="IOR", min=1.0, max=10.0, default=1.0) + + # shape_as_light = StringProperty(name="Light",maxlen=1024) + # fake_caustics_power = FloatProperty( + # name="Power", description="Fake caustics power", + # min=0.0, max=10.0,default=0.0) + # target = BoolProperty(name="Target",description="",default=False) + # target_value = FloatProperty( + # name="Value", description="", + # min=0.0, max=1.0,default=1.0) + # refraction = BoolProperty(name="Refraction",description="",default=False) + # dispersion = BoolProperty(name="Dispersion",description="",default=False) + # dispersion_value = FloatProperty( + # name="Dispersion", description="Good values are 1.01 to 1.1. ", + # min=1.0, max=1.2,default=1.01) + # dispersion_samples = IntProperty(name="Samples",min=2, max=100,default=7) + # reflection = BoolProperty(name="Reflection",description="",default=False) + # pass_through = BoolProperty(name="Pass through",description="",default=False) + no_shadow: BoolProperty(name="No Shadow", default=False) + + no_image: BoolProperty(name="No Image", default=False) + + no_reflection: BoolProperty(name="No Reflection", default=False) + + no_radiosity: BoolProperty(name="No Radiosity", default=False) + + inverse: BoolProperty(name="Inverse", default=False) + + sturm: BoolProperty(name="Sturm", default=False) + + double_illuminate: BoolProperty(name="Double Illuminate", default=False) + + hierarchy: BoolProperty(name="Hierarchy", default=False) + + hollow: BoolProperty(name="Hollow", default=False) + + boundorclip: EnumProperty( + name="Boundorclip", + items=( + ("none", "None", ""), + ("bounded_by", "Bounded_by", ""), + ("clipped_by", "Clipped_by", ""), + ), + default="none", + ) + + boundorclipob: StringProperty(maxlen=1024) + + addboundorclip: BoolProperty(description="", default=False) + + blob_threshold: FloatProperty(name="Threshold", min=0.00, max=10.0, default=0.6) + + blob_strength: FloatProperty(name="Strength", min=-10.00, max=10.0, default=1.00) + + res_u: IntProperty(name="U", min=100, max=1000, default=500) + + res_v: IntProperty(name="V", min=100, max=1000, default=500) + + contained_by: EnumProperty( + name="Contained by", items=(("box", "Box", ""), ("sphere", "Sphere", "")), default="box" + ) + + container_scale: FloatProperty(name="Container Scale", min=0.0, max=10.0, default=1.00) + + threshold: FloatProperty(name="Threshold", min=0.0, max=10.0, default=0.00) + + accuracy: FloatProperty(name="Accuracy", min=0.0001, max=0.1, default=0.001) + + max_gradient: FloatProperty(name="Max Gradient", min=0.0, max=100.0, default=5.0) + + all_intersections: BoolProperty(name="All Intersections", default=False) + + max_trace: IntProperty(name="Max Trace", min=1, max=100, default=1) + + ###########Cylinder + def prop_update_cylinder(self, context): + """Update POV cylinder primitive parameters not only at creation but anytime they are changed in UI.""" + if bpy.ops.pov.cylinder_update.poll(): + bpy.ops.pov.cylinder_update() + + cylinder_radius: FloatProperty( + name="Cylinder R", min=0.00, max=10.0, default=0.04, update=prop_update_cylinder + ) + + cylinder_location_cap: FloatVectorProperty( + name="Cylinder Cap Location", + subtype="TRANSLATION", + description="The position of the 'other' end of the cylinder (relative to object location)", + default=(0.0, 0.0, 2.0), + update=prop_update_cylinder, + ) + + imported_cyl_loc: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0) + ) + + imported_cyl_loc_cap: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 2.0) + ) + + ###########Sphere + def prop_update_sphere(self, context): + + """Update POV sphere primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.sphere_update() + + sphere_radius: FloatProperty( + name="Sphere radius", min=0.00, max=10.0, default=0.5, update=prop_update_sphere + ) + + ###########Cone + def prop_update_cone(self, context): + + """Update POV cone primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.cone_update() + + cone_base_radius: FloatProperty( + name="Base radius", + description="The first radius of the cone", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_cone, + ) + + cone_cap_radius: FloatProperty( + name="Cap radius", + description="The second radius of the cone", + default=0.3, + min=0.0, + max=100.0, + update=prop_update_cone, + ) + + cone_segments: IntProperty( + name="Segments", + description="Radial segmentation of proxy mesh", + default=16, + min=3, + max=265, + update=prop_update_cone, + ) + + cone_height: FloatProperty( + name="Height", + description="Height of the cone", + default=2.0, + min=0.01, + max=100.0, + update=prop_update_cone, + ) + + cone_base_z: FloatProperty() + + cone_cap_z: FloatProperty() + + ###########Parametric + def prop_update_parametric(self, context): + + """Update POV parametric surface primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.parametric_update() + + u_min: FloatProperty(name="U Min", description="", default=0.0, update=prop_update_parametric) + + v_min: FloatProperty(name="V Min", description="", default=0.0, update=prop_update_parametric) + + u_max: FloatProperty(name="U Max", description="", default=6.28, update=prop_update_parametric) + + v_max: FloatProperty(name="V Max", description="", default=12.57, update=prop_update_parametric) + + x_eq: StringProperty( + maxlen=1024, default="cos(v)*(1+cos(u))*sin(v/8)", update=prop_update_parametric + ) + + y_eq: StringProperty( + maxlen=1024, default="sin(u)*sin(v/8)+cos(v/8)*1.5", update=prop_update_parametric + ) + + z_eq: StringProperty( + maxlen=1024, default="sin(v)*(1+cos(u))*sin(v/8)", update=prop_update_parametric + ) + + ###########Torus + + def prop_update_torus(self, context): + + """Update POV torus primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.torus_update() + + torus_major_segments: IntProperty( + name="Segments", + description="Radial segmentation of proxy mesh", + default=48, + min=3, + max=720, + update=prop_update_torus, + ) + + torus_minor_segments: IntProperty( + name="Segments", + description="Cross-section segmentation of proxy mesh", + default=12, + min=3, + max=720, + update=prop_update_torus, + ) + + torus_major_radius: FloatProperty( + name="Major radius", + description="Major radius", + min=0.00, + max=100.00, + default=1.0, + update=prop_update_torus, + ) + + torus_minor_radius: FloatProperty( + name="Minor radius", + description="Minor radius", + min=0.00, + max=100.00, + default=0.25, + update=prop_update_torus, + ) + + ###########Rainbow + arc_angle: FloatProperty( + name="Arc angle", + description="The angle of the raynbow arc in degrees", + default=360, + min=0.01, + max=360.0, + ) + + falloff_angle: FloatProperty( + name="Falloff angle", + description="The angle after which rainbow dissolves into background", + default=360, + min=0.0, + max=360, + ) + + ###########HeightFields + + quality: IntProperty(name="Quality", description="", default=100, min=1, max=100) + + hf_filename: StringProperty(maxlen=1024) + + hf_gamma: FloatProperty(name="Gamma", description="Gamma", min=0.0001, max=20.0, default=1.0) + + hf_premultiplied: BoolProperty(name="Premultiplied", description="Premultiplied", default=True) + + hf_smooth: BoolProperty(name="Smooth", description="Smooth", default=False) + + hf_water: FloatProperty( + name="Water Level", description="Wather Level", min=0.00, max=1.00, default=0.0 + ) + + hf_hierarchy: BoolProperty(name="Hierarchy", description="Height field hierarchy", default=True) + + ##############Superellipsoid + def prop_update_superellipsoid(self, context): + + """Update POV superellipsoid primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.superellipsoid_update() + + se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04) + + se_param2: FloatProperty(name="Parameter 2", description="", min=0.00, max=10.0, default=0.04) + + se_u: IntProperty( + name="U-segments", + description="radial segmentation", + default=20, + min=4, + max=265, + update=prop_update_superellipsoid, + ) + + se_v: IntProperty( + name="V-segments", + description="lateral segmentation", + default=20, + min=4, + max=265, + update=prop_update_superellipsoid, + ) + + se_n1: FloatProperty( + name="Ring manipulator", + description="Manipulates the shape of the Ring", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_superellipsoid, + ) + + se_n2: FloatProperty( + name="Cross manipulator", + description="Manipulates the shape of the cross-section", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_superellipsoid, + ) + + se_edit: EnumProperty( + items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")], + name="Fill up and down", + description="", + default="TRIANGLES", + update=prop_update_superellipsoid, + ) + + #############Used for loft but also Superellipsoid, etc. + curveshape: EnumProperty( + name="Povray Shape Type", + items=( + ("birail", "Birail", ""), + ("cairo", "Cairo", ""), + ("lathe", "Lathe", ""), + ("loft", "Loft", ""), + ("prism", "Prism", ""), + ("sphere_sweep", "Sphere Sweep", ""), + ("sor", "Surface of Revolution", ""), + ), + default="sphere_sweep", + ) + + #############Supertorus + def prop_update_supertorus(self, context): + + """Update POV supertorus primitive parameters not only at creation but anytime they are changed in UI.""" + + bpy.ops.pov.supertorus_update() + + st_major_radius: FloatProperty( + name="Major radius", + description="Major radius", + min=0.00, + max=100.00, + default=1.0, + update=prop_update_supertorus, + ) + + st_minor_radius: FloatProperty( + name="Minor radius", + description="Minor radius", + min=0.00, + max=100.00, + default=0.25, + update=prop_update_supertorus, + ) + + st_ring: FloatProperty( + name="Ring", + description="Ring manipulator", + min=0.0001, + max=100.00, + default=1.00, + update=prop_update_supertorus, + ) + + st_cross: FloatProperty( + name="Cross", + description="Cross manipulator", + min=0.0001, + max=100.00, + default=1.00, + update=prop_update_supertorus, + ) + + st_accuracy: FloatProperty( + name="Accuracy", description="Supertorus accuracy", min=0.00001, max=1.00, default=0.001 + ) + + st_max_gradient: FloatProperty( + name="Gradient", + description="Max gradient", + min=0.0001, + max=100.00, + default=10.00, + update=prop_update_supertorus, + ) + + st_R: FloatProperty( + name="big radius", + description="The radius inside the tube", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_supertorus, + ) + + st_r: FloatProperty( + name="small radius", + description="The radius of the tube", + default=0.3, + min=0.01, + max=100.0, + update=prop_update_supertorus, + ) + + st_u: IntProperty( + name="U-segments", + description="radial segmentation", + default=16, + min=3, + max=265, + update=prop_update_supertorus, + ) + + st_v: IntProperty( + name="V-segments", + description="lateral segmentation", + default=8, + min=3, + max=265, + update=prop_update_supertorus, + ) + + st_n1: FloatProperty( + name="Ring manipulator", + description="Manipulates the shape of the Ring", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_supertorus, + ) + + st_n2: FloatProperty( + name="Cross manipulator", + description="Manipulates the shape of the cross-section", + default=1.0, + min=0.01, + max=100.0, + update=prop_update_supertorus, + ) + + st_ie: BoolProperty( + name="Use Int.+Ext. radii", + description="Use internal and external radii", + default=False, + update=prop_update_supertorus, + ) + + st_edit: BoolProperty( + name="", description="", default=False, options={"HIDDEN"}, update=prop_update_supertorus + ) + + ########################Loft + loft_n: IntProperty( + name="Segments", description="Vertical segments", default=16, min=3, max=720 + ) + + loft_rings_bottom: IntProperty( + name="Bottom", description="Bottom rings", default=5, min=2, max=100 + ) + + loft_rings_side: IntProperty(name="Side", description="Side rings", default=10, min=2, max=100) + + loft_thick: FloatProperty( + name="Thickness", + description="Manipulates the shape of the Ring", + default=0.3, + min=0.01, + max=1.0, + ) + + loft_r: FloatProperty(name="Radius", description="Radius", default=1, min=0.01, max=10) + + loft_height: FloatProperty( + name="Height", + description="Manipulates the shape of the Ring", + default=2, + min=0.01, + max=10.0, + ) + + ###################Prism + prism_n: IntProperty(name="Sides", description="Number of sides", default=5, min=3, max=720) + + prism_r: FloatProperty(name="Radius", description="Radius", default=1.0) + + ##################Isosurface + iso_function_text: StringProperty( + name="Function Text", maxlen=1024 + ) # ,update=iso_props_update_callback) + + ##################PolygonToCircle + polytocircle_resolution: IntProperty( + name="Resolution", description="", default=3, min=0, max=256 + ) + + polytocircle_ngon: IntProperty(name="NGon", description="", min=3, max=64, default=5) + + polytocircle_ngonR: FloatProperty(name="NGon Radius", description="", default=0.3) + + polytocircle_circleR: FloatProperty(name="Circle Radius", description="", default=1.0) + + ############################################################################### + # Modifiers POV properties. + ############################################################################### + # class RenderPovSettingsModifier(PropertyGroup): + boolean_mod: EnumProperty( + name="Operation", + description="Choose the type of calculation for Boolean modifier", + items=( + ("BMESH", "Use the BMesh Boolean Solver", ""), + ("CARVE", "Use the Carve Boolean Solver", ""), + ("POV", "Use POV Constructive Solid Geometry", ""), + ), + default="BMESH", + ) + + #################Avogadro + # filename_ext = ".png" + + # filter_glob = StringProperty( + # default="*.exr;*.gif;*.hdr;*.iff;*.jpeg;*.jpg;*.pgm;*.png;*.pot;*.ppm;*.sys;*.tga;*.tiff;*.EXR;*.GIF;*.HDR;*.IFF;*.JPEG;*.JPG;*.PGM;*.PNG;*.POT;*.PPM;*.SYS;*.TGA;*.TIFF", + # options={'HIDDEN'}, + # ) + + +classes = (RenderPovSettingsObject,) + + +def register(): + for cls in classes: + register_class(cls) + bpy.types.Object.pov = PointerProperty(type=RenderPovSettingsObject) + + +def unregister(): + del bpy.types.Object.pov + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/render.py b/render_povray/render.py old mode 100644 new mode 100755 index c4a93ebd4..45a94912b --- a/render_povray/render.py +++ b/render_povray/render.py @@ -17,200 +17,79 @@ # #**** END GPL LICENSE BLOCK #**** # <pep8 compliant> - +"""Wirte the POV file using this file's functions and some from other modules then render it.""" import bpy import subprocess import os -import sys +from sys import platform import time -from math import atan, pi, degrees, sqrt, cos, sin -#################### -## Faster mesh export -import numpy as np -#################### +from math import ( + pi, +) # maybe move to scenography.py and topology_*****_data.py respectively with smoke and matrix + import re -import random -import platform # -import subprocess # import tempfile # generate temporary files with random names from bpy.types import Operator -from imghdr import what # imghdr is a python lib to identify image file types -from bpy.utils import register_class +from bpy.utils import register_class, unregister_class -from . import df3 # for smoke rendering +from . import ( + scripting, +) # for writing, importing and rendering directly POV Scene Description Language items +from . import scenography # for atmosphere, environment, effects, lighting, camera from . import shading # for BI POV shaders emulation -from . import primitives # for import and export of POV specific primitives -from . import nodes # for POV specific nodes - -##############################SF########################### -##############find image texture -def imageFormat(imgF): - """Identify input image filetypes to transmit to POV.""" - # First use the below explicit extensions to identify image file prospects - ext = { - 'JPG': "jpeg", - 'JPEG': "jpeg", - 'GIF': "gif", - 'TGA': "tga", - 'IFF': "iff", - 'PPM': "ppm", - 'PNG': "png", - 'SYS': "sys", - 'TIFF': "tiff", - 'TIF': "tiff", - 'EXR': "exr", - 'HDR': "hdr", - }.get(os.path.splitext(imgF)[-1].upper(), "") - # Then, use imghdr to really identify the filetype as it can be different - if not ext: - # maybe add a check for if path exists here? - print(" WARNING: texture image has no extension") # too verbose - - ext = what(imgF) # imghdr is a python lib to identify image file types - return ext - - -def imgMap(ts): - """Translate mapping type from Blender UI to POV syntax and return that string.""" - image_map = "" - texdata = bpy.data.textures[ts.texture] - if ts.mapping == 'FLAT': - image_map = "map_type 0 " - elif ts.mapping == 'SPHERE': - image_map = "map_type 1 " - elif ts.mapping == 'TUBE': - image_map = "map_type 2 " - - ## map_type 3 and 4 in development (?) (ENV in pov 3.8) - ## for POV-Ray, currently they just seem to default back to Flat (type 0) - # elif ts.mapping=="?": - # image_map = " map_type 3 " - # elif ts.mapping=="?": - # image_map = " map_type 4 " - if ts.use_interpolation: # Available if image sampling class reactivated? - image_map += " interpolate 2 " - if texdata.extension == 'CLIP': - image_map += " once " - # image_map += "}" - # if ts.mapping=='CUBE': - # image_map+= "warp { cubic } rotate <-90,0,180>" - # no direct cube type mapping. Though this should work in POV 3.7 - # it doesn't give that good results(best suited to environment maps?) - # if image_map == "": - # print(" No texture image found ") - return image_map - - -def imgMapTransforms(ts): - """Translate mapping transformations from Blender UI to POV syntax and return that string.""" - # XXX TODO: unchecked textures give error of variable referenced before assignment XXX - # POV-Ray "scale" is not a number of repetitions factor, but ,its - # inverse, a standard scale factor. - # 0.5 Offset is needed relatively to scale because center of the - # scale is 0.5,0.5 in blender and 0,0 in POV - # Strange that the translation factor for scale is not the same as for - # translate. - # TODO: verify both matches with blender internal. - image_map_transforms = "" - image_map_transforms = ( - "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" - % ( - ts.scale[0], - ts.scale[1], - ts.scale[2], - ts.offset[0], - ts.offset[1], - ts.offset[2], - ) - ) - # image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \ - # ( 1.0 / ts.scale.x, - # 1.0 / ts.scale.y, - # 1.0 / ts.scale.z, - # (0.5 / ts.scale.x) + ts.offset.x, - # (0.5 / ts.scale.y) + ts.offset.y, - # ts.offset.z)) - # image_map_transforms = ("translate <-0.5,-0.5,0> scale <-1,-1,1> * <%.4g,%.4g,%.4g> translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \ - # (1.0 / ts.scale.x, - # 1.0 / ts.scale.y, - # 1.0 / ts.scale.z, - # ts.offset.x, - # ts.offset.y, - # ts.offset.z)) - return image_map_transforms - - -def imgMapBG(wts): - """Translate world mapping from Blender UI to POV syntax and return that string.""" - tex = bpy.data.textures[wts.texture] - image_mapBG = "" - # texture_coords refers to the mapping of world textures: - if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL': - image_mapBG = " map_type 0 " - elif wts.texture_coords == 'ANGMAP': - image_mapBG = " map_type 1 " - elif wts.texture_coords == 'TUBE': - image_mapBG = " map_type 2 " - - if tex.use_interpolation: - image_mapBG += " interpolate 2 " - if tex.extension == 'CLIP': - image_mapBG += " once " - # image_mapBG += "}" - # if wts.mapping == 'CUBE': - # image_mapBG += "warp { cubic } rotate <-90,0,180>" - # no direct cube type mapping. Though this should work in POV 3.7 - # it doesn't give that good results(best suited to environment maps?) - # if image_mapBG == "": - # print(" No background texture image found ") - return image_mapBG - - -def path_image(image): - """Conform a path string to POV syntax to avoid POV errors.""" - return bpy.path.abspath(image.filepath, library=image.library).replace( - "\\", "/" - ) - # .replace("\\","/") to get only forward slashes as it's what POV prefers, - # even on windows +from . import object_mesh_topology # for mesh based geometry +from . import object_curve_topology # for curves based geometry + +# from . import object_primitives # for import and export of POV specific primitives -# end find image texture -# ----------------------------------------------------------------------------- +from .scenography import image_format, img_map, img_map_transforms, path_image + +from .shading import write_object_material +from .object_primitives import write_object_modifiers def string_strip_hyphen(name): + """Remove hyphen characters from a string to avoid POV errors.""" + return name.replace("-", "") -def safety(name, Level): +def safety(name, ref_level_bound): """append suffix characters to names of various material declinations. Material declinations are necessary to POV syntax and used in shading.py - by the povHasnoSpecularMaps function to create the finish map trick and + by the pov_has_no_specular_maps function to create the finish map trick and the suffixes avoid name collisions. Keyword arguments: name -- the initial material name as a string - Level -- the enum number of the Level being written: - Level=1 is for texture with No specular nor Mirror reflection - Level=2 is for texture with translation of spec and mir levels + ref_level_bound -- the enum number of the ref_level_bound being written: + ref_level_bound=1 is for texture with No specular nor Mirror reflection + ref_level_bound=2 is for texture with translation of spec and mir levels for when no map influences them - Level=3 is for texture with Maximum Spec and Mirror + ref_level_bound=3 is for texture with Maximum Spec and Mirror """ - - try: - if int(name) > 0: - prefix = "shader" - except: - prefix = "" + # All the try except clause below seems useless as each time + # prefix rewritten even after and outside of it what was the point? + # It may not even be any longer possible to feed no arg from Blender UI + # try: + # if name: # if int(name) > 0: # could be zero if no argument provided + # # and always triggered exception so is this similar ? + # prefix = "shader" + # except BaseException as e: + # print(e.__doc__) + # print('An exXXXception occurred: {}'.format(e)) + # prefix = "" # rewritten below... prefix = "shader_" name = string_strip_hyphen(name) - if Level == 2: + if ref_level_bound == 2: return prefix + name - elif Level == 1: + # implicit else-if (no return yet) + if ref_level_bound == 1: return prefix + name + "0" # used for 0 of specular map - elif Level == 3: + # implicit else-if (no return yet) + if ref_level_bound == 3: return prefix + name + "1" # used for 1 of specular map @@ -220,174 +99,107 @@ def safety(name, Level): csg_list = [] -def is_renderable(scene, ob): +def is_renderable(ob): + """test for objects flagged as hidden or boolean operands not to render""" return not ob.hide_render and ob not in csg_list def renderable_objects(scene): - return [ob for ob in bpy.data.objects if is_renderable(scene, ob)] + """test for non hidden, non boolean operands objects to render""" + return [ob for ob in bpy.data.objects if is_renderable(ob)] -def no_renderable_objects(scene): - return [ob for ob in csg_list] +def no_renderable_objects(): + """Boolean operands only. Not to render""" + return list(csg_list) -tabLevel = 0 +tab_level = 0 unpacked_images = [] user_dir = bpy.utils.resource_path('USER') preview_dir = os.path.join(user_dir, "preview") ## Make sure Preview directory exists and is empty -smokePath = os.path.join(preview_dir, "smoke.df3") -''' -def write_global_setting(scene,file): - file.write("global_settings {\n") - file.write(" assumed_gamma %.6f\n"%scene.pov.assumed_gamma) - if scene.pov.global_settings_advanced: - if scene.pov.radio_enable == False: - file.write(" adc_bailout %.6f\n"%scene.pov.adc_bailout) - file.write(" ambient_light <%.6f,%.6f,%.6f>\n"%scene.pov.ambient_light[:]) - file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n"%scene.pov.irid_wavelength[:]) - file.write(" charset %s\n"%scene.pov.charset) - file.write(" max_trace_level %s\n"%scene.pov.max_trace_level) - file.write(" max_intersections %s\n"%scene.pov.max_intersections) - file.write(" number_of_waves %s\n"%scene.pov.number_of_waves) - file.write(" noise_generator %s\n"%scene.pov.noise_generator) - - # below properties not added to __init__ yet to avoid conflicts with material sss scale - # unless it would override then should be interfaced also in scene units property tab - - # if scene.pov.sslt_enable: - # file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit) - # file.write(" subsurface {\n") - # file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min)) - # if scene.pov.sslt_radiosity: - # file.write(" radiosity on\n") - # file.write("}\n") - - if scene.pov.radio_enable: - file.write(" radiosity {\n") - file.write(" pretrace_start %.6f\n"%scene.pov.radio_pretrace_start) - file.write(" pretrace_end %.6f\n"%scene.pov.radio_pretrace_end) - file.write(" count %s\n"%scene.pov.radio_count) - file.write(" nearest_count %s\n"%scene.pov.radio_nearest_count) - file.write(" error_bound %.6f\n"%scene.pov.radio_error_bound) - file.write(" recursion_limit %s\n"%scene.pov.radio_recursion_limit) - file.write(" low_error_factor %.6f\n"%scene.pov.radio_low_error_factor) - file.write(" gray_threshold %.6f\n"%scene.pov.radio_gray_threshold) - file.write(" maximum_reuse %.6f\n"%scene.pov.radio_maximum_reuse) - file.write(" minimum_reuse %.6f\n"%scene.pov.radio_minimum_reuse) - file.write(" brightness %.6f\n"%scene.pov.radio_brightness) - file.write(" adc_bailout %.6f\n"%scene.pov.radio_adc_bailout) - if scene.pov.radio_normal: - file.write(" normal on\n") - if scene.pov.radio_always_sample: - file.write(" always_sample on\n") - if scene.pov.radio_media: - file.write(" media on\n") - if scene.pov.radio_subsurface: - file.write(" subsurface on\n") - file.write(" }\n") - - if scene.pov.photon_enable: - file.write(" photons {\n") - if scene.pov.photon_enable_count: - file.write(" count %s\n"%scene.pov.photon_count) - else: - file.write(" spacing %.6g\n"%scene.pov.photon_spacing) - if scene.pov.photon_gather: - file.write(" gather %s, %s\n"%(scene.pov.photon_gather_min,scene.pov.photon_gather_max)) - if scene.pov.photon_autostop: - file.write(" autostop %.4g\n"%scene.pov.photon_autostop_value) - if scene.pov.photon_jitter_enable: - file.write(" jitter %.4g\n"%scene.pov.photon_jitter) - file.write(" max_trace_level %s\n"%scene.pov.photon_max_trace_level) - if scene.pov.photon_adc: - file.write(" adc_bailout %.6f\n"%scene.pov.photon_adc_bailout) - if scene.pov.photon_media_enable: - file.write(" media %s, %s\n"%(scene.pov.photon_media_steps,scene.pov.photon_media_factor)) - if scene.pov.photon_map_file_save_load in {'save'}: - filePhName = 'Photon_map_file.ph' - if scene.pov.photon_map_file != '': - filePhName = scene.pov.photon_map_file+'.ph' - filePhDir = tempfile.gettempdir() - path = bpy.path.abspath(scene.pov.photon_map_dir) - if os.path.exists(path): - filePhDir = path - fullFileName = os.path.join(filePhDir,filePhName) - file.write(' save_file "%s"\n'%fullFileName) - scene.pov.photon_map_file = fullFileName - if scene.pov.photon_map_file_save_load in {'load'}: - fullFileName = bpy.path.abspath(scene.pov.photon_map_file) - if os.path.exists(fullFileName): - file.write(' load_file "%s"\n'%fullFileName) - file.write("}\n") - file.write("}\n") +smoke_path = os.path.join(preview_dir, "smoke.df3") + ''' +# below properties not added to __init__ yet to avoid conflicts with material sss scale +# unless it would override then should be interfaced also in scene units property tab -def write_object_modifiers(scene, ob, File): - """Translate some object level POV statements from Blender UI - to POV syntax and write to exported file """ +# if scene.pov.sslt_enable: + # file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit) + # file.write(" subsurface {\n") + # file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min)) + # if scene.pov.sslt_radiosity: + # file.write(" radiosity on\n") + # file.write("}\n") - # Maybe return that string to be added instead of directly written. +''' - '''XXX WIP - onceCSG = 0 - for mod in ob.modifiers: - if onceCSG == 0: - if mod : - if mod.type == 'BOOLEAN': - if ob.pov.boolean_mod == "POV": - File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" % - (ob.pov.inside_vector[0], - ob.pov.inside_vector[1], - ob.pov.inside_vector[2])) - onceCSG = 1 - ''' - if ob.pov.hollow: - File.write("\thollow\n") - if ob.pov.double_illuminate: - File.write("\tdouble_illuminate\n") - if ob.pov.sturm: - File.write("\tsturm\n") - if ob.pov.no_shadow: - File.write("\tno_shadow\n") - if ob.pov.no_image: - File.write("\tno_image\n") - if ob.pov.no_reflection: - File.write("\tno_reflection\n") - if ob.pov.no_radiosity: - File.write("\tno_radiosity\n") - if ob.pov.inverse: - File.write("\tinverse\n") - if ob.pov.hierarchy: - File.write("\thierarchy\n") - - # XXX, Commented definitions - ''' - if scene.pov.photon_enable: - File.write("photons {\n") - if ob.pov.target: - File.write("target %.4g\n"%ob.pov.target_value) - if ob.pov.refraction: - File.write("refraction on\n") - if ob.pov.reflection: - File.write("reflection on\n") - if ob.pov.pass_through: - File.write("pass_through\n") - File.write("}\n") - if ob.pov.object_ior > 1: - File.write("interior {\n") - File.write("ior %.4g\n"%ob.pov.object_ior) - if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion: - File.write("ior %.4g\n"%ob.pov.dispersion_value) - File.write("ior %s\n"%ob.pov.dispersion_samples) - if scene.pov.photon_enable == False: - File.write("caustics %.4g\n"%ob.pov.fake_caustics_power) - ''' +# def write_object_modifiers(scene, ob, File): +# """Translate some object level POV statements from Blender UI +# to POV syntax and write to exported file """ + +# # Maybe return that string to be added instead of directly written. + +# '''XXX WIP +# onceCSG = 0 +# for mod in ob.modifiers: +# if onceCSG == 0: +# if mod : +# if mod.type == 'BOOLEAN': +# if ob.pov.boolean_mod == "POV": +# File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" % +# (ob.pov.inside_vector[0], +# ob.pov.inside_vector[1], +# ob.pov.inside_vector[2])) +# onceCSG = 1 +# ''' + +# if ob.pov.hollow: +# File.write("\thollow\n") +# if ob.pov.double_illuminate: +# File.write("\tdouble_illuminate\n") +# if ob.pov.sturm: +# File.write("\tsturm\n") +# if ob.pov.no_shadow: +# File.write("\tno_shadow\n") +# if ob.pov.no_image: +# File.write("\tno_image\n") +# if ob.pov.no_reflection: +# File.write("\tno_reflection\n") +# if ob.pov.no_radiosity: +# File.write("\tno_radiosity\n") +# if ob.pov.inverse: +# File.write("\tinverse\n") +# if ob.pov.hierarchy: +# File.write("\thierarchy\n") + +# # XXX, Commented definitions +# ''' +# if scene.pov.photon_enable: +# File.write("photons {\n") +# if ob.pov.target: +# File.write("target %.4g\n"%ob.pov.target_value) +# if ob.pov.refraction: +# File.write("refraction on\n") +# if ob.pov.reflection: +# File.write("reflection on\n") +# if ob.pov.pass_through: +# File.write("pass_through\n") +# File.write("}\n") +# if ob.pov.object_ior > 1: +# File.write("interior {\n") +# File.write("ior %.4g\n"%ob.pov.object_ior) +# if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion: +# File.write("ior %.4g\n"%ob.pov.dispersion_value) +# File.write("ior %s\n"%ob.pov.dispersion_samples) +# if scene.pov.photon_enable == False: +# File.write("caustics %.4g\n"%ob.pov.fake_caustics_power) +# ''' def write_pov(filename, scene=None, info_callback=None): @@ -406,12 +218,8 @@ def write_pov(filename, scene=None, info_callback=None): world = scene.world global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X') comments = scene.pov.comments_enable and not scene.pov.tempfiles_enable - linebreaksinlists = ( - scene.pov.list_lf_enable and not scene.pov.tempfiles_enable - ) - feature_set = bpy.context.preferences.addons[ - __package__ - ].preferences.branch_feature_set_povray + linebreaksinlists = scene.pov.list_lf_enable and not scene.pov.tempfiles_enable + feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray using_uberpov = feature_set == 'uberpov' pov_binary = PovrayRender._locate_binary() @@ -420,73 +228,64 @@ def write_pov(filename, scene=None, info_callback=None): else: print("Official POV-Ray 3.7 feature set chosen in preferences") if 'uber' in pov_binary: - print( - "The name of the binary suggests you are probably rendering with Uber POV engine" - ) + print("The name of the binary suggests you are probably rendering with Uber POV engine") else: - print( - "The name of the binary suggests you are probably rendering with standard POV engine" - ) + print("The name of the binary suggests you are probably rendering with standard POV engine") - def setTab(tabtype, spaces): - TabStr = "" + def set_tab(tabtype, spaces): + tab_str = "" if tabtype == 'NONE': - TabStr = "" + tab_str = "" elif tabtype == 'TAB': - TabStr = "\t" + tab_str = "\t" elif tabtype == 'SPACE': - TabStr = spaces * " " - return TabStr + tab_str = spaces * " " + return tab_str - tab = setTab(scene.pov.indentation_character, scene.pov.indentation_spaces) + tab = set_tab(scene.pov.indentation_character, scene.pov.indentation_spaces) if not scene.pov.tempfiles_enable: - def tabWrite(str_o): + def tab_write(str_o): """Indent POV syntax from brackets levels and write to exported file """ - global tabLevel - brackets = ( - str_o.count("{") - - str_o.count("}") - + str_o.count("[") - - str_o.count("]") - ) + global tab_level + brackets = str_o.count("{") - str_o.count("}") + str_o.count("[") - str_o.count("]") if brackets < 0: - tabLevel = tabLevel + brackets - if tabLevel < 0: - print("Indentation Warning: tabLevel = %s" % tabLevel) - tabLevel = 0 - if tabLevel >= 1: - file.write("%s" % tab * tabLevel) + tab_level = tab_level + brackets + if tab_level < 0: + print("Indentation Warning: tab_level = %s" % tab_level) + tab_level = 0 + if tab_level >= 1: + file.write("%s" % tab * tab_level) file.write(str_o) if brackets > 0: - tabLevel = tabLevel + brackets + tab_level = tab_level + brackets else: - def tabWrite(str_o): + def tab_write(str_o): """write directly to exported file if user checked autonamed temp files (faster).""" file.write(str_o) - def uniqueName(name, nameSeq): + def unique_name(name, name_seq): """Increment any generated POV name that could get identical to avoid collisions""" - if name not in nameSeq: + if name not in name_seq: name = string_strip_hyphen(name) return name name_orig = name i = 1 - while name in nameSeq: + while name in name_seq: name = "%s_%.3d" % (name_orig, i) i += 1 name = string_strip_hyphen(name) return name - def writeMatrix(matrix): + def write_matrix(matrix): """Translate some tranform matrix from Blender UI to POV syntax and write to exported file """ - tabWrite( + tab_write( "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n" % ( matrix[0][0], @@ -504,1539 +303,12 @@ def write_pov(filename, scene=None, info_callback=None): ) ) - def MatrixAsPovString(matrix): - """Translate some tranform matrix from Blender UI - to POV syntax and return that string """ - sMatrix = ( - "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n" - % ( - matrix[0][0], - matrix[1][0], - matrix[2][0], - matrix[0][1], - matrix[1][1], - matrix[2][1], - matrix[0][2], - matrix[1][2], - matrix[2][2], - matrix[0][3], - matrix[1][3], - matrix[2][3], - ) - ) - return sMatrix - - def writeObjectMaterial(material, ob): - """Translate some object level material from Blender UI (VS data level) - to POV interior{} syntax and write it to exported file """ - - # DH - modified some variables to be function local, avoiding RNA write - # this should be checked to see if it is functionally correct - - # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections... - # if material and material.transparency_method == 'RAYTRACE': - if material: - # But there can be only one! - if ( - material.pov_subsurface_scattering.use - ): # SSS IOR get highest priority - tabWrite("interior {\n") - tabWrite("ior %.6f\n" % material.pov_subsurface_scattering.ior) - # Then the raytrace IOR taken from raytrace transparency properties and used for - # reflections if IOR Mirror option is checked. - elif material.pov.mirror_use_IOR: - tabWrite("interior {\n") - tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior) - elif material.pov.transparency_method == 'Z_TRANSPARENCY': - tabWrite("interior {\n") - tabWrite("ior 1.0\n") - else: - tabWrite("interior {\n") - tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior) - - pov_fake_caustics = False - pov_photons_refraction = False - pov_photons_reflection = False - - if material.pov.photons_reflection: - pov_photons_reflection = True - if not material.pov.refraction_caustics: - pov_fake_caustics = False - pov_photons_refraction = False - elif material.pov.refraction_type == "1": - pov_fake_caustics = True - pov_photons_refraction = False - elif material.pov.refraction_type == "2": - pov_fake_caustics = False - pov_photons_refraction = True - - # If only Raytrace transparency is set, its IOR will be used for refraction, but user - # can set up 'un-physical' fresnel reflections in raytrace mirror parameters. - # Last, if none of the above is specified, user can set up 'un-physical' fresnel - # reflections in raytrace mirror parameters. And pov IOR defaults to 1. - if material.pov.caustics_enable: - if pov_fake_caustics: - tabWrite( - "caustics %.3g\n" % material.pov.fake_caustics_power - ) - if pov_photons_refraction: - # Default of 1 means no dispersion - tabWrite( - "dispersion %.6f\n" % material.pov.photons_dispersion - ) - tabWrite( - "dispersion_samples %.d\n" - % material.pov.photons_dispersion_samples - ) - # TODO - # Other interior args - if ( - material.pov.use_transparency - and material.pov.transparency_method == 'RAYTRACE' - ): - # fade_distance - # In Blender this value has always been reversed compared to what tooltip says. - # 100.001 rather than 100 so that it does not get to 0 - # which deactivates the feature in POV - tabWrite( - "fade_distance %.3g\n" - % (100.001 - material.pov_raytrace_transparency.depth_max) - ) - # fade_power - tabWrite( - "fade_power %.3g\n" - % material.pov_raytrace_transparency.falloff - ) - # fade_color - tabWrite( - "fade_color <%.3g, %.3g, %.3g>\n" - % material.pov.interior_fade_color[:] - ) - - # (variable) dispersion_samples (constant count for now) - tabWrite("}\n") - if ( - material.pov.photons_reflection - or material.pov.refraction_type == "2" - ): - tabWrite("photons{") - tabWrite("target %.3g\n" % ob.pov.spacing_multiplier) - if not ob.pov.collect_photons: - tabWrite("collect off\n") - if pov_photons_refraction: - tabWrite("refraction on\n") - if pov_photons_reflection: - tabWrite("reflection on\n") - tabWrite("}\n") - - materialNames = {} + material_names_dictionary = {} DEF_MAT_NAME = "" # or "Default"? - def exportCamera(): - """Translate camera from Blender UI to POV syntax and write to exported file.""" - camera = scene.camera - - # DH disabled for now, this isn't the correct context - active_object = ( - None - ) # bpy.context.active_object # does not always work MR - matrix = global_matrix @ camera.matrix_world - focal_point = camera.data.dof.focus_distance - - # compute resolution - Qsize = render.resolution_x / render.resolution_y - tabWrite( - "#declare camLocation = <%.6f, %.6f, %.6f>;\n" - % matrix.translation[:] - ) - tabWrite( - "#declare camLookAt = <%.6f, %.6f, %.6f>;\n" - % tuple([degrees(e) for e in matrix.to_3x3().to_euler()]) - ) - - tabWrite("camera {\n") - if ( - scene.pov.baking_enable - and active_object - and active_object.type == 'MESH' - ): - tabWrite( - "mesh_camera{ 1 3\n" - ) # distribution 3 is what we want here - tabWrite("mesh{%s}\n" % active_object.name) - tabWrite("}\n") - tabWrite("location <0,0,.01>") - tabWrite("direction <0,0,-1>") - - else: - if camera.data.type == 'ORTHO': - SensorHeightRatio = render.resolution_x * camera.data.ortho_scale / render.resolution_y - tabWrite("orthographic\n") - # Blender angle is radian so should be converted to degrees: - # % (camera.data.angle * (180.0 / pi) ) - # but actually argument is not compulsory after angle in pov ortho mode - tabWrite("angle\n") - tabWrite("right <%6f, 0, 0>\n" % -camera.data.ortho_scale) - tabWrite("location <0, 0, 0>\n") - tabWrite("look_at <0, 0, -1>\n") - tabWrite("up <0, %6f, 0>\n" % (camera.data.ortho_scale / Qsize)) - - elif camera.data.type == 'PANO': - tabWrite("panoramic\n") - tabWrite("location <0, 0, 0>\n") - tabWrite("look_at <0, 0, -1>\n") - tabWrite("right <%s, 0, 0>\n" % -Qsize) - tabWrite("up <0, 1, 0>\n") - tabWrite( - "angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi) - ) - elif camera.data.type == 'PERSP': - # Standard camera otherwise would be default in pov - tabWrite("location <0, 0, 0>\n") - tabWrite("look_at <0, 0, -1>\n") - tabWrite("right <%s, 0, 0>\n" % -Qsize) - tabWrite("up <0, 1, 0>\n") - tabWrite( - "angle %f\n" % ( 2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi ) - ) - - tabWrite( - "rotate <%.6f, %.6f, %.6f>\n" - % tuple([degrees(e) for e in matrix.to_3x3().to_euler()]) - ) - tabWrite("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:]) - if camera.data.dof.use_dof and ( - focal_point != 0 or camera.data.dof.focus_object - ): - tabWrite( - "aperture %.3g\n" - % (1 / camera.data.dof.aperture_fstop * 1000) - ) - tabWrite( - "blur_samples %d %d\n" - % ( - camera.data.pov.dof_samples_min, - camera.data.pov.dof_samples_max, - ) - ) - tabWrite("variance 1/%d\n" % camera.data.pov.dof_variance) - tabWrite("confidence %.3g\n" % camera.data.pov.dof_confidence) - if camera.data.dof.focus_object: - focalOb = scene.objects[camera.data.dof.focus_object.name] - matrixBlur = global_matrix @ focalOb.matrix_world - tabWrite( - "focal_point <%.4f,%.4f,%.4f>\n" - % matrixBlur.translation[:] - ) - else: - tabWrite("focal_point <0, 0, %f>\n" % focal_point) - if camera.data.pov.normal_enable: - tabWrite( - "normal {%s %.4f turbulence %.4f scale %.4f}\n" - % ( - camera.data.pov.normal_patterns, - camera.data.pov.cam_normal, - camera.data.pov.turbulence, - camera.data.pov.scale, - ) - ) - tabWrite("}\n") - - def exportLamps(lamps): - """Translate lights from Blender UI to POV syntax and write to exported file.""" - - # Incremented after each lamp export to declare its target - # currently used for Fresnel diffuse shader as their slope vector: - global lampCount - lampCount = 0 - # Get all lamps - for ob in lamps: - lamp = ob.data - - matrix = global_matrix @ ob.matrix_world - - # Color is no longer modified by energy - color = tuple([c for c in lamp.color]) - - tabWrite("light_source {\n") - tabWrite("< 0,0,0 >\n") - tabWrite("color srgb<%.3g, %.3g, %.3g>\n" % color) - - if lamp.type == 'POINT': - pass - elif lamp.type == 'SPOT': - tabWrite("spotlight\n") - - # Falloff is the main radius from the centre line - tabWrite( - "falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0) - ) # 1 TO 179 FOR BOTH - tabWrite( - "radius %.6f\n" - % ( - (degrees(lamp.spot_size) / 2.0) - * (1.0 - lamp.spot_blend) - ) - ) - - # Blender does not have a tightness equivalent, 0 is most like blender default. - tabWrite("tightness 0\n") # 0:10f - - tabWrite("point_at <0, 0, -1>\n") - if lamp.pov.use_halo: - tabWrite("looks_like{\n") - tabWrite("sphere{<0,0,0>,%.6f\n" % lamp.distance) - tabWrite("hollow\n") - tabWrite("material{\n") - tabWrite("texture{\n") - tabWrite( - "pigment{rgbf<1,1,1,%.4f>}\n" - % (lamp.pov.halo_intensity * 5.0) - ) - tabWrite("}\n") - tabWrite("interior{\n") - tabWrite("media{\n") - tabWrite("emission 1\n") - tabWrite("scattering {1, 0.5}\n") - tabWrite("density{\n") - tabWrite("spherical\n") - tabWrite("color_map{\n") - tabWrite("[0.0 rgb <0,0,0>]\n") - tabWrite("[0.5 rgb <1,1,1>]\n") - tabWrite("[1.0 rgb <1,1,1>]\n") - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - elif lamp.type == 'SUN': - tabWrite("parallel\n") - tabWrite("point_at <0, 0, -1>\n") # *must* be after 'parallel' - - elif lamp.type == 'AREA': - tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0)) - # Area lights have no falloff type, so always use blenders lamp quad equivalent - # for those? - tabWrite("fade_power %d\n" % 2) - size_x = lamp.size - samples_x = lamp.pov.shadow_ray_samples_x - if lamp.shape == 'SQUARE': - size_y = size_x - samples_y = samples_x - else: - size_y = lamp.size_y - samples_y = lamp.pov.shadow_ray_samples_y - - tabWrite( - "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n" - % (size_x, size_y, samples_x, samples_y) - ) - tabWrite("area_illumination\n") - if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED': - if lamp.pov.use_jitter: - tabWrite("jitter\n") - else: - tabWrite("adaptive 1\n") - tabWrite("jitter\n") - - # No shadow checked either at global or light level: - if not scene.pov.use_shadows or ( - lamp.pov.shadow_method == 'NOSHADOW' - ): - tabWrite("shadowless\n") - - # Sun shouldn't be attenuated. Area lights have no falloff attribute so they - # are put to type 2 attenuation a little higher above. - if lamp.type not in {'SUN', 'AREA'}: - if lamp.falloff_type == 'INVERSE_SQUARE': - tabWrite( - "fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0)) - ) - tabWrite( - "fade_power %d\n" % 2 - ) # Use blenders lamp quad equivalent - elif lamp.falloff_type == 'INVERSE_LINEAR': - tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0)) - tabWrite("fade_power %d\n" % 1) # Use blenders lamp linear - elif lamp.falloff_type == 'CONSTANT': - tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0)) - tabWrite("fade_power %d\n" % 3) - # Use blenders lamp constant equivalent no attenuation. - # Using Custom curve for fade power 3 for now. - elif lamp.falloff_type == 'CUSTOM_CURVE': - tabWrite("fade_power %d\n" % 4) - - writeMatrix(matrix) - - tabWrite("}\n") - - lampCount += 1 - - # v(A,B) rotates vector A about origin by vector B. - file.write( - "#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n" - % ( - lampCount, - -(ob.location.x), - -(ob.location.y), - -(ob.location.z), - ob.rotation_euler.x, - ob.rotation_euler.y, - ob.rotation_euler.z, - ) - ) - - #################################################################################################### - def exportRainbows(rainbows): - """write all POV rainbows primitives to exported file """ - for ob in rainbows: - povdataname = ob.data.name # enough? - angle = degrees(ob.data.spot_size / 2.5) # radians in blender (2 - width = ob.data.spot_blend * 10 - distance = ob.data.shadow_buffer_clip_start - # eps=0.0000001 - # angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero - # width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height - # formerly: - # cz-bz # let's say width of the rainbow is height of the cone (interfacing choice - - # v(A,B) rotates vector A about origin by vector B. - # and avoid a 0 length vector by adding 1 - - # file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \ - # (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1), - # ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z)) - - direction = ( - ob.location.x, - ob.location.y, - ob.location.z, - ) # not taking matrix into account - rmatrix = global_matrix @ ob.matrix_world - - # ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1)) - # XXX Is result of the below offset by 90 degrees? - up = ob.matrix_world.to_3x3()[1].xyz # * global_matrix - - # XXX TO CHANGE: - # formerly: - # tabWrite("#declare %s = rainbow {\n"%povdataname) - - # clumsy for now but remove the rainbow from instancing - # system because not an object. use lamps later instead of meshes - - # del data_ref[dataname] - tabWrite("rainbow {\n") - - tabWrite("angle %.4f\n" % angle) - tabWrite("width %.4f\n" % width) - tabWrite("distance %.4f\n" % distance) - tabWrite("arc_angle %.4f\n" % ob.pov.arc_angle) - tabWrite("falloff_angle %.4f\n" % ob.pov.falloff_angle) - tabWrite("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:]) - tabWrite("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2])) - tabWrite("color_map {\n") - tabWrite("[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n") - tabWrite("[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n") - tabWrite("[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n") - tabWrite("[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n") - tabWrite("[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n") - tabWrite("[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n") - tabWrite("[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n") - tabWrite("[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n") - tabWrite("[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n") - tabWrite("}\n") - - povMatName = "Default_texture" - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - # matrix = global_matrix @ ob.matrix_world - # writeMatrix(matrix) - tabWrite("}\n") - # continue #Don't render proxy mesh, skip to next object - - ################################XXX LOFT, ETC. - def exportCurves(scene, ob): - """write all curves based POV primitives to exported file """ - name_orig = "OB" + ob.name - dataname_orig = "DATA" + ob.data.name - - name = string_strip_hyphen(bpy.path.clean_name(name_orig)) - dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig)) - - global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X') - matrix = global_matrix @ ob.matrix_world - bezier_sweep = False - if ob.pov.curveshape == 'sphere_sweep': - # inlined spheresweep macro, which itself calls Shapes.inc: - file.write(' #include "shapes.inc"\n') - - file.write( - ' #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n' - ) - file.write(' //input adjusting and inspection\n') - file.write(' #if(_resolution <= 1)\n') - file.write(' #local res = 1;\n') - file.write(' #else\n') - file.write(' #local res = int(_resolution);\n') - file.write(' #end\n') - file.write( - ' #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n' - ) - file.write(' #error ""\n') - file.write( - ' #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n' - ) - file.write(' #error ""\n') - file.write( - ' #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n' - ) - file.write(' #error ""\n') - file.write(' #else\n') - file.write( - ' #local n_of_seg = div(dimension_size(_points_array,1), 4);\n' - ) - file.write(' #local ctrl_pts_array = array[n_of_seg]\n') - file.write(' #local ctrl_rs_array = array[n_of_seg]\n') - file.write(' #for(i, 0, n_of_seg-1)\n') - file.write( - ' #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n' - ) - file.write( - ' #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n' - ) - file.write(' #end\n') - file.write(' #end\n') - - file.write(' //drawing\n') - file.write(' #local mockup1 =\n') - file.write(' #if(_merge_shape) merge{ #else union{ #end\n') - file.write(' #for(i, 0, n_of_seg-1)\n') - file.write(' #local has_head = true;\n') - file.write(' #if(i = 0)\n') - file.write( - ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n' - ) - file.write(' #local has_head = false;\n') - file.write(' #end\n') - file.write(' #else\n') - file.write( - ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n' - ) - file.write(' #local has_head = false;\n') - file.write(' #end\n') - file.write(' #end\n') - file.write(' #if(has_head = true)\n') - file.write(' sphere{\n') - file.write( - ' ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n' - ) - file.write(' }\n') - file.write(' #end\n') - file.write(' #local para_t = (1/2)/res;\n') - file.write( - ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n' - ) - file.write( - ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n' - ) - file.write( - ' #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n' - ) - file.write(' object{\n') - file.write( - ' Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n' - ) - file.write(' }\n') - file.write(' #end\n') - file.write(' sphere{\n') - file.write(' this_point, this_radius\n') - file.write(' }\n') - file.write(' #for(j, 1, res-1)\n') - file.write(' #local last_point = this_point;\n') - file.write( - ' #local last_radius = this_radius;\n' - ) - file.write(' #local para_t = (1/2+j)/res;\n') - file.write( - ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n' - ) - file.write( - ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n' - ) - file.write( - ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n' - ) - file.write(' object{\n') - file.write( - ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n' - ) - file.write(' }\n') - file.write(' #end\n') - file.write(' sphere{\n') - file.write(' this_point, this_radius\n') - file.write(' }\n') - file.write(' #end\n') - file.write(' #local last_point = this_point;\n') - file.write(' #local last_radius = this_radius;\n') - file.write( - ' #local this_point = ctrl_pts_array[i][3];\n' - ) - file.write( - ' #local this_radius = ctrl_rs_array[i][3];\n' - ) - file.write( - ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n' - ) - file.write(' object{\n') - file.write( - ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n' - ) - file.write(' }\n') - file.write(' #end\n') - file.write(' sphere{\n') - file.write(' this_point, this_radius\n') - file.write(' }\n') - file.write(' #end\n') - file.write(' }\n') - file.write(' mockup1\n') - file.write(' #end\n') - - for spl in ob.data.splines: - if spl.type == "BEZIER": - bezier_sweep = True - if ob.pov.curveshape in {'loft', 'birail'}: - n = 0 - for spline in ob.data.splines: - n += 1 - tabWrite('#declare %s%s=spline {\n' % (dataname, n)) - tabWrite('cubic_spline\n') - lp = len(spline.points) - delta = 1 / (lp) - d = -delta - point = spline.points[lp - 1] - x, y, z, w = point.co[:] - tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) - d += delta - for point in spline.points: - x, y, z, w = point.co[:] - tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) - d += delta - for i in range(2): - point = spline.points[i] - x, y, z, w = point.co[:] - tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z)) - d += delta - tabWrite('}\n') - if ob.pov.curveshape in {'loft'}: - n = len(ob.data.splines) - tabWrite('#declare %s = array[%s]{\n' % (dataname, (n + 3))) - tabWrite('spline{%s%s},\n' % (dataname, n)) - for i in range(n): - tabWrite('spline{%s%s},\n' % (dataname, (i + 1))) - tabWrite('spline{%s1},\n' % (dataname)) - tabWrite('spline{%s2}\n' % (dataname)) - tabWrite('}\n') - # Use some of the Meshmaker.inc macro, here inlined - file.write('#macro CheckFileName(FileName)\n') - file.write(' #local Len=strlen(FileName);\n') - file.write(' #if(Len>0)\n') - file.write(' #if(file_exists(FileName))\n') - file.write(' #if(Len>=4)\n') - file.write( - ' #local Ext=strlwr(substr(FileName,Len-3,4))\n' - ) - file.write( - ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n' - ) - file.write(' #local Return=99;\n') - file.write(' #else\n') - file.write(' #local Return=0;\n') - file.write(' #end\n') - file.write(' #else\n') - file.write(' #local Return=0;\n') - file.write(' #end\n') - file.write(' #else\n') - file.write(' #if(Len>=4)\n') - file.write( - ' #local Ext=strlwr(substr(FileName,Len-3,4))\n' - ) - file.write( - ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n' - ) - file.write(' #if (strcmp(Ext,".obj")=0)\n') - file.write(' #local Return=2;\n') - file.write(' #end\n') - file.write(' #if (strcmp(Ext,".pcm")=0)\n') - file.write(' #local Return=3;\n') - file.write(' #end\n') - file.write(' #if (strcmp(Ext,".arr")=0)\n') - file.write(' #local Return=4;\n') - file.write(' #end\n') - file.write(' #else\n') - file.write(' #local Return=1;\n') - file.write(' #end\n') - file.write(' #else\n') - file.write(' #local Return=1;\n') - file.write(' #end\n') - file.write(' #end\n') - file.write(' #else\n') - file.write(' #local Return=1;\n') - file.write(' #end\n') - file.write(' (Return)\n') - file.write('#end\n') - - file.write('#macro BuildSpline(Arr, SplType)\n') - file.write(' #local Ds=dimension_size(Arr,1);\n') - file.write(' #local Asc=asc(strupr(SplType));\n') - file.write(' #if(Asc!=67 & Asc!=76 & Asc!=81) \n') - file.write(' #local Asc=76;\n') - file.write( - ' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n' - ) - file.write(' #end\n') - file.write(' spline {\n') - file.write(' #switch (Asc)\n') - file.write(' #case (67) //C cubic_spline\n') - file.write(' cubic_spline\n') - file.write(' #break\n') - file.write(' #case (76) //L linear_spline\n') - file.write(' linear_spline\n') - file.write(' #break\n') - file.write(' #case (78) //N linear_spline\n') - file.write(' natural_spline\n') - file.write(' #break\n') - file.write(' #case (81) //Q Quadratic_spline\n') - file.write(' quadratic_spline\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' #local Add=1/((Ds-2)-1);\n') - file.write(' #local J=0-Add;\n') - file.write(' #local I=0;\n') - file.write(' #while (I<Ds)\n') - file.write(' J\n') - file.write(' Arr[I]\n') - file.write(' #local I=I+1;\n') - file.write(' #local J=J+Add;\n') - file.write(' #end\n') - file.write(' }\n') - file.write('#end\n') - - file.write( - '#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n' - ) - # suppressed some file checking from original macro because no more separate files - file.write(' #local Write=0;\n') - file.write( - ' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n' - ) - file.write(' #local NumVertices=dimension_size(VecArr,1);\n') - file.write(' #switch (Write)\n') - file.write(' #case(1)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write(' " vertex_vectors {\\n",\n') - file.write(' " ", str(NumVertices,0,0),"\\n "\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write(' str(2*NumVertices,0,0),",\\n"\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' mesh2 {\n') - file.write(' vertex_vectors {\n') - file.write(' NumVertices\n') - file.write(' #local I=0;\n') - file.write(' #while (I<NumVertices)\n') - file.write(' VecArr[I]\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile, VecArr[I])\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile, VecArr[I])\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' #local I=I+1;\n') - file.write(' #if(Write=1 | Write=4)\n') - file.write(' #if(mod(I,3)=0)\n') - file.write(' #write(MeshFile,"\\n ")\n') - file.write(' #end\n') - file.write(' #end \n') - file.write(' #end\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile,"\\n }\\n")\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(MeshFile,"\\n")\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' // do nothing\n') - file.write(' #break\n') - file.write(' #case(4) \n') - file.write(' #write(MeshFile,"\\n}\\n")\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' }\n') - - file.write(' #debug concat(" - normal_vectors\\n") \n') - file.write(' #local NumVertices=dimension_size(NormArr,1);\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write(' " normal_vectors {\\n",\n') - file.write(' " ", str(NumVertices,0,0),"\\n "\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "# Normals: ",str(NumVertices,0,0),"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' // do nothing\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' normal_vectors {\n') - file.write(' NumVertices\n') - file.write(' #local I=0;\n') - file.write(' #while (I<NumVertices)\n') - file.write(' NormArr[I]\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile NormArr[I])\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile NormArr[I])\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' #local I=I+1;\n') - file.write(' #if(Write=1 | Write=4) \n') - file.write(' #if(mod(I,3)=0)\n') - file.write(' #write(MeshFile,"\\n ")\n') - file.write(' #end\n') - file.write(' #end\n') - file.write(' #end\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile,"\\n }\\n")\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(MeshFile,"\\n")\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' //do nothing\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile,"\\n}\\n")\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' }\n') - - file.write(' #debug concat(" - uv_vectors\\n") \n') - file.write(' #local NumVertices=dimension_size(UVArr,1);\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(\n') - file.write(' MeshFile, \n') - file.write(' " uv_vectors {\\n",\n') - file.write(' " ", str(NumVertices,0,0),"\\n "\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write( - ' // do nothing, *.pcm does not support uv-vectors\n' - ) - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' uv_vectors {\n') - file.write(' NumVertices\n') - file.write(' #local I=0;\n') - file.write(' #while (I<NumVertices)\n') - file.write(' UVArr[I]\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile UVArr[I])\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' //do nothing\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile UVArr[I])\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' #local I=I+1; \n') - file.write(' #if(Write=1 | Write=4)\n') - file.write(' #if(mod(I,3)=0)\n') - file.write(' #write(MeshFile,"\\n ")\n') - file.write(' #end \n') - file.write(' #end\n') - file.write(' #end \n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile,"\\n }\\n")\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(MeshFile,"\\n")\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' //do nothing\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile,"\\n}\\n")\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' }\n') - file.write('\n') - file.write(' #debug concat(" - face_indices\\n") \n') - file.write(' #declare NumFaces=U*V*2;\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write(' " face_indices {\\n"\n') - file.write(' " ", str(NumFaces,0,0),"\\n "\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write (\n') - file.write(' MeshFile,\n') - file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #write (\n') - file.write(' MeshFile,\n') - file.write(' "0,",str(NumFaces,0,0),",\\n"\n') - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' face_indices {\n') - file.write(' NumFaces\n') - file.write(' #local I=0;\n') - file.write(' #local H=0;\n') - file.write(' #local NumVertices=dimension_size(VecArr,1);\n') - file.write(' #while (I<V)\n') - file.write(' #local J=0;\n') - file.write(' #while (J<U)\n') - file.write(' #local Ind=(I*U)+I+J;\n') - file.write( - ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n' - ) - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n' - ) - file.write( - ' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n' - ) - file.write( - ' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(\n') - file.write(' MeshFile,\n') - file.write( - ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n' - ) - file.write(' )\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' #local J=J+1;\n') - file.write(' #local H=H+1;\n') - file.write(' #if(Write=1 | Write=4)\n') - file.write(' #if(mod(H,3)=0)\n') - file.write(' #write(MeshFile,"\\n ")\n') - file.write(' #end \n') - file.write(' #end\n') - file.write(' #end\n') - file.write(' #local I=I+1;\n') - file.write(' #end\n') - file.write(' }\n') - file.write(' #switch(Write)\n') - file.write(' #case(1)\n') - file.write(' #write(MeshFile, "\\n }\\n}")\n') - file.write(' #fclose MeshFile\n') - file.write(' #debug concat(" Done writing\\n")\n') - file.write(' #break\n') - file.write(' #case(2)\n') - file.write(' #fclose MeshFile\n') - file.write(' #debug concat(" Done writing\\n")\n') - file.write(' #break\n') - file.write(' #case(3)\n') - file.write(' #fclose MeshFile\n') - file.write(' #debug concat(" Done writing\\n")\n') - file.write(' #break\n') - file.write(' #case(4)\n') - file.write(' #write(MeshFile, "\\n}\\n}")\n') - file.write(' #fclose MeshFile\n') - file.write(' #debug concat(" Done writing\\n")\n') - file.write(' #break\n') - file.write(' #end\n') - file.write(' }\n') - file.write('#end\n') - - file.write( - '#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n' - ) - file.write(' #declare Build=CheckFileName(FileName);\n') - file.write(' #if(Build=0)\n') - file.write( - ' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n' - ) - file.write(' #include FileName\n') - file.write(' object{Surface}\n') - file.write(' #else\n') - file.write(' #local NumVertices=(SplRes+1)*(InterpRes+1);\n') - file.write(' #local NumFaces=SplRes*InterpRes*2;\n') - file.write( - ' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n' - ) - file.write(' #local VecArr=array[NumVertices]\n') - file.write(' #local NormArr=array[NumVertices]\n') - file.write(' #local UVArr=array[NumVertices]\n') - file.write(' #local N=dimension_size(SplineArray,1);\n') - file.write(' #local TempSplArr0=array[N];\n') - file.write(' #local TempSplArr1=array[N];\n') - file.write(' #local TempSplArr2=array[N];\n') - file.write(' #local PosStep=1/SplRes;\n') - file.write(' #local InterpStep=1/InterpRes;\n') - file.write(' #local Count=0;\n') - file.write(' #local Pos=0;\n') - file.write(' #while(Pos<=1)\n') - file.write(' #local I=0;\n') - file.write(' #if (Pos=0)\n') - file.write(' #while (I<N)\n') - file.write( - ' #local Spl=spline{SplineArray[I]}\n' - ) - file.write( - ' #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n' - ) - file.write( - ' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n' - ) - file.write( - ' #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n' - ) - file.write(' #local I=I+1;\n') - file.write(' #end\n') - file.write( - ' #local S0=BuildSpline(TempSplArr0, Interp_type)\n' - ) - file.write( - ' #local S1=BuildSpline(TempSplArr1, Interp_type)\n' - ) - file.write( - ' #local S2=BuildSpline(TempSplArr2, Interp_type)\n' - ) - file.write(' #else\n') - file.write(' #while (I<N)\n') - file.write( - ' #local Spl=spline{SplineArray[I]}\n' - ) - file.write( - ' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n' - ) - file.write(' #local I=I+1;\n') - file.write(' #end\n') - file.write( - ' #local S1=BuildSpline(TempSplArr1, Interp_type)\n' - ) - file.write(' #end\n') - file.write(' #local J=0;\n') - file.write(' #while (J<=1)\n') - file.write(' #local P0=<0,0,0>+S0(J);\n') - file.write(' #local P1=<0,0,0>+S1(J);\n') - file.write(' #local P2=<0,0,0>+S2(J);\n') - file.write(' #local P3=<0,0,0>+S0(J+InterpStep);\n') - file.write(' #local P4=<0,0,0>+S0(J-InterpStep);\n') - file.write(' #local B1=P4-P0;\n') - file.write(' #local B2=P2-P0;\n') - file.write(' #local B3=P3-P0;\n') - file.write(' #local B4=P1-P0;\n') - file.write(' #local N1=vcross(B1,B2);\n') - file.write(' #local N2=vcross(B2,B3);\n') - file.write(' #local N3=vcross(B3,B4);\n') - file.write(' #local N4=vcross(B4,B1);\n') - file.write( - ' #local Norm=vnormalize((N1+N2+N3+N4));\n' - ) - file.write(' #local VecArr[Count]=P0;\n') - file.write(' #local NormArr[Count]=Norm;\n') - file.write(' #local UVArr[Count]=<J,Pos>;\n') - file.write(' #local J=J+InterpStep;\n') - file.write(' #local Count=Count+1;\n') - file.write(' #end\n') - file.write(' #local S2=spline{S0}\n') - file.write(' #local S0=spline{S1}\n') - file.write( - ' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n' - ) - file.write(' #local Pos=Pos+PosStep;\n') - file.write(' #end\n') - file.write( - ' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n' - ) - file.write(' #end\n') - file.write('#end\n\n') - - file.write( - '#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n' - ) - file.write(' #declare Build=CheckFileName(FileName);\n') - file.write(' #if(Build=0)\n') - file.write( - ' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n' - ) - file.write(' #include FileName\n') - file.write(' object{Surface}\n') - file.write(' #else\n') - file.write(' #local NumVertices=(Iter_U+1)*(Iter_V+1);\n') - file.write(' #local NumFaces=Iter_U*Iter_V*2;\n') - file.write( - ' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n' - ) - file.write(' #declare VecArr=array[NumVertices] \n') - file.write(' #declare NormArr=array[NumVertices] \n') - file.write(' #local UVArr=array[NumVertices] \n') - file.write(' #local Spl1_0=Spl1(0);\n') - file.write(' #local Spl2_0=Spl2(0);\n') - file.write(' #local Spl3_0=Spl3(0);\n') - file.write(' #local Spl4_0=Spl4(0);\n') - file.write(' #local UStep=1/Iter_U;\n') - file.write(' #local VStep=1/Iter_V;\n') - file.write(' #local Count=0;\n') - file.write(' #local I=0;\n') - file.write(' #while (I<=1)\n') - file.write(' #local Im=1-I;\n') - file.write(' #local J=0;\n') - file.write(' #while (J<=1)\n') - file.write(' #local Jm=1-J;\n') - file.write( - ' #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n' - ) - file.write( - ' #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n' - ) - file.write( - ' LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n' - ) - file.write(' #declare VecArr[Count]=P0;\n') - file.write(' #local UVArr[Count]=<J,I>;\n') - file.write(' #local J=J+UStep;\n') - file.write(' #local Count=Count+1;\n') - file.write(' #end\n') - file.write(' #debug concat(\n') - file.write( - ' "\r Done ", str(Count,0,0)," vertices : ",\n' - ) - file.write(' str(100*Count/NumVertices,0,2)," %"\n') - file.write(' )\n') - file.write(' #local I=I+VStep;\n') - file.write(' #end\n') - file.write( - ' #debug "\r Normals "\n' - ) - file.write(' #local Count=0;\n') - file.write(' #local I=0;\n') - file.write(' #while (I<=Iter_V)\n') - file.write(' #local J=0;\n') - file.write(' #while (J<=Iter_U)\n') - file.write(' #local Ind=(I*Iter_U)+I+J;\n') - file.write(' #local P0=VecArr[Ind];\n') - file.write(' #if(J=0)\n') - file.write(' #local P1=P0+(P0-VecArr[Ind+1]);\n') - file.write(' #else\n') - file.write(' #local P1=VecArr[Ind-1];\n') - file.write(' #end\n') - file.write(' #if (J=Iter_U)\n') - file.write(' #local P2=P0+(P0-VecArr[Ind-1]);\n') - file.write(' #else\n') - file.write(' #local P2=VecArr[Ind+1];\n') - file.write(' #end\n') - file.write(' #if (I=0)\n') - file.write( - ' #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n' - ) - file.write(' #else\n') - file.write(' #local P3=VecArr[Ind-Iter_U-1];\n') - file.write(' #end\n') - file.write(' #if (I=Iter_V)\n') - file.write( - ' #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n' - ) - file.write(' #else\n') - file.write(' #local P4=VecArr[Ind+Iter_U+1];\n') - file.write(' #end\n') - file.write(' #local B1=P4-P0;\n') - file.write(' #local B2=P2-P0;\n') - file.write(' #local B3=P3-P0;\n') - file.write(' #local B4=P1-P0;\n') - file.write(' #local N1=vcross(B1,B2);\n') - file.write(' #local N2=vcross(B2,B3);\n') - file.write(' #local N3=vcross(B3,B4);\n') - file.write(' #local N4=vcross(B4,B1);\n') - file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n') - file.write(' #declare NormArr[Count]=Norm;\n') - file.write(' #local J=J+1;\n') - file.write(' #local Count=Count+1;\n') - file.write(' #end\n') - file.write( - ' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n' - ) - file.write(' #local I=I+1;\n') - file.write(' #end\n') - file.write( - ' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n' - ) - file.write(' #end\n') - file.write('#end\n\n') - # Empty curves - if len(ob.data.splines) == 0: - tabWrite("\n//dummy sphere to represent empty curve location\n") - tabWrite("#declare %s =\n" % dataname) - tabWrite( - "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n" - % (ob.location.x, ob.location.y, ob.location.z) - ) # ob.name > povdataname) - # And non empty curves - else: - if bezier_sweep == False: - tabWrite("#declare %s =\n" % dataname) - if ob.pov.curveshape == 'sphere_sweep' and bezier_sweep == False: - tabWrite("union {\n") - for spl in ob.data.splines: - if spl.type != "BEZIER": - spl_type = "linear" - if spl.type == "NURBS": - spl_type = "cubic" - points = spl.points - numPoints = len(points) - if spl.use_cyclic_u: - numPoints += 3 - - tabWrite( - "sphere_sweep { %s_spline %s,\n" - % (spl_type, numPoints) - ) - if spl.use_cyclic_u: - pt1 = points[len(points) - 1] - wpt1 = pt1.co - tabWrite( - "<%.4g,%.4g,%.4g>,%.4g\n" - % ( - wpt1[0], - wpt1[1], - wpt1[2], - pt1.radius * ob.data.bevel_depth, - ) - ) - for pt in points: - wpt = pt.co - tabWrite( - "<%.4g,%.4g,%.4g>,%.4g\n" - % ( - wpt[0], - wpt[1], - wpt[2], - pt.radius * ob.data.bevel_depth, - ) - ) - if spl.use_cyclic_u: - for i in range(0, 2): - endPt = points[i] - wpt = endPt.co - tabWrite( - "<%.4g,%.4g,%.4g>,%.4g\n" - % ( - wpt[0], - wpt[1], - wpt[2], - endPt.radius * ob.data.bevel_depth, - ) - ) - - tabWrite("}\n") - # below not used yet? - if ob.pov.curveshape == 'sor': - for spl in ob.data.splines: - if spl.type in {'POLY', 'NURBS'}: - points = spl.points - numPoints = len(points) - tabWrite("sor { %s,\n" % numPoints) - for pt in points: - wpt = pt.co - tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) - else: - tabWrite("box { 0,0\n") - if ob.pov.curveshape in {'lathe', 'prism'}: - spl = ob.data.splines[0] - if spl.type == "BEZIER": - points = spl.bezier_points - lenCur = len(points) - 1 - lenPts = lenCur * 4 - ifprism = '' - if ob.pov.curveshape in {'prism'}: - height = ob.data.extrude - ifprism = '-%s, %s,' % (height, height) - lenCur += 1 - lenPts += 4 - tabWrite( - "%s { bezier_spline %s %s,\n" - % (ob.pov.curveshape, ifprism, lenPts) - ) - for i in range(0, lenCur): - p1 = points[i].co - pR = points[i].handle_right - end = i + 1 - if i == lenCur - 1 and ob.pov.curveshape in {'prism'}: - end = 0 - pL = points[end].handle_left - p2 = points[end].co - line = "<%.4g,%.4g>" % (p1[0], p1[1]) - line += "<%.4g,%.4g>" % (pR[0], pR[1]) - line += "<%.4g,%.4g>" % (pL[0], pL[1]) - line += "<%.4g,%.4g>" % (p2[0], p2[1]) - tabWrite("%s\n" % line) - else: - points = spl.points - lenCur = len(points) - lenPts = lenCur - ifprism = '' - if ob.pov.curveshape in {'prism'}: - height = ob.data.extrude - ifprism = '-%s, %s,' % (height, height) - lenPts += 3 - spl_type = 'quadratic' - if spl.type == 'POLY': - spl_type = 'linear' - tabWrite( - "%s { %s_spline %s %s,\n" - % (ob.pov.curveshape, spl_type, ifprism, lenPts) - ) - if ob.pov.curveshape in {'prism'}: - pt = points[len(points) - 1] - wpt = pt.co - tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) - for pt in points: - wpt = pt.co - tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) - if ob.pov.curveshape in {'prism'}: - for i in range(2): - pt = points[i] - wpt = pt.co - tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1])) - if bezier_sweep: - for p in range(len(ob.data.splines)): - br = [] - depth = ob.data.bevel_depth - spl = ob.data.splines[p] - points = spl.bezier_points - lenCur = len(points) - 1 - numPoints = lenCur * 4 - if spl.use_cyclic_u: - lenCur += 1 - numPoints += 4 - tabWrite( - "#declare %s_points_%s = array[%s]{\n" - % (dataname, p, numPoints) - ) - for i in range(lenCur): - p1 = points[i].co - pR = points[i].handle_right - end = i + 1 - if spl.use_cyclic_u and i == (lenCur - 1): - end = 0 - pL = points[end].handle_left - p2 = points[end].co - r3 = points[end].radius * depth - r0 = points[i].radius * depth - r1 = 2 / 3 * r0 + 1 / 3 * r3 - r2 = 1 / 3 * r0 + 2 / 3 * r3 - br.append((r0, r1, r2, r3)) - line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2]) - line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2]) - line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2]) - line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2]) - tabWrite("%s\n" % line) - tabWrite("}\n") - tabWrite( - "#declare %s_radii_%s = array[%s]{\n" - % (dataname, p, len(br) * 4) - ) - for Tuple in br: - tabWrite( - '%.4f,%.4f,%.4f,%.4f\n' - % (Tuple[0], Tuple[1], Tuple[2], Tuple[3]) - ) - tabWrite("}\n") - if len(ob.data.splines) == 1: - tabWrite('#declare %s = object{\n' % dataname) - tabWrite( - ' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n' - % (ob.data.resolution_u, dataname, p, dataname, p) - ) - else: - tabWrite('#declare %s = union{\n' % dataname) - for p in range(len(ob.data.splines)): - tabWrite( - ' object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n' - % (ob.data.resolution_u, dataname, p, dataname, p) - ) - # tabWrite('#include "bezier_spheresweep.inc"\n') #now inlined - # tabWrite('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth)) - if ob.pov.curveshape in {'loft'}: - tabWrite( - 'object {MSM(%s,%s,"c",%s,"")\n' - % (dataname, ob.pov.res_u, ob.pov.res_v) - ) - if ob.pov.curveshape in {'birail'}: - splines = '%s1,%s2,%s3,%s4' % ( - dataname, - dataname, - dataname, - dataname, - ) - tabWrite( - 'object {Coons(%s, %s, %s, "")\n' - % (splines, ob.pov.res_u, ob.pov.res_v) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - if ob.pov.curveshape in {'prism'}: - tabWrite("rotate <90,0,0>\n") - tabWrite("scale y*-1\n") - tabWrite("}\n") - ################################################################# - def exportMeta(metas): + def export_meta(metas): """write all POV blob primitives and Blender Metas to exported file """ # TODO - blenders 'motherball' naming is not supported. @@ -2052,8 +324,7 @@ def write_pov(filename, scene=None, info_callback=None): elems = [ (elem, ob) for elem in ob.data.elements - if elem.type - in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'} + if elem.type in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'} ] if prefix in meta_elems: meta_elems[prefix].extend(elems) @@ -2062,19 +333,16 @@ def write_pov(filename, scene=None, info_callback=None): # empty metaball if len(elems) == 0: - tabWrite("\n//dummy sphere to represent empty meta location\n") - tabWrite( + tab_write("\n//dummy sphere to represent empty meta location\n") + tab_write( "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n" % (ob.location.x, ob.location.y, ob.location.z) ) # ob.name > povdataname) # other metaballs else: - for mg, ob in meta_group.items(): + for mg, mob in meta_group.items(): if len(meta_elems[mg]) != 0: - tabWrite( - "blob{threshold %.4g // %s \n" - % (ob.data.threshold, mg) - ) + tab_write("blob{threshold %.4g // %s \n" % (mob.data.threshold, mg)) for elems in meta_elems[mg]: elem = elems[0] loc = elem.co @@ -2082,22 +350,14 @@ def write_pov(filename, scene=None, info_callback=None): if elem.use_negative: stiffness = -stiffness if elem.type == 'BALL': - tabWrite( + tab_write( "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g " - % ( - loc.x, - loc.y, - loc.z, - elem.radius, - stiffness, - ) + % (loc.x, loc.y, loc.z, elem.radius, stiffness) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") elif elem.type == 'ELLIPSOID': - tabWrite( + tab_write( "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g " % ( loc.x / elem.size_x, @@ -2107,16 +367,14 @@ def write_pov(filename, scene=None, info_callback=None): stiffness, ) ) - tabWrite( + tab_write( "scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") elif elem.type == 'CAPSULE': - tabWrite( + tab_write( "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g " % ( (loc.x - elem.size_x), @@ -2129,14 +387,12 @@ def write_pov(filename, scene=None, info_callback=None): stiffness, ) ) - # tabWrite("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z)) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") + # tab_write("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z)) + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") elif elem.type == 'CUBE': - tabWrite( + tab_write( "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n" % ( elem.radius * 2.0, @@ -2149,11 +405,9 @@ def write_pov(filename, scene=None, info_callback=None): elem.size_z, ) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") - tabWrite( + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + tab_write( "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n" % ( elem.radius * 2.0, @@ -2166,11 +420,9 @@ def write_pov(filename, scene=None, info_callback=None): elem.size_z, ) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") - tabWrite( + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + tab_write( "cylinder { -z*8, +z*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1,1/4> scale <%.6g, %.6g, %.6g>\n" % ( elem.radius * 2.0, @@ -2183,13 +435,11 @@ def write_pov(filename, scene=None, info_callback=None): elem.size_z, ) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") elif elem.type == 'PLANE': - tabWrite( + tab_write( "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n" % ( elem.radius * 2.0, @@ -2202,11 +452,9 @@ def write_pov(filename, scene=None, info_callback=None): elem.size_z, ) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") - tabWrite( + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + tab_write( "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n" % ( elem.radius * 2.0, @@ -2219,16 +467,16 @@ def write_pov(filename, scene=None, info_callback=None): elem.size_z, ) ) - writeMatrix( - global_matrix @ elems[1].matrix_world - ) - tabWrite("}\n") + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") try: material = elems[1].data.materials[ 0 ] # lame! - blender cant do enything else. - except: + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) material = None if material: diffuse_color = material.diffuse_color @@ -2237,40 +485,34 @@ def write_pov(filename, scene=None, info_callback=None): material.use_transparency and material.transparency_method == 'RAYTRACE' ): - povFilter = ( - material.pov_raytrace_transparency.filter - * (1.0 - material.alpha) + pov_filter = material.pov_raytrace_transparency.filter * ( + 1.0 - material.alpha ) - trans = (1.0 - material.pov.alpha) - povFilter + trans = (1.0 - material.pov.alpha) - pov_filter else: - povFilter = 0.0 - material_finish = materialNames[material.name] - tabWrite( + pov_filter = 0.0 + material_finish = material_names_dictionary[material.name] + tab_write( "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" % ( diffuse_color[0], diffuse_color[1], diffuse_color[2], - povFilter, + pov_filter, trans, ) ) - tabWrite( - "finish{%s} " % safety(material_finish, Level=2) - ) + tab_write("finish{%s} " % safety(material_finish, ref_level_bound=2)) else: - tabWrite( + tab_write( "pigment{srgb 1} finish{%s} " - % (safety(DEF_MAT_NAME, Level=2)) + % (safety(DEF_MAT_NAME, ref_level_bound=2)) ) - writeObjectMaterial(material, ob) - # writeObjectMaterial(material, elems[1]) - tabWrite( - "radiosity{importance %3g}\n" - % ob.pov.importance_value - ) - tabWrite("}\n\n") # End of Metaball block + write_object_material(material, mob, tab_write) + # write_object_material(material, elems[1]) + tab_write("radiosity{importance %3g}\n" % mob.pov.importance_value) + tab_write("}\n\n") # End of Metaball block ''' meta = ob.data @@ -2279,8 +521,8 @@ def write_pov(filename, scene=None, info_callback=None): elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}] if elements: - tabWrite("blob {\n") - tabWrite("threshold %.4g\n" % meta.threshold) + tab_write("blob {\n") + tab_write("threshold %.4g\n" % meta.threshold) importance = ob.pov.importance_value try: @@ -2297,7 +539,7 @@ def write_pov(filename, scene=None, info_callback=None): if elem.type == 'BALL': - tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" % + tab_write("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" % (loc.x, loc.y, loc.z, elem.radius, stiffness)) # After this wecould do something simple like... @@ -2306,2682 +548,137 @@ def write_pov(filename, scene=None, info_callback=None): elif elem.type == 'ELLIPSOID': # location is modified by scale - tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" % + tab_write("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" % (loc.x / elem.size_x, loc.y / elem.size_y, loc.z / elem.size_z, elem.radius, stiffness)) - tabWrite("scale <%.6g, %.6g, %.6g> \n" % + tab_write("scale <%.6g, %.6g, %.6g> \n" % (elem.size_x, elem.size_y, elem.size_z)) if material: diffuse_color = material.diffuse_color trans = 1.0 - material.pov.alpha if material.use_transparency and material.transparency_method == 'RAYTRACE': - povFilter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha) - trans = (1.0 - material.pov.alpha) - povFilter + pov_filter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha) + trans = (1.0 - material.pov.alpha) - pov_filter else: - povFilter = 0.0 + pov_filter = 0.0 - material_finish = materialNames[material.name] + material_finish = material_names_dictionary[material.name] - tabWrite("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" % + tab_write("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" % (diffuse_color[0], diffuse_color[1], diffuse_color[2], - povFilter, trans)) - tabWrite("finish {%s}\n" % safety(material_finish, Level=2)) + pov_filter, trans)) + tab_write("finish {%s}\n" % safety(material_finish, ref_level_bound=2)) else: - tabWrite("pigment {srgb 1} \n") + tab_write("pigment {srgb 1} \n") # Write the finish last. - tabWrite("finish {%s}\n" % (safety(DEF_MAT_NAME, Level=2))) + tab_write("finish {%s}\n" % (safety(DEF_MAT_NAME, ref_level_bound=2))) - writeObjectMaterial(material, elems[1]) + write_object_material(material, elems[1]) - writeMatrix(global_matrix @ ob.matrix_world) + write_matrix(global_matrix @ ob.matrix_world) # Importance for radiosity sampling added here - tabWrite("radiosity { \n") + tab_write("radiosity { \n") # importance > ob.pov.importance_value - tabWrite("importance %3g \n" % importance) - tabWrite("}\n") + tab_write("importance %3g \n" % importance) + tab_write("}\n") - tabWrite("}\n") # End of Metaball block + tab_write("}\n") # End of Metaball block if comments and len(metas) >= 1: file.write("\n") ''' - # objectNames = {} - DEF_OBJ_NAME = "Default" - - def exportMeshes(scene, sel, csg): - """write all meshes as POV mesh2{} syntax to exported file """ - #some numpy functions to speed up mesh export - - # TODO: also write a numpy function to read matrices at object level? - # feed below with mesh object.data, but only after doing data.calc_loop_triangles() - def read_verts_co(self, mesh): - #'float64' would be a slower 64-bit floating-point number numpy datatype - # using 'float32' vert coordinates for now until any issue is reported - mverts_co = np.zeros((len(mesh.vertices)*3), dtype=np.float32) - mesh.vertices.foreach_get("co", mverts_co) - return np.reshape(mverts_co, (len(mesh.vertices), 3)) - - def read_verts_idx(self, mesh): - mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64) - mesh.vertices.foreach_get("index", mverts_idx) - return np.reshape(mverts_idx, (len(mesh.vertices), 1)) - - def read_verts_norms(self, mesh): - #'float64' would be a slower 64-bit floating-point number numpy datatype - # using less accurate 'float16' normals for now until any issue is reported - mverts_no = np.zeros((len(mesh.vertices)*3), dtype=np.float16) - mesh.vertices.foreach_get("normal", mverts_no) - return np.reshape(mverts_no, (len(mesh.vertices), 3)) - - def read_faces_idx(self, mesh): - mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64) - mesh.loop_triangles.foreach_get("index", mfaces_idx) - return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1)) - - def read_faces_verts_indices(self, mesh): - mfaces_verts_idx = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64) - mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx) - return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3)) - - #Why is below different from verex indices? - def read_faces_verts_loops(self, mesh): - mfaces_verts_loops = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64) - mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops) - return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3)) - - def read_faces_norms(self, mesh): - #'float64' would be a slower 64-bit floating-point number numpy datatype - # using less accurate 'float16' normals for now until any issue is reported - mfaces_no = np.zeros((len(mesh.loop_triangles)*3), dtype=np.float16) - mesh.loop_triangles.foreach_get("normal", mfaces_no) - return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3)) - - def read_faces_smooth(self, mesh): - mfaces_smth = np.zeros((len(mesh.loop_triangles)*1), dtype=np.bool) - mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth) - return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1)) - - def read_faces_material_indices(self, mesh): - mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16) - mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx) - return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1)) - - - - # obmatslist = [] - # def hasUniqueMaterial(): - # # Grab materials attached to object instances ... - # if hasattr(ob, 'material_slots'): - # for ms in ob.material_slots: - # if ms.material is not None and ms.link == 'OBJECT': - # if ms.material in obmatslist: - # return False - # else: - # obmatslist.append(ms.material) - # return True - # def hasObjectMaterial(ob): - # # Grab materials attached to object instances ... - # if hasattr(ob, 'material_slots'): - # for ms in ob.material_slots: - # if ms.material is not None and ms.link == 'OBJECT': - # # If there is at least one material slot linked to the object - # # and not the data (mesh), always create a new, "private" data instance. - # return True - # return False - # For objects using local material(s) only! - # This is a mapping between a tuple (dataname, materialnames, ...), and the POV dataname. - # As only objects using: - # * The same data. - # * EXACTLY the same materials, in EXACTLY the same sockets. - # ... can share a same instance in POV export. - obmats2data = {} - - def checkObjectMaterials(ob, name, dataname): - if hasattr(ob, 'material_slots'): - has_local_mats = False - key = [dataname] - for ms in ob.material_slots: - if ms.material is not None: - key.append(ms.material.name) - if ms.link == 'OBJECT' and not has_local_mats: - has_local_mats = True - else: - # Even if the slot is empty, it is important to grab it... - key.append("") - if has_local_mats: - # If this object uses local material(s), lets find if another object - # using the same data and exactly the same list of materials - # (in the same slots) has already been processed... - # Note that here also, we use object name as new, unique dataname for Pov. - key = tuple(key) # Lists are not hashable... - if key not in obmats2data: - obmats2data[key] = name - return obmats2data[key] - return None - - data_ref = {} - - def store(scene, ob, name, dataname, matrix): - # The Object needs to be written at least once but if its data is - # already in data_ref this has already been done. - # This func returns the "povray" name of the data, or None - # if no writing is needed. - if ob.is_modified(scene, 'RENDER'): - # Data modified. - # Create unique entry in data_ref by using object name - # (always unique in Blender) as data name. - data_ref[name] = [(name, MatrixAsPovString(matrix))] - return name - # Here, we replace dataname by the value returned by checkObjectMaterials, only if - # it is not evaluated to False (i.e. only if the object uses some local material(s)). - dataname = checkObjectMaterials(ob, name, dataname) or dataname - if dataname in data_ref: - # Data already known, just add the object instance. - data_ref[dataname].append((name, MatrixAsPovString(matrix))) - # No need to write data - return None - else: - # Data not yet processed, create a new entry in data_ref. - data_ref[dataname] = [(name, MatrixAsPovString(matrix))] - return dataname - - def exportSmoke(smoke_obj_name): - # if LuxManager.CurrentScene.name == 'preview': - # return 1, 1, 1, 1.0 - # else: - flowtype = -1 - smoke_obj = bpy.data.objects[smoke_obj_name] - domain = None - - # Search smoke domain target for smoke modifiers - for mod in smoke_obj.modifiers: - if mod.name == 'Smoke': - if mod.smoke_type == 'FLOW': - if mod.flow_settings.smoke_flow_type == 'BOTH': - flowtype = 2 - else: - if mod.flow_settings.smoke_flow_type == 'SMOKE': - flowtype = 0 - else: - if mod.flow_settings.smoke_flow_type == 'FIRE': - flowtype = 1 - - if mod.smoke_type == 'DOMAIN': - domain = smoke_obj - smoke_modifier = mod - - eps = 0.000001 - if domain is not None: - # if bpy.app.version[0] >= 2 and bpy.app.version[1] >= 71: - # Blender version 2.71 supports direct access to smoke data structure - set = mod.domain_settings - channeldata = [] - for v in set.density_grid: - channeldata.append(v.real) - print(v.real) - ## Usage en voxel texture: - # channeldata = [] - # if channel == 'density': - # for v in set.density_grid: - # channeldata.append(v.real) - - # if channel == 'fire': - # for v in set.flame_grid: - # channeldata.append(v.real) - - resolution = set.resolution_max - big_res = [] - big_res.append(set.domain_resolution[0]) - big_res.append(set.domain_resolution[1]) - big_res.append(set.domain_resolution[2]) - - if set.use_high_resolution: - big_res[0] = big_res[0] * (set.amplify + 1) - big_res[1] = big_res[1] * (set.amplify + 1) - big_res[2] = big_res[2] * (set.amplify + 1) - # else: - # p = [] - ##gather smoke domain settings - # BBox = domain.bound_box - # p.append([BBox[0][0], BBox[0][1], BBox[0][2]]) - # p.append([BBox[6][0], BBox[6][1], BBox[6][2]]) - # set = mod.domain_settings - # resolution = set.resolution_max - # smokecache = set.point_cache - # ret = read_cache(smokecache, set.use_high_resolution, set.amplify + 1, flowtype) - # res_x = ret[0] - # res_y = ret[1] - # res_z = ret[2] - # density = ret[3] - # fire = ret[4] - - # if res_x * res_y * res_z > 0: - ##new cache format - # big_res = [] - # big_res.append(res_x) - # big_res.append(res_y) - # big_res.append(res_z) - # else: - # max = domain.dimensions[0] - # if (max - domain.dimensions[1]) < -eps: - # max = domain.dimensions[1] - - # if (max - domain.dimensions[2]) < -eps: - # max = domain.dimensions[2] - - # big_res = [int(round(resolution * domain.dimensions[0] / max, 0)), - # int(round(resolution * domain.dimensions[1] / max, 0)), - # int(round(resolution * domain.dimensions[2] / max, 0))] - - # if set.use_high_resolution: - # big_res = [big_res[0] * (set.amplify + 1), big_res[1] * (set.amplify + 1), - # big_res[2] * (set.amplify + 1)] - - # if channel == 'density': - # channeldata = density - - # if channel == 'fire': - # channeldata = fire - - # sc_fr = '%s/%s/%s/%05d' % (efutil.export_path, efutil.scene_filename(), bpy.context.scene.name, bpy.context.scene.frame_current) - # if not os.path.exists( sc_fr ): - # os.makedirs(sc_fr) - # - # smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name) - # smoke_path = '/'.join([sc_fr, smoke_filename]) - # - # with open(smoke_path, 'wb') as smoke_file: - # # Binary densitygrid file format - # # - # # File header - # smoke_file.write(b'SMOKE') #magic number - # smoke_file.write(struct.pack('<I', big_res[0])) - # smoke_file.write(struct.pack('<I', big_res[1])) - # smoke_file.write(struct.pack('<I', big_res[2])) - # Density data - # smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata)) - # - # LuxLog('Binary SMOKE file written: %s' % (smoke_path)) - - # return big_res[0], big_res[1], big_res[2], channeldata - - mydf3 = df3.df3(big_res[0], big_res[1], big_res[2]) - sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size() - for x in range(sim_sizeX): - for y in range(sim_sizeY): - for z in range(sim_sizeZ): - mydf3.set( - x, - y, - z, - channeldata[ - ((z * sim_sizeY + y) * sim_sizeX + x) - ], - ) - - mydf3.exportDF3(smokePath) - print('Binary smoke.df3 file written in preview directory') - if comments: - file.write("\n//--Smoke--\n\n") - - # Note: We start with a default unit cube. - # This is mandatory to read correctly df3 data - otherwise we could just directly use bbox - # coordinates from the start, and avoid scale/translate operations at the end... - file.write("box{<0,0,0>, <1,1,1>\n") - file.write(" pigment{ rgbt 1 }\n") - file.write(" hollow\n") - file.write(" interior{ //---------------------\n") - file.write(" media{ method 3\n") - file.write( - " emission <1,1,1>*1\n" - ) # 0>1 for dark smoke to white vapour - file.write(" scattering{ 1, // Type\n") - file.write(" <1,1,1>*0.1\n") - file.write(" } // end scattering\n") - file.write( - " density{density_file df3 \"%s\"\n" - % (smokePath) - ) - file.write(" color_map {\n") - file.write(" [0.00 rgb 0]\n") - file.write(" [0.05 rgb 0]\n") - file.write(" [0.20 rgb 0.2]\n") - file.write(" [0.30 rgb 0.6]\n") - file.write(" [0.40 rgb 1]\n") - file.write(" [1.00 rgb 1]\n") - file.write(" } // end color_map\n") - file.write(" } // end of density\n") - file.write( - " samples %i // higher = more precise\n" - % resolution - ) - file.write( - " } // end of media --------------------------\n" - ) - file.write(" } // end of interior\n") - - # START OF TRANSFORMATIONS - - # Size to consider here are bbox dimensions (i.e. still in object space, *before* applying - # loc/rot/scale and other transformations (like parent stuff), aka matrix_world). - bbox = smoke_obj.bound_box - dim = [ - abs(bbox[6][0] - bbox[0][0]), - abs(bbox[6][1] - bbox[0][1]), - abs(bbox[6][2] - bbox[0][2]), - ] - - # We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox). - file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2])) - - # We offset our cube such that (0,0,0) coordinate matches Blender's object center. - file.write( - "translate<%.6g,%.6g,%.6g>\n" - % (bbox[0][0], bbox[0][1], bbox[0][2]) - ) - - # We apply object's transformations to get final loc/rot/size in world space! - # Note: we could combine the two previous transformations with this matrix directly... - writeMatrix(global_matrix @ smoke_obj.matrix_world) - - # END OF TRANSFORMATIONS - - file.write("}\n") - - # file.write(" interpolate 1\n") - # file.write(" frequency 0\n") - # file.write(" }\n") - # file.write("}\n") - - ob_num = 0 - for ob in sel: - # subtract original from the count of their instances as were not counted before 2.8 - if not (ob.is_instancer and ob.original != ob): - ob_num += 1 - - # XXX I moved all those checks here, as there is no need to compute names - # for object we won't export here! - if ob.type in { - 'LIGHT', - 'CAMERA', #'EMPTY', #empties can bear dupligroups - 'META', - 'ARMATURE', - 'LATTICE', - }: - continue - smokeFlag = False - for mod in ob.modifiers: - if mod and hasattr(mod, 'smoke_type'): - smokeFlag = True - if mod.smoke_type == 'DOMAIN': - exportSmoke(ob.name) - break # don't render domain mesh or flow emitter mesh, skip to next object. - if not smokeFlag: - # Export Hair - renderEmitter = True - if hasattr(ob, 'particle_systems'): - renderEmitter = False - if ob.show_instancer_for_render: - renderEmitter = True - for pSys in ob.particle_systems: - for mod in [ - m - for m in ob.modifiers - if (m is not None) - and (m.type == 'PARTICLE_SYSTEM') - ]: - if ( - (pSys.settings.render_type == 'PATH') - and mod.show_render - and (pSys.name == mod.particle_system.name) - ): - tstart = time.time() - texturedHair = 0 - if ( - ob.material_slots[ - pSys.settings.material - 1 - ].material - and ob.active_material is not None - ): - pmaterial = ob.material_slots[ - pSys.settings.material - 1 - ].material - #XXX Todo: replace by pov_(Particles?)_texture_slot - for th in pmaterial.texture_slots: - if th and th.use: - if ( - ( - th.texture.type - == 'IMAGE' - and th.texture.image - ) - or th.texture.type - != 'IMAGE' - ): - if th.use_map_color_diffuse: - texturedHair = 1 - if pmaterial.strand.use_blender_units: - strandStart = ( - pmaterial.strand.root_size - ) - strandEnd = ( - pmaterial.strand.tip_size - ) - strandShape = pmaterial.strand.shape - else: # Blender unit conversion - strandStart = ( - pmaterial.strand.root_size - / 200.0 - ) - strandEnd = ( - pmaterial.strand.tip_size - / 200.0 - ) - strandShape = pmaterial.strand.shape - else: - pmaterial = ( - "default" - ) # No material assigned in blender, use default one - strandStart = 0.01 - strandEnd = 0.01 - strandShape = 0.0 - # Set the number of particles to render count rather than 3d view display - # pSys.set_resolution(scene, ob, 'RENDER') # DEPRECATED - # When you render, the entire dependency graph will be - # evaluated at render resolution, including the particles. - # In the viewport it will be at viewport resolution. - # So there is no need fo render engines to use this function anymore, - # it's automatic now. - steps = pSys.settings.display_step - steps = ( - 3 ** steps - ) # or (power of 2 rather than 3) + 1 # Formerly : len(particle.hair_keys) - - totalNumberOfHairs = ( - pSys.settings.count - + pSys.settings.rendered_child_count - ) - # hairCounter = 0 - file.write( - '#declare HairArray = array[%i] {\n' - % totalNumberOfHairs - ) - for pindex in range(0, totalNumberOfHairs): - - # if particle.is_exist and particle.is_visible: - # hairCounter += 1 - # controlPointCounter = 0 - # Each hair is represented as a separate sphere_sweep in POV-Ray. - - file.write('sphere_sweep{') - if pSys.settings.use_hair_bspline: - file.write('b_spline ') - file.write( - '%i,\n' % (steps + 2) - ) # +2 because the first point needs tripling to be more than a handle in POV - else: - file.write('linear_spline ') - file.write('%i,\n' % (steps)) - # changing world coordinates to object local coordinates by multiplying with inverted matrix - initCo = ob.matrix_world.inverted() @ ( - pSys.co_hair( - ob, particle_no=pindex, step=0 - ) - ) - if ( - ob.material_slots[ - pSys.settings.material - 1 - ].material - and ob.active_material is not None - ): - pmaterial = ob.material_slots[ - pSys.settings.material - 1 - ].material - for th in pmaterial.texture_slots: - if ( - th - and th.use - and th.use_map_color_diffuse - ): - # treat POV textures as bitmaps - if ( - th.texture.type - == 'IMAGE' - and th.texture.image - and th.texture_coords - == 'UV' - and ob.data.uv_textures - is not None - ): # or (th.texture.pov.tex_pattern_type != 'emulator' and th.texture_coords == 'UV' and ob.data.uv_textures is not None): - image = th.texture.image - image_width = image.size[ - 0 - ] - image_height = image.size[ - 1 - ] - image_pixels = image.pixels[ - : - ] - uv_co = pSys.uv_on_emitter( - mod, - pSys.particles[ - pindex - ], - pindex, - 0, - ) - x_co = round( - uv_co[0] - * (image_width - 1) - ) - y_co = round( - uv_co[1] - * (image_height - 1) - ) - pixelnumber = ( - image_width * y_co - ) + x_co - r = image_pixels[ - pixelnumber * 4 - ] - g = image_pixels[ - pixelnumber * 4 + 1 - ] - b = image_pixels[ - pixelnumber * 4 + 2 - ] - a = image_pixels[ - pixelnumber * 4 + 3 - ] - initColor = (r, g, b, a) - else: - # only overwrite variable for each competing texture for now - initColor = th.texture.evaluate( - ( - initCo[0], - initCo[1], - initCo[2], - ) - ) - for step in range(0, steps): - co = ob.matrix_world.inverted() @ ( - pSys.co_hair( - ob, - particle_no=pindex, - step=step, - ) - ) - # for controlPoint in particle.hair_keys: - if pSys.settings.clump_factor != 0: - hDiameter = ( - pSys.settings.clump_factor - / 200.0 - * random.uniform(0.5, 1) - ) - elif step == 0: - hDiameter = strandStart - else: - hDiameter += ( - strandEnd - strandStart - ) / ( - pSys.settings.display_step - + 1 - ) # XXX +1 or not? - if ( - step == 0 - and pSys.settings.use_hair_bspline - ): - # Write three times the first point to compensate pov Bezier handling - file.write( - '<%.6g,%.6g,%.6g>,%.7g,\n' - % ( - co[0], - co[1], - co[2], - abs(hDiameter), - ) - ) - file.write( - '<%.6g,%.6g,%.6g>,%.7g,\n' - % ( - co[0], - co[1], - co[2], - abs(hDiameter), - ) - ) - # file.write('<%.6g,%.6g,%.6g>,%.7g' % (particle.location[0], particle.location[1], particle.location[2], abs(hDiameter))) # Useless because particle location is the tip, not the root. - # file.write(',\n') - # controlPointCounter += 1 - # totalNumberOfHairs += len(pSys.particles)# len(particle.hair_keys) - - # Each control point is written out, along with the radius of the - # hair at that point. - file.write( - '<%.6g,%.6g,%.6g>,%.7g' - % ( - co[0], - co[1], - co[2], - abs(hDiameter), - ) - ) - # All coordinates except the last need a following comma. - - if step != steps - 1: - file.write(',\n') - else: - if texturedHair: - # Write pigment and alpha (between Pov and Blender alpha 0 and 1 are reversed) - file.write( - '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n' - % ( - initColor[0], - initColor[1], - initColor[2], - 1.0 - initColor[3], - ) - ) - # End the sphere_sweep declaration for this hair - file.write('}\n') - - # All but the final sphere_sweep (each array element) needs a terminating comma. - if pindex != totalNumberOfHairs: - file.write(',\n') - else: - file.write('\n') - - # End the array declaration. - - file.write('}\n') - file.write('\n') - - if not texturedHair: - # Pick up the hair material diffuse color and create a default POV-Ray hair texture. - - file.write('#ifndef (HairTexture)\n') - file.write( - ' #declare HairTexture = texture {\n' - ) - file.write( - ' pigment {srgbt <%s,%s,%s,%s>}\n' - % ( - pmaterial.diffuse_color[0], - pmaterial.diffuse_color[1], - pmaterial.diffuse_color[2], - ( - pmaterial.strand.width_fade - + 0.05 - ), - ) - ) - file.write(' }\n') - file.write('#end\n') - file.write('\n') - - # Dynamically create a union of the hairstrands (or a subset of them). - # By default use every hairstrand, commented line is for hand tweaking test renders. - file.write( - '//Increasing HairStep divides the amount of hair for test renders.\n' - ) - file.write( - '#ifndef(HairStep) #declare HairStep = 1; #end\n' - ) - file.write('union{\n') - file.write(' #local I = 0;\n') - file.write( - ' #while (I < %i)\n' - % totalNumberOfHairs - ) - file.write(' object {HairArray[I]') - if not texturedHair: - file.write(' texture{HairTexture}\n') - else: - file.write('\n') - # Translucency of the hair: - file.write(' hollow\n') - file.write(' double_illuminate\n') - file.write(' interior {\n') - file.write(' ior 1.45\n') - file.write(' media {\n') - file.write( - ' scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n' - ) - file.write( - ' absorption 10/<0.83, 0.75, 0.15>\n' - ) - file.write(' samples 1\n') - file.write(' method 2\n') - file.write( - ' density {cylindrical\n' - ) - file.write( - ' color_map {\n' - ) - file.write( - ' [0.0 rgb <0.83, 0.45, 0.35>]\n' - ) - file.write( - ' [0.5 rgb <0.8, 0.8, 0.4>]\n' - ) - file.write( - ' [1.0 rgb <1,1,1>]\n' - ) - file.write(' }\n') - file.write(' }\n') - file.write(' }\n') - file.write(' }\n') - file.write(' }\n') - - file.write(' #local I = I + HairStep;\n') - file.write(' #end\n') - - writeMatrix(global_matrix @ ob.matrix_world) - - file.write('}') - print( - 'Totals hairstrands written: %i' - % totalNumberOfHairs - ) - print( - 'Number of tufts (particle systems)', - len(ob.particle_systems), - ) - - # Set back the displayed number of particles to preview count - # pSys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED - # When you render, the entire dependency graph will be - # evaluated at render resolution, including the particles. - # In the viewport it will be at viewport resolution. - # So there is no need fo render engines to use this function anymore, - # it's automatic now. - if renderEmitter == False: - continue # don't render mesh, skip to next object. - - ############################################# - # Generating a name for object just like materials to be able to use it - # (baking for now or anything else). - # XXX I don't understand that if we are here, sel if a non-empty iterable, - # so this condition is always True, IMO -- mont29 - if ob.data: - name_orig = "OB" + ob.name - dataname_orig = "DATA" + ob.data.name - elif ob.is_instancer: - if ob.instance_type == 'COLLECTION': - name_orig = "OB" + ob.name - dataname_orig = "DATA" + ob.instance_collection.name - else: - # hoping only dupligroups have several source datablocks - # ob_dupli_list_create(scene) #deprecated in 2.8 - depsgraph = bpy.context.evaluated_depsgraph_get() - for eachduplicate in depsgraph.object_instances: - if ( - eachduplicate.is_instance - ): # Real dupli instance filtered because original included in list since 2.8 - dataname_orig = ( - "DATA" + eachduplicate.object.name - ) - # ob.dupli_list_clear() #just don't store any reference to instance since 2.8 - elif ob.type == 'EMPTY': - name_orig = "OB" + ob.name - dataname_orig = "DATA" + ob.name - else: - name_orig = DEF_OBJ_NAME - dataname_orig = DEF_OBJ_NAME - name = string_strip_hyphen(bpy.path.clean_name(name_orig)) - dataname = string_strip_hyphen( - bpy.path.clean_name(dataname_orig) - ) - ## for slot in ob.material_slots: - ## if slot.material is not None and slot.link == 'OBJECT': - ## obmaterial = slot.material - - ############################################# - - if info_callback: - info_callback( - "Object %2.d of %2.d (%s)" - % (ob_num, len(sel), ob.name) - ) - - # if ob.type != 'MESH': - # continue - # me = ob.data - - matrix = global_matrix @ ob.matrix_world - povdataname = store(scene, ob, name, dataname, matrix) - if povdataname is None: - print("This is an instance of " + name) - continue - - print("Writing Down First Occurrence of " + name) - - ############################################Povray Primitives - # special exportCurves() function takes care of writing - # lathe, sphere_sweep, birail, and loft except with modifiers - # converted to mesh - if not ob.is_modified(scene, 'RENDER'): - if ob.type == 'CURVE' and ( - ob.pov.curveshape - in {'lathe', 'sphere_sweep', 'loft'} - ): - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'ISOSURFACE': - tabWrite("#declare %s = isosurface{ \n" % povdataname) - tabWrite("function{ \n") - textName = ob.pov.iso_function_text - if textName: - node_tree = bpy.context.scene.node_tree - for node in node_tree.nodes: - if ( - node.bl_idname == "IsoPropsNode" - and node.label == ob.name - ): - for inp in node.inputs: - if inp: - tabWrite( - "#declare %s = %.6g;\n" - % (inp.name, inp.default_value) - ) - - text = bpy.data.texts[textName] - for line in text.lines: - split = line.body.split() - if split[0] != "#declare": - tabWrite("%s\n" % line.body) - else: - tabWrite("abs(x) - 2 + y") - tabWrite("}\n") - tabWrite("threshold %.6g\n" % ob.pov.threshold) - tabWrite("max_gradient %.6g\n" % ob.pov.max_gradient) - tabWrite("accuracy %.6g\n" % ob.pov.accuracy) - tabWrite("contained_by { ") - if ob.pov.contained_by == "sphere": - tabWrite( - "sphere {0,%.6g}}\n" % ob.pov.container_scale - ) - else: - tabWrite( - "box {-%.6g,%.6g}}\n" - % ( - ob.pov.container_scale, - ob.pov.container_scale, - ) - ) - if ob.pov.all_intersections: - tabWrite("all_intersections\n") - else: - if ob.pov.max_trace > 1: - tabWrite("max_trace %.6g\n" % ob.pov.max_trace) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - tabWrite("scale %.6g\n" % (1 / ob.pov.container_scale)) - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'SUPERELLIPSOID': - tabWrite( - "#declare %s = superellipsoid{ <%.4f,%.4f>\n" - % (povdataname, ob.pov.se_n2, ob.pov.se_n1) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'SUPERTORUS': - rMajor = ob.pov.st_major_radius - rMinor = ob.pov.st_minor_radius - ring = ob.pov.st_ring - cross = ob.pov.st_cross - accuracy = ob.pov.st_accuracy - gradient = ob.pov.st_max_gradient - ############Inline Supertorus macro - file.write( - "#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)\n" - ) - file.write(" #local CP = 2/MinorControl;\n") - file.write(" #local RP = 2/MajorControl;\n") - file.write(" isosurface {\n") - file.write( - " function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }\n" - ) - file.write(" threshold 0\n") - file.write( - " contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}\n" - ) - file.write(" #if(MaxGradient >= 1)\n") - file.write(" max_gradient MaxGradient\n") - file.write(" #else\n") - file.write(" evaluate 1, 10, 0.1\n") - file.write(" #end\n") - file.write(" accuracy Accuracy\n") - file.write(" }\n") - file.write("#end\n") - ############ - tabWrite( - "#declare %s = object{ Supertorus( %.4g,%.4g,%.4g,%.4g,%.4g,%.4g)\n" - % ( - povdataname, - rMajor, - rMinor, - ring, - cross, - accuracy, - gradient, - ) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'PLANE': - tabWrite( - "#declare %s = plane{ <0,0,1>,1\n" % povdataname - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'BOX': - tabWrite("#declare %s = box { -1,1\n" % povdataname) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'CONE': - br = ob.pov.cone_base_radius - cr = ob.pov.cone_cap_radius - bz = ob.pov.cone_base_z - cz = ob.pov.cone_cap_z - tabWrite( - "#declare %s = cone { <0,0,%.4f>,%.4f,<0,0,%.4f>,%.4f\n" - % (povdataname, bz, br, cz, cr) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'CYLINDER': - r = ob.pov.cylinder_radius - x2 = ob.pov.cylinder_location_cap[0] - y2 = ob.pov.cylinder_location_cap[1] - z2 = ob.pov.cylinder_location_cap[2] - tabWrite( - "#declare %s = cylinder { <0,0,0>,<%6f,%6f,%6f>,%6f\n" - % (povdataname, x2, y2, z2, r) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - # cylinders written at origin, translated below - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'HEIGHT_FIELD': - data = "" - filename = ob.pov.hf_filename - data += '"%s"' % filename - gamma = ' gamma %.4f' % ob.pov.hf_gamma - data += gamma - if ob.pov.hf_premultiplied: - data += ' premultiplied on' - if ob.pov.hf_smooth: - data += ' smooth' - if ob.pov.hf_water > 0: - data += ' water_level %.4f' % ob.pov.hf_water - # hierarchy = ob.pov.hf_hierarchy - tabWrite( - '#declare %s = height_field { %s\n' - % (povdataname, data) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - tabWrite("rotate x*90\n") - tabWrite("translate <-0.5,0.5,0>\n") - tabWrite("scale <0,-1,0>\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'SPHERE': - - tabWrite( - "#declare %s = sphere { 0,%6f\n" - % (povdataname, ob.pov.sphere_radius) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - # tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'TORUS': - tabWrite( - "#declare %s = torus { %.4f,%.4f\n" - % ( - povdataname, - ob.pov.torus_major_radius, - ob.pov.torus_minor_radius, - ) - ) - povMatName = "Default_texture" - if ob.active_material: - # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) - try: - material = ob.active_material - writeObjectMaterial(material, ob) - except IndexError: - print(me) - # tabWrite("texture {%s}\n"%povMatName) - write_object_modifiers(scene, ob, file) - tabWrite("rotate x*90\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'PARAMETRIC': - tabWrite("#declare %s = parametric {\n" % povdataname) - tabWrite("function { %s }\n" % ob.pov.x_eq) - tabWrite("function { %s }\n" % ob.pov.y_eq) - tabWrite("function { %s }\n" % ob.pov.z_eq) - tabWrite( - "<%.4f,%.4f>, <%.4f,%.4f>\n" - % ( - ob.pov.u_min, - ob.pov.v_min, - ob.pov.u_max, - ob.pov.v_max, - ) - ) - # Previous to 3.8 default max_gradient 1.0 was too slow - tabWrite("max_gradient 0.001\n") - if ob.pov.contained_by == "sphere": - tabWrite("contained_by { sphere{0, 2} }\n") - else: - tabWrite("contained_by { box{-2, 2} }\n") - tabWrite("max_gradient %.6f\n" % ob.pov.max_gradient) - tabWrite("accuracy %.6f\n" % ob.pov.accuracy) - tabWrite("precompute 10 x,y,z\n") - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - if ob.pov.object_as == 'POLYCIRCLE': - # TODO write below macro Once: - # if write_polytocircle_macro_once == 0: - file.write("/****************************\n") - file.write("This macro was written by 'And'.\n") - file.write( - "Link:(http://news.povray.org/povray.binaries.scene-files/)\n" - ) - file.write("****************************/\n") - file.write("//from math.inc:\n") - file.write("#macro VPerp_Adjust(V, Axis)\n") - file.write( - " vnormalize(vcross(vcross(Axis, V), Axis))\n" - ) - file.write("#end\n") - file.write("//Then for the actual macro\n") - file.write( - "#macro Shape_Slice_Plane_2P_1V(point1, point2, clip_direct)\n" - ) - file.write("#local p1 = point1 + <0,0,0>;\n") - file.write("#local p2 = point2 + <0,0,0>;\n") - file.write( - "#local clip_v = vnormalize(clip_direct + <0,0,0>);\n" - ) - file.write("#local direct_v1 = vnormalize(p2 - p1);\n") - file.write("#if(vdot(direct_v1, clip_v) = 1)\n") - file.write( - ' #error "Shape_Slice_Plane_2P_1V error: Can\'t decide plane"\n' - ) - file.write("#end\n\n") - file.write( - "#local norm = -vnormalize(clip_v - direct_v1*vdot(direct_v1,clip_v));\n" - ) - file.write("#local d = vdot(norm, p1);\n") - file.write("plane{\n") - file.write("norm, d\n") - file.write("}\n") - file.write("#end\n\n") - file.write("//polygon to circle\n") - file.write( - "#macro Shape_Polygon_To_Circle_Blending(_polygon_n, _side_face, _polygon_circumscribed_radius, _circle_radius, _height)\n" - ) - file.write("#local n = int(_polygon_n);\n") - file.write("#if(n < 3)\n") - file.write(" #error " "\n") - file.write("#end\n\n") - file.write( - "#local front_v = VPerp_Adjust(_side_face, z);\n" - ) - file.write("#if(vdot(front_v, x) >= 0)\n") - file.write( - " #local face_ang = acos(vdot(-y, front_v));\n" - ) - file.write("#else\n") - file.write( - " #local face_ang = -acos(vdot(-y, front_v));\n" - ) - file.write("#end\n") - file.write("#local polyg_ext_ang = 2*pi/n;\n") - file.write( - "#local polyg_outer_r = _polygon_circumscribed_radius;\n" - ) - file.write( - "#local polyg_inner_r = polyg_outer_r*cos(polyg_ext_ang/2);\n" - ) - file.write("#local cycle_r = _circle_radius;\n") - file.write("#local h = _height;\n") - file.write( - "#if(polyg_outer_r < 0 | cycle_r < 0 | h <= 0)\n" - ) - file.write( - ' #error "error: each side length must be positive"\n' - ) - file.write("#end\n\n") - file.write("#local multi = 1000;\n") - file.write("#local poly_obj =\n") - file.write("polynomial{\n") - file.write("4,\n") - file.write("xyz(0,2,2): multi*1,\n") - file.write("xyz(2,0,1): multi*2*h,\n") - file.write( - "xyz(1,0,2): multi*2*(polyg_inner_r-cycle_r),\n" - ) - file.write("xyz(2,0,0): multi*(-h*h),\n") - file.write( - "xyz(0,0,2): multi*(-pow(cycle_r - polyg_inner_r, 2)),\n" - ) - file.write( - "xyz(1,0,1): multi*2*h*(-2*polyg_inner_r + cycle_r),\n" - ) - file.write("xyz(1,0,0): multi*2*h*h*polyg_inner_r,\n") - file.write( - "xyz(0,0,1): multi*2*h*polyg_inner_r*(polyg_inner_r - cycle_r),\n" - ) - file.write( - "xyz(0,0,0): multi*(-pow(polyg_inner_r*h, 2))\n" - ) - file.write("sturm\n") - file.write("}\n\n") - file.write("#local mockup1 =\n") - file.write("difference{\n") - file.write(" cylinder{\n") - file.write( - " <0,0,0.0>,<0,0,h>, max(polyg_outer_r, cycle_r)\n" - ) - file.write(" }\n\n") - file.write(" #for(i, 0, n-1)\n") - file.write(" object{\n") - file.write(" poly_obj\n") - file.write(" inverse\n") - file.write( - " rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n" - ) - file.write(" }\n") - file.write(" object{\n") - file.write( - " Shape_Slice_Plane_2P_1V(<polyg_inner_r,0,0>,<cycle_r,0,h>,x)\n" - ) - file.write( - " rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n" - ) - file.write(" }\n") - file.write(" #end\n") - file.write("}\n\n") - file.write("object{\n") - file.write("mockup1\n") - file.write("rotate <0, 0, degrees(face_ang)>\n") - file.write("}\n") - file.write("#end\n") - # Use the macro - ngon = ob.pov.polytocircle_ngon - ngonR = ob.pov.polytocircle_ngonR - circleR = ob.pov.polytocircle_circleR - tabWrite( - "#declare %s = object { Shape_Polygon_To_Circle_Blending(%s, z, %.4f, %.4f, 2) rotate x*180 translate z*1\n" - % (povdataname, ngon, ngonR, circleR) - ) - tabWrite("}\n") - continue # Don't render proxy mesh, skip to next object - - ############################################else try to export mesh - elif ( - ob.is_instancer == False - ): # except duplis which should be instances groups for now but all duplis later - if ob.type == 'EMPTY': - tabWrite( - "\n//dummy sphere to represent Empty location\n" - ) - tabWrite( - "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" - % povdataname - ) - - # TODO(sergey): PovRay is a render engine, so should be using dependency graph - # which was given to it via render engine API. - depsgraph = bpy.context.evaluated_depsgraph_get() - ob_eval = ob.evaluated_get(depsgraph) - try: - me = ob_eval.to_mesh() - - # XXX Here? identify the specific exception for mesh object with no data - # XXX So that we can write something for the dataname ! - except: - - # also happens when curves cant be made into meshes because of no-data - continue - - importance = ob.pov.importance_value - if me: - me.calc_loop_triangles() - me_materials = me.materials - me_faces = me.loop_triangles[:] - ## numpytest - #me_looptris = me.loops - - ## otypes = ['int32'] is a 32-bit signed integer number numpy datatype - #get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True) - #faces_verts_idx = get_v_index(me_looptris) - - - # if len(me_faces)==0: - # tabWrite("\n//dummy sphere to represent empty mesh location\n") - # tabWrite("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname) - - if not me or not me_faces: - tabWrite( - "\n//dummy sphere to represent empty mesh location\n" - ) - tabWrite( - "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" - % povdataname - ) - continue - - uv_layers = me.uv_layers - if len(uv_layers) > 0: - if me.uv_layers.active and uv_layers.active.data: - uv_layer = uv_layers.active.data - else: - uv_layer = None - - try: - # vcol_layer = me.vertex_colors.active.data - vcol_layer = me.vertex_colors.active.data - except AttributeError: - vcol_layer = None - - faces_verts = [f.vertices[:] for f in me_faces] - faces_normals = [f.normal[:] for f in me_faces] - verts_normals = [v.normal[:] for v in me.vertices] - - # Use named declaration to allow reference e.g. for baking. MR - file.write("\n") - tabWrite("#declare %s =\n" % povdataname) - tabWrite("mesh2 {\n") - tabWrite("vertex_vectors {\n") - tabWrite("%d" % len(me.vertices)) # vert count - - tabStr = tab * tabLevel - for v in me.vertices: - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr + "<%.6f, %.6f, %.6f>" % v.co[:] - ) # vert count - else: - file.write(", ") - file.write( - "<%.6f, %.6f, %.6f>" % v.co[:] - ) # vert count - # tabWrite("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count - file.write("\n") - tabWrite("}\n") - - # Build unique Normal list - uniqueNormals = {} - for fi, f in enumerate(me_faces): - fv = faces_verts[fi] - # [-1] is a dummy index, use a list so we can modify in place - if f.use_smooth: # Use vertex normals - for v in fv: - key = verts_normals[v] - uniqueNormals[key] = [-1] - else: # Use face normal - key = faces_normals[fi] - uniqueNormals[key] = [-1] - - tabWrite("normal_vectors {\n") - tabWrite("%d" % len(uniqueNormals)) # vert count - idx = 0 - tabStr = tab * tabLevel - for no, index in uniqueNormals.items(): - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr + "<%.6f, %.6f, %.6f>" % no - ) # vert count - else: - file.write(", ") - file.write( - "<%.6f, %.6f, %.6f>" % no - ) # vert count - index[0] = idx - idx += 1 - file.write("\n") - tabWrite("}\n") - - # Vertex colors - vertCols = {} # Use for material colors also. - - if uv_layer: - # Generate unique UV's - uniqueUVs = {} - # n = 0 - for f in me_faces: # me.faces in 2.7 - uvs = [uv_layer[l].uv[:] for l in f.loops] - - for uv in uvs: - uniqueUVs[uv[:]] = [-1] - - tabWrite("uv_vectors {\n") - # print unique_uvs - tabWrite("%d" % len(uniqueUVs)) # vert count - idx = 0 - tabStr = tab * tabLevel - for uv, index in uniqueUVs.items(): - if linebreaksinlists: - file.write(",\n") - file.write(tabStr + "<%.6f, %.6f>" % uv) - else: - file.write(", ") - file.write("<%.6f, %.6f>" % uv) - index[0] = idx - idx += 1 - ''' - else: - # Just add 1 dummy vector, no real UV's - tabWrite('1') # vert count - file.write(',\n\t\t<0.0, 0.0>') - ''' - file.write("\n") - tabWrite("}\n") - - if me.vertex_colors: - # Write down vertex colors as a texture for each vertex - tabWrite("texture_list {\n") - tabWrite( - "%d\n" % (len(me_faces) * 3) - ) # assumes we have only triangles - VcolIdx = 0 - if comments: - file.write( - "\n //Vertex colors: one simple pigment texture per vertex\n" - ) - for fi, f in enumerate(me_faces): - # annoying, index may be invalid - material_index = f.material_index - try: - material = me_materials[material_index] - except: - material = None - if ( - material - ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now - - cols = [ - vcol_layer[l].color[:] for l in f.loops - ] - - for col in cols: - key = ( - col[0], - col[1], - col[2], - material_index, - ) # Material index! - VcolIdx += 1 - vertCols[key] = [VcolIdx] - if linebreaksinlists: - tabWrite( - "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n" - % (col[0], col[1], col[2]) - ) - else: - tabWrite( - "texture {pigment{ color srgb <%6f,%6f,%6f> }}" - % (col[0], col[1], col[2]) - ) - tabStr = tab * tabLevel - else: - if material: - # Multiply diffuse with SSS Color - if ( - material.pov_subsurface_scattering.use - ): - diffuse_color = [ - i * j - for i, j in zip( - material.pov_subsurface_scattering.color[ - : - ], - material.diffuse_color[:], - ) - ] - key = ( - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - material_index, - ) - vertCols[key] = [-1] - else: - diffuse_color = material.diffuse_color[ - : - ] - key = ( - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - material_index, - ) - vertCols[key] = [-1] - - tabWrite("\n}\n") - # Face indices - tabWrite("\nface_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - - for fi, f in enumerate(me_faces): - fv = faces_verts[fi] - material_index = f.material_index - - if vcol_layer: - cols = [ - vcol_layer[l].color[:] for l in f.loops - ] - - if ( - not me_materials - or me_materials[material_index] is None - ): # No materials - if linebreaksinlists: - file.write(",\n") - # vert count - file.write( - tabStr - + "<%d,%d,%d>" - % (fv[0], fv[1], fv[2]) - ) - else: - file.write(", ") - file.write( - "<%d,%d,%d>" % (fv[0], fv[1], fv[2]) - ) # vert count - else: - material = me_materials[material_index] - if ( - me.vertex_colors - ): # and material.use_vertex_color_paint: - # Color per vertex - vertex color - - col1 = cols[0] - col2 = cols[1] - col3 = cols[2] - - ci1 = vertCols[ - col1[0], - col1[1], - col1[2], - material_index, - ][0] - ci2 = vertCols[ - col2[0], - col2[1], - col2[2], - material_index, - ][0] - ci3 = vertCols[ - col3[0], - col3[1], - col3[2], - material_index, - ][0] - else: - # Color per material - flat material color - if ( - material.pov_subsurface_scattering.use - ): - diffuse_color = [ - i * j - for i, j in zip( - material.pov_subsurface_scattering.color[ - : - ], - material.diffuse_color[:], - ) - ] - else: - diffuse_color = material.diffuse_color[ - : - ] - ci1 = ci2 = ci3 = vertCols[ - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - f.material_index, - ][0] - # ci are zero based index so we'll subtract 1 from them - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>, %d,%d,%d" - % ( - fv[0], - fv[1], - fv[2], - ci1 - 1, - ci2 - 1, - ci3 - 1, - ) - ) # vert count - else: - file.write(", ") - file.write( - "<%d,%d,%d>, %d,%d,%d" - % ( - fv[0], - fv[1], - fv[2], - ci1 - 1, - ci2 - 1, - ci3 - 1, - ) - ) # vert count - - file.write("\n") - tabWrite("}\n") - - # normal_indices indices - tabWrite("normal_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - for fi, fv in enumerate(faces_verts): - - if me_faces[fi].use_smooth: - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" - % ( - uniqueNormals[ - verts_normals[fv[0]] - ][0], - uniqueNormals[ - verts_normals[fv[1]] - ][0], - uniqueNormals[ - verts_normals[fv[2]] - ][0], - ) - ) # vert count - else: - file.write(", ") - file.write( - "<%d,%d,%d>" - % ( - uniqueNormals[ - verts_normals[fv[0]] - ][0], - uniqueNormals[ - verts_normals[fv[1]] - ][0], - uniqueNormals[ - verts_normals[fv[2]] - ][0], - ) - ) # vert count - else: - idx = uniqueNormals[faces_normals[fi]][0] - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" % (idx, idx, idx) - ) # vert count - else: - file.write(", ") - file.write( - "<%d,%d,%d>" % (idx, idx, idx) - ) # vert count - - file.write("\n") - tabWrite("}\n") - - if uv_layer: - tabWrite("uv_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - for f in me_faces: - uvs = [uv_layer[l].uv[:] for l in f.loops] - - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" - % ( - uniqueUVs[uvs[0]][0], - uniqueUVs[uvs[1]][0], - uniqueUVs[uvs[2]][0], - ) - ) - else: - file.write(", ") - file.write( - "<%d,%d,%d>" - % ( - uniqueUVs[uvs[0]][0], - uniqueUVs[uvs[1]][0], - uniqueUVs[uvs[2]][0], - ) - ) - - file.write("\n") - tabWrite("}\n") - - # XXX BOOLEAN - onceCSG = 0 - for mod in ob.modifiers: - if onceCSG == 0: - if mod: - if mod.type == 'BOOLEAN': - if ob.pov.boolean_mod == "POV": - file.write( - "\tinside_vector <%.6g, %.6g, %.6g>\n" - % ( - ob.pov.inside_vector[0], - ob.pov.inside_vector[1], - ob.pov.inside_vector[2], - ) - ) - onceCSG = 1 - - if me.materials: - try: - material = me.materials[0] # dodgy - writeObjectMaterial(material, ob) - except IndexError: - print(me) - - # POV object modifiers such as - # hollow / sturm / double_illuminate etc. - write_object_modifiers(scene, ob, file) - - # Importance for radiosity sampling added here: - tabWrite("radiosity { \n") - tabWrite("importance %3g \n" % importance) - tabWrite("}\n") - - tabWrite("}\n") # End of mesh block - else: - facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!! - if me_materials: - for f in me_faces: - if f.material_index not in facesMaterials: - facesMaterials.append(f.material_index) - # No vertex colors, so write material colors as vertex colors - for i, material in enumerate(me_materials): - - if ( - material - and material.pov.material_use_nodes == False - ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # Multiply diffuse with SSS Color - if material.pov_subsurface_scattering.use: - diffuse_color = [ - i * j - for i, j in zip( - material.pov_subsurface_scattering.color[ - : - ], - material.diffuse_color[:], - ) - ] - key = ( - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - i, - ) # i == f.mat - vertCols[key] = [-1] - else: - diffuse_color = material.diffuse_color[ - : - ] - key = ( - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - i, - ) # i == f.mat - vertCols[key] = [-1] - - idx = 0 - LocalMaterialNames = [] - for col, index in vertCols.items(): - # if me_materials: - mater = me_materials[col[3]] - if me_materials is None: # XXX working? - material_finish = ( - DEF_MAT_NAME - ) # not working properly, - trans = 0.0 - - else: - shading.writeTextureInfluence( - using_uberpov, - mater, - materialNames, - LocalMaterialNames, - path_image, - lampCount, - imageFormat, - imgMap, - imgMapTransforms, - tabWrite, - comments, - string_strip_hyphen, - safety, - col, - os, - preview_dir, - unpacked_images, - ) - ################################################################### - index[0] = idx - idx += 1 - - # Vert Colors - tabWrite("texture_list {\n") - # In case there's is no material slot, give at least one texture - # (an empty one so it uses pov default) - if len(vertCols) == 0: - file.write(tabStr + "1") - else: - file.write( - tabStr + "%s" % (len(vertCols)) - ) # vert count - - # below "material" alias, added check ob.active_material - # to avoid variable referenced before assignment error - try: - material = ob.active_material - except IndexError: - # when no material slot exists, - material = None - - # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - if ( - material - and ob.active_material is not None - and material.pov.material_use_nodes == False - ): - if material.pov.replacement_text != "": - file.write("\n") - file.write( - " texture{%s}\n" - % material.pov.replacement_text - ) - - else: - # Loop through declared materials list - for cMN in LocalMaterialNames: - if material != "Default": - file.write( - "\n texture{MAT_%s}\n" % cMN - ) - # use string_strip_hyphen(materialNames[material])) - # or Something like that to clean up the above? - elif material and material.pov.material_use_nodes: - for index in facesMaterials: - faceMaterial = string_strip_hyphen( - bpy.path.clean_name( - me_materials[index].name - ) - ) - file.write( - "\n texture{%s}\n" % faceMaterial - ) - # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - else: - file.write(" texture{}\n") - tabWrite("}\n") - - # Face indices - tabWrite("face_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - - for fi, f in enumerate(me_faces): - fv = faces_verts[fi] - material_index = f.material_index - - if vcol_layer: - cols = [ - vcol_layer[l].color[:] for l in f.loops - ] - - if ( - not me_materials - or me_materials[material_index] is None - ): # No materials - if linebreaksinlists: - file.write(",\n") - # vert count - file.write( - tabStr - + "<%d,%d,%d>" - % (fv[0], fv[1], fv[2]) - ) - else: - file.write(", ") - file.write( - "<%d,%d,%d>" % (fv[0], fv[1], fv[2]) - ) # vert count - else: - material = me_materials[material_index] - ci1 = ci2 = ci3 = f.material_index - if ( - me.vertex_colors - ): # and material.use_vertex_color_paint: - # Color per vertex - vertex color - - col1 = cols[0] - col2 = cols[1] - col3 = cols[2] - - ci1 = vertCols[ - col1[0], - col1[1], - col1[2], - material_index, - ][0] - ci2 = vertCols[ - col2[0], - col2[1], - col2[2], - material_index, - ][0] - ci3 = vertCols[ - col3[0], - col3[1], - col3[2], - material_index, - ][0] - elif material.pov.material_use_nodes: - ci1 = ci2 = ci3 = 0 - else: - # Color per material - flat material color - if ( - material.pov_subsurface_scattering.use - ): - diffuse_color = [ - i * j - for i, j in zip( - material.pov_subsurface_scattering.color[ - : - ], - material.diffuse_color[:], - ) - ] - else: - diffuse_color = material.diffuse_color[ - : - ] - ci1 = ci2 = ci3 = vertCols[ - diffuse_color[0], - diffuse_color[1], - diffuse_color[2], - f.material_index, - ][0] - - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>, %d,%d,%d" - % ( - fv[0], - fv[1], - fv[2], - ci1, - ci2, - ci3, - ) - ) # vert count - else: - file.write(", ") - file.write( - "<%d,%d,%d>, %d,%d,%d" - % ( - fv[0], - fv[1], - fv[2], - ci1, - ci2, - ci3, - ) - ) # vert count - - file.write("\n") - tabWrite("}\n") - - # normal_indices indices - tabWrite("normal_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - for fi, fv in enumerate(faces_verts): - if me_faces[fi].use_smooth: - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" - % ( - uniqueNormals[ - verts_normals[fv[0]] - ][0], - uniqueNormals[ - verts_normals[fv[1]] - ][0], - uniqueNormals[ - verts_normals[fv[2]] - ][0], - ) - ) # vert count - else: - file.write(", ") - file.write( - "<%d,%d,%d>" - % ( - uniqueNormals[ - verts_normals[fv[0]] - ][0], - uniqueNormals[ - verts_normals[fv[1]] - ][0], - uniqueNormals[ - verts_normals[fv[2]] - ][0], - ) - ) # vert count - else: - idx = uniqueNormals[faces_normals[fi]][0] - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" % (idx, idx, idx) - ) # vertcount - else: - file.write(", ") - file.write( - "<%d,%d,%d>" % (idx, idx, idx) - ) # vert count - - file.write("\n") - tabWrite("}\n") - - if uv_layer: - tabWrite("uv_indices {\n") - tabWrite("%d" % (len(me_faces))) # faces count - tabStr = tab * tabLevel - for f in me_faces: - uvs = [uv_layer[l].uv[:] for l in f.loops] - - if linebreaksinlists: - file.write(",\n") - file.write( - tabStr - + "<%d,%d,%d>" - % ( - uniqueUVs[uvs[0]][0], - uniqueUVs[uvs[1]][0], - uniqueUVs[uvs[2]][0], - ) - ) - else: - file.write(", ") - file.write( - "<%d,%d,%d>" - % ( - uniqueUVs[uvs[0]][0], - uniqueUVs[uvs[1]][0], - uniqueUVs[uvs[2]][0], - ) - ) - - file.write("\n") - tabWrite("}\n") - - # XXX BOOLEAN - onceCSG = 0 - for mod in ob.modifiers: - if onceCSG == 0: - if mod: - if mod.type == 'BOOLEAN': - if ob.pov.boolean_mod == "POV": - file.write( - "\tinside_vector <%.6g, %.6g, %.6g>\n" - % ( - ob.pov.inside_vector[0], - ob.pov.inside_vector[1], - ob.pov.inside_vector[2], - ) - ) - onceCSG = 1 - - if me.materials: - try: - material = me.materials[0] # dodgy - writeObjectMaterial(material, ob) - except IndexError: - print(me) - - # POV object modifiers such as - # hollow / sturm / double_illuminate etc. - write_object_modifiers(scene, ob, file) - - # Importance for radiosity sampling added here: - tabWrite("radiosity { \n") - tabWrite("importance %3g \n" % importance) - tabWrite("}\n") - - tabWrite("}\n") # End of mesh block - - ob_eval.to_mesh_clear() - - if csg: - duplidata_ref = [] - _dupnames_seen = ( - dict() - ) # avoid duplicate output during introspection - for ob in sel: - # matrix = global_matrix @ ob.matrix_world - if ob.is_instancer: - tabWrite("\n//--DupliObjects in %s--\n\n" % ob.name) - # ob.dupli_list_create(scene) #deprecated in 2.8 - depsgraph = bpy.context.evaluated_depsgraph_get() - dup = "" - if ob.is_modified(scene, 'RENDER'): - # modified object always unique so using object name rather than data name - dup = "#declare OB%s = union{\n" % ( - string_strip_hyphen(bpy.path.clean_name(ob.name)) - ) - else: - dup = "#declare DATA%s = union{\n" % ( - string_strip_hyphen(bpy.path.clean_name(ob.name)) - ) - for eachduplicate in depsgraph.object_instances: - if ( - eachduplicate.is_instance - ): # Real dupli instance filtered because original included in list since 2.8 - _dupname = eachduplicate.object.name - _dupobj = bpy.data.objects[_dupname] - # BEGIN introspection for troubleshooting purposes - if not "name" in dir(_dupobj.data): - if _dupname not in _dupnames_seen: - print( - "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute" - % (_dupname, type(_dupobj.data)) - ) - for _thing in dir(_dupobj): - print( - "|| %s.%s = %s" - % ( - _dupname, - _thing, - getattr(_dupobj, _thing), - ) - ) - _dupnames_seen[_dupname] = 1 - print( - "''=> Unparseable objects so far: %s" - % (_dupnames_seen) - ) - else: - _dupnames_seen[_dupname] += 1 - continue # don't try to parse data objects with no name attribute - # END introspection for troubleshooting purposes - duplidataname = "OB" + string_strip_hyphen( - bpy.path.clean_name(_dupobj.data.name) - ) - dupmatrix = ( - eachduplicate.matrix_world.copy() - ) # has to be copied to not store instance since 2.8 - dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % ( - string_strip_hyphen( - bpy.path.clean_name(_dupobj.data.name) - ), - MatrixAsPovString( - ob.matrix_world.inverted() @ dupmatrix - ), - ) - # add object to a list so that it is not rendered for some instance_types - if ( - ob.instance_type not in {'COLLECTION'} - and duplidataname not in duplidata_ref - ): - duplidata_ref.append( - duplidataname - ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))] - dup += "}\n" - # ob.dupli_list_clear()# just do not store any reference to instance since 2.8 - tabWrite(dup) - else: - continue - print( - "WARNING: Unparseable objects in current .blend file:\n''=> %s" - % (_dupnames_seen) - ) - print("duplidata_ref = %s" % (duplidata_ref)) - for data_name, inst in data_ref.items(): - for ob_name, matrix_str in inst: - if ( - ob_name not in duplidata_ref - ): # .items() for a dictionary - tabWrite( - "\n//----Blender Object Name:%s----\n" % ob_name - ) - if ob.pov.object_as == '': - tabWrite("object { \n") - tabWrite("%s\n" % data_name) - tabWrite("%s\n" % matrix_str) - tabWrite("}\n") - else: - no_boolean = True - for mod in ob.modifiers: - if mod.type == 'BOOLEAN': - operation = None - no_boolean = False - if mod.operation == 'INTERSECT': - operation = 'intersection' - else: - operation = mod.operation.lower() - mod_ob_name = string_strip_hyphen( - bpy.path.clean_name(mod.object.name) - ) - mod_matrix = ( - global_matrix @ mod.object.matrix_world - ) - mod_ob_matrix = MatrixAsPovString( - mod_matrix - ) - tabWrite("%s { \n" % operation) - tabWrite("object { \n") - tabWrite("%s\n" % data_name) - tabWrite("%s\n" % matrix_str) - tabWrite("}\n") - tabWrite("object { \n") - tabWrite("%s\n" % ('DATA' + mod_ob_name)) - tabWrite("%s\n" % mod_ob_matrix) - tabWrite("}\n") - tabWrite("}\n") - break - if no_boolean: - tabWrite("object { \n") - tabWrite("%s\n" % data_name) - tabWrite("%s\n" % matrix_str) - tabWrite("}\n") - - def exportWorld(world): - """write world as POV backgrounbd and sky_sphere to exported file """ - render = scene.pov - camera = scene.camera - matrix = global_matrix @ camera.matrix_world - if not world: - return - #############Maurice#################################### - # These lines added to get sky gradient (visible with PNG output) - if world: - # For simple flat background: - if not world.pov.use_sky_blend: - # Non fully transparent background could premultiply alpha and avoid anti-aliasing - # display issue: - if render.alpha_mode == 'TRANSPARENT': - tabWrite( - "background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n" - % (world.pov.horizon_color[:]) - ) - # Currently using no alpha with Sky option: - elif render.alpha_mode == 'SKY': - tabWrite( - "background {rgbt<%.3g, %.3g, %.3g, 0>}\n" - % (world.pov.horizon_color[:]) - ) - # StraightAlpha: - # XXX Does not exists anymore - # else: - # tabWrite("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:])) - - worldTexCount = 0 - # For Background image textures - for ( - t - ) in ( - world.pov_texture_slots - ): # risk to write several sky_spheres but maybe ok. - if t: - tex = bpy.data.textures[t.texture] - if tex.type is not None: - worldTexCount += 1 - # XXX No enable checkbox for world textures yet (report it?) - # if t and tex.type == 'IMAGE' and t.use: - if tex.type == 'IMAGE': - image_filename = path_image(tex.image) - if tex.image.filepath != image_filename: - tex.image.filepath = image_filename - if image_filename != "" and t.use_map_blend: - texturesBlend = image_filename - # colvalue = t.default_value - t_blend = t - - # Commented below was an idea to make the Background image oriented as camera - # taken here: - # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/ - # Replace 4/3 by the ratio of each image found by some custom or existing - # function - # mappingBlend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \ - # "(atan((camLocation - camLookAt).x/(camLocation - " \ - # "camLookAt).y)) rotate x*degrees(atan((camLocation - " \ - # "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \ - # "degrees(atan((camLocation - camLookAt).z/(camLocation - " \ - # "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \ - # (t_blend.offset.x / 10 , t_blend.offset.y / 10 , - # t_blend.offset.z / 10, t_blend.scale.x , - # t_blend.scale.y , t_blend.scale.z)) - # using camera rotation valuesdirectly from blender seems much easier - if t_blend.texture_coords == 'ANGMAP': - mappingBlend = "" - else: - # POV-Ray "scale" is not a number of repetitions factor, but its - # inverse, a standard scale factor. - # 0.5 Offset is needed relatively to scale because center of the - # UV scale is 0.5,0.5 in blender and 0,0 in POV - # Further Scale by 2 and translate by -1 are - # required for the sky_sphere not to repeat - - mappingBlend = ( - "scale 2 scale <%.4g,%.4g,%.4g> translate -1 " - "translate <%.4g,%.4g,%.4g> rotate<0,0,0> " - % ( - (1.0 / t_blend.scale.x), - (1.0 / t_blend.scale.y), - (1.0 / t_blend.scale.z), - 0.5 - - (0.5 / t_blend.scale.x) - - t_blend.offset.x, - 0.5 - - (0.5 / t_blend.scale.y) - - t_blend.offset.y, - t_blend.offset.z, - ) - ) - - # The initial position and rotation of the pov camera is probably creating - # the rotation offset should look into it someday but at least background - # won't rotate with the camera now. - # Putting the map on a plane would not introduce the skysphere distortion and - # allow for better image scale matching but also some waay to chose depth and - # size of the plane relative to camera. - tabWrite("sky_sphere {\n") - tabWrite("pigment {\n") - tabWrite( - "image_map{%s \"%s\" %s}\n" - % ( - imageFormat(texturesBlend), - texturesBlend, - imgMapBG(t_blend), - ) - ) - tabWrite("}\n") - tabWrite("%s\n" % (mappingBlend)) - # The following layered pigment opacifies to black over the texture for - # transmit below 1 or otherwise adds to itself - tabWrite( - "pigment {rgb 0 transmit %s}\n" % (tex.intensity) - ) - tabWrite("}\n") - # tabWrite("scale 2\n") - # tabWrite("translate -1\n") - - # For only Background gradient - - if worldTexCount == 0: - if world.pov.use_sky_blend: - tabWrite("sky_sphere {\n") - tabWrite("pigment {\n") - # maybe Should follow the advice of POV doc about replacing gradient - # for skysphere..5.5 - tabWrite("gradient y\n") - tabWrite("color_map {\n") - # XXX Does not exists anymore - # if render.alpha_mode == 'STRAIGHT': - # tabWrite("[0.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.horizon_color[:])) - # tabWrite("[1.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.zenith_color[:])) - if render.alpha_mode == 'TRANSPARENT': - tabWrite( - "[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" - % (world.pov.horizon_color[:]) - ) - # aa premult not solved with transmit 1 - tabWrite( - "[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" - % (world.pov.zenith_color[:]) - ) - else: - tabWrite( - "[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" - % (world.pov.horizon_color[:]) - ) - tabWrite( - "[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" - % (world.pov.zenith_color[:]) - ) - tabWrite("}\n") - tabWrite("}\n") - tabWrite("}\n") - # Sky_sphere alpha (transmit) is not translating into image alpha the same - # way as 'background' - - # if world.pov.light_settings.use_indirect_light: - # scene.pov.radio_enable=1 - - # Maybe change the above to a function copyInternalRenderer settings when - # user pushes a button, then: - # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light - # and other such translations but maybe this would not be allowed either? - - ############################################################### - - mist = world.mist_settings - - if mist.use_mist: - tabWrite("fog {\n") - if mist.falloff == 'LINEAR': - tabWrite( - "distance %.6f\n" % ((mist.start + mist.depth) * 0.368) - ) - elif mist.falloff == 'QUADRATIC': # n**2 or squrt(n)? - tabWrite( - "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368) - ) - elif mist.falloff == 'INVERSE_QUADRATIC': # n**2 or squrt(n)? - tabWrite( - "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368) - ) - tabWrite( - "color rgbt<%.3g, %.3g, %.3g, %.3g>\n" - % (*world.pov.horizon_color, 1.0 - mist.intensity) - ) - # tabWrite("fog_offset %.6f\n" % mist.start) #create a pov property to prepend - # tabWrite("fog_alt %.6f\n" % mist.height) #XXX right? - # tabWrite("turbulence 0.2\n") - # tabWrite("turb_depth 0.3\n") - tabWrite("fog_type 1\n") # type2 for height - tabWrite("}\n") - if scene.pov.media_enable: - tabWrite("media {\n") - tabWrite( - "scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n" - % ( - int(scene.pov.media_scattering_type), - (scene.pov.media_diffusion_scale), - *(scene.pov.media_diffusion_color[:]), - ) - ) - if scene.pov.media_scattering_type == '5': - tabWrite("eccentricity %.3g\n" % scene.pov.media_eccentricity) - tabWrite("}\n") - tabWrite( - "absorption %.12f*<%.4g, %.4g, %.4g>\n" - % ( - scene.pov.media_absorption_scale, - *(scene.pov.media_absorption_color[:]), - ) - ) - tabWrite("\n") - tabWrite("samples %.d\n" % scene.pov.media_samples) - tabWrite("}\n") - - def exportGlobalSettings(scene): + def export_global_settings(scene): """write all POV global settings to exported file """ - tabWrite("global_settings {\n") - tabWrite("assumed_gamma 1.0\n") - tabWrite("max_trace_level %d\n" % scene.pov.max_trace_level) + tab_write("global_settings {\n") + tab_write("assumed_gamma 1.0\n") + tab_write("max_trace_level %d\n" % scene.pov.max_trace_level) - # Deprecated (autodetected in pov3.8): - # if scene.pov.charset != 'ascii': - # file.write(" charset %s\n" % scene.pov.charset) if scene.pov.global_settings_advanced: - if scene.pov.radio_enable == False: + if not scene.pov.radio_enable: file.write(" adc_bailout %.6f\n" % scene.pov.adc_bailout) - file.write( - " ambient_light <%.6f,%.6f,%.6f>\n" - % scene.pov.ambient_light[:] - ) - file.write( - " irid_wavelength <%.6f,%.6f,%.6f>\n" - % scene.pov.irid_wavelength[:] - ) - file.write( - " max_intersections %s\n" % scene.pov.max_intersections - ) + file.write(" ambient_light <%.6f,%.6f,%.6f>\n" % scene.pov.ambient_light[:]) + file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n" % scene.pov.irid_wavelength[:]) file.write(" number_of_waves %s\n" % scene.pov.number_of_waves) file.write(" noise_generator %s\n" % scene.pov.noise_generator) if scene.pov.radio_enable: - tabWrite("radiosity {\n") - tabWrite("adc_bailout %.4g\n" % scene.pov.radio_adc_bailout) - tabWrite("brightness %.4g\n" % scene.pov.radio_brightness) - tabWrite("count %d\n" % scene.pov.radio_count) - tabWrite("error_bound %.4g\n" % scene.pov.radio_error_bound) - tabWrite("gray_threshold %.4g\n" % scene.pov.radio_gray_threshold) - tabWrite( - "low_error_factor %.4g\n" % scene.pov.radio_low_error_factor - ) - tabWrite("maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse) - tabWrite("minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse) - tabWrite("nearest_count %d\n" % scene.pov.radio_nearest_count) - tabWrite("pretrace_start %.3g\n" % scene.pov.radio_pretrace_start) - tabWrite("pretrace_end %.3g\n" % scene.pov.radio_pretrace_end) - tabWrite("recursion_limit %d\n" % scene.pov.radio_recursion_limit) - tabWrite("always_sample %d\n" % scene.pov.radio_always_sample) - tabWrite("normal %d\n" % scene.pov.radio_normal) - tabWrite("media %d\n" % scene.pov.radio_media) - tabWrite("subsurface %d\n" % scene.pov.radio_subsurface) - tabWrite("}\n") - onceSss = 1 - onceAmbient = 1 - oncePhotons = 1 + tab_write("radiosity {\n") + tab_write("adc_bailout %.4g\n" % scene.pov.radio_adc_bailout) + tab_write("brightness %.4g\n" % scene.pov.radio_brightness) + tab_write("count %d\n" % scene.pov.radio_count) + tab_write("error_bound %.4g\n" % scene.pov.radio_error_bound) + tab_write("gray_threshold %.4g\n" % scene.pov.radio_gray_threshold) + tab_write("low_error_factor %.4g\n" % scene.pov.radio_low_error_factor) + tab_write("maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse) + tab_write("minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse) + tab_write("nearest_count %d\n" % scene.pov.radio_nearest_count) + tab_write("pretrace_start %.3g\n" % scene.pov.radio_pretrace_start) + tab_write("pretrace_end %.3g\n" % scene.pov.radio_pretrace_end) + tab_write("recursion_limit %d\n" % scene.pov.radio_recursion_limit) + tab_write("always_sample %d\n" % scene.pov.radio_always_sample) + tab_write("normal %d\n" % scene.pov.radio_normal) + tab_write("media %d\n" % scene.pov.radio_media) + tab_write("subsurface %d\n" % scene.pov.radio_subsurface) + tab_write("}\n") + once_sss = 1 + once_ambient = 1 + once_photons = 1 for material in bpy.data.materials: - if material.pov_subsurface_scattering.use and onceSss: + if material.pov_subsurface_scattering.use and once_sss: # In pov, the scale has reversed influence compared to blender. these number # should correct that - tabWrite( - "mm_per_unit %.6f\n" - % (material.pov_subsurface_scattering.scale * 1000.0) + tab_write( + "mm_per_unit %.6f\n" % (material.pov_subsurface_scattering.scale * 1000.0) ) # 1000 rather than scale * (-100.0) + 15.0)) # In POV-Ray, the scale factor for all subsurface shaders needs to be the same # formerly sslt_samples were multiplied by 100 instead of 10 - sslt_samples = ( - 11 - material.pov_subsurface_scattering.error_threshold - ) * 10 + sslt_samples = (11 - material.pov_subsurface_scattering.error_threshold) * 10 - tabWrite( - "subsurface { samples %d, %d }\n" - % (sslt_samples, sslt_samples / 10) - ) - onceSss = 0 + tab_write("subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10)) + once_sss = 0 - if world and onceAmbient: - tabWrite( - "ambient_light rgb<%.3g, %.3g, %.3g>\n" - % world.pov.ambient_color[:] - ) - onceAmbient = 0 + if world and once_ambient: + tab_write("ambient_light rgb<%.3g, %.3g, %.3g>\n" % world.pov.ambient_color[:]) + once_ambient = 0 if scene.pov.photon_enable: - if oncePhotons and ( - material.pov.refraction_type == "2" - or material.pov.photons_reflection == True + if once_photons and ( + material.pov.refraction_type == "2" or material.pov.photons_reflection ): - tabWrite("photons {\n") - tabWrite("spacing %.6f\n" % scene.pov.photon_spacing) - tabWrite( - "max_trace_level %d\n" - % scene.pov.photon_max_trace_level - ) - tabWrite( - "adc_bailout %.3g\n" % scene.pov.photon_adc_bailout - ) - tabWrite( + tab_write("photons {\n") + tab_write("spacing %.6f\n" % scene.pov.photon_spacing) + tab_write("max_trace_level %d\n" % scene.pov.photon_max_trace_level) + tab_write("adc_bailout %.3g\n" % scene.pov.photon_adc_bailout) + tab_write( "gather %d, %d\n" - % ( - scene.pov.photon_gather_min, - scene.pov.photon_gather_max, - ) + % (scene.pov.photon_gather_min, scene.pov.photon_gather_max) ) if scene.pov.photon_map_file_save_load in {'save'}: - filePhName = 'Photon_map_file.ph' + ph_file_name = 'Photon_map_file.ph' if scene.pov.photon_map_file != '': - filePhName = scene.pov.photon_map_file + '.ph' - filePhDir = tempfile.gettempdir() + ph_file_name = scene.pov.photon_map_file + '.ph' + ph_file_dir = tempfile.gettempdir() path = bpy.path.abspath(scene.pov.photon_map_dir) if os.path.exists(path): - filePhDir = path - fullFileName = os.path.join(filePhDir, filePhName) - tabWrite('save_file "%s"\n' % fullFileName) - scene.pov.photon_map_file = fullFileName + ph_file_dir = path + full_file_name = os.path.join(ph_file_dir, ph_file_name) + tab_write('save_file "%s"\n' % full_file_name) + scene.pov.photon_map_file = full_file_name if scene.pov.photon_map_file_save_load in {'load'}: - fullFileName = bpy.path.abspath( - scene.pov.photon_map_file - ) - if os.path.exists(fullFileName): - tabWrite('load_file "%s"\n' % fullFileName) - tabWrite("}\n") - oncePhotons = 0 + full_file_name = bpy.path.abspath(scene.pov.photon_map_file) + if os.path.exists(full_file_name): + tab_write('load_file "%s"\n' % full_file_name) + tab_write("}\n") + once_photons = 0 - tabWrite("}\n") - - def exportCustomCode(): - """write all POV user defined custom code to exported file """ - # Write CurrentAnimation Frame for use in Custom POV Code - file.write( - "#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current - ) - # Change path and uncomment to add an animated include file by hand: - file.write( - "//#include \"/home/user/directory/animation_include_file.inc\"\n" - ) - for txt in bpy.data.texts: - if txt.pov.custom_code == 'both': - # Why are the newlines needed? - file.write("\n") - file.write(txt.as_string()) - file.write("\n") + tab_write("}\n") # sel = renderable_objects(scene) #removed for booleans if comments: @@ -4990,51 +687,44 @@ def write_pov(filename, scene=None, info_callback=None): "//--Exported with POV-Ray exporter for Blender--\n" "//----------------------------------------------\n\n" ) - file.write("#version 3.7;\n") #Switch below as soon as 3.8 beta gets easy linked - #file.write("#version 3.8;\n") + file.write("#version 3.7;\n") # Switch below as soon as 3.8 beta gets easy linked + # file.write("#version 3.8;\n") file.write( - "#declare Default_texture = texture{pigment {rgb 0.8} " - "finish {brilliance 3.8} }\n\n" + "#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n" ) if comments: file.write("\n//--Global settings--\n\n") - exportGlobalSettings(scene) + export_global_settings(scene) if comments: file.write("\n//--Custom Code--\n\n") - exportCustomCode() + scripting.export_custom_code(file) if comments: file.write("\n//--Patterns Definitions--\n\n") - LocalPatternNames = [] + local_pattern_names = [] for texture in bpy.data.textures: # ok? if texture.users > 0: - currentPatName = string_strip_hyphen( - bpy.path.clean_name(texture.name) - ) + current_pat_name = string_strip_hyphen(bpy.path.clean_name(texture.name)) # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above - LocalPatternNames.append(currentPatName) + local_pattern_names.append(current_pat_name) # use above list to prevent writing texture instances several times and assign in mats? if ( - texture.type not in {'NONE', 'IMAGE'} - and texture.pov.tex_pattern_type == 'emulator' - ) or ( - texture.type in {'NONE', 'IMAGE'} - and texture.pov.tex_pattern_type != 'emulator' - ): - file.write("\n#declare PAT_%s = \n" % currentPatName) - file.write(shading.exportPattern(texture, string_strip_hyphen)) + texture.type not in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type == 'emulator' + ) or (texture.type in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type != 'emulator'): + file.write("\n#declare PAT_%s = \n" % current_pat_name) + file.write(shading.export_pattern(texture)) file.write("\n") if comments: file.write("\n//--Background--\n\n") - exportWorld(scene.world) + scenography.export_world(scene.world, scene, global_matrix, tab_write) if comments: file.write("\n//--Cameras--\n\n") - exportCamera() + scenography.export_camera(scene, global_matrix, render, tab_write) if comments: file.write("\n//--Lamps--\n\n") @@ -5047,20 +737,57 @@ def write_pov(filename, scene=None, info_callback=None): csg_list.append(mod.object) if csg_list != []: csg = False - sel = no_renderable_objects(scene) - exportMeshes(scene, sel, csg) + sel = no_renderable_objects() + object_mesh_topology.export_meshes( + preview_dir, + file, + scene, + sel, + csg, + string_strip_hyphen, + safety, + write_object_modifiers, + material_names_dictionary, + write_object_material, + scenography.exported_lights_count, + unpacked_images, + image_format, + img_map, + img_map_transforms, + path_image, + smoke_path, + global_matrix, + write_matrix, + using_uberpov, + comments, + linebreaksinlists, + tab, + tab_level, + tab_write, + info_callback, + ) csg = True sel = renderable_objects(scene) - exportLamps( - [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')] + scenography.export_lights( + [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')], + file, + scene, + global_matrix, + write_matrix, + tab_write, ) if comments: file.write("\n//--Rainbows--\n\n") - exportRainbows( - [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')] + scenography.export_rainbows( + [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')], + file, + scene, + global_matrix, + write_matrix, + tab_write, ) if comments: @@ -5068,10 +795,9 @@ def write_pov(filename, scene=None, info_callback=None): for c in sel: if c.is_modified(scene, 'RENDER'): continue # don't export as pov curves objects with modifiers, but as mesh - elif c.type == 'CURVE' and ( - c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'} - ): - exportCurves(scene, c) + # Implicit else-if (as not skipped by previous "continue") + if c.type == 'CURVE' and (c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'}): + object_curve_topology.export_curves(c, string_strip_hyphen, global_matrix, tab_write) if comments: file.write("\n//--Material Definitions--\n\n") @@ -5079,84 +805,101 @@ def write_pov(filename, scene=None, info_callback=None): file.write("#default{ pigment{ color srgb 0.8 }}\n") # Convert all materials to strings we can access directly per vertex. # exportMaterials() - shading.writeMaterial( + shading.write_material( using_uberpov, DEF_MAT_NAME, - scene, - tabWrite, + tab_write, safety, comments, - uniqueName, - materialNames, + unique_name, + material_names_dictionary, None, ) # default material for material in bpy.data.materials: if material.users > 0: + r, g, b, a = material.diffuse_color[:] + pigment_color = "pigment {rgbt <%.4g,%.4g,%.4g,%.4g>}" % (r, g, b, 1 - a) if material.pov.material_use_nodes: + # Also make here other pigment_color fallback using BSDF node main color ? ntree = material.node_tree - povMatName = string_strip_hyphen( - bpy.path.clean_name(material.name) - ) + pov_mat_name = string_strip_hyphen(bpy.path.clean_name(material.name)) if len(ntree.nodes) == 0: - file.write( - '#declare %s = texture {%s}\n' % (povMatName, color) - ) + file.write('#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color)) else: - shading.write_nodes(scene, povMatName, ntree, file) + shading.write_nodes(scene, pov_mat_name, ntree, file) for node in ntree.nodes: if node: if node.bl_idname == "PovrayOutputNode": if node.inputs["Texture"].is_linked: for link in ntree.links: - if ( - link.to_node.bl_idname - == "PovrayOutputNode" - ): - povMatName = ( + if link.to_node.bl_idname == "PovrayOutputNode": + pov_mat_name = ( string_strip_hyphen( - bpy.path.clean_name( - link.from_node.name - ) + bpy.path.clean_name(link.from_node.name) ) - + "_%s" % povMatName + + "_%s" % pov_mat_name ) else: file.write( - '#declare %s = texture {%s}\n' - % (povMatName, color) + '#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color) ) else: - shading.writeMaterial( + shading.write_material( using_uberpov, DEF_MAT_NAME, - scene, - tabWrite, + tab_write, safety, comments, - uniqueName, - materialNames, + unique_name, + material_names_dictionary, material, ) # attributes are all the variables needed by the other python file... if comments: file.write("\n") - exportMeta([m for m in sel if m.type == 'META']) + export_meta([m for m in sel if m.type == 'META']) if comments: file.write("//--Mesh objects--\n") - - #tbefore = time.time() - exportMeshes(scene, sel, csg) - #totime = time.time() - tbefore - #print("exportMeshes took" + str(totime)) + # tbefore = time.time() + object_mesh_topology.export_meshes( + preview_dir, + file, + scene, + sel, + csg, + string_strip_hyphen, + safety, + write_object_modifiers, + material_names_dictionary, + write_object_material, + scenography.exported_lights_count, + unpacked_images, + image_format, + img_map, + img_map_transforms, + path_image, + smoke_path, + global_matrix, + write_matrix, + using_uberpov, + comments, + linebreaksinlists, + tab, + tab_level, + tab_write, + info_callback, + ) + # totime = time.time() - tbefore + # print("export_meshes took" + str(totime)) # What follow used to happen here: - # exportCamera() - # exportWorld(scene.world) - # exportGlobalSettings(scene) + # export_camera() + # scenography.export_world(scene.world, scene, global_matrix, tab_write) + # export_global_settings(scene) # MR:..and the order was important for implementing pov 3.7 baking # (mesh camera) comment for the record # CR: Baking should be a special case than. If "baking", than we could change the order. @@ -5166,13 +909,9 @@ def write_pov(filename, scene=None, info_callback=None): # print("pov file closed %s" % file.closed) -def write_pov_ini( - scene, filename_ini, filename_log, filename_pov, filename_image -): +def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_image): """Write ini file.""" - feature_set = bpy.context.preferences.addons[ - __package__ - ].preferences.branch_feature_set_povray + feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray using_uberpov = feature_set == 'uberpov' # scene = bpy.data.scenes[0] scene = bpy.context.scene @@ -5182,7 +921,7 @@ def write_pov_ini( y = int(render.resolution_y * render.resolution_percentage * 0.01) file = open(filename_ini, "w") - file.write("Version=3.8\n") + file.write("Version=3.7\n") # write povray text stream to temporary file of same name with _log suffix # file.write("All_File='%s'\n" % filename_log) # DEBUG.OUT log if none specified: @@ -5202,9 +941,7 @@ def write_pov_ini( file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y)) file.write("End_Row=%4g\n" % (1.0 - render.border_min_y)) - file.write( - "Bounding_Method=2\n" - ) # The new automatic BSP is faster in most scenes + file.write("Bounding_Method=2\n") # The new automatic BSP is faster in most scenes # Activated (turn this back off when better live exchange is done between the two programs # (see next comment) @@ -5228,16 +965,10 @@ def write_pov_ini( file.write("Antialias_Depth=%d\n" % scene.pov.antialias_depth) file.write("Antialias_Threshold=%.3g\n" % scene.pov.antialias_threshold) if using_uberpov and scene.pov.antialias_method == '2': - file.write( - "Sampling_Method=%s\n" % method[scene.pov.antialias_method] - ) - file.write( - "Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence - ) + file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method]) + file.write("Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence) else: - file.write( - "Sampling_Method=%s\n" % method[scene.pov.antialias_method] - ) + file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method]) file.write("Antialias_Gamma=%.3g\n" % scene.pov.antialias_gamma) if scene.pov.jitter_enable: file.write("Jitter=on\n") @@ -5270,15 +1001,12 @@ class PovrayRender(bpy.types.RenderEngine): if pov_binary: if os.path.exists(pov_binary): return pov_binary - else: - print( - "User Preferences path to povray %r NOT FOUND, checking $PATH" - % pov_binary - ) + # Implicit else, as here return was still not triggered: + print("User Preferences path to povray %r NOT FOUND, checking $PATH" % pov_binary) # Windows Only # assume if there is a 64bit binary that the user has a 64bit capable OS - if sys.platform[:3] == "win": + if platform.startswith('win'): import winreg win_reg_key = winreg.OpenKey( @@ -5317,36 +1045,27 @@ class PovrayRender(bpy.types.RenderEngine): return pov_binary return "" - def _export(self, depsgraph, povPath, renderImagePath): + def _export(self, depsgraph, pov_path, image_render_path): """gather all necessary output files paths user defined and auto generated and export there""" - import tempfile scene = bpy.context.scene if scene.pov.tempfiles_enable: - self._temp_file_in = tempfile.NamedTemporaryFile( - suffix=".pov", delete=False - ).name + self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name # PNG with POV 3.7, can show the background color with alpha. In the long run using the # POV-Ray interactive preview like bishop 3D could solve the preview for all formats. - self._temp_file_out = tempfile.NamedTemporaryFile( - suffix=".png", delete=False - ).name + self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name - self._temp_file_ini = tempfile.NamedTemporaryFile( - suffix=".ini", delete=False - ).name - self._temp_file_log = os.path.join( - tempfile.gettempdir(), "alltext.out" - ) + self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name + self._temp_file_log = os.path.join(tempfile.gettempdir(), "alltext.out") else: - self._temp_file_in = povPath + ".pov" + self._temp_file_in = pov_path + ".pov" # PNG with POV 3.7, can show the background color with alpha. In the long run using the # POV-Ray interactive preview like bishop 3D could solve the preview for all formats. - self._temp_file_out = renderImagePath + ".png" - # self._temp_file_out = renderImagePath + ".tga" - self._temp_file_ini = povPath + ".ini" - logPath = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/') - self._temp_file_log = os.path.join(logPath, "alltext.out") + self._temp_file_out = image_render_path + ".png" + # self._temp_file_out = image_render_path + ".tga" + self._temp_file_ini = pov_path + ".ini" + log_path = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/') + self._temp_file_log = os.path.join(log_path, "alltext.out") ''' self._temp_file_in = "/test.pov" # PNG with POV 3.7, can show the background color with alpha. In the long run using the @@ -5377,17 +1096,11 @@ class PovrayRender(bpy.types.RenderEngine): pov_binary = PovrayRender._locate_binary() if not pov_binary: - print( - "POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed" - ) + print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed") return False write_pov_ini( - scene, - self._temp_file_ini, - self._temp_file_log, - self._temp_file_in, - self._temp_file_out, + scene, self._temp_file_ini, self._temp_file_log, self._temp_file_in, self._temp_file_out ) print("***-STARTING-***") @@ -5395,11 +1108,11 @@ class PovrayRender(bpy.types.RenderEngine): extra_args = [] if scene.pov.command_line_switches != "": - for newArg in scene.pov.command_line_switches.split(" "): - extra_args.append(newArg) + for new_arg in scene.pov.command_line_switches.split(" "): + extra_args.append(new_arg) self._is_windows = False - if sys.platform[:3] == "win": + if platform.startswith('win'): self._is_windows = True if "/EXIT" not in extra_args and not scene.pov.pov_editor: extra_args.append("/EXIT") @@ -5442,7 +1155,7 @@ class PovrayRender(bpy.types.RenderEngine): # and Windows does not know how to delete a file in use! time.sleep(self.DELAY) for i in unpacked_images: - for c in range(5): + for j in range(5): try: os.unlink(i) break @@ -5453,7 +1166,6 @@ class PovrayRender(bpy.types.RenderEngine): def render(self, depsgraph): """Export necessary files from text editor and render image.""" - import tempfile scene = bpy.context.scene r = scene.render @@ -5475,8 +1187,11 @@ class PovrayRender(bpy.types.RenderEngine): except OSError: pass return False - - poll_result = self._process.poll() + try: + poll_result = self._process.poll() + except AttributeError: + print("***CHECK POV PATH IN PREFERENCES***") + return False # POV process is finisehd, one way or the other if poll_result is not None: if poll_result < 0: @@ -5488,27 +1203,18 @@ class PovrayRender(bpy.types.RenderEngine): if bpy.context.scene.pov.text_block != "": if scene.pov.tempfiles_enable: - self._temp_file_in = tempfile.NamedTemporaryFile( - suffix=".pov", delete=False - ).name - self._temp_file_out = tempfile.NamedTemporaryFile( - suffix=".png", delete=False - ).name + self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name + self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name - self._temp_file_ini = tempfile.NamedTemporaryFile( - suffix=".ini", delete=False - ).name - self._temp_file_log = os.path.join( - tempfile.gettempdir(), "alltext.out" - ) + self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name + self._temp_file_log = os.path.join(tempfile.gettempdir(), "alltext.out") else: - povPath = scene.pov.text_block - renderImagePath = os.path.splitext(povPath)[0] - self._temp_file_out = os.path.join(preview_dir, renderImagePath) - self._temp_file_in = os.path.join(preview_dir, povPath) + pov_path = scene.pov.text_block + image_render_path = os.path.splitext(pov_path)[0] + self._temp_file_out = os.path.join(preview_dir, image_render_path) + self._temp_file_in = os.path.join(preview_dir, pov_path) self._temp_file_ini = os.path.join( - preview_dir, - (os.path.splitext(self._temp_file_in)[0] + ".INI"), + preview_dir, (os.path.splitext(self._temp_file_in)[0] + ".INI") ) self._temp_file_log = os.path.join(preview_dir, "alltext.out") @@ -5533,15 +1239,11 @@ class PovrayRender(bpy.types.RenderEngine): pov_binary = PovrayRender._locate_binary() if not pov_binary: - print( - "POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed" - ) + print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed") return False # start ini UI options export - self.update_stats( - "", "POV-Ray 3.7: Exporting ini options from Blender" - ) + self.update_stats("", "POV-Ray 3.7: Exporting ini options from Blender") write_pov_ini( scene, @@ -5556,10 +1258,10 @@ class PovrayRender(bpy.types.RenderEngine): extra_args = [] if scene.pov.command_line_switches != "": - for newArg in scene.pov.command_line_switches.split(" "): - extra_args.append(newArg) + for new_arg in scene.pov.command_line_switches.split(" "): + extra_args.append(new_arg) - if sys.platform[:3] == "win": + if platform.startswith('win'): if "/EXIT" not in extra_args and not scene.pov.pov_editor: extra_args.append("/EXIT") else: @@ -5568,8 +1270,8 @@ class PovrayRender(bpy.types.RenderEngine): # Start Rendering! try: - if ( - sys.platform[:3] != "win" and scene.pov.sdl_window_enable + if scene.pov.sdl_window_enable and not platform.startswith( + 'win' ): # segfault on linux == False !!! env = {'POV_DISPLAY_SCALED': 'off'} env.update(os.environ) @@ -5638,12 +1340,10 @@ class PovrayRender(bpy.types.RenderEngine): ## r.image_settings.file_format = 'TARGA' ## r.image_settings.color_mode = 'RGBA' - blendSceneName = bpy.data.filepath.split(os.path.sep)[-1].split( - "." - )[0] - povSceneName = "" - povPath = "" - renderImagePath = "" + blend_scene_name = bpy.data.filepath.split(os.path.sep)[-1].split(".")[0] + pov_scene_name = "" + pov_path = "" + image_render_path = "" # has to be called to update the frame on exporting animations scene.frame_set(scene.frame_current) @@ -5651,54 +1351,49 @@ class PovrayRender(bpy.types.RenderEngine): if not scene.pov.tempfiles_enable: # check paths - povPath = bpy.path.abspath(scene.pov.scene_path).replace( - '\\', '/' - ) - if povPath == "": + pov_path = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/') + if pov_path == "": if bpy.data.is_saved: - povPath = bpy.path.abspath("//") + pov_path = bpy.path.abspath("//") else: - povPath = tempfile.gettempdir() - elif povPath.endswith("/"): - if povPath == "/": - povPath = bpy.path.abspath("//") + pov_path = tempfile.gettempdir() + elif pov_path.endswith("/"): + if pov_path == "/": + pov_path = bpy.path.abspath("//") else: - povPath = bpy.path.abspath(scene.pov.scene_path) + pov_path = bpy.path.abspath(scene.pov.scene_path) - if not os.path.exists(povPath): + if not os.path.exists(pov_path): try: - os.makedirs(povPath) - except: + os.makedirs(pov_path) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) import traceback traceback.print_exc() - print( - "POV-Ray 3.7: Cannot create scenes directory: %r" - % povPath - ) + print("POV-Ray 3.7: Cannot create scenes directory: %r" % pov_path) self.update_stats( - "", - "POV-Ray 3.7: Cannot create scenes directory %r" - % povPath, + "", "POV-Ray 3.7: Cannot create scenes directory %r" % pov_path ) time.sleep(2.0) # return ''' # Bug in POV-Ray RC3 - renderImagePath = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/') - if renderImagePath == "": + image_render_path = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/') + if image_render_path == "": if bpy.data.is_saved: - renderImagePath = bpy.path.abspath("//") + image_render_path = bpy.path.abspath("//") else: - renderImagePath = tempfile.gettempdir() - #print("Path: " + renderImagePath) + image_render_path = tempfile.gettempdir() + #print("Path: " + image_render_path) elif path.endswith("/"): - if renderImagePath == "/": - renderImagePath = bpy.path.abspath("//") + if image_render_path == "/": + image_render_path = bpy.path.abspath("//") else: - renderImagePath = bpy.path.abspath(scene.pov.renderimage_path) + image_render_path = bpy.path.abspath(scene.pov.) if not os.path.exists(path): print("POV-Ray 3.7: Cannot find render image directory") self.update_stats("", "POV-Ray 3.7: Cannot find render image directory") @@ -5708,38 +1403,38 @@ class PovrayRender(bpy.types.RenderEngine): # check name if scene.pov.scene_name == "": - if blendSceneName != "": - povSceneName = blendSceneName + if blend_scene_name != "": + pov_scene_name = blend_scene_name else: - povSceneName = "untitled" + pov_scene_name = "untitled" else: - povSceneName = scene.pov.scene_name - if os.path.isfile(povSceneName): - povSceneName = os.path.basename(povSceneName) - povSceneName = povSceneName.split('/')[-1].split('\\')[-1] - if not povSceneName: + pov_scene_name = scene.pov.scene_name + if os.path.isfile(pov_scene_name): + pov_scene_name = os.path.basename(pov_scene_name) + pov_scene_name = pov_scene_name.split('/')[-1].split('\\')[-1] + if not pov_scene_name: print("POV-Ray 3.7: Invalid scene name") self.update_stats("", "POV-Ray 3.7: Invalid scene name") time.sleep(2.0) # return - povSceneName = os.path.splitext(povSceneName)[0] + pov_scene_name = os.path.splitext(pov_scene_name)[0] - print("Scene name: " + povSceneName) - print("Export path: " + povPath) - povPath = os.path.join(povPath, povSceneName) - povPath = os.path.realpath(povPath) + print("Scene name: " + pov_scene_name) + print("Export path: " + pov_path) + pov_path = os.path.join(pov_path, pov_scene_name) + pov_path = os.path.realpath(pov_path) # for now this has to be the same like the pov output. Bug in POV-Ray RC3. - # renderImagePath = renderImagePath + "\\" + povSceneName - renderImagePath = povPath # Bugfix for POV-Ray RC3 bug - # renderImagePath = os.path.realpath(renderImagePath) # Bugfix for POV-Ray RC3 bug + # image_render_path = image_render_path + "\\" + pov_scene_name + image_render_path = pov_path # Bugfix for POV-Ray RC3 bug + # image_render_path = os.path.realpath(image_render_path) # Bugfix for POV-Ray RC3 bug - # print("Export path: %s" % povPath) - # print("Render Image path: %s" % renderImagePath) + # print("Export path: %s" % pov_path) + # print("Render Image path: %s" % image_render_path) # start export self.update_stats("", "POV-Ray 3.7: Exporting data from Blender") - self._export(depsgraph, povPath, renderImagePath) + self._export(depsgraph, pov_path, image_render_path) self.update_stats("", "POV-Ray 3.7: Parsing File") if not self._render(depsgraph): @@ -5774,11 +1469,7 @@ class PovrayRender(bpy.types.RenderEngine): # XXX This is working for UNIX, not sure whether it might need adjustments for # other OSs # First replace is for windows - t_data = ( - str(t_data) - .replace('\\r\\n', '\\n') - .replace('\\r', '\r') - ) + t_data = str(t_data).replace('\\r\\n', '\\n').replace('\\r', '\r') lines = t_data.split('\\n') last_line += lines[0] lines[0] = last_line @@ -5789,11 +1480,7 @@ class PovrayRender(bpy.types.RenderEngine): _pov_rendering = True match = percent.findall(str(data)) if match: - self.update_stats( - "", - "POV-Ray 3.7: Rendering File (%s%%)" - % match[-1], - ) + self.update_stats("", "POV-Ray 3.7: Rendering File (%s%%)" % match[-1]) else: self.update_stats("", "POV-Ray 3.7: Rendering File") @@ -5882,107 +1569,116 @@ class PovrayRender(bpy.types.RenderEngine): # print(filename_log) #bring the pov log to blender console with proper path? with open( - self._temp_file_log, - encoding='utf-8' + self._temp_file_log, encoding='utf-8' ) as f: # The with keyword automatically closes the file when you are done msg = f.read() - #if isinstance(msg, str): - #stdmsg = msg - #decoded = False - #else: - #if type(msg) == bytes: - #stdmsg = msg.split('\n') - #stdmsg = msg.encode('utf-8', "replace") - #stdmsg = msg.encode("utf-8", "replace") - - #stdmsg = msg.decode(encoding) - #decoded = True - #msg.encode('utf-8').decode('utf-8') + # if isinstance(msg, str): + # stdmsg = msg + # decoded = False + # else: + # if type(msg) == bytes: + # stdmsg = msg.split('\n') + # stdmsg = msg.encode('utf-8', "replace") + # stdmsg = msg.encode("utf-8", "replace") + + # stdmsg = msg.decode(encoding) + # decoded = True + # msg.encode('utf-8').decode('utf-8') + msg.replace("\t", " ") print(msg) # Also print to the interactive console used in POV centric workspace # To do: get a grip on new line encoding # and make this a function to be used elsewhere for win in bpy.context.window_manager.windows: - if win.screen != None: + if win.screen is not None: scr = win.screen for area in scr.areas: if area.type == 'CONSOLE': - #context override - #ctx = {'window': win, 'screen': scr, 'area':area}#bpy.context.copy() + # context override + # ctx = {'window': win, 'screen': scr, 'area':area}#bpy.context.copy() ctx = {} ctx['area'] = area ctx['region'] = area.regions[-1] ctx['space_data'] = area.spaces.active - ctx['screen'] = scr#C.screen + ctx['screen'] = scr # C.screen ctx['window'] = win - #bpy.ops.console.banner(ctx, text = "Hello world") + # bpy.ops.console.banner(ctx, text = "Hello world") bpy.ops.console.clear_line(ctx) - stdmsg = msg.split('\n') #XXX todo , test and see + stdmsg = msg.split('\n') # XXX todo , test and see for i in stdmsg: - bpy.ops.console.insert(ctx, text = i) + # Crashes if no Terminal displayed on Windows + bpy.ops.console.scrollback_append(ctx, text=i, type='INFO') + # bpy.ops.console.insert(ctx, text=(i + "\n")) self.update_stats("", "") if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable: self._cleanup() - sound_on = bpy.context.preferences.addons[ - __package__ - ].preferences.use_sounds + sound_on = bpy.context.preferences.addons[__package__].preferences.use_sounds + finished_render_message = "\'Render completed\'" - if sys.platform[:3] == "win" and sound_on: + if platform.startswith('win') and sound_on: # Could not find tts Windows command so playing beeps instead :-) # "Korobeiniki"(КоробеĚйники) # aka "A-Type" Tetris theme import winsound - winsound.Beep(494,250) #B - winsound.Beep(370,125) #F - winsound.Beep(392,125) #G - winsound.Beep(440,250) #A - winsound.Beep(392,125) #G - winsound.Beep(370,125) #F# - winsound.Beep(330,275) #E - winsound.Beep(330,125) #E - winsound.Beep(392,125) #G - winsound.Beep(494,275) #B - winsound.Beep(440,125) #A - winsound.Beep(392,125) #G - winsound.Beep(370,275) #F - winsound.Beep(370,125) #F - winsound.Beep(392,125) #G - winsound.Beep(440,250) #A - winsound.Beep(494,250) #B - winsound.Beep(392,250) #G - winsound.Beep(330,350) #E + + winsound.Beep(494, 250) # B + winsound.Beep(370, 125) # F + winsound.Beep(392, 125) # G + winsound.Beep(440, 250) # A + winsound.Beep(392, 125) # G + winsound.Beep(370, 125) # F# + winsound.Beep(330, 275) # E + winsound.Beep(330, 125) # E + winsound.Beep(392, 125) # G + winsound.Beep(494, 275) # B + winsound.Beep(440, 125) # A + winsound.Beep(392, 125) # G + winsound.Beep(370, 275) # F + winsound.Beep(370, 125) # F + winsound.Beep(392, 125) # G + winsound.Beep(440, 250) # A + winsound.Beep(494, 250) # B + winsound.Beep(392, 250) # G + winsound.Beep(330, 350) # E time.sleep(0.5) - winsound.Beep(440,250) #A - winsound.Beep(440,150) #A - winsound.Beep(523,125) #D8 - winsound.Beep(659,250) #E8 - winsound.Beep(587,125) #D8 - winsound.Beep(523,125) #C8 - winsound.Beep(494,250) #B - winsound.Beep(494,125) #B - winsound.Beep(392,125) #G - winsound.Beep(494,250) #B - winsound.Beep(440,150) #A - winsound.Beep(392,125) #G - winsound.Beep(370,250) #F# - winsound.Beep(370,125) #F# - winsound.Beep(392,125) #G - winsound.Beep(440,250) #A - winsound.Beep(494,250) #B - winsound.Beep(392,250) #G - winsound.Beep(330,300) #E - - #Does Linux support say command? - elif sys.platform[:3] != "win" : - finished_render_message = "\'Render completed\'" + winsound.Beep(440, 250) # A + winsound.Beep(440, 150) # A + winsound.Beep(523, 125) # D8 + winsound.Beep(659, 250) # E8 + winsound.Beep(587, 125) # D8 + winsound.Beep(523, 125) # C8 + winsound.Beep(494, 250) # B + winsound.Beep(494, 125) # B + winsound.Beep(392, 125) # G + winsound.Beep(494, 250) # B + winsound.Beep(440, 150) # A + winsound.Beep(392, 125) # G + winsound.Beep(370, 250) # F# + winsound.Beep(370, 125) # F# + winsound.Beep(392, 125) # G + winsound.Beep(440, 250) # A + winsound.Beep(494, 250) # B + winsound.Beep(392, 250) # G + winsound.Beep(330, 300) # E + + # Mac supports natively say command + elif platform == "darwin": # We don't want the say command to block Python, # so we add an ampersand after the message os.system("say %s &" % (finished_render_message)) + # While Linux frequently has espeak installed or at least can suggest + # Maybe windows could as well ? + elif platform == "linux": + # We don't want the say command to block Python, + # so we add an ampersand after the message + os.system("echo %s | espeak &" % (finished_render_message)) + + ################################################################################## #################################Operators######################################## ################################################################################## @@ -5993,82 +1689,78 @@ class RenderPovTexturePreview(Operator): bl_label = "Update preview" def execute(self, context): - tex = ( - bpy.context.object.active_material.active_texture - ) # context.texture - texPrevName = ( - string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev" - ) + tex = bpy.context.object.active_material.active_texture # context.texture + tex_prev_name = string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev" ## Make sure Preview directory exists and is empty if not os.path.isdir(preview_dir): os.mkdir(preview_dir) - iniPrevFile = os.path.join(preview_dir, "Preview.ini") - inputPrevFile = os.path.join(preview_dir, "Preview.pov") - outputPrevFile = os.path.join(preview_dir, texPrevName) + ini_prev_file = os.path.join(preview_dir, "Preview.ini") + input_prev_file = os.path.join(preview_dir, "Preview.pov") + output_prev_file = os.path.join(preview_dir, tex_prev_name) ##################### ini ########################################## - fileIni = open("%s" % iniPrevFile, "w") - fileIni.write('Version=3.8\n') - fileIni.write('Input_File_Name="%s"\n' % inputPrevFile) - fileIni.write('Output_File_Name="%s.png"\n' % outputPrevFile) - fileIni.write('Library_Path="%s"\n' % preview_dir) - fileIni.write('Width=256\n') - fileIni.write('Height=256\n') - fileIni.write('Pause_When_Done=0\n') - fileIni.write('Output_File_Type=N\n') - fileIni.write('Output_Alpha=1\n') - fileIni.write('Antialias=on\n') - fileIni.write('Sampling_Method=2\n') - fileIni.write('Antialias_Depth=3\n') - fileIni.write('-d\n') - fileIni.close() + file_ini = open("%s" % ini_prev_file, "w") + file_ini.write('Version=3.8\n') + file_ini.write('Input_File_Name="%s"\n' % input_prev_file) + file_ini.write('Output_File_Name="%s.png"\n' % output_prev_file) + file_ini.write('Library_Path="%s"\n' % preview_dir) + file_ini.write('Width=256\n') + file_ini.write('Height=256\n') + file_ini.write('Pause_When_Done=0\n') + file_ini.write('Output_File_Type=N\n') + file_ini.write('Output_Alpha=1\n') + file_ini.write('Antialias=on\n') + file_ini.write('Sampling_Method=2\n') + file_ini.write('Antialias_Depth=3\n') + file_ini.write('-d\n') + file_ini.close() ##################### pov ########################################## - filePov = open("%s" % inputPrevFile, "w") - PATname = "PAT_" + string_strip_hyphen(bpy.path.clean_name(tex.name)) - filePov.write("#declare %s = \n" % PATname) - filePov.write(shading.exportPattern(tex, string_strip_hyphen)) - - filePov.write("#declare Plane =\n") - filePov.write("mesh {\n") - filePov.write( + file_pov = open("%s" % input_prev_file, "w") + pat_name = "PAT_" + string_strip_hyphen(bpy.path.clean_name(tex.name)) + file_pov.write("#declare %s = \n" % pat_name) + file_pov.write(shading.export_pattern(tex)) + + file_pov.write("#declare Plane =\n") + file_pov.write("mesh {\n") + file_pov.write( " triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n" ) - filePov.write( + file_pov.write( " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n" ) - filePov.write(" texture{%s}\n" % PATname) - filePov.write("}\n") - filePov.write("object {Plane}\n") - filePov.write("light_source {\n") - filePov.write(" <0,4.38,-1.92e-07>\n") - filePov.write(" color rgb<4, 4, 4>\n") - filePov.write(" parallel\n") - filePov.write(" point_at <0, 0, -1>\n") - filePov.write("}\n") - filePov.write("camera {\n") - filePov.write(" location <0, 0, 0>\n") - filePov.write(" look_at <0, 0, -1>\n") - filePov.write(" right <-1.0, 0, 0>\n") - filePov.write(" up <0, 1, 0>\n") - filePov.write(" angle 96.805211\n") - filePov.write(" rotate <-90.000003, -0.000000, 0.000000>\n") - filePov.write(" translate <0.000000, 0.000000, 0.000000>\n") - filePov.write("}\n") - filePov.close() + file_pov.write(" texture{%s}\n" % pat_name) + file_pov.write("}\n") + file_pov.write("object {Plane}\n") + file_pov.write("light_source {\n") + file_pov.write(" <0,4.38,-1.92e-07>\n") + file_pov.write(" color rgb<4, 4, 4>\n") + file_pov.write(" parallel\n") + file_pov.write(" point_at <0, 0, -1>\n") + file_pov.write("}\n") + file_pov.write("camera {\n") + file_pov.write(" location <0, 0, 0>\n") + file_pov.write(" look_at <0, 0, -1>\n") + file_pov.write(" right <-1.0, 0, 0>\n") + file_pov.write(" up <0, 1, 0>\n") + file_pov.write(" angle 96.805211\n") + file_pov.write(" rotate <-90.000003, -0.000000, 0.000000>\n") + file_pov.write(" translate <0.000000, 0.000000, 0.000000>\n") + file_pov.write("}\n") + file_pov.close() ##################### end write ########################################## pov_binary = PovrayRender._locate_binary() - if sys.platform[:3] == "win": + if platform.startswith('win'): p1 = subprocess.Popen( - ["%s" % pov_binary, "/EXIT", "%s" % iniPrevFile], + ["%s" % pov_binary, "/EXIT", "%s" % ini_prev_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) else: p1 = subprocess.Popen( - ["%s" % pov_binary, "-d", "%s" % iniPrevFile], + ["%s" % pov_binary, "-d", "%s" % ini_prev_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) @@ -6080,9 +1772,9 @@ class RenderPovTexturePreview(Operator): for n in tree.nodes: tree.nodes.remove(n) im = tree.nodes.new("TextureNodeImage") - pathPrev = "%s.png" % outputPrevFile - im.image = bpy.data.images.load(pathPrev) - name = pathPrev + path_prev = "%s.png" % output_prev_file + im.image = bpy.data.images.load(path_prev) + name = path_prev name = name.split("/") name = name[len(name) - 1] im.name = name @@ -6119,14 +1811,10 @@ classes = (PovrayRender, RenderPovTexturePreview, RunPovTextRender) def register(): - # from bpy.utils import register_class - for cls in classes: register_class(cls) def unregister(): - from bpy.utils import unregister_class - for cls in reversed(classes): unregister_class(cls) diff --git a/render_povray/render_gui.py b/render_povray/render_gui.py new file mode 100755 index 000000000..5148c0d94 --- /dev/null +++ b/render_povray/render_gui.py @@ -0,0 +1,562 @@ +# ##### 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 ##### + +# <pep8 compliant> + +"""User interface for rendering parameters""" + +import bpy +from sys import platform # really import here, as in render.py? + +# Or todo: handle this more crossplatform using QTpovray for Linux for instance +# from os.path import isfile +from bl_operators.presets import AddPresetBase +from bpy.utils import register_class, unregister_class +from bpy.props import EnumProperty +from bpy.types import Operator, Menu, Panel + + +# Example of wrapping every class 'as is' +from bl_ui import properties_output + +for member in dir(properties_output): + subclass = getattr(properties_output, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +del properties_output + +from bl_ui import properties_freestyle + +for member in dir(properties_freestyle): + subclass = getattr(properties_freestyle, member) + try: + if not (subclass.bl_space_type == 'PROPERTIES' and subclass.bl_context == "render"): + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + # subclass.bl_parent_id = "RENDER_PT_POV_filter" + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +del properties_freestyle + +from bl_ui import properties_view_layer + +for member in dir(properties_view_layer): + subclass = getattr(properties_view_layer, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +del properties_view_layer + +# Use some of the existing buttons. +from bl_ui import properties_render + +# DEPRECATED#properties_render.RENDER_PT_render.COMPAT_ENGINES.add('POVRAY_RENDER') +# DEPRECATED#properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('POVRAY_RENDER') +# properties_render.RENDER_PT_antialiasing.COMPAT_ENGINES.add('POVRAY_RENDER') +# TORECREATE##DEPRECATED#properties_render.RENDER_PT_shading.COMPAT_ENGINES.add('POVRAY_RENDER') +# DEPRECATED#properties_render.RENDER_PT_output.COMPAT_ENGINES.add('POVRAY_RENDER') +del properties_render + + +def check_render_freestyle_svg(): + """Test if Freestyle SVG Exporter addon is activated + + This addon is currently used to generate the SVG lines file + when Freestyle is enabled alongside POV + """ + if "render_freestyle_svg" in bpy.context.preferences.addons.keys(): + return True + return False + + +class RenderButtonsPanel: + """Use this class to define buttons from the render tab of + properties window.""" + + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "render" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + rd = context.scene.render + return rd.engine in cls.COMPAT_ENGINES + + +class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel): + """Use this class to define pov ini settingss buttons.""" + + bl_options = {'DEFAULT_CLOSED'} + bl_label = "Auto Start" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw_header(self, context): + scene = context.scene + if scene.pov.tempfiles_enable: + self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='AUTO') + else: + self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='CONSOLE') + + def draw(self, context): + + layout = self.layout + + scene = context.scene + + layout.active = scene.pov.max_trace_level != 0 + split = layout.split() + + col = split.column() + col.label(text="Command line options:") + col.prop(scene.pov, "command_line_switches", text="", icon='RIGHTARROW') + split = layout.split() + + # layout.active = not scene.pov.tempfiles_enable + if not scene.pov.tempfiles_enable: + split.prop(scene.pov, "deletefiles_enable", text="Delete files") + split.prop(scene.pov, "pov_editor", text="POV Editor") + + col = layout.column() + col.prop(scene.pov, "scene_name", text="Name") + col.prop(scene.pov, "scene_path", text="Path to files") + # col.prop(scene.pov, "scene_path", text="Path to POV-file") + # col.prop(scene.pov, "renderimage_path", text="Path to image") + + split = layout.split() + split.prop(scene.pov, "indentation_character", text="Indent") + if scene.pov.indentation_character == 'SPACE': + split.prop(scene.pov, "indentation_spaces", text="Spaces") + + row = layout.row() + row.prop(scene.pov, "comments_enable", text="Comments") + row.prop(scene.pov, "list_lf_enable", text="Line breaks in lists") + + +class RENDER_PT_POV_render_settings(RenderButtonsPanel, Panel): + """Use this class to define pov render settings buttons.""" + + bl_label = "Global Settings" + bl_icon = 'SETTINGS' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw_header(self, context): + scene = context.scene + if scene.pov.global_settings_advanced: + self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='SETTINGS') + else: + self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='PREFERENCES') + + def draw(self, context): + layout = self.layout + + scene = context.scene + # rd = context.scene.render + # layout.active = (scene.pov.max_trace_level != 0) + + if not platform.startswith('win'): + layout.prop(scene.pov, "sdl_window_enable", text="POV-Ray SDL Window") + + col = layout.column() + col.label(text="Main Path Tracing:") + col.prop(scene.pov, "max_trace_level", text="Ray Depth") + align = True + layout.active = scene.pov.global_settings_advanced + row = layout.row(align=align) + row.prop(scene.pov, "adc_bailout") + row = layout.row(align=align) + row.prop(scene.pov, "ambient_light") + row = layout.row(align=align) + row.prop(scene.pov, "irid_wavelength") + row = layout.row(align=align) + row.prop(scene.pov, "number_of_waves") + row = layout.row(align=align) + row.prop(scene.pov, "noise_generator") + + split = layout.split() + split.label(text="Shading:") + split = layout.split() + + row = split.row(align=align) + row.prop(scene.pov, "use_shadows") + row.prop(scene.pov, "alpha_mode") + + +class RENDER_PT_POV_photons(RenderButtonsPanel, Panel): + """Use this class to define pov photons buttons.""" + + bl_label = "Photons" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + # def draw_header(self, context): + # self.layout.label(icon='SETTINGS') + + def draw_header(self, context): + scene = context.scene + if scene.pov.photon_enable: + self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER_ACT') + else: + self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER') + + def draw(self, context): + scene = context.scene + layout = self.layout + layout.active = scene.pov.photon_enable + col = layout.column() + # col.label(text="Global Photons:") + col.prop(scene.pov, "photon_max_trace_level", text="Photon Depth") + + split = layout.split() + + col = split.column() + col.prop(scene.pov, "photon_spacing", text="Spacing") + col.prop(scene.pov, "photon_gather_min") + + col = split.column() + col.prop(scene.pov, "photon_adc_bailout", text="Photon ADC") + col.prop(scene.pov, "photon_gather_max") + + box = layout.box() + box.label(text='Photon Map File:') + row = box.row() + row.prop(scene.pov, "photon_map_file_save_load", expand=True) + if scene.pov.photon_map_file_save_load in {'save'}: + box.prop(scene.pov, "photon_map_dir") + box.prop(scene.pov, "photon_map_filename") + if scene.pov.photon_map_file_save_load in {'load'}: + box.prop(scene.pov, "photon_map_file") + # end main photons + + +class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel): + """Use this class to define pov antialiasing buttons.""" + + bl_label = "Anti-Aliasing" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw_header(self, context): + prefs = bpy.context.preferences.addons[__package__].preferences + scene = context.scene + if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2': + self.layout.prop(scene.pov, "antialias_enable", text="", icon='ERROR') + elif scene.pov.antialias_enable: + self.layout.prop(scene.pov, "antialias_enable", text="", icon='ANTIALIASED') + else: + self.layout.prop(scene.pov, "antialias_enable", text="", icon='ALIASED') + + def draw(self, context): + prefs = bpy.context.preferences.addons[__package__].preferences + layout = self.layout + scene = context.scene + + layout.active = scene.pov.antialias_enable + + row = layout.row() + row.prop(scene.pov, "antialias_method", text="") + + if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2': + col = layout.column() + col.alignment = 'CENTER' + col.label(text="Stochastic Anti Aliasing is") + col.label(text="Only Available with UberPOV") + col.label(text="Feature Set in User Preferences.") + col.label(text="Using Type 2 (recursive) instead") + else: + row.prop(scene.pov, "jitter_enable", text="Jitter") + + split = layout.split() + col = split.column() + col.prop(scene.pov, "antialias_depth", text="AA Depth") + sub = split.column() + sub.prop(scene.pov, "jitter_amount", text="Jitter Amount") + if scene.pov.jitter_enable: + sub.enabled = True + else: + sub.enabled = False + + row = layout.row() + row.prop(scene.pov, "antialias_threshold", text="AA Threshold") + row.prop(scene.pov, "antialias_gamma", text="AA Gamma") + + if prefs.branch_feature_set_povray == 'uberpov': + row = layout.row() + row.prop(scene.pov, "antialias_confidence", text="AA Confidence") + if scene.pov.antialias_method == '2': + row.enabled = True + else: + row.enabled = False + + +class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel): + """Use this class to define pov radiosity buttons.""" + + bl_label = "Diffuse Radiosity" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw_header(self, context): + scene = context.scene + if scene.pov.radio_enable: + self.layout.prop(scene.pov, "radio_enable", text="", icon='OUTLINER_OB_LIGHTPROBE') + else: + self.layout.prop(scene.pov, "radio_enable", text="", icon='LIGHTPROBE_CUBEMAP') + + def draw(self, context): + layout = self.layout + + scene = context.scene + + layout.active = scene.pov.radio_enable + + split = layout.split() + + col = split.column() + col.prop(scene.pov, "radio_count", text="Rays") + col.prop(scene.pov, "radio_recursion_limit", text="Recursions") + + split.prop(scene.pov, "radio_error_bound", text="Error Bound") + + layout.prop(scene.pov, "radio_display_advanced") + + if scene.pov.radio_display_advanced: + split = layout.split() + + col = split.column() + col.prop(scene.pov, "radio_adc_bailout", slider=True) + col.prop(scene.pov, "radio_minimum_reuse", text="Min Reuse") + col.prop(scene.pov, "radio_gray_threshold", slider=True) + col.prop(scene.pov, "radio_pretrace_start", slider=True) + col.prop(scene.pov, "radio_low_error_factor", slider=True) + + col = split.column() + col.prop(scene.pov, "radio_brightness") + col.prop(scene.pov, "radio_maximum_reuse", text="Max Reuse") + col.prop(scene.pov, "radio_nearest_count") + col.prop(scene.pov, "radio_pretrace_end", slider=True) + + col = layout.column() + col.label(text="Estimation Influence:") + col.prop(scene.pov, "radio_always_sample") + col.prop(scene.pov, "radio_normal") + col.prop(scene.pov, "radio_media") + col.prop(scene.pov, "radio_subsurface") + + +class POV_RADIOSITY_MT_presets(Menu): + """Use this class to define pov radiosity presets menu.""" + + bl_label = "Radiosity Presets" + preset_subdir = "pov/radiosity" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class RENDER_OT_POV_radiosity_add_preset(AddPresetBase, Operator): + """Use this class to define pov radiosity add presets button""" + + '''Add a Radiosity Preset''' + bl_idname = "scene.radiosity_preset_add" + bl_label = "Add Radiosity Preset" + preset_menu = "POV_RADIOSITY_MT_presets" + + # variable used for all preset values + preset_defines = ["scene = bpy.context.scene"] + + # properties to store in the preset + preset_values = [ + "scene.pov.radio_display_advanced", + "scene.pov.radio_adc_bailout", + "scene.pov.radio_always_sample", + "scene.pov.radio_brightness", + "scene.pov.radio_count", + "scene.pov.radio_error_bound", + "scene.pov.radio_gray_threshold", + "scene.pov.radio_low_error_factor", + "scene.pov.radio_media", + "scene.pov.radio_subsurface", + "scene.pov.radio_minimum_reuse", + "scene.pov.radio_maximum_reuse", + "scene.pov.radio_nearest_count", + "scene.pov.radio_normal", + "scene.pov.radio_recursion_limit", + "scene.pov.radio_pretrace_start", + "scene.pov.radio_pretrace_end", + ] + + # where to store the preset + preset_subdir = "pov/radiosity" + + +# Draw into an existing panel +def rad_panel_func(self, context): + """Display radiosity presets rolldown menu""" + layout = self.layout + + row = layout.row(align=True) + row.menu(POV_RADIOSITY_MT_presets.__name__, text=POV_RADIOSITY_MT_presets.bl_label) + row.operator(RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='ADD') + row.operator( + RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='REMOVE' + ).remove_active = True + + +############################################################################### +# Freestyle +############################################################################### +# import addon_utils +# addon_utils.paths()[0] +# addon_utils.modules() +# mod.bl_info['name'] == 'Freestyle SVG Exporter': +bpy.utils.script_paths("addons") +# render_freestyle_svg = os.path.join(bpy.utils.script_paths("addons"), "render_freestyle_svg.py") + +render_freestyle_svg = bpy.context.preferences.addons.get('render_freestyle_svg') +# mpath=addon_utils.paths()[0].render_freestyle_svg +# import mpath +# from mpath import render_freestyle_svg #= addon_utils.modules(['Freestyle SVG Exporter']) +# from scripts\\addons import render_freestyle_svg +if check_render_freestyle_svg(): + ''' + snippetsWIP + import myscript + import importlib + + importlib.reload(myscript) + myscript.main() + ''' + for member in dir(render_freestyle_svg): + subclass = getattr(render_freestyle_svg, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + if subclass.bl_idname == "RENDER_PT_SVGExporterPanel": + subclass.bl_parent_id = "RENDER_PT_POV_filter" + subclass.bl_options = {'HIDE_HEADER'} + # subclass.bl_order = 11 + print(subclass.bl_info) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass + + # del render_freestyle_svg.RENDER_PT_SVGExporterPanel.bl_parent_id + + +class RENDER_PT_POV_filter(RenderButtonsPanel, Panel): + """Use this class to invoke stuff like Freestyle UI.""" + + bl_label = "Freestyle" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + with_freestyle = bpy.app.build_options.freestyle + engine = context.scene.render.engine + return with_freestyle and engine == 'POVRAY_RENDER' + + def draw_header(self, context): + + # scene = context.scene + rd = context.scene.render + layout = self.layout + + if rd.use_freestyle: + layout.prop(rd, "use_freestyle", text="", icon='LINE_DATA') + + else: + layout.prop(rd, "use_freestyle", text="", icon='OUTLINER_OB_IMAGE') + + def draw(self, context): + rd = context.scene.render + layout = self.layout + layout.active = rd.use_freestyle + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + flow = layout.grid_flow( + row_major=True, columns=0, even_columns=True, even_rows=False, align=True + ) + + flow.prop(rd, "line_thickness_mode", expand=True) + + if rd.line_thickness_mode == 'ABSOLUTE': + flow.prop(rd, "line_thickness") + + # Warning if the Freestyle SVG Exporter addon is not enabled + if not check_render_freestyle_svg(): + # col = box.column() + layout.label(text="Please enable Freestyle SVG Exporter addon", icon="INFO") + # layout.separator() + layout.operator( + "preferences.addon_show", + text="Go to Render: Freestyle SVG Exporter addon", + icon="PREFERENCES", + ).module = "render_freestyle_svg" + + +##class RENDER_PT_povray_baking(RenderButtonsPanel, Panel): +## bl_label = "Baking" +## COMPAT_ENGINES = {'POVRAY_RENDER'} +## +## def draw_header(self, context): +## scene = context.scene +## +## self.layout.prop(scene.pov, "baking_enable", text="") +## +## def draw(self, context): +## layout = self.layout +## +## scene = context.scene +## rd = scene.render +## +## layout.active = scene.pov.baking_enable + + +classes = ( + RENDER_PT_POV_export_settings, + RENDER_PT_POV_render_settings, + RENDER_PT_POV_photons, + RENDER_PT_POV_antialias, + RENDER_PT_POV_radiosity, + RENDER_PT_POV_filter, + # RENDER_PT_povray_baking, + POV_RADIOSITY_MT_presets, + RENDER_OT_POV_radiosity_add_preset, +) + + +def register(): + for cls in classes: + register_class(cls) + bpy.types.RENDER_PT_POV_radiosity.prepend(rad_panel_func) + + +def unregister(): + bpy.types.RENDER_PT_POV_radiosity.remove(rad_panel_func) + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/render_properties.py b/render_povray/render_properties.py new file mode 100755 index 000000000..9096c9864 --- /dev/null +++ b/render_povray/render_properties.py @@ -0,0 +1,687 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Declare rendering properties controllable in UI""" + +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import ( + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + StringProperty, + EnumProperty, + PointerProperty, +) + +############################################################################### +# Scene POV properties. +############################################################################### +class RenderPovSettingsScene(PropertyGroup): + + """Declare scene level properties controllable in UI and translated to POV""" + + # Linux SDL-window enable + sdl_window_enable: BoolProperty( + name="Enable SDL window", description="Enable the SDL window in Linux OS", default=True + ) + # File Options + text_block: StringProperty( + name="Text Scene Name", + description="Name of POV scene to use. " + "Set when clicking Run to render current text only", + maxlen=1024, + ) + tempfiles_enable: BoolProperty( + name="Enable Tempfiles", + description="Enable the OS-Tempfiles. Otherwise set the path where to save the files", + default=True, + ) + pov_editor: BoolProperty( + name="POV editor", + description="Don't Close POV editor after rendering (Overridden by /EXIT command)", + default=False, + ) + deletefiles_enable: BoolProperty( + name="Delete files", + description="Delete files after rendering. Doesn't work with the image", + default=True, + ) + scene_name: StringProperty( + name="Scene Name", + description="Name of POV scene to create. Empty name will use " + "the name of the blend file", + maxlen=1024, + ) + scene_path: StringProperty( + name="Export scene path", + # Bug in POV-Ray RC3 + # description="Path to directory where the exported scene " + # "(POV and INI) is created", + description="Path to directory where the files are created", + maxlen=1024, + subtype="DIR_PATH", + ) + renderimage_path: StringProperty( + name="Rendered image path", + description="Full path to directory where the rendered image is saved", + maxlen=1024, + subtype="DIR_PATH", + ) + list_lf_enable: BoolProperty( + name="LF in lists", + description="Enable line breaks in lists (vectors and indices). " + "Disabled: lists are exported in one line", + default=False, + ) + + # Not a real pov option, just to know if we should write + radio_enable: BoolProperty( + name="Enable Radiosity", description="Enable POV radiosity calculation", default=True + ) + + radio_display_advanced: BoolProperty( + name="Advanced Options", description="Show advanced options", default=False + ) + + media_enable: BoolProperty( + name="Enable Media", description="Enable POV atmospheric media", default=False + ) + + media_samples: IntProperty( + name="Samples", + description="Number of samples taken from camera to first object " + "encountered along ray path for media calculation", + min=1, + max=100, + default=35, + ) + + media_scattering_type: EnumProperty( + name="Scattering Type", + description="Scattering model", + items=( + ( + "1", + "1 Isotropic", + "The simplest form of scattering because it is independent of direction.", + ), + ( + "2", + "2 Mie haze ", + "For relatively small particles such as " + "minuscule water droplets of fog, cloud " + "particles, and particles responsible " + "for the polluted sky. In this model the" + " scattering is extremely directional in" + " the forward direction i.e. the amount " + "of scattered light is largest when the " + "incident light is anti-parallel to the " + "viewing direction (the light goes " + "directly to the viewer). It is smallest" + " when the incident light is parallel to" + " the viewing direction. ", + ), + ("3", "3 Mie murky", "Like haze but much more directional"), + ( + "4", + "4 Rayleigh", + "For extremely small particles such as " + "molecules of the air. The amount of " + "scattered light depends on the incident" + " light angle. It is largest when the " + "incident light is parallel or " + "anti-parallel to the viewing direction " + "and smallest when the incident light is " + "perpendicular to viewing direction.", + ), + ( + "5", + "5 Henyey-Greenstein", + "The default eccentricity value " + "of zero defines isotropic " + "scattering while positive " + "values lead to scattering in " + "the direction of the light and " + "negative values lead to " + "scattering in the opposite " + "direction of the light. Larger " + "values of e (or smaller values " + "in the negative case) increase " + "the directional property of the" + " scattering.", + ), + ), + default="1", + ) + + media_diffusion_scale: FloatProperty( + name="Scale", + description="Scale factor of Media Diffusion Color", + precision=6, + step=0.00000001, + min=0.000000001, + max=1.0, + default=(1.0), + ) + + media_diffusion_color: FloatVectorProperty( + name="Media Diffusion Color", + description="The atmospheric media color", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.001, 0.001, 0.001), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + media_absorption_scale: FloatProperty( + name="Scale", + description="Scale factor of Media Absorption Color. " + "use 1/depth of media volume in meters", + precision=6, + step=0.000001, + min=0.000000001, + max=1.0, + default=(0.00002), + ) + + media_absorption_color: FloatVectorProperty( + name="Media Absorption Color", + description="The atmospheric media absorption color", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + media_eccentricity: FloatProperty( + name="Media Eccenticity Factor", + description="Positive values lead" + " to scattering in the direction of the light and negative " + "values lead to scattering in the opposite direction of the " + "light. Larger values of e (or smaller values in the negative" + " case) increase the directional property of the scattering", + precision=2, + step=0.01, + min=-1.0, + max=1.0, + default=(0.0), + options={"ANIMATABLE"}, + ) + + baking_enable: BoolProperty( + name="Enable Baking", description="Enable POV texture baking", default=False + ) + + indentation_character: EnumProperty( + name="Indentation", + description="Select the indentation type", + items=( + ("NONE", "None", "No indentation"), + ("TAB", "Tabs", "Indentation with tabs"), + ("SPACE", "Spaces", "Indentation with spaces"), + ), + default="SPACE", + ) + + indentation_spaces: IntProperty( + name="Quantity of spaces", + description="The number of spaces for indentation", + min=1, + max=10, + default=4, + ) + + comments_enable: BoolProperty( + name="Enable Comments", description="Add comments to pov file", default=True + ) + + # Real pov options + command_line_switches: StringProperty( + name="Command Line Switches", + description="Command line switches consist of a + (plus) or - " + "(minus) sign, followed by one or more alphabetic " + "characters and possibly a numeric value", + maxlen=500, + ) + + antialias_enable: BoolProperty( + name="Anti-Alias", description="Enable Anti-Aliasing", default=True + ) + + antialias_method: EnumProperty( + name="Method", + description="AA-sampling method. Type 1 is an adaptive, " + "non-recursive, super-sampling (as in the plain old render " + "bigger and scale down trick. Type 2 is a slightly " + "more efficient adaptive and recursive super-sampling. " + "Type 3 is a stochastic halton based super-sampling method so " + "rather artifact free and sampling rays so depth of field can " + "use them at no additional cost, as do area lights and " + "subsurface scattering materials, making it the best " + "quality / time trade-off in complex scenes", + items=( + ("0", "non-recursive AA", "Type 1 Sampling in POV"), + ("1", "recursive AA", "Type 2 Sampling in POV"), + ("2", "stochastic AA", "Type 3 Sampling in POV"), + ), + default="1", + ) + + antialias_confidence: FloatProperty( + name="Antialias Confidence", + description="how surely the computed color " + "of a given pixel is indeed" + "within the threshold error margin", + min=0.0001, + max=1.0000, + default=0.9900, + precision=4, + ) + + antialias_depth: IntProperty( + name="Antialias Depth", description="Depth of pixel for sampling", min=1, max=9, default=2 + ) + + antialias_threshold: FloatProperty( + name="Antialias Threshold", + description="Tolerance for sub-pixels", + min=0.0, + max=1.0, + soft_min=0.05, + soft_max=0.5, + default=0.03, + ) + + jitter_enable: BoolProperty( + name="Jitter", + description="Enable Jittering. Adds noise into the sampling " + "process (it should be avoided to use jitter in " + "animation)", + default=False, + ) + + jitter_amount: FloatProperty( + name="Jitter Amount", + description="Amount of jittering", + min=0.0, + max=1.0, + soft_min=0.01, + soft_max=1.0, + default=1.0, + ) + + antialias_gamma: FloatProperty( + name="Antialias Gamma", + description="POV-Ray compares gamma-adjusted values for super " + "sampling. Antialias Gamma sets the Gamma before " + "comparison", + min=0.0, + max=5.0, + soft_min=0.01, + soft_max=2.5, + default=2.5, + ) + + alpha_mode: EnumProperty( + name="Alpha", + description="Representation of alpha information in the RGBA pixels", + items=( + ("SKY", "Sky", "Transparent pixels are filled with sky color"), + ( + "TRANSPARENT", + "Transparent", + "Transparent, World background is transparent with premultiplied alpha", + ), + ), + default="SKY", + ) + + use_shadows: BoolProperty( + name="Shadows", description="Calculate shadows while rendering", default=True + ) + + max_trace_level: IntProperty( + name="Max Trace Level", + description="Number of reflections/refractions allowed on ray " "path", + min=1, + max=256, + default=5, + ) + + adc_bailout_enable: BoolProperty(name="Enable", description="", default=False) + + adc_bailout: FloatProperty( + name="ADC Bailout", + description="Adaptive Depth Control (ADC) to stop computing additional" + "reflected or refracted rays when their contribution is insignificant." + "The default value is 1/255, or approximately 0.0039, since a change " + "smaller than that could not be visible in a 24 bit image. Generally " + "this value is fine and should be left alone." + "Setting adc_bailout to 0 will disable ADC, relying completely on " + "max_trace_level to set an upper limit on the number of rays spawned. ", + min=0.0, + max=1000.0, + default=0.00392156862745, + precision=3, + ) + + ambient_light_enable: BoolProperty(name="Enable", description="", default=False) + + ambient_light: FloatVectorProperty( + name="Ambient Light", + description="Ambient light is used to simulate the effect of inter-diffuse reflection", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(1, 1, 1), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + global_settings_advanced: BoolProperty(name="Advanced", description="", default=False) + + irid_wavelength_enable: BoolProperty(name="Enable", description="", default=False) + + irid_wavelength: FloatVectorProperty( + name="Irid Wavelength", + description=( + "Iridescence calculations depend upon the dominant " + "wavelengths of the primary colors of red, green and blue light" + ), + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.25, 0.18, 0.14), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + number_of_waves_enable: BoolProperty(name="Enable", description="", default=False) + + number_of_waves: IntProperty( + name="Number Waves", + description=( + "The waves and ripples patterns are generated by summing a series of waves, " + "each with a slightly different center and size" + ), + min=1, + max=10, + default=1000, + ) + + noise_generator_enable: BoolProperty(name="Enable", description="", default=False) + + noise_generator: IntProperty( + name="Noise Generator", + description="There are three noise generators implemented", + min=1, + max=3, + default=2, + ) + + ########################### PHOTONS ####################################### + photon_enable: BoolProperty(name="Photons", description="Enable global photons", default=False) + + photon_enable_count: BoolProperty( + name="Spacing / Count", description="Enable count photons", default=False + ) + + photon_count: IntProperty( + name="Count", description="Photons count", min=1, max=100000000, default=20000 + ) + + photon_spacing: FloatProperty( + name="Spacing", + description="Average distance between photons on surfaces. half " + "this get four times as many surface photons", + min=0.001, + max=1.000, + soft_min=0.001, + soft_max=1.000, + precision=3, + default=0.005, + ) + + photon_max_trace_level: IntProperty( + name="Max Trace Level", + description="Number of reflections/refractions allowed on ray " "path", + min=1, + max=256, + default=5, + ) + + photon_adc_bailout: FloatProperty( + name="ADC Bailout", + description="The adc_bailout for photons. Use adc_bailout = " + "0.01 / brightest_ambient_object for good results", + min=0.0, + max=1000.0, + soft_min=0.0, + soft_max=1.0, + precision=3, + default=0.1, + ) + + photon_gather_min: IntProperty( + name="Gather Min", + description="Minimum number of photons gathered" "for each point", + min=1, + max=256, + default=20, + ) + + photon_gather_max: IntProperty( + name="Gather Max", + description="Maximum number of photons gathered for each point", + min=1, + max=256, + default=100, + ) + + photon_map_file_save_load: EnumProperty( + name="Operation", + description="Load or Save photon map file", + items=(("NONE", "None", ""), ("save", "Save", ""), ("load", "Load", "")), + default="NONE", + ) + + photon_map_filename: StringProperty(name="Filename", description="", maxlen=1024) + + photon_map_dir: StringProperty( + name="Directory", description="", maxlen=1024, subtype="DIR_PATH" + ) + + photon_map_file: StringProperty(name="File", description="", maxlen=1024, subtype="FILE_PATH") + + #########RADIOSITY######## + radio_adc_bailout: FloatProperty( + name="ADC Bailout", + description="The adc_bailout for radiosity rays. Use " + "adc_bailout = 0.01 / brightest_ambient_object for good results", + min=0.0, + max=1000.0, + soft_min=0.0, + soft_max=1.0, + default=0.0039, + precision=4, + ) + + radio_always_sample: BoolProperty( + name="Always Sample", + description="Only use the data from the pretrace step and not gather " + "any new samples during the final radiosity pass", + default=False, + ) + + radio_brightness: FloatProperty( + name="Brightness", + description="Amount objects are brightened before being returned " + "upwards to the rest of the system", + min=0.0, + max=1000.0, + soft_min=0.0, + soft_max=10.0, + default=1.0, + ) + + radio_count: IntProperty( + name="Ray Count", + description="Number of rays for each new radiosity value to be calculated " + "(halton sequence over 1600)", + min=1, + max=10000, + soft_max=1600, + default=35, + ) + + radio_error_bound: FloatProperty( + name="Error Bound", + description="One of the two main speed/quality tuning values, " + "lower values are more accurate", + min=0.0, + max=1000.0, + soft_min=0.1, + soft_max=10.0, + default=10.0, + ) + + radio_gray_threshold: FloatProperty( + name="Gray Threshold", + description="One of the two main speed/quality tuning values, " + "lower values are more accurate", + min=0.0, + max=1.0, + soft_min=0, + soft_max=1, + default=0.0, + ) + + radio_low_error_factor: FloatProperty( + name="Low Error Factor", + description="Just enough samples is slightly blotchy. Low error changes error " + "tolerance for less critical last refining pass", + min=0.000001, + max=1.0, + soft_min=0.000001, + soft_max=1.0, + default=0.5, + ) + + radio_media: BoolProperty( + name="Media", description="Radiosity estimation can be affected by media", default=True + ) + + radio_subsurface: BoolProperty( + name="Subsurface", + description="Radiosity estimation can be affected by Subsurface Light Transport", + default=False, + ) + + radio_minimum_reuse: FloatProperty( + name="Minimum Reuse", + description="Fraction of the screen width which sets the minimum radius of reuse " + "for each sample point (At values higher than 2% expect errors)", + min=0.0, + max=1.0, + soft_min=0.1, + soft_max=0.1, + default=0.015, + precision=3, + ) + + radio_maximum_reuse: FloatProperty( + name="Maximum Reuse", + description="The maximum reuse parameter works in conjunction with, and is similar to that of minimum reuse, " + "the only difference being that it is an upper bound rather than a lower one", + min=0.0, + max=1.0, + default=0.2, + precision=3, + ) + + radio_nearest_count: IntProperty( + name="Nearest Count", + description="Number of old ambient values blended together to " + "create a new interpolated value", + min=1, + max=20, + default=1, + ) + + radio_normal: BoolProperty( + name="Normals", description="Radiosity estimation can be affected by normals", default=False + ) + + radio_recursion_limit: IntProperty( + name="Recursion Limit", + description="how many recursion levels are used to calculate " + "the diffuse inter-reflection", + min=1, + max=20, + default=1, + ) + + radio_pretrace_start: FloatProperty( + name="Pretrace Start", + description="Fraction of the screen width which sets the size of the " + "blocks in the mosaic preview first pass", + min=0.005, + max=1.00, + soft_min=0.02, + soft_max=1.0, + default=0.04, + ) + # XXX TODO set automatically to pretrace_end = 8 / max (image_width, image_height) + # for non advanced mode + radio_pretrace_end: FloatProperty( + name="Pretrace End", + description="Fraction of the screen width which sets the size of the blocks " + "in the mosaic preview last pass", + min=0.000925, + max=1.00, + soft_min=0.01, + soft_max=1.00, + default=0.004, + precision=3, + ) + + +classes = (RenderPovSettingsScene,) + + +def register(): + for cls in classes: + register_class(cls) + bpy.types.Scene.pov = PointerProperty(type=RenderPovSettingsScene) + + +def unregister(): + del bpy.types.Scene.pov + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/scenography.py b/render_povray/scenography.py new file mode 100755 index 000000000..4b0c99e32 --- /dev/null +++ b/render_povray/scenography.py @@ -0,0 +1,847 @@ +# ***** 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 #**** + +# <pep8 compliant> + +"""With respect to camera frame and optics distortions, also export environment + +with world, sky, atmospheric effects such as rainbows or smoke """ + +import bpy +from bpy.utils import register_class, unregister_class +import os +from imghdr import what # imghdr is a python lib to identify image file types +from math import atan, pi, sqrt, degrees +from . import df3_library # for smoke rendering +from .object_primitives import write_object_modifiers + +##############find image texture # used for export_world +def image_format(imgF): + """Identify input image filetypes to transmit to POV.""" + # First use the below explicit extensions to identify image file prospects + ext = { + 'JPG': "jpeg", + 'JPEG': "jpeg", + 'GIF': "gif", + 'TGA': "tga", + 'IFF': "iff", + 'PPM': "ppm", + 'PNG': "png", + 'SYS': "sys", + 'TIFF': "tiff", + 'TIF': "tiff", + 'EXR': "exr", + 'HDR': "hdr", + }.get(os.path.splitext(imgF)[-1].upper(), "") + # Then, use imghdr to really identify the filetype as it can be different + if not ext: + # maybe add a check for if path exists here? + print(" WARNING: texture image has no extension") # too verbose + + ext = what(imgF) # imghdr is a python lib to identify image file types + return ext + + +def img_map(ts): + """Translate mapping type from Blender UI to POV syntax and return that string.""" + image_map = "" + texdata = bpy.data.textures[ts.texture] + if ts.mapping == 'FLAT': + image_map = "map_type 0 " + elif ts.mapping == 'SPHERE': + image_map = "map_type 1 " + elif ts.mapping == 'TUBE': + image_map = "map_type 2 " + + ## map_type 3 and 4 in development (?) (ENV in pov 3.8) + ## for POV-Ray, currently they just seem to default back to Flat (type 0) + # elif ts.mapping=="?": + # image_map = " map_type 3 " + # elif ts.mapping=="?": + # image_map = " map_type 4 " + if ts.use_interpolation: # Available if image sampling class reactivated? + image_map += " interpolate 2 " + if texdata.extension == 'CLIP': + image_map += " once " + # image_map += "}" + # if ts.mapping=='CUBE': + # image_map+= "warp { cubic } rotate <-90,0,180>" + # no direct cube type mapping. Though this should work in POV 3.7 + # it doesn't give that good results(best suited to environment maps?) + # if image_map == "": + # print(" No texture image found ") + return image_map + + +def img_map_transforms(ts): + """Translate mapping transformations from Blender UI to POV syntax and return that string.""" + # XXX TODO: unchecked textures give error of variable referenced before assignment XXX + # POV-Ray "scale" is not a number of repetitions factor, but ,its + # inverse, a standard scale factor. + # 0.5 Offset is needed relatively to scale because center of the + # scale is 0.5,0.5 in blender and 0,0 in POV + # Strange that the translation factor for scale is not the same as for + # translate. + # TODO: verify both matches with other blender renderers / internal in previous versions. + image_map_transforms = "" + image_map_transforms = "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % ( + ts.scale[0], + ts.scale[1], + ts.scale[2], + ts.offset[0], + ts.offset[1], + ts.offset[2], + ) + # image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \ + # ( 1.0 / ts.scale.x, + # 1.0 / ts.scale.y, + # 1.0 / ts.scale.z, + # (0.5 / ts.scale.x) + ts.offset.x, + # (0.5 / ts.scale.y) + ts.offset.y, + # ts.offset.z)) + # image_map_transforms = ( + # "translate <-0.5,-0.5,0> " + # "scale <-1,-1,1> * <%.4g,%.4g,%.4g> " + # "translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \ + # (1.0 / ts.scale.x, + # 1.0 / ts.scale.y, + # 1.0 / ts.scale.z, + # ts.offset.x, + # ts.offset.y, + # ts.offset.z) + # ) + return image_map_transforms + + +def img_map_bg(wts): + """Translate world mapping from Blender UI to POV syntax and return that string.""" + tex = bpy.data.textures[wts.texture] + image_mapBG = "" + # texture_coords refers to the mapping of world textures: + if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL': + image_mapBG = " map_type 0 " + elif wts.texture_coords == 'ANGMAP': + image_mapBG = " map_type 1 " + elif wts.texture_coords == 'TUBE': + image_mapBG = " map_type 2 " + + if tex.use_interpolation: + image_mapBG += " interpolate 2 " + if tex.extension == 'CLIP': + image_mapBG += " once " + # image_mapBG += "}" + # if wts.mapping == 'CUBE': + # image_mapBG += "warp { cubic } rotate <-90,0,180>" + # no direct cube type mapping. Though this should work in POV 3.7 + # it doesn't give that good results(best suited to environment maps?) + # if image_mapBG == "": + # print(" No background texture image found ") + return image_mapBG + + +def path_image(image): + """Conform a path string to POV syntax to avoid POV errors.""" + return bpy.path.abspath(image.filepath, library=image.library).replace("\\", "/") + # .replace("\\","/") to get only forward slashes as it's what POV prefers, + # even on windows + + +# end find image texture +# ----------------------------------------------------------------------------- + + +def export_camera(scene, global_matrix, render, tab_write): + """Translate camera from Blender UI to POV syntax and write to exported file.""" + camera = scene.camera + + # DH disabled for now, this isn't the correct context + active_object = None # bpy.context.active_object # does not always work MR + matrix = global_matrix @ camera.matrix_world + focal_point = camera.data.dof.focus_distance + + # compute resolution + q_size = render.resolution_x / render.resolution_y + tab_write("#declare camLocation = <%.6f, %.6f, %.6f>;\n" % matrix.translation[:]) + tab_write( + "#declare camLookAt = <%.6f, %.6f, %.6f>;\n" + % tuple([degrees(e) for e in matrix.to_3x3().to_euler()]) + ) + + tab_write("camera {\n") + if scene.pov.baking_enable and active_object and active_object.type == 'MESH': + tab_write("mesh_camera{ 1 3\n") # distribution 3 is what we want here + tab_write("mesh{%s}\n" % active_object.name) + tab_write("}\n") + tab_write("location <0,0,.01>") + tab_write("direction <0,0,-1>") + + else: + if camera.data.type == 'ORTHO': + # todo: track when SensorHeightRatio was added to see if needed (not used) + sensor_height_ratio = ( + render.resolution_x * camera.data.ortho_scale / render.resolution_y + ) + tab_write("orthographic\n") + # Blender angle is radian so should be converted to degrees: + # % (camera.data.angle * (180.0 / pi) ) + # but actually argument is not compulsory after angle in pov ortho mode + tab_write("angle\n") + tab_write("right <%6f, 0, 0>\n" % -camera.data.ortho_scale) + tab_write("location <0, 0, 0>\n") + tab_write("look_at <0, 0, -1>\n") + tab_write("up <0, %6f, 0>\n" % (camera.data.ortho_scale / q_size)) + + elif camera.data.type == 'PANO': + tab_write("panoramic\n") + tab_write("location <0, 0, 0>\n") + tab_write("look_at <0, 0, -1>\n") + tab_write("right <%s, 0, 0>\n" % -q_size) + tab_write("up <0, 1, 0>\n") + tab_write("angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi)) + elif camera.data.type == 'PERSP': + # Standard camera otherwise would be default in pov + tab_write("location <0, 0, 0>\n") + tab_write("look_at <0, 0, -1>\n") + tab_write("right <%s, 0, 0>\n" % -q_size) + tab_write("up <0, 1, 0>\n") + tab_write( + "angle %f\n" + % (2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi) + ) + + tab_write( + "rotate <%.6f, %.6f, %.6f>\n" % tuple([degrees(e) for e in matrix.to_3x3().to_euler()]) + ) + tab_write("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:]) + if camera.data.dof.use_dof and (focal_point != 0 or camera.data.dof.focus_object): + tab_write("aperture %.3g\n" % (1 / (camera.data.dof.aperture_fstop * 10000) * 1000)) + tab_write( + "blur_samples %d %d\n" + % (camera.data.pov.dof_samples_min, camera.data.pov.dof_samples_max) + ) + tab_write("variance 1/%d\n" % camera.data.pov.dof_variance) + tab_write("confidence %.3g\n" % camera.data.pov.dof_confidence) + if camera.data.dof.focus_object: + focal_ob = scene.objects[camera.data.dof.focus_object.name] + matrix_blur = global_matrix @ focal_ob.matrix_world + tab_write("focal_point <%.4f,%.4f,%.4f>\n" % matrix_blur.translation[:]) + else: + tab_write("focal_point <0, 0, %f>\n" % focal_point) + if camera.data.pov.normal_enable: + tab_write( + "normal {%s %.4f turbulence %.4f scale %.4f}\n" + % ( + camera.data.pov.normal_patterns, + camera.data.pov.cam_normal, + camera.data.pov.turbulence, + camera.data.pov.scale, + ) + ) + tab_write("}\n") + + +exported_lights_count = 0 + + +def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write): + """Translate lights from Blender UI to POV syntax and write to exported file.""" + + # Incremented after each lamp export to declare its target + # currently used for Fresnel diffuse shader as their slope vector: + global exported_lights_count + exported_lights_count = 0 + # Get all lamps + for ob in lamps: + lamp = ob.data + + matrix = global_matrix @ ob.matrix_world + + # Color is no longer modified by energy + # any way to directly get bpy_prop_array as tuple? + color = tuple(lamp.color) + + tab_write("light_source {\n") + tab_write("< 0,0,0 >\n") + tab_write("color srgb<%.3g, %.3g, %.3g>\n" % color) + + if lamp.type == 'POINT': + pass + elif lamp.type == 'SPOT': + tab_write("spotlight\n") + + # Falloff is the main radius from the centre line + tab_write("falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)) # 1 TO 179 FOR BOTH + tab_write("radius %.6f\n" % ((degrees(lamp.spot_size) / 2.0) * (1.0 - lamp.spot_blend))) + + # Blender does not have a tightness equivalent, 0 is most like blender default. + tab_write("tightness 0\n") # 0:10f + + tab_write("point_at <0, 0, -1>\n") + if lamp.pov.use_halo: + tab_write("looks_like{\n") + tab_write("sphere{<0,0,0>,%.6f\n" % lamp.distance) + tab_write("hollow\n") + tab_write("material{\n") + tab_write("texture{\n") + tab_write("pigment{rgbf<1,1,1,%.4f>}\n" % (lamp.pov.halo_intensity * 5.0)) + tab_write("}\n") + tab_write("interior{\n") + tab_write("media{\n") + tab_write("emission 1\n") + tab_write("scattering {1, 0.5}\n") + tab_write("density{\n") + tab_write("spherical\n") + tab_write("color_map{\n") + tab_write("[0.0 rgb <0,0,0>]\n") + tab_write("[0.5 rgb <1,1,1>]\n") + tab_write("[1.0 rgb <1,1,1>]\n") + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + elif lamp.type == 'SUN': + tab_write("parallel\n") + tab_write("point_at <0, 0, -1>\n") # *must* be after 'parallel' + + elif lamp.type == 'AREA': + tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0)) + # Area lights have no falloff type, so always use blenders lamp quad equivalent + # for those? + tab_write("fade_power %d\n" % 2) + size_x = lamp.size + samples_x = lamp.pov.shadow_ray_samples_x + if lamp.shape == 'SQUARE': + size_y = size_x + samples_y = samples_x + else: + size_y = lamp.size_y + samples_y = lamp.pov.shadow_ray_samples_y + + tab_write( + "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n" % (size_x, size_y, samples_x, samples_y) + ) + tab_write("area_illumination\n") + if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED': + if lamp.pov.use_jitter: + tab_write("jitter\n") + else: + tab_write("adaptive 1\n") + tab_write("jitter\n") + + # No shadow checked either at global or light level: + if not scene.pov.use_shadows or (lamp.pov.shadow_method == 'NOSHADOW'): + tab_write("shadowless\n") + + # Sun shouldn't be attenuated. Area lights have no falloff attribute so they + # are put to type 2 attenuation a little higher above. + if lamp.type not in {'SUN', 'AREA'}: + if lamp.falloff_type == 'INVERSE_SQUARE': + tab_write("fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0))) + tab_write("fade_power %d\n" % 2) # Use blenders lamp quad equivalent + elif lamp.falloff_type == 'INVERSE_LINEAR': + tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0)) + tab_write("fade_power %d\n" % 1) # Use blenders lamp linear + elif lamp.falloff_type == 'CONSTANT': + tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0)) + tab_write("fade_power %d\n" % 3) + # Use blenders lamp constant equivalent no attenuation. + # Using Custom curve for fade power 3 for now. + elif lamp.falloff_type == 'CUSTOM_CURVE': + tab_write("fade_power %d\n" % 4) + + write_matrix(matrix) + + tab_write("}\n") + + exported_lights_count += 1 + + # v(A,B) rotates vector A about origin by vector B. + file.write( + "#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n" + % ( + exported_lights_count, + -(ob.location.x), + -(ob.location.y), + -(ob.location.z), + ob.rotation_euler.x, + ob.rotation_euler.y, + ob.rotation_euler.z, + ) + ) + + +def export_world(world, scene, global_matrix, tab_write): + """write world as POV backgrounbd and sky_sphere to exported file """ + render = scene.pov + camera = scene.camera + matrix = global_matrix @ camera.matrix_world # view dependant for later use + if not world: + return + #############Maurice#################################### + # These lines added to get sky gradient (visible with PNG output) + if world: + # For simple flat background: + if not world.pov.use_sky_blend: + # Non fully transparent background could premultiply alpha and avoid anti-aliasing + # display issue: + if render.alpha_mode == 'TRANSPARENT': + tab_write( + "background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n" % (world.pov.horizon_color[:]) + ) + # Currently using no alpha with Sky option: + elif render.alpha_mode == 'SKY': + tab_write("background {rgbt<%.3g, %.3g, %.3g, 0>}\n" % (world.pov.horizon_color[:])) + # StraightAlpha: + # XXX Does not exists anymore + # else: + # tab_write("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:])) + + world_tex_count = 0 + # For Background image textures + for t in world.pov_texture_slots: # risk to write several sky_spheres but maybe ok. + if t: + tex = bpy.data.textures[t.texture] + if tex.type is not None: + world_tex_count += 1 + # XXX No enable checkbox for world textures yet (report it?) + # if t and tex.type == 'IMAGE' and t.use: + if tex.type == 'IMAGE': + image_filename = path_image(tex.image) + if tex.image.filepath != image_filename: + tex.image.filepath = image_filename + if image_filename != "" and t.use_map_blend: + textures_blend = image_filename + # colvalue = t.default_value + t_blend = t + + # Commented below was an idea to make the Background image oriented as camera + # taken here: + # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/ + # Replace 4/3 by the ratio of each image found by some custom or existing + # function + # mapping_blend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \ + # "(atan((camLocation - camLookAt).x/(camLocation - " \ + # "camLookAt).y)) rotate x*degrees(atan((camLocation - " \ + # "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \ + # "degrees(atan((camLocation - camLookAt).z/(camLocation - " \ + # "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \ + # (t_blend.offset.x / 10 , t_blend.offset.y / 10 , + # t_blend.offset.z / 10, t_blend.scale.x , + # t_blend.scale.y , t_blend.scale.z)) + # using camera rotation valuesdirectly from blender seems much easier + if t_blend.texture_coords == 'ANGMAP': + mapping_blend = "" + else: + # POV-Ray "scale" is not a number of repetitions factor, but its + # inverse, a standard scale factor. + # 0.5 Offset is needed relatively to scale because center of the + # UV scale is 0.5,0.5 in blender and 0,0 in POV + # Further Scale by 2 and translate by -1 are + # required for the sky_sphere not to repeat + + mapping_blend = ( + "scale 2 scale <%.4g,%.4g,%.4g> translate -1 " + "translate <%.4g,%.4g,%.4g> rotate<0,0,0> " + % ( + (1.0 / t_blend.scale.x), + (1.0 / t_blend.scale.y), + (1.0 / t_blend.scale.z), + 0.5 - (0.5 / t_blend.scale.x) - t_blend.offset.x, + 0.5 - (0.5 / t_blend.scale.y) - t_blend.offset.y, + t_blend.offset.z, + ) + ) + + # The initial position and rotation of the pov camera is probably creating + # the rotation offset should look into it someday but at least background + # won't rotate with the camera now. + # Putting the map on a plane would not introduce the skysphere distortion and + # allow for better image scale matching but also some waay to chose depth and + # size of the plane relative to camera. + tab_write("sky_sphere {\n") + tab_write("pigment {\n") + tab_write( + "image_map{%s \"%s\" %s}\n" + % (image_format(textures_blend), textures_blend, img_map_bg(t_blend)) + ) + tab_write("}\n") + tab_write("%s\n" % (mapping_blend)) + # The following layered pigment opacifies to black over the texture for + # transmit below 1 or otherwise adds to itself + tab_write("pigment {rgb 0 transmit %s}\n" % (tex.intensity)) + tab_write("}\n") + # tab_write("scale 2\n") + # tab_write("translate -1\n") + + # For only Background gradient + + if world_tex_count == 0: + if world.pov.use_sky_blend: + tab_write("sky_sphere {\n") + tab_write("pigment {\n") + # maybe Should follow the advice of POV doc about replacing gradient + # for skysphere..5.5 + tab_write("gradient y\n") + tab_write("color_map {\n") + # XXX Does not exists anymore + # if render.alpha_mode == 'STRAIGHT': + # tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.horizon_color[:])) + # tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.zenith_color[:])) + if render.alpha_mode == 'TRANSPARENT': + tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.horizon_color[:])) + # aa premult not solved with transmit 1 + tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.zenith_color[:])) + else: + tab_write("[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.horizon_color[:])) + tab_write("[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.zenith_color[:])) + tab_write("}\n") + tab_write("}\n") + tab_write("}\n") + # Sky_sphere alpha (transmit) is not translating into image alpha the same + # way as 'background' + + # if world.pov.light_settings.use_indirect_light: + # scene.pov.radio_enable=1 + + # Maybe change the above to a function copyInternalRenderer settings when + # user pushes a button, then: + # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light + # and other such translations but maybe this would not be allowed either? + + ############################################################### + + mist = world.mist_settings + + if mist.use_mist: + tab_write("fog {\n") + if mist.falloff == 'LINEAR': + tab_write("distance %.6f\n" % ((mist.start + mist.depth) * 0.368)) + elif mist.falloff == 'QUADRATIC': # n**2 or squrt(n)? + tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)) + elif mist.falloff == 'INVERSE_QUADRATIC': # n**2 or squrt(n)? + tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)) + tab_write( + "color rgbt<%.3g, %.3g, %.3g, %.3g>\n" + % (*world.pov.horizon_color, 1.0 - mist.intensity) + ) + # tab_write("fog_offset %.6f\n" % mist.start) #create a pov property to prepend + # tab_write("fog_alt %.6f\n" % mist.height) #XXX right? + # tab_write("turbulence 0.2\n") + # tab_write("turb_depth 0.3\n") + tab_write("fog_type 1\n") # type2 for height + tab_write("}\n") + if scene.pov.media_enable: + tab_write("media {\n") + tab_write( + "scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n" + % ( + int(scene.pov.media_scattering_type), + (scene.pov.media_diffusion_scale), + *(scene.pov.media_diffusion_color[:]), + ) + ) + if scene.pov.media_scattering_type == '5': + tab_write("eccentricity %.3g\n" % scene.pov.media_eccentricity) + tab_write("}\n") + tab_write( + "absorption %.12f*<%.4g, %.4g, %.4g>\n" + % (scene.pov.media_absorption_scale, *(scene.pov.media_absorption_color[:])) + ) + tab_write("\n") + tab_write("samples %.d\n" % scene.pov.media_samples) + tab_write("}\n") + + +#################################################################################################### +def export_rainbows(rainbows, file, scene, global_matrix, write_matrix, tab_write): + """write all POV rainbows primitives to exported file """ + for ob in rainbows: + povdataname = ob.data.name # enough? XXX not used nor matrix fn? + angle = degrees(ob.data.spot_size / 2.5) # radians in blender (2 + width = ob.data.spot_blend * 10 + distance = ob.data.shadow_buffer_clip_start + # eps=0.0000001 + # angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero + # width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height + # formerly: + # cz-bz # let's say width of the rainbow is height of the cone (interfacing choice + + # v(A,B) rotates vector A about origin by vector B. + # and avoid a 0 length vector by adding 1 + + # file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \ + # (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1), + # ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z)) + + direction = ( # XXX currently not used (replaced by track to?) + ob.location.x, + ob.location.y, + ob.location.z, + ) # not taking matrix into account + rmatrix = global_matrix @ ob.matrix_world + + # ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1)) + # XXX Is result of the below offset by 90 degrees? + up = ob.matrix_world.to_3x3()[1].xyz # * global_matrix + + # XXX TO CHANGE: + # formerly: + # tab_write("#declare %s = rainbow {\n"%povdataname) + + # clumsy for now but remove the rainbow from instancing + # system because not an object. use lamps later instead of meshes + + # del data_ref[dataname] + tab_write("rainbow {\n") + + tab_write("angle %.4f\n" % angle) + tab_write("width %.4f\n" % width) + tab_write("distance %.4f\n" % distance) + tab_write("arc_angle %.4f\n" % ob.pov.arc_angle) + tab_write("falloff_angle %.4f\n" % ob.pov.falloff_angle) + tab_write("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:]) + tab_write("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2])) + tab_write("color_map {\n") + tab_write("[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n") + tab_write("[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n") + tab_write("[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n") + tab_write("[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n") + tab_write("[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n") + tab_write("[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n") + tab_write("[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n") + tab_write("[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n") + tab_write("[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n") + tab_write("}\n") + + pov_mat_name = "Default_texture" + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(scene, ob, file) + # tab_write("rotate x*90\n") + # matrix = global_matrix @ ob.matrix_world + # write_matrix(matrix) + tab_write("}\n") + # continue #Don't render proxy mesh, skip to next object + + +def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, write_matrix): + """export Blender smoke type fluids to pov media using df3 library""" + + flowtype = -1 # XXX todo: not used yet? should trigger emissive for fire type + depsgraph = bpy.context.evaluated_depsgraph_get() + smoke_obj = bpy.data.objects[smoke_obj_name].evaluated_get(depsgraph) + domain = None + smoke_modifier = None + # Search smoke domain target for smoke modifiers + for mod in smoke_obj.modifiers: + if mod.type == 'FLUID': + if mod.fluid_type == 'FLOW': + if mod.flow_settings.flow_type == 'BOTH': + flowtype = 2 + else: + if mod.flow_settings.smoke_flow_type == 'SMOKE': + flowtype = 0 + else: + if mod.flow_settings.smoke_flow_type == 'FIRE': + flowtype = 1 + + if mod.fluid_type == 'DOMAIN': + domain = smoke_obj + smoke_modifier = mod + + eps = 0.000001 # XXX not used currently. restore from corner case ... zero div? + if domain is not None: + mod_set = smoke_modifier.domain_settings + channeldata = [] + for v in mod_set.density_grid: + channeldata.append(v.real) + print(v.real) + ## Usage en voxel texture: + # channeldata = [] + # if channel == 'density': + # for v in mod_set.density_grid: + # channeldata.append(v.real) + + # if channel == 'fire': + # for v in mod_set.flame_grid: + # channeldata.append(v.real) + + resolution = mod_set.resolution_max + big_res = [] + big_res.append(mod_set.domain_resolution[0]) + big_res.append(mod_set.domain_resolution[1]) + big_res.append(mod_set.domain_resolution[2]) + + if mod_set.use_noise: + big_res[0] = big_res[0] * (mod_set.noise_scale + 1) + big_res[1] = big_res[1] * (mod_set.noise_scale + 1) + big_res[2] = big_res[2] * (mod_set.noise_scale + 1) + # else: + # p = [] + ##gather smoke domain settings + # BBox = domain.bound_box + # p.append([BBox[0][0], BBox[0][1], BBox[0][2]]) + # p.append([BBox[6][0], BBox[6][1], BBox[6][2]]) + # mod_set = smoke_modifier.domain_settings + # resolution = mod_set.resolution_max + # smokecache = mod_set.point_cache + # ret = read_cache(smokecache, mod_set.use_noise, mod_set.noise_scale + 1, flowtype) + # res_x = ret[0] + # res_y = ret[1] + # res_z = ret[2] + # density = ret[3] + # fire = ret[4] + + # if res_x * res_y * res_z > 0: + ##new cache format + # big_res = [] + # big_res.append(res_x) + # big_res.append(res_y) + # big_res.append(res_z) + # else: + # max = domain.dimensions[0] + # if (max - domain.dimensions[1]) < -eps: + # max = domain.dimensions[1] + + # if (max - domain.dimensions[2]) < -eps: + # max = domain.dimensions[2] + + # big_res = [int(round(resolution * domain.dimensions[0] / max, 0)), + # int(round(resolution * domain.dimensions[1] / max, 0)), + # int(round(resolution * domain.dimensions[2] / max, 0))] + + # if mod_set.use_noise: + # big_res = [big_res[0] * (mod_set.noise_scale + 1), + # big_res[1] * (mod_set.noise_scale + 1), + # big_res[2] * (mod_set.noise_scale + 1)] + + # if channel == 'density': + # channeldata = density + + # if channel == 'fire': + # channeldata = fire + + # sc_fr = '%s/%s/%s/%05d' % ( + # efutil.export_path, + # efutil.scene_filename(), + # bpy.context.scene.name, + # bpy.context.scene.frame_current + # ) + # if not os.path.exists( sc_fr ): + # os.makedirs(sc_fr) + # + # smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name) + # smoke_path = '/'.join([sc_fr, smoke_filename]) + # + # with open(smoke_path, 'wb') as smoke_file: + # # Binary densitygrid file format + # # + # # File header + # smoke_file.write(b'SMOKE') #magic number + # smoke_file.write(struct.pack('<I', big_res[0])) + # smoke_file.write(struct.pack('<I', big_res[1])) + # smoke_file.write(struct.pack('<I', big_res[2])) + # Density data + # smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata)) + # + # LuxLog('Binary SMOKE file written: %s' % (smoke_path)) + + # return big_res[0], big_res[1], big_res[2], channeldata + + mydf3 = df3_library.df3(big_res[0], big_res[1], big_res[2]) + sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size() + for x in range(sim_sizeX): + for y in range(sim_sizeY): + for z in range(sim_sizeZ): + mydf3.set(x, y, z, channeldata[((z * sim_sizeY + y) * sim_sizeX + x)]) + + mydf3.exportDF3(smoke_path) + print('Binary smoke.df3 file written in preview directory') + if comments: + file.write("\n//--Smoke--\n\n") + + # Note: We start with a default unit cube. + # This is mandatory to read correctly df3 data - otherwise we could just directly use bbox + # coordinates from the start, and avoid scale/translate operations at the end... + file.write("box{<0,0,0>, <1,1,1>\n") + file.write(" pigment{ rgbt 1 }\n") + file.write(" hollow\n") + file.write(" interior{ //---------------------\n") + file.write(" media{ method 3\n") + file.write(" emission <1,1,1>*1\n") # 0>1 for dark smoke to white vapour + file.write(" scattering{ 1, // Type\n") + file.write(" <1,1,1>*0.1\n") + file.write(" } // end scattering\n") + file.write(" density{density_file df3 \"%s\"\n" % (smoke_path)) + file.write(" color_map {\n") + file.write(" [0.00 rgb 0]\n") + file.write(" [0.05 rgb 0]\n") + file.write(" [0.20 rgb 0.2]\n") + file.write(" [0.30 rgb 0.6]\n") + file.write(" [0.40 rgb 1]\n") + file.write(" [1.00 rgb 1]\n") + file.write(" } // end color_map\n") + file.write(" } // end of density\n") + file.write(" samples %i // higher = more precise\n" % resolution) + file.write(" } // end of media --------------------------\n") + file.write(" } // end of interior\n") + + # START OF TRANSFORMATIONS + + # Size to consider here are bbox dimensions (i.e. still in object space, *before* applying + # loc/rot/scale and other transformations (like parent stuff), aka matrix_world). + bbox = smoke_obj.bound_box + dim = [ + abs(bbox[6][0] - bbox[0][0]), + abs(bbox[6][1] - bbox[0][1]), + abs(bbox[6][2] - bbox[0][2]), + ] + + # We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox). + file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2])) + + # We offset our cube such that (0,0,0) coordinate matches Blender's object center. + file.write("translate<%.6g,%.6g,%.6g>\n" % (bbox[0][0], bbox[0][1], bbox[0][2])) + + # We apply object's transformations to get final loc/rot/size in world space! + # Note: we could combine the two previous transformations with this matrix directly... + write_matrix(global_matrix @ smoke_obj.matrix_world) + + # END OF TRANSFORMATIONS + + file.write("}\n") + + # file.write(" interpolate 1\n") + # file.write(" frequency 0\n") + # file.write(" }\n") + # file.write("}\n") + + +classes = () + + +def register(): + for cls in classes: + register_class(cls) + + +def unregister(): + for cls in classes: + unregister_class(cls) diff --git a/render_povray/scenography_gui.py b/render_povray/scenography_gui.py new file mode 100755 index 000000000..307d8f41a --- /dev/null +++ b/render_povray/scenography_gui.py @@ -0,0 +1,800 @@ +# ##### 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 ##### + +# <pep8 compliant> + +"""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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +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) + bpy.types.LIGHT_PT_POV_light.prepend(light_panel_func) + + +def unregister(): + + for cls in reversed(classes): + unregister_class(cls) + bpy.types.LIGHT_PT_POV_light.remove(light_panel_func) diff --git a/render_povray/scenography_properties.py b/render_povray/scenography_properties.py new file mode 100755 index 000000000..b45922eb2 --- /dev/null +++ b/render_povray/scenography_properties.py @@ -0,0 +1,514 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Declare stage set and surrounding (camera, lights, environment) properties controllable in UI""" +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import ( + FloatVectorProperty, + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + CollectionProperty, +) + +from .shading_properties import ( + active_texture_name_from_uilist, + active_texture_name_from_search, + brush_texture_update, +) + +############################################################################### +# Camera POV properties. +############################################################################### +class RenderPovSettingsCamera(PropertyGroup): + + """Declare camera properties controllable in UI and translated to POV.""" + + # DOF Toggle + dof_enable: BoolProperty( + name="Depth Of Field", description="Enable POV Depth Of Field ", default=False + ) + + # Aperture (Intensity of the Blur) + dof_aperture: FloatProperty( + name="Aperture", + description="Similar to a real camera's aperture effect over focal blur (though not " + "in physical units and independent of focal length). " + "Increase to get more blur", + min=0.01, + max=1.00, + default=0.50, + ) + + # Aperture adaptive sampling + dof_samples_min: IntProperty( + name="Samples Min", + description="Minimum number of rays to use for each pixel", + min=1, + max=128, + default=3, + ) + + dof_samples_max: IntProperty( + name="Samples Max", + description="Maximum number of rays to use for each pixel", + min=1, + max=128, + default=9, + ) + + dof_variance: IntProperty( + name="Variance", + description="Minimum threshold (fractional value) for adaptive DOF sampling (up " + "increases quality and render time). The value for the variance should " + "be in the range of the smallest displayable color difference", + min=1, + max=100000, + soft_max=10000, + default=8192, + ) + + dof_confidence: FloatProperty( + name="Confidence", + description="Probability to reach the real color value. Larger confidence values " + "will lead to more samples, slower traces and better images", + min=0.01, + max=0.99, + default=0.20, + ) + + normal_enable: BoolProperty(name="Perturbated Camera", default=False) + + cam_normal: FloatProperty(name="Normal Strength", min=0.0, max=1.0, default=0.001) + + normal_patterns: EnumProperty( + name="Pattern", + description="", + items=( + ("agate", "Agate", ""), + ("boxed", "Boxed", ""), + ("bumps", "Bumps", ""), + ("cells", "Cells", ""), + ("crackle", "Crackle", ""), + ("dents", "Dents", ""), + ("granite", "Granite", ""), + ("leopard", "Leopard", ""), + ("marble", "Marble", ""), + ("onion", "Onion", ""), + ("pavement", "Pavement", ""), + ("planar", "Planar", ""), + ("quilted", "Quilted", ""), + ("ripples", "Ripples", ""), + ("radial", "Radial", ""), + ("spherical", "Spherical", ""), + ("spiral1", "Spiral1", ""), + ("spiral2", "Spiral2", ""), + ("spotted", "Spotted", ""), + ("square", "Square", ""), + ("tiling", "Tiling", ""), + ("waves", "Waves", ""), + ("wood", "Wood", ""), + ("wrinkles", "Wrinkles", ""), + ), + default="agate", + ) + + turbulence: FloatProperty(name="Turbulence", min=0.0, max=100.0, default=0.1) + + scale: FloatProperty(name="Scale", min=0.0, default=1.0) + + ##################################CustomPOV Code############################ + # Only DUMMIES below for now: + replacement_text: StringProperty( + name="Texts in blend file", + description="Type the declared name in custom POV code or an external .inc " + "it points at. camera {} expected", + default="", + ) + + +############################################################################### +# Light POV properties. +############################################################################### +class RenderPovSettingsLight(PropertyGroup): + + """Declare light properties controllable in UI and translated to POV.""" + + # former Space properties from removed Blender Internal + use_limited_texture_context: BoolProperty( + name="", + description="Use the limited version of texture user (for â€old shading’ mode)", + default=True, + ) + + texture_context: EnumProperty( + name="Texture context", + description="Type of texture data to display and edit", + items=( + ("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures" + ("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures" + ("LAMP", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures" + ( + "PARTICLES", + "", + "Show particles textures", + "PARTICLES", + 3, + ), # "Show particles textures" + ( + "LINESTYLE", + "", + "Show linestyle textures", + "LINE_DATA", + 4, + ), # "Show linestyle textures" + ( + "OTHER", + "", + "Show other data textures", + "TEXTURE_DATA", + 5, + ), # "Show other data textures" + ), + default="MATERIAL", + ) + + shadow_method: EnumProperty( + name="Shadow", + description="", + items=( + ("NOSHADOW", "No Shadow", "No Shadow"), + ("RAY_SHADOW", "Ray Shadow", "Ray Shadow, Use ray tracing for shadow"), + ), + default="RAY_SHADOW", + ) + + active_texture_index: IntProperty(name="Index for texture_slots", default=0) + + use_halo: BoolProperty( + name="Halo", description="Render spotlight with a volumetric halo", default=False + ) + + halo_intensity: FloatProperty( + name="Halo intensity", + description="Brightness of the spotlight halo cone", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + shadow_ray_samples_x: IntProperty( + name="Number of samples taken extra (samples x samples)", min=1, soft_max=64, default=1 + ) + + shadow_ray_samples_y: IntProperty( + name="Number of samples taken extra (samples x samples)", min=1, soft_max=64, default=1 + ) + + shadow_ray_sample_method: EnumProperty( + name="", + description="Method for generating shadow samples: Adaptive QMC is fastest," + "Constant QMC is less noisy but slower", + items=( + ("ADAPTIVE_QMC", "", "Halton samples distribution", "", 0), + ("CONSTANT_QMC", "", "QMC samples distribution", "", 1), + ( + "CONSTANT_JITTERED", + "", + "Uses POV jitter keyword", + "", + 2, + ), # "Show other data textures" + ), + default="CONSTANT_JITTERED", + ) + + use_jitter: BoolProperty( + name="Jitter", + description="Use noise for sampling (Constant Jittered sampling)", + default=False, + ) + + +############################################################################### +# World POV properties. +############################################################################### +class RenderPovSettingsWorld(PropertyGroup): + + """Declare world properties controllable in UI and translated to POV.""" + + # former Space properties from removed Blender Internal + use_limited_texture_context: BoolProperty( + name="", + description="Use the limited version of texture user (for â€old shading’ mode)", + default=True, + ) + + texture_context: EnumProperty( + name="Texture context", + description="Type of texture data to display and edit", + items=( + ("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures" + ("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures" + ("LIGHT", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures" + ( + "PARTICLES", + "", + "Show particles textures", + "PARTICLES", + 3, + ), # "Show particles textures" + ( + "LINESTYLE", + "", + "Show linestyle textures", + "LINE_DATA", + 4, + ), # "Show linestyle textures" + ( + "OTHER", + "", + "Show other data textures", + "TEXTURE_DATA", + 5, + ), # "Show other data textures" + ), + default="MATERIAL", + ) + + use_sky_blend: BoolProperty( + name="Blend Sky", + description="Render background with natural progression from horizon to zenith", + default=False, + ) + + use_sky_paper: BoolProperty( + name="Paper Sky", description="Flatten blend or texture coordinates", default=False + ) + + use_sky_real: BoolProperty( + name="Real Sky", + description="Render background with a real horizon, relative to the camera angle", + default=False, + ) + + horizon_color: FloatVectorProperty( + name="Horizon Color", + description="Color at the horizon", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.050876, 0.050876, 0.050876), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + zenith_color: FloatVectorProperty( + name="Zenith Color", + description="Color at the zenith", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + ambient_color: FloatVectorProperty( + name="Ambient Color", + description="Ambient color of the world", + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + active_texture_index: IntProperty( + name="Index for texture_slots", default=0, update=brush_texture_update + ) + + +class WorldTextureSlot(PropertyGroup): + """Declare world texture slot level properties for UI and translated to POV.""" + + bl_idname = ("pov_texture_slots",) + bl_description = ("Texture_slots from Blender-2.79",) + + # Adding a "real" texture datablock as property is not possible + # (or at least not easy through a dynamically populated EnumProperty). + # That's why we'll use a prop_search() UILayout function in texturing_gui.py. + # So we'll assign the name of the needed texture datablock to the below StringProperty. + texture: StringProperty(update=active_texture_name_from_uilist) + # and use another temporary StringProperty to change the linked data + texture_search: StringProperty( + name="", update=active_texture_name_from_search, description="Browse Texture to be linked" + ) + + blend_factor: FloatProperty( + name="Blend", + description="Amount texture affects color progression of the " "background", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + horizon_factor: FloatProperty( + name="Horizon", + description="Amount texture affects color of the horizon", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + object: StringProperty( + name="Object", + description="Object to use for mapping with Object texture coordinates", + default="", + ) + + offset: FloatVectorProperty( + name="Offset", + description=("Fine tune of the texture mapping X, Y and Z locations "), + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="TRANSLATION", + ) + + scale: FloatVectorProperty( + name="Size", + subtype="XYZ", + size=3, + description="Set scaling for the texture’s X, Y and Z sizes ", + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + ) + + texture_coords: EnumProperty( + name="Coordinates", + description="Texture coordinates used to map the texture onto the background", + items=( + ("VIEW", "View", "Use view vector for the texture coordinates"), + ( + "GLOBAL", + "Global", + "Use global coordinates for the texture coordinates (interior mist)", + ), + ( + "ANGMAP", + "AngMap", + "Use 360 degree angular coordinates, e.g. for spherical light probes", + ), + ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"), + ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"), + ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"), + ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates"), + ), + default="VIEW", + ) + + use_map_blend: BoolProperty( + name="Blend Map", description="Affect the color progression of the background", default=True + ) + + use_map_horizon: BoolProperty( + name="Horizon Map", description="Affect the color of the horizon", default=False + ) + + use_map_zenith_down: BoolProperty( + name="", description="Affect the color of the zenith below", default=False + ) + + use_map_zenith_up: BoolProperty( + name="Zenith Up Map", description="Affect the color of the zenith above", default=False + ) + + zenith_down_factor: FloatProperty( + name="Zenith Down", + description="Amount texture affects color of the zenith below", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + zenith_up_factor: FloatProperty( + name="Zenith Up", + description="Amount texture affects color of the zenith above", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + +""" +# class WORLD_TEXTURE_SLOTS_UL_layerlist(bpy.types.UIList): +# texture_slots: + +class WorldTextureSlots(bpy.props.PropertyGroup): + index = bpy.prop.PropertyInt(name='index') + # foo = random prop + +bpy.types.World.texture_slots = bpy.props.CollectionProperty(type=PropertyGroup) + +for i in range(18): # length of world texture slots + world.texture_slots.add() +""" + +classes = ( + RenderPovSettingsCamera, + RenderPovSettingsLight, + RenderPovSettingsWorld, + WorldTextureSlot, +) + + +def register(): + for cls in classes: + register_class(cls) + + bpy.types.Camera.pov = PointerProperty(type=RenderPovSettingsCamera) + bpy.types.Light.pov = PointerProperty(type=RenderPovSettingsLight) + bpy.types.World.pov = PointerProperty(type=RenderPovSettingsWorld) + bpy.types.World.pov_texture_slots = CollectionProperty(type=WorldTextureSlot) + + +def unregister(): + del bpy.types.Camera.pov + del bpy.types.Light.pov + del bpy.types.World.pov + del bpy.types.World.pov_texture_slots + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/scripting.py b/render_povray/scripting.py new file mode 100755 index 000000000..02ca6444b --- /dev/null +++ b/render_povray/scripting.py @@ -0,0 +1,529 @@ +# ***** 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 #**** + +# <pep8 compliant> + +"""Support POV Scene Description Language snippets or full includes: import, + +load, create or edit""" + +import bpy +from bpy.props import StringProperty, BoolProperty, CollectionProperty +from bpy_extras.io_utils import ImportHelper + +from mathutils import Vector +from math import pi + + +def export_custom_code(file): + """write all POV user defined custom code to exported file """ + # Write CurrentAnimation Frame for use in Custom POV Code + file.write("#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current) + # Change path and uncomment to add an animated include file by hand: + file.write("//#include \"/home/user/directory/animation_include_file.inc\"\n") + for txt in bpy.data.texts: + if txt.pov.custom_code == 'both': + # Why are the newlines needed? + file.write("\n") + file.write(txt.as_string()) + file.write("\n") + + +#############################IMPORT + + +class ImportPOV(bpy.types.Operator, ImportHelper): + """Load Povray files""" + + bl_idname = "import_scene.pov" + bl_label = "POV-Ray files (.pov/.inc)" + bl_options = {'PRESET', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + # ----------- + # File props. + files: CollectionProperty( + type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'} + ) + directory: StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'}) + + filename_ext = {".pov", ".inc"} + filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'}) + + import_at_cur: BoolProperty( + name="Import at Cursor Location", description="Ignore Object Matrix", default=False + ) + + def execute(self, context): + from mathutils import Matrix + + verts = [] + faces = [] + materials = [] + blend_mats = [] ############## + pov_mats = [] ############## + colors = [] + mat_names = [] + lenverts = None + lenfaces = None + suffix = -1 + name = 'Mesh2_%s' % suffix + name_search = False + verts_search = False + faces_search = False + plane_search = False + box_search = False + cylinder_search = False + sphere_search = False + cone_search = False + tex_search = False ################## + cache = [] + matrixes = {} + write_matrix = False + index = None + value = None + # file_pov = bpy.path.abspath(self.filepath) #was used for single files + + def mat_search(cache): + r = g = b = 0.5 + f = t = 0 + color = None + + for item, value in enumerate(cache): + + if value == 'texture': + pass + + if value == 'pigment': + + if cache[item + 2] in {'rgb', 'srgb'}: + pass + + elif cache[item + 2] in {'rgbf', 'srgbf'}: + pass + + elif cache[item + 2] in {'rgbt', 'srgbt'}: + try: + r, g, b, t = ( + float(cache[item + 3]), + float(cache[item + 4]), + float(cache[item + 5]), + float(cache[item + 6]), + ) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + r = g = b = t = float(cache[item + 2]) + color = (r, g, b, t) + + elif cache[item + 2] in {'rgbft', 'srgbft'}: + pass + + else: + pass + + if colors == [] or (colors != [] and color not in colors): + colors.append(color) + name = ob.name + "_mat" + mat_names.append(name) + mat = bpy.data.materials.new(name) + mat.diffuse_color = (r, g, b) + mat.alpha = 1 - t + if mat.alpha != 1: + mat.use_transparency = True + ob.data.materials.append(mat) + + else: + for i, value in enumerate(colors): + if color == value: + ob.data.materials.append(bpy.data.materials[mat_names[i]]) + + for file in self.files: + print("Importing file: " + file.name) + file_pov = self.directory + file.name + for line in open(file_pov): + string = line.replace("{", " ") + string = string.replace("}", " ") + string = string.replace("<", " ") + string = string.replace(">", " ") + string = string.replace(",", " ") + lw = string.split() + # lenwords = len(lw) # Not used... why written? + if lw: + if lw[0] == "object": + write_matrix = True + if write_matrix: + if lw[0] not in {"object", "matrix"}: + index = lw[0] + if lw[0] in {"matrix"}: + value = [ + float(lw[1]), + float(lw[2]), + float(lw[3]), + float(lw[4]), + float(lw[5]), + float(lw[6]), + float(lw[7]), + float(lw[8]), + float(lw[9]), + float(lw[10]), + float(lw[11]), + float(lw[12]), + ] + matrixes[index] = value + write_matrix = False + for line in open(file_pov): + S = line.replace("{", " { ") + S = S.replace("}", " } ") + S = S.replace(",", " ") + S = S.replace("<", "") + S = S.replace(">", " ") + S = S.replace("=", " = ") + S = S.replace(";", " ; ") + S = S.split() + # lenS = len(S) # Not used... why written? + for i, word in enumerate(S): + ##################Primitives Import################## + if word == 'cone': + cone_search = True + name_search = False + if cone_search: + cache.append(word) + if cache[-1] == '}': + try: + x0 = float(cache[2]) + y0 = float(cache[3]) + z0 = float(cache[4]) + r0 = float(cache[5]) + x1 = float(cache[6]) + y1 = float(cache[7]) + z1 = float(cache[8]) + r1 = float(cache[9]) + # Y is height in most pov files, not z + bpy.ops.pov.cone_add(base=r0, cap=r1, height=(y1 - y0)) + ob = context.object + ob.location = (x0, y0, z0) + # ob.scale = (r,r,r) + mat_search(cache) + except ValueError: + pass + cache = [] + cone_search = False + if word == 'plane': + plane_search = True + name_search = False + if plane_search: + cache.append(word) + if cache[-1] == '}': + try: + bpy.ops.pov.addplane() + ob = context.object + mat_search(cache) + except ValueError: + pass + cache = [] + plane_search = False + if word == 'box': + box_search = True + name_search = False + if box_search: + cache.append(word) + if cache[-1] == '}': + try: + x0 = float(cache[2]) + y0 = float(cache[3]) + z0 = float(cache[4]) + x1 = float(cache[5]) + y1 = float(cache[6]) + z1 = float(cache[7]) + # imported_corner_1=(x0, y0, z0) + # imported_corner_2 =(x1, y1, z1) + center = ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2) + bpy.ops.pov.addbox() + ob = context.object + ob.location = center + mat_search(cache) + + except ValueError: + pass + cache = [] + box_search = False + if word == 'cylinder': + cylinder_search = True + name_search = False + if cylinder_search: + cache.append(word) + if cache[-1] == '}': + try: + x0 = float(cache[2]) + y0 = float(cache[3]) + z0 = float(cache[4]) + x1 = float(cache[5]) + y1 = float(cache[6]) + z1 = float(cache[7]) + imported_cyl_loc = (x0, y0, z0) + imported_cyl_loc_cap = (x1, y1, z1) + + r = float(cache[8]) + + vec = Vector(imported_cyl_loc_cap) - Vector(imported_cyl_loc) + depth = vec.length + rot = Vector((0, 0, 1)).rotation_difference( + vec + ) # Rotation from Z axis. + trans = rot @ Vector( # XXX Not used, why written? + (0, 0, depth / 2) + ) # Such that origin is at center of the base of the cylinder. + # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2) + scale_z = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2) / 2 + bpy.ops.pov.addcylinder( + R=r, + imported_cyl_loc=imported_cyl_loc, + imported_cyl_loc_cap=imported_cyl_loc_cap, + ) + ob = context.object + ob.location = (x0, y0, z0) + ob.rotation_euler = rot.to_euler() + ob.scale = (1, 1, scale_z) + + # scale data rather than obj? + # bpy.ops.object.mode_set(mode='EDIT') + # bpy.ops.mesh.reveal() + # bpy.ops.mesh.select_all(action='SELECT') + # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL') + # bpy.ops.mesh.hide(unselected=False) + # bpy.ops.object.mode_set(mode='OBJECT') + + mat_search(cache) + + except ValueError: + pass + cache = [] + cylinder_search = False + if word == 'sphere': + sphere_search = True + name_search = False + if sphere_search: + cache.append(word) + if cache[-1] == '}': + x = y = z = r = 0 + try: + x = float(cache[2]) + y = float(cache[3]) + z = float(cache[4]) + r = float(cache[5]) + + except ValueError: + pass + except: + x = y = z = float(cache[2]) + r = float(cache[3]) + bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z)) + ob = context.object + ob.location = (x, y, z) + ob.scale = (r, r, r) + mat_search(cache) + cache = [] + sphere_search = False + ##################End Primitives Import################## + if word == '#declare': + name_search = True + if name_search: + cache.append(word) + if word == 'mesh2': + name_search = False + if cache[-2] == '=': + name = cache[-3] + else: + suffix += 1 + cache = [] + if word in {'texture', ';'}: + name_search = False + cache = [] + if word == 'vertex_vectors': + verts_search = True + if verts_search: + cache.append(word) + if word == '}': + verts_search = False + lenverts = cache[2] + cache.pop() + cache.pop(0) + cache.pop(0) + cache.pop(0) + for j in range(int(lenverts)): + x = j * 3 + y = (j * 3) + 1 + z = (j * 3) + 2 + verts.append((float(cache[x]), float(cache[y]), float(cache[z]))) + cache = [] + # if word == 'face_indices': + # faces_search = True + if word == 'texture_list': ######## + tex_search = True ####### + if tex_search: ######### + if ( + word not in {'texture_list', 'texture', '{', '}', 'face_indices'} + and not word.isdigit() + ): ############## + pov_mats.append(word) ################# + if word == 'face_indices': + tex_search = False ################ + faces_search = True + if faces_search: + cache.append(word) + if word == '}': + faces_search = False + lenfaces = cache[2] + cache.pop() + cache.pop(0) + cache.pop(0) + cache.pop(0) + lf = int(lenfaces) + var = int(len(cache) / lf) + for k in range(lf): + if var == 3: + v0 = k * 3 + v1 = k * 3 + 1 + v2 = k * 3 + 2 + faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2]))) + if var == 4: + v0 = k * 4 + v1 = k * 4 + 1 + v2 = k * 4 + 2 + m = k * 4 + 3 + materials.append((int(cache[m]))) + faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2]))) + if var == 6: + v0 = k * 6 + v1 = k * 6 + 1 + v2 = k * 6 + 2 + m0 = k * 6 + 3 + m1 = k * 6 + 4 + m2 = k * 6 + 5 + materials.append( + (int(cache[m0]), int(cache[m1]), int(cache[m2])) + ) + faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2]))) + # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False) + # ob = object_utils.object_data_add(context, mesh, operator=None) + + me = bpy.data.meshes.new(name) ######## + ob = bpy.data.objects.new(name, me) ########## + bpy.context.collection.objects.link(ob) ######### + me.from_pydata(verts, [], faces) ############ + + for mat in bpy.data.materials: ############## + blend_mats.append(mat.name) ############# + for m_name in pov_mats: ##################### + if m_name not in blend_mats: ########### + povMat = bpy.data.materials.new(m_name) ################# + mat_search(cache) + ob.data.materials.append( + bpy.data.materials[m_name] + ) ################### + if materials: ################## + for l, val in enumerate(materials): #################### + try: ################### + ob.data.polygons[ + l + ].material_index = val #################### + except TypeError: ################### + ob.data.polygons[l].material_index = int( + val[0] + ) ################## + + blend_mats = [] ######################### + pov_mats = [] ######################### + materials = [] ######################### + cache = [] + name_search = True + if name in matrixes and not self.import_at_cur: + global_matrix = Matrix.Rotation(pi / 2.0, 4, 'X') + ob = bpy.context.object + matrix = ob.matrix_world + v = matrixes[name] + matrix[0][0] = v[0] + matrix[1][0] = v[1] + matrix[2][0] = v[2] + matrix[0][1] = v[3] + matrix[1][1] = v[4] + matrix[2][1] = v[5] + matrix[0][2] = v[6] + matrix[1][2] = v[7] + matrix[2][2] = v[8] + matrix[0][3] = v[9] + matrix[1][3] = v[10] + matrix[2][3] = v[11] + matrix = global_matrix * ob.matrix_world + ob.matrix_world = matrix + verts = [] + faces = [] + + # if word == 'pigment': + # try: + # #all indices have been incremented once to fit a bad test file + # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5]) + # color = (r,g,b,t) + + # except IndexError: + # #all indices have been incremented once to fit alternate test file + # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6]) + # color = (r,g,b,t) + # except UnboundLocalError: + # # In case no transmit is specified ? put it to 0 + # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0) + # color = (r,g,b,t) + + # except ValueError: + # color = (0.8,0.8,0.8,0) + # pass + + # if colors == [] or (colors != [] and color not in colors): + # colors.append(color) + # name = ob.name+"_mat" + # mat_names.append(name) + # mat = bpy.data.materials.new(name) + # mat.diffuse_color = (r,g,b) + # mat.alpha = 1-t + # if mat.alpha != 1: + # mat.use_transparency=True + # ob.data.materials.append(mat) + # print (colors) + # else: + # for m in range(len(colors)): + # if color == colors[m]: + # ob.data.materials.append(bpy.data.materials[mat_names[m]]) + + ##To keep Avogadro Camera angle: + # for obj in bpy.context.view_layer.objects: + # if obj.type == "CAMERA": + # track = obj.constraints.new(type = "TRACK_TO") + # track.target = ob + # track.track_axis ="TRACK_NEGATIVE_Z" + # track.up_axis = "UP_Y" + # obj.location = (0,0,0) + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(ImportPOV) + + +def unregister(): + bpy.utils.unregister_class(ImportPOV) diff --git a/render_povray/scripting_gui.py b/render_povray/scripting_gui.py new file mode 100755 index 000000000..99006af15 --- /dev/null +++ b/render_povray/scripting_gui.py @@ -0,0 +1,268 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""User interface to POV Scene Description Language snippets or full includes: + +import, load, create or edit """ + +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import Operator, Menu, Panel +from sys import platform # really import here, as in ui.py and in render.py? +import os # really import here and in render.py? +from os.path import isfile + + +def locate_docpath(): + """POV can be installed with some include files. + + Get their path as defined in user preferences or registry keys for + the user to be able to invoke them.""" + + addon_prefs = bpy.context.preferences.addons[__package__].preferences + # Use the system preference if its set. + pov_documents = addon_prefs.docpath_povray + if pov_documents: + if os.path.exists(pov_documents): + return pov_documents + # Implicit else, as here return was still not triggered: + print( + "User Preferences path to povray documents %r NOT FOUND, checking $PATH" % pov_documents + ) + + # Windows Only + if platform.startswith('win'): + import winreg + + try: + win_reg_key = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows" + ) + win_docpath = winreg.QueryValueEx(win_reg_key, "DocPath")[0] + pov_documents = os.path.join(win_docpath, "Insert Menu") + if os.path.exists(pov_documents): + return pov_documents + except FileNotFoundError: + return "" + # search the path all os's + pov_documents_default = "include" + + os_path_ls = os.getenv("PATH").split(':') + [""] + + for dir_name in os_path_ls: + pov_documents = os.path.join(dir_name, pov_documents_default) + if os.path.exists(pov_documents): + return pov_documents + return "" + + +################################################################################ +class TextButtonsPanel: + """Use this class to define buttons from the side tab of + text window.""" + + bl_space_type = 'TEXT_EDITOR' + bl_region_type = 'UI' + bl_label = "POV-Ray" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + text = context.space_data + rd = context.scene.render + return text and (rd.engine in cls.COMPAT_ENGINES) + + +############################################################################### +# Text Povray Settings +############################################################################### + + +class TEXT_OT_POV_insert(Operator): + """Use this class to create blender text editor operator to insert pov snippets like other pov IDEs""" + + bl_idname = "text.povray_insert" + bl_label = "Insert" + + filepath: bpy.props.StringProperty(name="Filepath", subtype='FILE_PATH') + + @classmethod + def poll(cls, context): + text = context.space_data.text + return context.area.type == 'TEXT_EDITOR' and text is not None + # return bpy.ops.text.insert.poll() this Bpy op has no poll() + + def execute(self, context): + if self.filepath and isfile(self.filepath): + file = open(self.filepath, "r") + bpy.ops.text.insert(text=file.read()) + + # places the cursor at the end without scrolling -.- + # context.space_data.text.write(file.read()) + file.close() + return {'FINISHED'} + + +def validinsert(ext): + """Since preview images could be in same folder, filter only insertable text""" + return ext in {".txt", ".inc", ".pov"} + + +class TEXT_MT_POV_insert(Menu): + """Use this class to create a menu launcher in text editor for the TEXT_OT_POV_insert operator .""" + + bl_label = "Insert" + bl_idname = "TEXT_MT_POV_insert" + + def draw(self, context): + pov_documents = locate_docpath() + prop = self.layout.operator("wm.path_open", text="Open folder", icon='FILE_FOLDER') + prop.filepath = pov_documents + self.layout.separator() + + pov_insert_items_list = [] + for root, dirs, files in os.walk(pov_documents): # todo: structure submenus by dir + pov_insert_items_list.append(root) + print(pov_insert_items_list) + self.path_menu( + pov_insert_items_list, + "text.povray_insert", + # {"internal": True}, + filter_ext=validinsert, + ) + + +class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel): + """Use this class to create a panel in text editor for the user to decide if he renders text + + only or adds to 3d scene.""" + + bl_label = "POV" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + layout = self.layout + + text = context.space_data.text + + pov_documents = locate_docpath() + if not pov_documents: + layout.label(text="Please configure ", icon="INFO") + layout.label(text="default pov include path ") + layout.label(text="in addon preferences") + # layout.separator() + layout.operator( + "preferences.addon_show", + text="Go to Render: Persistence of Vision addon", + icon="PREFERENCES", + ).module = "render_povray" + + # layout.separator() + else: + # print(pov_documents) + layout.menu(TEXT_MT_POV_insert.bl_idname) + + if text: + box = layout.box() + box.label(text='Source to render:', icon='RENDER_STILL') + row = box.row() + row.prop(text.pov, "custom_code", expand=True) + if text.pov.custom_code in {'3dview'}: + box.operator("render.render", icon='OUTLINER_DATA_ARMATURE') + if text.pov.custom_code in {'text'}: + rtext = bpy.context.space_data.text # is r a typo ? or why written, not used + box.operator("text.run", icon='ARMATURE_DATA') + # layout.prop(text.pov, "custom_code") + elif text.pov.custom_code in {'both'}: + box.operator("render.render", icon='POSE_HLT') + layout.label(text="Please specify declared", icon="INFO") + layout.label(text="items in properties ") + # layout.label(text="") + layout.label(text="replacement fields") + + +############################################### +# Text editor templates from header menu + + +class TEXT_MT_POV_templates(Menu): + """Use this class to create a menu for the same pov templates scenes as other pov IDEs.""" + + bl_label = "POV" + + # We list templates on file evaluation, we can assume they are static data, + # and better avoid running this on every draw call. + template_paths = [os.path.join(os.path.dirname(__file__), "templates_pov")] + + def draw(self, context): + self.path_menu(self.template_paths, "text.open", props_default={"internal": True}) + + +def menu_func_templates(self, context): + """Add POV files to the text editor templates menu""" + # Do not depend on POV being active renderer here... + self.layout.menu("TEXT_MT_POV_templates") + + +############################################### +# POV Import menu +class VIEW_MT_POV_import(Menu): + """Use this class for the import menu.""" + + bl_idname = "POVRAY_MT_import_tools" + bl_label = "Import" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES") + + +def menu_func_import(self, context): + """Add the import operator to menu""" + engine = context.scene.render.engine + if engine == 'POVRAY_RENDER': + self.layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES") + + +classes = ( + VIEW_MT_POV_import, + TEXT_OT_POV_insert, + TEXT_MT_POV_insert, + TEXT_PT_POV_custom_code, + TEXT_MT_POV_templates, +) + + +def register(): + + for cls in classes: + register_class(cls) + + bpy.types.TOPBAR_MT_file_import.append(menu_func_import) + bpy.types.TEXT_MT_templates.append(menu_func_templates) + + +def unregister(): + + bpy.types.TEXT_MT_templates.remove(menu_func_templates) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/scripting_properties.py b/render_povray/scripting_properties.py new file mode 100755 index 000000000..3e743da37 --- /dev/null +++ b/render_povray/scripting_properties.py @@ -0,0 +1,57 @@ +# ##### 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 ##### + +# <pep8 compliant> +import bpy + +"""Declare pov native file syntax properties controllable in UI hooks and text blocks""" + +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import EnumProperty, PointerProperty + +############################################################################### +# Text POV properties. +############################################################################### + + +class RenderPovSettingsText(PropertyGroup): + + """Declare text properties to use UI as an IDE or render text snippets to POV.""" + + custom_code: EnumProperty( + name="Custom Code", + description="rendered source: Both adds text at the " "top of the exported POV file", + items=(("3dview", "View", ""), ("text", "Text", ""), ("both", "Both", "")), + default="text", + ) + + +classes = (RenderPovSettingsText,) + + +def register(): + for cls in classes: + register_class(cls) + bpy.types.Text.pov = PointerProperty(type=RenderPovSettingsText) + + +def unregister(): + del bpy.types.Text.pov + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/shading.py b/render_povray/shading.py old mode 100644 new mode 100755 index a3f907dce..680be99cd --- a/render_povray/shading.py +++ b/render_povray/shading.py @@ -22,248 +22,366 @@ import bpy -def writeMaterial(using_uberpov, DEF_MAT_NAME, scene, tabWrite, safety, comments, uniqueName, materialNames, material): + +def write_object_material(material, ob, tab_write): + """Translate some object level material from Blender UI (VS data level) + + to POV interior{} syntax and write it to exported file. + """ + # DH - modified some variables to be function local, avoiding RNA write + # this should be checked to see if it is functionally correct + + # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections... + # if material and material.transparency_method == 'RAYTRACE': + if material: + # But there can be only one! + if material.pov_subsurface_scattering.use: # SSS IOR get highest priority + tab_write("interior {\n") + tab_write("ior %.6f\n" % material.pov_subsurface_scattering.ior) + # Then the raytrace IOR taken from raytrace transparency properties and used for + # reflections if IOR Mirror option is checked. + elif material.pov.mirror_use_IOR: + tab_write("interior {\n") + tab_write("ior %.6f\n" % material.pov_raytrace_transparency.ior) + elif material.pov.transparency_method == 'Z_TRANSPARENCY': + tab_write("interior {\n") + tab_write("ior 1.0\n") + else: + tab_write("interior {\n") + tab_write("ior %.6f\n" % material.pov_raytrace_transparency.ior) + + pov_fake_caustics = False + pov_photons_refraction = False + pov_photons_reflection = False + + if material.pov.photons_reflection: + pov_photons_reflection = True + if not material.pov.refraction_caustics: + pov_fake_caustics = False + pov_photons_refraction = False + elif material.pov.refraction_type == "1": + pov_fake_caustics = True + pov_photons_refraction = False + elif material.pov.refraction_type == "2": + pov_fake_caustics = False + pov_photons_refraction = True + + # If only Raytrace transparency is set, its IOR will be used for refraction, but user + # can set up 'un-physical' fresnel reflections in raytrace mirror parameters. + # Last, if none of the above is specified, user can set up 'un-physical' fresnel + # reflections in raytrace mirror parameters. And pov IOR defaults to 1. + if material.pov.caustics_enable: + if pov_fake_caustics: + tab_write("caustics %.3g\n" % material.pov.fake_caustics_power) + if pov_photons_refraction: + # Default of 1 means no dispersion + tab_write("dispersion %.6f\n" % material.pov.photons_dispersion) + tab_write("dispersion_samples %.d\n" % material.pov.photons_dispersion_samples) + # TODO + # Other interior args + if material.pov.use_transparency and material.pov.transparency_method == 'RAYTRACE': + # fade_distance + # In Blender this value has always been reversed compared to what tooltip says. + # 100.001 rather than 100 so that it does not get to 0 + # which deactivates the feature in POV + tab_write( + "fade_distance %.3g\n" % (100.001 - material.pov_raytrace_transparency.depth_max) + ) + # fade_power + tab_write("fade_power %.3g\n" % material.pov_raytrace_transparency.falloff) + # fade_color + tab_write("fade_color <%.3g, %.3g, %.3g>\n" % material.pov.interior_fade_color[:]) + + # (variable) dispersion_samples (constant count for now) + tab_write("}\n") + if material.pov.photons_reflection or material.pov.refraction_type == "2": + tab_write("photons{") + tab_write("target %.3g\n" % ob.pov.spacing_multiplier) + if not ob.pov.collect_photons: + tab_write("collect off\n") + if pov_photons_refraction: + tab_write("refraction on\n") + if pov_photons_reflection: + tab_write("reflection on\n") + tab_write("}\n") + + +def write_material( + using_uberpov, DEF_MAT_NAME, tab_write, safety, comments, unique_name, material_names, material +): """Translate Blender material POV texture{} block and write to exported file.""" # Assumes only called once on each material if material: name_orig = material.name - name = materialNames[name_orig] = uniqueName(bpy.path.clean_name(name_orig), materialNames) + name = material_names[name_orig] = unique_name( + bpy.path.clean_name(name_orig), material_names + ) else: name = name_orig = DEF_MAT_NAME - if material: # If saturation(.s) is not zero, then color is not grey, and has a tint - colored_specular_found = ((material.pov.specular_color.s > 0.0) and (material.pov.diffuse_shader != 'MINNAERT')) + colored_specular_found = (material.pov.specular_color.s > 0.0) and ( + material.pov.diffuse_shader != "MINNAERT" + ) ################## - # Several versions of the finish: Level conditions are variations for specular/Mirror + # Several versions of the finish: ref_level_bound conditions are variations for specular/Mirror # texture channel map with alternative finish of 0 specular and no mirror reflection. - # Level=1 Means No specular nor Mirror reflection - # Level=2 Means translation of spec and mir levels for when no map influences them - # Level=3 Means Maximum Spec and Mirror + # ref_level_bound=1 Means No specular nor Mirror reflection + # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them + # ref_level_bound=3 Means Maximum Spec and Mirror - def povHasnoSpecularMaps(Level): + def pov_has_no_specular_maps(ref_level_bound): """Translate Blender specular map influence to POV finish map trick and write to file.""" - if Level == 1: + if ref_level_bound == 1: if comments: - tabWrite("//--No specular nor Mirror reflection--\n") + tab_write("//--No specular nor Mirror reflection--\n") else: - tabWrite("\n") - tabWrite("#declare %s = finish {\n" % safety(name, Level=1)) + tab_write("\n") + tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=1)) - elif Level == 2: + elif ref_level_bound == 2: if comments: - tabWrite("//--translation of spec and mir levels for when no map " \ - "influences them--\n") + tab_write( + "//--translation of spec and mir levels for when no map " "influences them--\n" + ) else: - tabWrite("\n") - tabWrite("#declare %s = finish {\n" % safety(name, Level=2)) + tab_write("\n") + tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=2)) - elif Level == 3: + elif ref_level_bound == 3: if comments: - tabWrite("//--Maximum Spec and Mirror--\n") + tab_write("//--Maximum Spec and Mirror--\n") else: - tabWrite("\n") - tabWrite("#declare %s = finish {\n" % safety(name, Level=3)) + tab_write("\n") + tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=3)) if material: # POV-Ray 3.7 now uses two diffuse values respectively for front and back shading # (the back diffuse is like blender translucency) - frontDiffuse = material.pov.diffuse_intensity - backDiffuse = material.pov.translucency + front_diffuse = material.pov.diffuse_intensity + back_diffuse = material.pov.translucency if material.pov.conserve_energy: - #Total should not go above one - if (frontDiffuse + backDiffuse) <= 1.0: + # Total should not go above one + if (front_diffuse + back_diffuse) <= 1.0: pass - elif frontDiffuse == backDiffuse: + elif front_diffuse == back_diffuse: # Try to respect the user's 'intention' by comparing the two values but # bringing the total back to one. - frontDiffuse = backDiffuse = 0.5 + front_diffuse = back_diffuse = 0.5 # Let the highest value stay the highest value. - elif frontDiffuse > backDiffuse: + elif front_diffuse > back_diffuse: # clamps the sum below 1 - backDiffuse = min(backDiffuse, (1.0 - frontDiffuse)) + back_diffuse = min(back_diffuse, (1.0 - front_diffuse)) else: - frontDiffuse = min(frontDiffuse, (1.0 - backDiffuse)) + front_diffuse = min(front_diffuse, (1.0 - back_diffuse)) # map hardness between 0.0 and 1.0 - roughness = ((1.0 - ((material.pov.specular_hardness - 1.0) / 510.0))) + roughness = 1.0 - ((material.pov.specular_hardness - 1.0) / 510.0) ## scale from 0.0 to 0.1 roughness *= 0.1 # add a small value because 0.0 is invalid. - roughness += (1.0 / 511.0) + roughness += 1.0 / 511.0 ################################Diffuse Shader###################################### - # Not used for Full spec (Level=3) of the shader. - if material.pov.diffuse_shader == 'OREN_NAYAR' and Level != 3: + # Not used for Full spec (ref_level_bound=3) of the shader. + if material.pov.diffuse_shader == "OREN_NAYAR" and ref_level_bound != 3: # Blender roughness is what is generally called oren nayar Sigma, # and brilliance in POV-Ray. - tabWrite("brilliance %.3g\n" % (0.9 + material.roughness)) + tab_write("brilliance %.3g\n" % (0.9 + material.roughness)) - if material.pov.diffuse_shader == 'TOON' and Level != 3: - tabWrite("brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25)) + if material.pov.diffuse_shader == "TOON" and ref_level_bound != 3: + tab_write("brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25)) # Lower diffuse and increase specular for toon effect seems to look better # in POV-Ray. - frontDiffuse *= 0.5 + front_diffuse *= 0.5 - if material.pov.diffuse_shader == 'MINNAERT' and Level != 3: - #tabWrite("aoi %.3g\n" % material.darkness) + if material.pov.diffuse_shader == "MINNAERT" and ref_level_bound != 3: + # tab_write("aoi %.3g\n" % material.darkness) pass # let's keep things simple for now - if material.pov.diffuse_shader == 'FRESNEL' and Level != 3: - #tabWrite("aoi %.3g\n" % material.diffuse_fresnel_factor) + if material.pov.diffuse_shader == "FRESNEL" and ref_level_bound != 3: + # tab_write("aoi %.3g\n" % material.diffuse_fresnel_factor) pass # let's keep things simple for now - if material.pov.diffuse_shader == 'LAMBERT' and Level != 3: + if material.pov.diffuse_shader == "LAMBERT" and ref_level_bound != 3: # trying to best match lambert attenuation by that constant brilliance value - tabWrite("brilliance 1\n") + tab_write("brilliance 1\n") - if Level == 2: + if ref_level_bound == 2: ###########################Specular Shader###################################### # No difference between phong and cook torrence in blender HaHa! - if (material.pov.specular_shader == 'COOKTORR' or - material.pov.specular_shader == 'PHONG'): - tabWrite("phong %.3g\n" % (material.pov.specular_intensity)) - tabWrite("phong_size %.3g\n" % (material.pov.specular_hardness /3.14)) + if ( + material.pov.specular_shader == "COOKTORR" + or material.pov.specular_shader == "PHONG" + ): + tab_write("phong %.3g\n" % (material.pov.specular_intensity)) + tab_write("phong_size %.3g\n" % (material.pov.specular_hardness / 3.14)) # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior. - elif material.pov.specular_shader == 'BLINN': + elif material.pov.specular_shader == "BLINN": # Use blender Blinn's IOR just as some factor for spec intensity - tabWrite("specular %.3g\n" % (material.pov.specular_intensity * - (material.pov.specular_ior / 4.0))) - tabWrite("roughness %.3g\n" % roughness) - #Could use brilliance 2(or varying around 2 depending on ior or factor) too. - - elif material.pov.specular_shader == 'TOON': - tabWrite("phong %.3g\n" % (material.pov.specular_intensity * 2.0)) + tab_write( + "specular %.3g\n" + % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)) + ) + tab_write("roughness %.3g\n" % roughness) + # Could use brilliance 2(or varying around 2 depending on ior or factor) too. + + elif material.pov.specular_shader == "TOON": + tab_write("phong %.3g\n" % (material.pov.specular_intensity * 2.0)) # use extreme phong_size - tabWrite("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)) + tab_write("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)) - elif material.pov.specular_shader == 'WARDISO': + elif material.pov.specular_shader == "WARDISO": # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("specular %.3g\n" % (material.pov.specular_intensity / - (material.pov.specular_slope + 0.0005))) + tab_write( + "specular %.3g\n" + % (material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)) + ) # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)) + tab_write("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)) # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8)) + tab_write("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8)) #################################################################################### - elif Level == 1: - if (material.pov.specular_shader == 'COOKTORR' or - material.pov.specular_shader == 'PHONG'): - tabWrite("phong 0\n")#%.3g\n" % (material.pov.specular_intensity/5)) - tabWrite("phong_size %.3g\n" % (material.pov.specular_hardness /3.14)) + elif ref_level_bound == 1: + if ( + material.pov.specular_shader == "COOKTORR" + or material.pov.specular_shader == "PHONG" + ): + tab_write("phong 0\n") #%.3g\n" % (material.pov.specular_intensity/5)) + tab_write("phong_size %.3g\n" % (material.pov.specular_hardness / 3.14)) # POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior. - elif material.pov.specular_shader == 'BLINN': + elif material.pov.specular_shader == "BLINN": # Use blender Blinn's IOR just as some factor for spec intensity - tabWrite("specular %.3g\n" % (material.pov.specular_intensity * - (material.pov.specular_ior / 4.0))) - tabWrite("roughness %.3g\n" % roughness) - #Could use brilliance 2(or varying around 2 depending on ior or factor) too. - - elif material.pov.specular_shader == 'TOON': - tabWrite("phong %.3g\n" % (material.pov.specular_intensity * 2.0)) + tab_write( + "specular %.3g\n" + % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)) + ) + tab_write("roughness %.3g\n" % roughness) + # Could use brilliance 2(or varying around 2 depending on ior or factor) too. + + elif material.pov.specular_shader == "TOON": + tab_write("phong %.3g\n" % (material.pov.specular_intensity * 2.0)) # use extreme phong_size - tabWrite("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)) + tab_write("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)) - elif material.pov.specular_shader == 'WARDISO': + elif material.pov.specular_shader == "WARDISO": # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("specular %.3g\n" % (material.pov.specular_intensity / - (material.pov.specular_slope + 0.0005))) + tab_write( + "specular %.3g\n" + % (material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)) + ) # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)) + tab_write("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)) # find best suited default constant for brilliance Use both phong and # specular for some values. - tabWrite("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8)) - elif Level == 3: - # Spec must be Max at Level 3 so that white of mixing texture always shows specularity + tab_write("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8)) + elif ref_level_bound == 3: + # Spec must be Max at ref_level_bound 3 so that white of mixing texture always shows specularity # That's why it's multiplied by 255. maybe replace by texture's brightest pixel value? - tabWrite("specular %.3g\n" % ((material.pov.specular_intensity*material.pov.specular_color.v)*(255* slot.specular_factor))) - tabWrite("roughness %.3g\n" % (1/material.pov.specular_hardness)) - tabWrite("diffuse %.3g %.3g\n" % (frontDiffuse, backDiffuse)) - - tabWrite("ambient %.3g\n" % material.pov.ambient) + tab_write( + "specular %.3g\n" + % ( + (material.pov.specular_intensity * material.pov.specular_color.v) + * (255 * slot.specular_factor) + ) + ) + tab_write("roughness %.3g\n" % (1 / material.pov.specular_hardness)) + tab_write("diffuse %.3g %.3g\n" % (front_diffuse, back_diffuse)) + + tab_write("ambient %.3g\n" % material.pov.ambient) # POV-Ray blends the global value - #tabWrite("ambient rgb <%.3g, %.3g, %.3g>\n" % \ + # tab_write("ambient rgb <%.3g, %.3g, %.3g>\n" % \ # tuple([c*material.pov.ambient for c in world.ambient_color])) - tabWrite("emission %.3g\n" % material.pov.emit) # New in POV-Ray 3.7 + tab_write("emission %.3g\n" % material.pov.emit) # New in POV-Ray 3.7 - #POV-Ray just ignores roughness if there's no specular keyword - #tabWrite("roughness %.3g\n" % roughness) + # POV-Ray just ignores roughness if there's no specular keyword + # tab_write("roughness %.3g\n" % roughness) if material.pov.conserve_energy: # added for more realistic shading. Needs some checking to see if it # really works. --Maurice. - tabWrite("conserve_energy\n") + tab_write("conserve_energy\n") - if colored_specular_found == True: - tabWrite("metallic\n") + if colored_specular_found: + tab_write("metallic\n") # 'phong 70.0 ' - if Level != 1: + if ref_level_bound != 1: if material.pov_raytrace_mirror.use: raytrace_mirror = material.pov_raytrace_mirror if raytrace_mirror.reflect_factor: - tabWrite("reflection {\n") - tabWrite("rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:]) + tab_write("reflection {\n") + tab_write("rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:]) if material.pov.mirror_metallic: - tabWrite("metallic %.3g\n" % (raytrace_mirror.reflect_factor)) + tab_write("metallic %.3g\n" % (raytrace_mirror.reflect_factor)) # Blurry reflections for UberPOV if using_uberpov and raytrace_mirror.gloss_factor < 1.0: - #tabWrite("#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n") - tabWrite("roughness %.6f\n" % \ - (0.000001/raytrace_mirror.gloss_factor)) - #tabWrite("#end #end #end\n") # This and previous comment for backward compatibility, messier pov code + # tab_write("#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n") + tab_write( + "roughness %.6f\n" % (0.000001 / raytrace_mirror.gloss_factor) + ) + # tab_write("#end #end #end\n") # This and previous comment for backward compatibility, messier pov code if material.pov.mirror_use_IOR: # WORKING ? # Removed from the line below: gives a more physically correct # material but needs proper IOR. --Maurice - tabWrite("fresnel 1 ") - tabWrite("falloff %.3g exponent %.3g} " % \ - (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor)) + tab_write("fresnel 1 ") + tab_write( + "falloff %.3g exponent %.3g} " + % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor) + ) if material.pov_subsurface_scattering.use: subsurface_scattering = material.pov_subsurface_scattering - tabWrite("subsurface { translucency <%.3g, %.3g, %.3g> }\n" % ( - (subsurface_scattering.radius[0]), - (subsurface_scattering.radius[1]), - (subsurface_scattering.radius[2]), - ) - ) + tab_write( + "subsurface { translucency <%.3g, %.3g, %.3g> }\n" + % ( + (subsurface_scattering.radius[0]), + (subsurface_scattering.radius[1]), + (subsurface_scattering.radius[2]), + ) + ) if material.pov.irid_enable: - tabWrite("irid { %.4g thickness %.4g turbulence %.4g }" % \ - (material.pov.irid_amount, material.pov.irid_thickness, - material.pov.irid_turbulence)) + tab_write( + "irid { %.4g thickness %.4g turbulence %.4g }" + % ( + material.pov.irid_amount, + material.pov.irid_thickness, + material.pov.irid_turbulence, + ) + ) else: - tabWrite("diffuse 0.8\n") - tabWrite("phong 70.0\n") + tab_write("diffuse 0.8\n") + tab_write("phong 70.0\n") - #tabWrite("specular 0.2\n") + # tab_write("specular 0.2\n") # This is written into the object - ''' + """ if material and material.pov.transparency_method=='RAYTRACE': 'interior { ior %.3g} ' % material.raytrace_transparency.ior - ''' + """ - #tabWrite("crand 1.0\n") # Sand granyness - #tabWrite("metallic %.6f\n" % material.spec) - #tabWrite("phong %.6f\n" % material.spec) - #tabWrite("phong_size %.6f\n" % material.spec) - #tabWrite("brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness + # tab_write("crand 1.0\n") # Sand granyness + # tab_write("metallic %.6f\n" % material.spec) + # tab_write("phong %.6f\n" % material.spec) + # tab_write("phong_size %.6f\n" % material.spec) + # tab_write("brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness - tabWrite("}\n\n") + tab_write("}\n\n") - # Level=2 Means translation of spec and mir levels for when no map influences them - povHasnoSpecularMaps(Level=2) + # ref_level_bound=2 Means translation of spec and mir levels for when no map influences them + pov_has_no_specular_maps(ref_level_bound=2) if material: special_texture_found = False @@ -271,1495 +389,1181 @@ def writeMaterial(using_uberpov, DEF_MAT_NAME, scene, tabWrite, safety, comments for t in material.pov_texture_slots: tmpidx += 1 # index = material.pov.active_texture_index - slot = material.pov_texture_slots[tmpidx] # [index] - povtex = slot.texture # slot.name + slot = material.pov_texture_slots[tmpidx] # [index] + povtex = slot.texture # slot.name tex = bpy.data.textures[povtex] if t and t.use and tex is not None: - - if (tex.type == 'IMAGE' and tex.image) or tex.type != 'IMAGE': - #validPath - if(t and t.use and - (t.use_map_specular or t.use_map_raymir or t.use_map_normal or t.use_map_alpha)): + if (tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE": + # validPath + if ( + t + and t.use + and ( + t.use_map_specular + or t.use_map_raymir + or t.use_map_normal + or t.use_map_alpha + ) + ): special_texture_found = True continue # Some texture found if special_texture_found or colored_specular_found: - # Level=1 Means No specular nor Mirror reflection - povHasnoSpecularMaps(Level=1) + # ref_level_bound=1 Means No specular nor Mirror reflection + pov_has_no_specular_maps(ref_level_bound=1) + + # ref_level_bound=3 Means Maximum Spec and Mirror + pov_has_no_specular_maps(ref_level_bound=3) - # Level=3 Means Maximum Spec and Mirror - povHasnoSpecularMaps(Level=3) -def exportPattern(texture, string_strip_hyphen): +def export_pattern(texture): """Translate Blender procedural textures to POV patterns and write to pov file. Function Patterns can be used to better access sub components of a pattern like - grey values for influence mapping""" - - tex=texture + grey values for influence mapping + """ + tex = texture pat = tex.pov - PATname = "PAT_%s"%string_strip_hyphen(bpy.path.clean_name(tex.name)) - mappingDif = ("translate <%.4g,%.4g,%.4g> scale <%.4g,%.4g,%.4g>" % \ - (pat.tex_mov_x, pat.tex_mov_y, pat.tex_mov_z, - 1.0 / pat.tex_scale_x, 1.0 / pat.tex_scale_y, 1.0 / pat.tex_scale_z)) - texStrg="" - def exportColorRamp(texture): - tex=texture + pat_name = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name)) + mapping_dif = "translate <%.4g,%.4g,%.4g> scale <%.4g,%.4g,%.4g>" % ( + pat.tex_mov_x, + pat.tex_mov_y, + pat.tex_mov_z, + 1.0 / pat.tex_scale_x, + 1.0 / pat.tex_scale_y, + 1.0 / pat.tex_scale_z, + ) + text_strg = "" + + def export_color_ramp(texture): + tex = texture pat = tex.pov - colRampStrg="color_map {\n" - numColor=0 + col_ramp_strg = "color_map {\n" + num_color = 0 for el in tex.color_ramp.elements: - numColor+=1 + num_color += 1 pos = el.position - col=el.color - colR,colG,colB,colA = col[0],col[1],col[2],1-col[3] - if pat.tex_pattern_type not in {'checker', 'hexagon', 'square', 'triangular', 'brick'} : - colRampStrg+="[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n"%(pos,colR,colG,colB,colA) - if pat.tex_pattern_type in {'brick','checker'} and numColor < 3: - colRampStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'hexagon' and numColor < 4 : - colRampStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'square' and numColor < 5 : - colRampStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'triangular' and numColor < 7 : - colRampStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - - colRampStrg+="} \n" - #end color map - return colRampStrg - #much work to be done here only defaults translated for now: - #pov noise_generator 3 means perlin noise - if tex.type not in {'NONE', 'IMAGE'} and pat.tex_pattern_type == 'emulator': - texStrg+="pigment {\n" + col = el.color + col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3] + if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}: + col_ramp_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % ( + pos, + col_r, + col_g, + col_b, + col_a, + ) + if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3: + col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "hexagon" and num_color < 4: + col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "square" and num_color < 5: + col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "triangular" and num_color < 7: + col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + + col_ramp_strg += "} \n" + # end color map + return col_ramp_strg + + # much work to be done here only defaults translated for now: + # pov noise_generator 3 means perlin noise + if tex.type not in {"NONE", "IMAGE"} and pat.tex_pattern_type == "emulator": + text_strg += "pigment {\n" ####################### EMULATE BLENDER VORONOI TEXTURE #################### - if tex.type == 'VORONOI': - texStrg+="crackle\n" - texStrg+=" offset %.4g\n"%tex.nabla - texStrg+=" form <%.4g,%.4g,%.4g>\n"%(tex.weight_1, tex.weight_2, tex.weight_3) - if tex.distance_metric == 'DISTANCE': - texStrg+=" metric 2.5\n" - if tex.distance_metric == 'DISTANCE_SQUARED': - texStrg+=" metric 2.5\n" - texStrg+=" poly_wave 2\n" - if tex.distance_metric == 'MINKOVSKY': - texStrg+=" metric %s\n"%tex.minkovsky_exponent - if tex.distance_metric == 'MINKOVSKY_FOUR': - texStrg+=" metric 4\n" - if tex.distance_metric == 'MINKOVSKY_HALF': - texStrg+=" metric 0.5\n" - if tex.distance_metric == 'CHEBYCHEV': - texStrg+=" metric 10\n" - if tex.distance_metric == 'MANHATTAN': - texStrg+=" metric 1\n" - - if tex.color_mode == 'POSITION': - texStrg+="solid\n" - texStrg+="scale 0.25\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "VORONOI": + text_strg += "crackle\n" + text_strg += " offset %.4g\n" % tex.nabla + text_strg += " form <%.4g,%.4g,%.4g>\n" % (tex.weight_1, tex.weight_2, tex.weight_3) + if tex.distance_metric == "DISTANCE": + text_strg += " metric 2.5\n" + if tex.distance_metric == "DISTANCE_SQUARED": + text_strg += " metric 2.5\n" + text_strg += " poly_wave 2\n" + if tex.distance_metric == "MINKOVSKY": + text_strg += " metric %s\n" % tex.minkovsky_exponent + if tex.distance_metric == "MINKOVSKY_FOUR": + text_strg += " metric 4\n" + if tex.distance_metric == "MINKOVSKY_HALF": + text_strg += " metric 0.5\n" + if tex.distance_metric == "CHEBYCHEV": + text_strg += " metric 10\n" + if tex.distance_metric == "MANHATTAN": + text_strg += " metric 1\n" + + if tex.color_mode == "POSITION": + text_strg += "solid\n" + text_strg += "scale 0.25\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<0,0,0,1>]\n" - texStrg+="[1 color rgbt<1,1,1,0>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<0,0,0,1>]\n" + text_strg += "[1 color rgbt<1,1,1,0>]\n" + text_strg += "}\n" ####################### EMULATE BLENDER CLOUDS TEXTURE #################### - if tex.type == 'CLOUDS': - if tex.noise_type == 'SOFT_NOISE': - texStrg+="wrinkles\n" - texStrg+="scale 0.25\n" + if tex.type == "CLOUDS": + if tex.noise_type == "SOFT_NOISE": + text_strg += "wrinkles\n" + text_strg += "scale 0.25\n" else: - texStrg+="granite\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += "granite\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<0,0,0,1>]\n" - texStrg+="[1 color rgbt<1,1,1,0>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<0,0,0,1>]\n" + text_strg += "[1 color rgbt<1,1,1,0>]\n" + text_strg += "}\n" ####################### EMULATE BLENDER WOOD TEXTURE #################### - if tex.type == 'WOOD': - if tex.wood_type == 'RINGS': - texStrg+="wood\n" - texStrg+="scale 0.25\n" - if tex.wood_type == 'RINGNOISE': - texStrg+="wood\n" - texStrg+="scale 0.25\n" - texStrg+="turbulence %.4g\n"%(tex.turbulence/100) - if tex.wood_type == 'BANDS': - texStrg+="marble\n" - texStrg+="scale 0.25\n" - texStrg+="rotate <45,-45,45>\n" - if tex.wood_type == 'BANDNOISE': - texStrg+="marble\n" - texStrg+="scale 0.25\n" - texStrg+="rotate <45,-45,45>\n" - texStrg+="turbulence %.4g\n"%(tex.turbulence/10) - - if tex.noise_basis_2 == 'SIN': - texStrg+="sine_wave\n" - if tex.noise_basis_2 == 'TRI': - texStrg+="triangle_wave\n" - if tex.noise_basis_2 == 'SAW': - texStrg+="ramp_wave\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "WOOD": + if tex.wood_type == "RINGS": + text_strg += "wood\n" + text_strg += "scale 0.25\n" + if tex.wood_type == "RINGNOISE": + text_strg += "wood\n" + text_strg += "scale 0.25\n" + text_strg += "turbulence %.4g\n" % (tex.turbulence / 100) + if tex.wood_type == "BANDS": + text_strg += "marble\n" + text_strg += "scale 0.25\n" + text_strg += "rotate <45,-45,45>\n" + if tex.wood_type == "BANDNOISE": + text_strg += "marble\n" + text_strg += "scale 0.25\n" + text_strg += "rotate <45,-45,45>\n" + text_strg += "turbulence %.4g\n" % (tex.turbulence / 10) + + if tex.noise_basis_2 == "SIN": + text_strg += "sine_wave\n" + if tex.noise_basis_2 == "TRI": + text_strg += "triangle_wave\n" + if tex.noise_basis_2 == "SAW": + text_strg += "ramp_wave\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<0,0,0,0>]\n" - texStrg+="[1 color rgbt<1,1,1,0>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<0,0,0,0>]\n" + text_strg += "[1 color rgbt<1,1,1,0>]\n" + text_strg += "}\n" ####################### EMULATE BLENDER STUCCI TEXTURE #################### - if tex.type == 'STUCCI': - texStrg+="bozo\n" - texStrg+="scale 0.25\n" - if tex.noise_type == 'HARD_NOISE': - texStrg+="triangle_wave\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "STUCCI": + text_strg += "bozo\n" + text_strg += "scale 0.25\n" + if tex.noise_type == "HARD_NOISE": + text_strg += "triangle_wave\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbf<1,1,1,0>]\n" - texStrg+="[1 color rgbt<0,0,0,1>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbf<1,1,1,0>]\n" + text_strg += "[1 color rgbt<0,0,0,1>]\n" + text_strg += "}\n" else: - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbf<0,0,0,1>]\n" - texStrg+="[1 color rgbt<1,1,1,0>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbf<0,0,0,1>]\n" + text_strg += "[1 color rgbt<1,1,1,0>]\n" + text_strg += "}\n" ####################### EMULATE BLENDER MAGIC TEXTURE #################### - if tex.type == 'MAGIC': - texStrg+="leopard\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "MAGIC": + text_strg += "leopard\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0.5>]\n" - texStrg+="[0.25 color rgbf<0,1,0,0.75>]\n" - texStrg+="[0.5 color rgbf<0,0,1,0.75>]\n" - texStrg+="[0.75 color rgbf<1,0,1,0.75>]\n" - texStrg+="[1 color rgbf<0,1,0,0.75>]\n" - texStrg+="}\n" - texStrg+="scale 0.1\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0.5>]\n" + text_strg += "[0.25 color rgbf<0,1,0,0.75>]\n" + text_strg += "[0.5 color rgbf<0,0,1,0.75>]\n" + text_strg += "[0.75 color rgbf<1,0,1,0.75>]\n" + text_strg += "[1 color rgbf<0,1,0,0.75>]\n" + text_strg += "}\n" + text_strg += "scale 0.1\n" ####################### EMULATE BLENDER MARBLE TEXTURE #################### - if tex.type == 'MARBLE': - texStrg+="marble\n" - texStrg+="turbulence 0.5\n" - texStrg+="noise_generator 3\n" - texStrg+="scale 0.75\n" - texStrg+="rotate <45,-45,45>\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "MARBLE": + text_strg += "marble\n" + text_strg += "turbulence 0.5\n" + text_strg += "noise_generator 3\n" + text_strg += "scale 0.75\n" + text_strg += "rotate <45,-45,45>\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - if tex.marble_type == 'SOFT': - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<0,0,0,0>]\n" - texStrg+="[0.05 color rgbt<0,0,0,0>]\n" - texStrg+="[1 color rgbt<0.9,0.9,0.9,0>]\n" - texStrg+="}\n" - elif tex.marble_type == 'SHARP': - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<0,0,0,0>]\n" - texStrg+="[0.025 color rgbt<0,0,0,0>]\n" - texStrg+="[1 color rgbt<0.9,0.9,0.9,0>]\n" - texStrg+="}\n" + if tex.marble_type == "SOFT": + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<0,0,0,0>]\n" + text_strg += "[0.05 color rgbt<0,0,0,0>]\n" + text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n" + text_strg += "}\n" + elif tex.marble_type == "SHARP": + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<0,0,0,0>]\n" + text_strg += "[0.025 color rgbt<0,0,0,0>]\n" + text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n" + text_strg += "}\n" else: - texStrg+="[0 color rgbt<0,0,0,0>]\n" - texStrg+="[1 color rgbt<1,1,1,0>]\n" - texStrg+="}\n" - if tex.noise_basis_2 == 'SIN': - texStrg+="sine_wave\n" - if tex.noise_basis_2 == 'TRI': - texStrg+="triangle_wave\n" - if tex.noise_basis_2 == 'SAW': - texStrg+="ramp_wave\n" + text_strg += "[0 color rgbt<0,0,0,0>]\n" + text_strg += "[1 color rgbt<1,1,1,0>]\n" + text_strg += "}\n" + if tex.noise_basis_2 == "SIN": + text_strg += "sine_wave\n" + if tex.noise_basis_2 == "TRI": + text_strg += "triangle_wave\n" + if tex.noise_basis_2 == "SAW": + text_strg += "ramp_wave\n" ####################### EMULATE BLENDER BLEND TEXTURE #################### - if tex.type == 'BLEND': - if tex.progression=='RADIAL': - texStrg+="radial\n" - if tex.use_flip_axis=='HORIZONTAL': - texStrg+="rotate x*90\n" + if tex.type == "BLEND": + if tex.progression == "RADIAL": + text_strg += "radial\n" + if tex.use_flip_axis == "HORIZONTAL": + text_strg += "rotate x*90\n" else: - texStrg+="rotate <-90,0,90>\n" - texStrg+="ramp_wave\n" - elif tex.progression=='SPHERICAL': - texStrg+="spherical\n" - texStrg+="scale 3\n" - texStrg+="poly_wave 1\n" - elif tex.progression=='QUADRATIC_SPHERE': - texStrg+="spherical\n" - texStrg+="scale 3\n" - texStrg+=" poly_wave 2\n" - elif tex.progression=='DIAGONAL': - texStrg+="gradient <1,1,0>\n" - texStrg+="scale 3\n" - elif tex.use_flip_axis=='HORIZONTAL': - texStrg+="gradient x\n" - texStrg+="scale 2.01\n" - elif tex.use_flip_axis=='VERTICAL': - texStrg+="gradient y\n" - texStrg+="scale 2.01\n" - #texStrg+="ramp_wave\n" - #texStrg+="frequency 0.5\n" - texStrg+="phase 0.5\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += "rotate <-90,0,90>\n" + text_strg += "ramp_wave\n" + elif tex.progression == "SPHERICAL": + text_strg += "spherical\n" + text_strg += "scale 3\n" + text_strg += "poly_wave 1\n" + elif tex.progression == "QUADRATIC_SPHERE": + text_strg += "spherical\n" + text_strg += "scale 3\n" + text_strg += " poly_wave 2\n" + elif tex.progression == "DIAGONAL": + text_strg += "gradient <1,1,0>\n" + text_strg += "scale 3\n" + elif tex.use_flip_axis == "HORIZONTAL": + text_strg += "gradient x\n" + text_strg += "scale 2.01\n" + elif tex.use_flip_axis == "VERTICAL": + text_strg += "gradient y\n" + text_strg += "scale 2.01\n" + # text_strg+="ramp_wave\n" + # text_strg+="frequency 0.5\n" + text_strg += "phase 0.5\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - if tex.progression == 'LINEAR': - texStrg+=" poly_wave 1\n" - if tex.progression == 'QUADRATIC': - texStrg+=" poly_wave 2\n" - if tex.progression == 'EASING': - texStrg+=" poly_wave 1.5\n" - + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + if tex.progression == "LINEAR": + text_strg += " poly_wave 1\n" + if tex.progression == "QUADRATIC": + text_strg += " poly_wave 2\n" + if tex.progression == "EASING": + text_strg += " poly_wave 1.5\n" ####################### EMULATE BLENDER MUSGRAVE TEXTURE #################### # if tex.type == 'MUSGRAVE': - # texStrg+="function{ f_ridged_mf( x, y, 0, 1, 2, 9, -0.5, 3,3 )*0.5}\n" - # texStrg+="color_map {\n" - # texStrg+="[0 color rgbf<0,0,0,1>]\n" - # texStrg+="[1 color rgbf<1,1,1,0>]\n" - # texStrg+="}\n" + # text_strg+="function{ f_ridged_mf( x, y, 0, 1, 2, 9, -0.5, 3,3 )*0.5}\n" + # text_strg+="color_map {\n" + # text_strg+="[0 color rgbf<0,0,0,1>]\n" + # text_strg+="[1 color rgbf<1,1,1,0>]\n" + # text_strg+="}\n" # simplified for now: - if tex.type == 'MUSGRAVE': - texStrg+="bozo scale 0.25 \n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "MUSGRAVE": + text_strg += "bozo scale 0.25 \n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {[0.5 color rgbf<0,0,0,1>][1 color rgbt<1,1,1,0>]}ramp_wave \n" + text_strg += ( + "color_map {[0.5 color rgbf<0,0,0,1>][1 color rgbt<1,1,1,0>]}ramp_wave \n" + ) ####################### EMULATE BLENDER DISTORTED NOISE TEXTURE #################### - if tex.type == 'DISTORTED_NOISE': - texStrg+="average\n" - texStrg+=" pigment_map {\n" - texStrg+=" [1 bozo scale 0.25 turbulence %.4g\n" %tex.distortion - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "DISTORTED_NOISE": + text_strg += "average\n" + text_strg += " pigment_map {\n" + text_strg += " [1 bozo scale 0.25 turbulence %.4g\n" % tex.distortion + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - texStrg+="]\n" - - if tex.noise_distortion == 'CELL_NOISE': - texStrg+=" [1 cells scale 0.1\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + text_strg += "]\n" + + if tex.noise_distortion == "CELL_NOISE": + text_strg += " [1 cells scale 0.1\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - texStrg+="]\n" - if tex.noise_distortion=='VORONOI_CRACKLE': - texStrg+=" [1 crackle scale 0.25\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + text_strg += "]\n" + if tex.noise_distortion == "VORONOI_CRACKLE": + text_strg += " [1 crackle scale 0.25\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - texStrg+="]\n" - if tex.noise_distortion in ['VORONOI_F1','VORONOI_F2','VORONOI_F3','VORONOI_F4','VORONOI_F2_F1']: - texStrg+=" [1 crackle metric 2.5 scale 0.25 turbulence %.4g\n" %(tex.distortion/2) - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + text_strg += "]\n" + if tex.noise_distortion in [ + "VORONOI_F1", + "VORONOI_F2", + "VORONOI_F3", + "VORONOI_F4", + "VORONOI_F2_F1", + ]: + text_strg += " [1 crackle metric 2.5 scale 0.25 turbulence %.4g\n" % ( + tex.distortion / 2 + ) + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - texStrg+="]\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + text_strg += "]\n" else: - texStrg+=" [1 wrinkles scale 0.25\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + text_strg += " [1 wrinkles scale 0.25\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0 color rgbt<1,1,1,0>]\n" - texStrg+="[1 color rgbf<0,0,0,1>]\n" - texStrg+="}\n" - texStrg+="]\n" - texStrg+=" }\n" + text_strg += "color_map {\n" + text_strg += "[0 color rgbt<1,1,1,0>]\n" + text_strg += "[1 color rgbf<0,0,0,1>]\n" + text_strg += "}\n" + text_strg += "]\n" + text_strg += " }\n" ####################### EMULATE BLENDER NOISE TEXTURE #################### - if tex.type == 'NOISE': - texStrg+="cells\n" - texStrg+="turbulence 3\n" - texStrg+="omega 3\n" - if tex.use_color_ramp == True: - texStrg+=exportColorRamp(tex) + if tex.type == "NOISE": + text_strg += "cells\n" + text_strg += "turbulence 3\n" + text_strg += "omega 3\n" + if tex.use_color_ramp: + text_strg += export_color_ramp(tex) else: - texStrg+="color_map {\n" - texStrg+="[0.75 color rgb<0,0,0,>]\n" - texStrg+="[1 color rgb<1,1,1,>]\n" - texStrg+="}\n" + text_strg += "color_map {\n" + text_strg += "[0.75 color rgb<0,0,0,>]\n" + text_strg += "[1 color rgb<1,1,1,>]\n" + text_strg += "}\n" ####################### IGNORE OTHER BLENDER TEXTURE #################### - else: #non translated textures + else: # non translated textures pass - texStrg+="}\n\n" - - texStrg+="#declare f%s=\n"%PATname - texStrg+="function{pigment{%s}}\n"%PATname - texStrg+="\n" - - elif pat.tex_pattern_type != 'emulator': - texStrg+="pigment {\n" - texStrg+="%s\n"%pat.tex_pattern_type - if pat.tex_pattern_type == 'agate': - texStrg+="agate_turb %.4g\n"%pat.modifier_turbulence - if pat.tex_pattern_type in {'spiral1', 'spiral2', 'tiling'}: - texStrg+="%s\n"%pat.modifier_numbers - if pat.tex_pattern_type == 'quilted': - texStrg+="control0 %s control1 %s\n"%(pat.modifier_control0, pat.modifier_control1) - if pat.tex_pattern_type == 'mandel': - texStrg+="%s exponent %s \n"%(pat.f_iter, pat.f_exponent) - if pat.tex_pattern_type == 'julia': - texStrg+="<%.4g, %.4g> %s exponent %s \n"%(pat.julia_complex_1, pat.julia_complex_2, pat.f_iter, pat.f_exponent) - if pat.tex_pattern_type == 'magnet' and pat.magnet_style == 'mandel': - texStrg+="%s mandel %s \n"%(pat.magnet_type, pat.f_iter) - if pat.tex_pattern_type == 'magnet' and pat.magnet_style == 'julia': - texStrg+="%s julia <%.4g, %.4g> %s\n"%(pat.magnet_type, pat.julia_complex_1, pat.julia_complex_2, pat.f_iter) - if pat.tex_pattern_type in {'mandel', 'julia', 'magnet'}: - texStrg+="interior %s, %.4g\n"%(pat.f_ior, pat.f_ior_fac) - texStrg+="exterior %s, %.4g\n"%(pat.f_eor, pat.f_eor_fac) - if pat.tex_pattern_type == 'gradient': - texStrg+="<%s, %s, %s> \n"%(pat.grad_orient_x, pat.grad_orient_y, pat.grad_orient_z) - if pat.tex_pattern_type == 'pavement': - numTiles=pat.pave_tiles - numPattern=1 - if pat.pave_sides == '4' and pat.pave_tiles == 3: - numPattern = pat.pave_pat_2 - if pat.pave_sides == '6' and pat.pave_tiles == 3: - numPattern = pat.pave_pat_3 - if pat.pave_sides == '3' and pat.pave_tiles == 4: - numPattern = pat.pave_pat_3 - if pat.pave_sides == '3' and pat.pave_tiles == 5: - numPattern = pat.pave_pat_4 - if pat.pave_sides == '4' and pat.pave_tiles == 4: - numPattern = pat.pave_pat_5 - if pat.pave_sides == '6' and pat.pave_tiles == 4: - numPattern = pat.pave_pat_7 - if pat.pave_sides == '4' and pat.pave_tiles == 5: - numPattern = pat.pave_pat_12 - if pat.pave_sides == '3' and pat.pave_tiles == 6: - numPattern = pat.pave_pat_12 - if pat.pave_sides == '6' and pat.pave_tiles == 5: - numPattern = pat.pave_pat_22 - if pat.pave_sides == '4' and pat.pave_tiles == 6: - numPattern = pat.pave_pat_35 - if pat.pave_sides == '6' and pat.pave_tiles == 6: - numTiles = 5 - texStrg+="number_of_sides %s number_of_tiles %s pattern %s form %s \n"%(pat.pave_sides, numTiles, numPattern, pat.pave_form) - ################ functions ##################################################################################################### - if pat.tex_pattern_type == 'function': - texStrg+="{ %s"%pat.func_list - texStrg+="(x" + text_strg += "}\n\n" + + text_strg += "#declare f%s=\n" % pat_name + text_strg += "function{pigment{%s}}\n" % pat_name + text_strg += "\n" + + elif pat.tex_pattern_type != "emulator": + text_strg += "pigment {\n" + text_strg += "%s\n" % pat.tex_pattern_type + if pat.tex_pattern_type == "agate": + text_strg += "agate_turb %.4g\n" % pat.modifier_turbulence + if pat.tex_pattern_type in {"spiral1", "spiral2", "tiling"}: + text_strg += "%s\n" % pat.modifier_numbers + if pat.tex_pattern_type == "quilted": + text_strg += "control0 %s control1 %s\n" % ( + pat.modifier_control0, + pat.modifier_control1, + ) + if pat.tex_pattern_type == "mandel": + text_strg += "%s exponent %s \n" % (pat.f_iter, pat.f_exponent) + if pat.tex_pattern_type == "julia": + text_strg += "<%.4g, %.4g> %s exponent %s \n" % ( + pat.julia_complex_1, + pat.julia_complex_2, + pat.f_iter, + pat.f_exponent, + ) + if pat.tex_pattern_type == "magnet" and pat.magnet_style == "mandel": + text_strg += "%s mandel %s \n" % (pat.magnet_type, pat.f_iter) + if pat.tex_pattern_type == "magnet" and pat.magnet_style == "julia": + text_strg += "%s julia <%.4g, %.4g> %s\n" % ( + pat.magnet_type, + pat.julia_complex_1, + pat.julia_complex_2, + pat.f_iter, + ) + if pat.tex_pattern_type in {"mandel", "julia", "magnet"}: + text_strg += "interior %s, %.4g\n" % (pat.f_ior, pat.f_ior_fac) + text_strg += "exterior %s, %.4g\n" % (pat.f_eor, pat.f_eor_fac) + if pat.tex_pattern_type == "gradient": + text_strg += "<%s, %s, %s> \n" % ( + pat.grad_orient_x, + pat.grad_orient_y, + pat.grad_orient_z, + ) + if pat.tex_pattern_type == "pavement": + num_tiles = pat.pave_tiles + num_pattern = 1 + if pat.pave_sides == "4" and pat.pave_tiles == 3: + num_pattern = pat.pave_pat_2 + if pat.pave_sides == "6" and pat.pave_tiles == 3: + num_pattern = pat.pave_pat_3 + if pat.pave_sides == "3" and pat.pave_tiles == 4: + num_pattern = pat.pave_pat_3 + if pat.pave_sides == "3" and pat.pave_tiles == 5: + num_pattern = pat.pave_pat_4 + if pat.pave_sides == "4" and pat.pave_tiles == 4: + num_pattern = pat.pave_pat_5 + if pat.pave_sides == "6" and pat.pave_tiles == 4: + num_pattern = pat.pave_pat_7 + if pat.pave_sides == "4" and pat.pave_tiles == 5: + num_pattern = pat.pave_pat_12 + if pat.pave_sides == "3" and pat.pave_tiles == 6: + num_pattern = pat.pave_pat_12 + if pat.pave_sides == "6" and pat.pave_tiles == 5: + num_pattern = pat.pave_pat_22 + if pat.pave_sides == "4" and pat.pave_tiles == 6: + num_pattern = pat.pave_pat_35 + if pat.pave_sides == "6" and pat.pave_tiles == 6: + num_tiles = 5 + text_strg += "number_of_sides %s number_of_tiles %s pattern %s form %s \n" % ( + pat.pave_sides, + num_tiles, + num_pattern, + pat.pave_form, + ) + ################ functions ########################################################## + if pat.tex_pattern_type == "function": + text_strg += "{ %s" % pat.func_list + text_strg += "(x" if pat.func_plus_x != "NONE": - if pat.func_plus_x =='increase': - texStrg+="*" - if pat.func_plus_x =='plus': - texStrg+="+" - texStrg+="%.4g"%pat.func_x - texStrg+=",y" + if pat.func_plus_x == "increase": + text_strg += "*" + if pat.func_plus_x == "plus": + text_strg += "+" + text_strg += "%.4g" % pat.func_x + text_strg += ",y" if pat.func_plus_y != "NONE": - if pat.func_plus_y =='increase': - texStrg+="*" - if pat.func_plus_y =='plus': - texStrg+="+" - texStrg+="%.4g"%pat.func_y - texStrg+=",z" + if pat.func_plus_y == "increase": + text_strg += "*" + if pat.func_plus_y == "plus": + text_strg += "+" + text_strg += "%.4g" % pat.func_y + text_strg += ",z" if pat.func_plus_z != "NONE": - if pat.func_plus_z =='increase': - texStrg+="*" - if pat.func_plus_z =='plus': - texStrg+="+" - texStrg+="%.4g"%pat.func_z + if pat.func_plus_z == "increase": + text_strg += "*" + if pat.func_plus_z == "plus": + text_strg += "+" + text_strg += "%.4g" % pat.func_z sort = -1 - if pat.func_list in {"f_comma","f_crossed_trough","f_cubic_saddle","f_cushion","f_devils_curve", - "f_enneper","f_glob","f_heart","f_hex_x","f_hex_y","f_hunt_surface", - "f_klein_bottle","f_kummer_surface_v1","f_lemniscate_of_gerono","f_mitre", - "f_nodal_cubic","f_noise_generator","f_odd","f_paraboloid","f_pillow", - "f_piriform","f_quantum","f_quartic_paraboloid","f_quartic_saddle", - "f_sphere","f_steiners_roman","f_torus_gumdrop","f_umbrella"}: + if pat.func_list in { + "f_comma", + "f_crossed_trough", + "f_cubic_saddle", + "f_cushion", + "f_devils_curve", + "f_enneper", + "f_glob", + "f_heart", + "f_hex_x", + "f_hex_y", + "f_hunt_surface", + "f_klein_bottle", + "f_kummer_surface_v1", + "f_lemniscate_of_gerono", + "f_mitre", + "f_nodal_cubic", + "f_noise_generator", + "f_odd", + "f_paraboloid", + "f_pillow", + "f_piriform", + "f_quantum", + "f_quartic_paraboloid", + "f_quartic_saddle", + "f_sphere", + "f_steiners_roman", + "f_torus_gumdrop", + "f_umbrella", + }: sort = 0 - if pat.func_list in {"f_bicorn","f_bifolia","f_boy_surface","f_superellipsoid","f_torus"}: + if pat.func_list in { + "f_bicorn", + "f_bifolia", + "f_boy_surface", + "f_superellipsoid", + "f_torus", + }: sort = 1 - if pat.func_list in {"f_ellipsoid","f_folium_surface","f_hyperbolic_torus", - "f_kampyle_of_eudoxus","f_parabolic_torus","f_quartic_cylinder","f_torus2"}: + if pat.func_list in { + "f_ellipsoid", + "f_folium_surface", + "f_hyperbolic_torus", + "f_kampyle_of_eudoxus", + "f_parabolic_torus", + "f_quartic_cylinder", + "f_torus2", + }: sort = 2 - if pat.func_list in {"f_blob2","f_cross_ellipsoids","f_flange_cover","f_isect_ellipsoids", - "f_kummer_surface_v2","f_ovals_of_cassini","f_rounded_box","f_spikes_2d","f_strophoid"}: + if pat.func_list in { + "f_blob2", + "f_cross_ellipsoids", + "f_flange_cover", + "f_isect_ellipsoids", + "f_kummer_surface_v2", + "f_ovals_of_cassini", + "f_rounded_box", + "f_spikes_2d", + "f_strophoid", + }: sort = 3 - if pat.func_list in {"f_algbr_cyl1","f_algbr_cyl2","f_algbr_cyl3","f_algbr_cyl4","f_blob","f_mesh1","f_poly4","f_spikes"}: + if pat.func_list in { + "f_algbr_cyl1", + "f_algbr_cyl2", + "f_algbr_cyl3", + "f_algbr_cyl4", + "f_blob", + "f_mesh1", + "f_poly4", + "f_spikes", + }: sort = 4 - if pat.func_list in {"f_devils_curve_2d","f_dupin_cyclid","f_folium_surface_2d","f_hetero_mf","f_kampyle_of_eudoxus_2d", - "f_lemniscate_of_gerono_2d","f_polytubes","f_ridge","f_ridged_mf","f_spiral","f_witch_of_agnesi"}: + if pat.func_list in { + "f_devils_curve_2d", + "f_dupin_cyclid", + "f_folium_surface_2d", + "f_hetero_mf", + "f_kampyle_of_eudoxus_2d", + "f_lemniscate_of_gerono_2d", + "f_polytubes", + "f_ridge", + "f_ridged_mf", + "f_spiral", + "f_witch_of_agnesi", + }: sort = 5 - if pat.func_list in {"f_helix1","f_helix2","f_piriform_2d","f_strophoid_2d"}: + if pat.func_list in {"f_helix1", "f_helix2", "f_piriform_2d", "f_strophoid_2d"}: sort = 6 if pat.func_list == "f_helical_torus": sort = 7 if sort > -1: - texStrg+=",%.4g"%pat.func_P0 + text_strg += ",%.4g" % pat.func_P0 if sort > 0: - texStrg+=",%.4g"%pat.func_P1 + text_strg += ",%.4g" % pat.func_P1 if sort > 1: - texStrg+=",%.4g"%pat.func_P2 + text_strg += ",%.4g" % pat.func_P2 if sort > 2: - texStrg+=",%.4g"%pat.func_P3 + text_strg += ",%.4g" % pat.func_P3 if sort > 3: - texStrg+=",%.4g"%pat.func_P4 + text_strg += ",%.4g" % pat.func_P4 if sort > 4: - texStrg+=",%.4g"%pat.func_P5 + text_strg += ",%.4g" % pat.func_P5 if sort > 5: - texStrg+=",%.4g"%pat.func_P6 + text_strg += ",%.4g" % pat.func_P6 if sort > 6: - texStrg+=",%.4g"%pat.func_P7 - texStrg+=",%.4g"%pat.func_P8 - texStrg+=",%.4g"%pat.func_P9 - texStrg+=")}\n" + text_strg += ",%.4g" % pat.func_P7 + text_strg += ",%.4g" % pat.func_P8 + text_strg += ",%.4g" % pat.func_P9 + text_strg += ")}\n" ############## end functions ############################################################### - if pat.tex_pattern_type not in {'checker', 'hexagon', 'square', 'triangular', 'brick'}: - texStrg+="color_map {\n" - numColor=0 - if tex.use_color_ramp == True: + if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}: + text_strg += "color_map {\n" + num_color = 0 + if tex.use_color_ramp: for el in tex.color_ramp.elements: - numColor+=1 + num_color += 1 pos = el.position - col=el.color - colR,colG,colB,colA = col[0],col[1],col[2],1-col[3] - if pat.tex_pattern_type not in {'checker', 'hexagon', 'square', 'triangular', 'brick'} : - texStrg+="[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n"%(pos,colR,colG,colB,colA) - if pat.tex_pattern_type in {'brick','checker'} and numColor < 3: - texStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'hexagon' and numColor < 4 : - texStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'square' and numColor < 5 : - texStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) - if pat.tex_pattern_type == 'triangular' and numColor < 7 : - texStrg+="color rgbf<%.4g,%.4g,%.4g,%.4g> \n"%(colR,colG,colB,colA) + col = el.color + col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3] + if pat.tex_pattern_type not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + text_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % ( + pos, + col_r, + col_g, + col_b, + col_a, + ) + if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3: + text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "hexagon" and num_color < 4: + text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "square" and num_color < 5: + text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) + if pat.tex_pattern_type == "triangular" and num_color < 7: + text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a) else: - texStrg+="[0 color rgbf<0,0,0,1>]\n" - texStrg+="[1 color rgbf<1,1,1,0>]\n" - if pat.tex_pattern_type not in {'checker', 'hexagon', 'square', 'triangular', 'brick'} : - texStrg+="} \n" - if pat.tex_pattern_type == 'brick': - texStrg+="brick_size <%.4g, %.4g, %.4g> mortar %.4g \n"%(pat.brick_size_x, pat.brick_size_y, pat.brick_size_z, pat.brick_mortar) - texStrg+="%s \n"%mappingDif - texStrg+="rotate <%.4g,%.4g,%.4g> \n"%(pat.tex_rot_x, pat.tex_rot_y, pat.tex_rot_z) - texStrg+="turbulence <%.4g,%.4g,%.4g> \n"%(pat.warp_turbulence_x, pat.warp_turbulence_y, pat.warp_turbulence_z) - texStrg+="octaves %s \n"%pat.modifier_octaves - texStrg+="lambda %.4g \n"%pat.modifier_lambda - texStrg+="omega %.4g \n"%pat.modifier_omega - texStrg+="frequency %.4g \n"%pat.modifier_frequency - texStrg+="phase %.4g \n"%pat.modifier_phase - texStrg+="}\n\n" - texStrg+="#declare f%s=\n"%PATname - texStrg+="function{pigment{%s}}\n"%PATname - texStrg+="\n" - return(texStrg) - - -def writeTextureInfluence(using_uberpov, mater, materialNames, LocalMaterialNames, path_image, lampCount, - imageFormat, imgMap, imgMapTransforms, tabWrite, comments, - string_strip_hyphen, safety, col, os, preview_dir, unpacked_images): - """Translate Blender texture influences to various POV texture tricks and write to pov file.""" - - material_finish = materialNames[mater.name] - if mater.pov.use_transparency: - trans = 1.0 - mater.pov.alpha - else: - trans = 0.0 - if ((mater.pov.specular_color.s == 0.0) or (mater.pov.diffuse_shader == 'MINNAERT')): - # No layered texture because of aoi pattern used for minnaert and pov can't layer patterned - colored_specular_found = False - else: - colored_specular_found = True - - if mater.pov.use_transparency and mater.pov.transparency_method == 'RAYTRACE': - povFilter = mater.pov_raytrace_transparency.filter * (1.0 - mater.pov.alpha) - trans = (1.0 - mater.pov.alpha) - povFilter - else: - povFilter = 0.0 - - ##############SF - texturesDif = "" - texturesSpec = "" - texturesNorm = "" - texturesAlpha = "" - #proceduralFlag=False - tmpidx = -1 - for t in mater.pov_texture_slots: - - - tmpidx += 1 - # index = mater.pov.active_texture_index - slot = mater.pov_texture_slots[tmpidx] # [index] - povtex = slot.texture # slot.name - tex = bpy.data.textures[povtex] - - if t and (t.use and (tex is not None)): - # 'NONE' ('NONE' type texture is different from no texture covered above) - if (tex.type == 'NONE' and tex.pov.tex_pattern_type == 'emulator'): - continue # move to next slot - # PROCEDURAL - elif (tex.type != 'IMAGE' and tex.type != 'NONE'): - proceduralFlag=True - image_filename = "PAT_%s"%string_strip_hyphen(bpy.path.clean_name(tex.name)) - if image_filename: - if t.use_map_color_diffuse: - texturesDif = image_filename - # colvalue = t.default_value # UNUSED - t_dif = t - if t_dif.texture.pov.tex_gamma_enable: - imgGamma = (" gamma %.3g " % t_dif.texture.pov.tex_gamma_value) - if t.use_map_specular or t.use_map_raymir: - texturesSpec = image_filename - # colvalue = t.default_value # UNUSED - t_spec = t - if t.use_map_normal: - texturesNorm = image_filename - # colvalue = t.normal_factor/10 # UNUSED - #textNormName=tex.image.name + ".normal" - #was the above used? --MR - t_nor = t - if t.use_map_alpha: - texturesAlpha = image_filename - # colvalue = t.alpha_factor * 10.0 # UNUSED - #textDispName=tex.image.name + ".displ" - #was the above used? --MR - t_alpha = t - - # RASTER IMAGE - elif (tex.type == 'IMAGE' and tex.image and tex.pov.tex_pattern_type == 'emulator'): - proceduralFlag=False - #PACKED - if tex.image.packed_file: - orig_image_filename=tex.image.filepath_raw - unpackedfilename= os.path.join(preview_dir,("unpacked_img_"+(string_strip_hyphen(bpy.path.clean_name(tex.name))))) - if not os.path.exists(unpackedfilename): - # record which images that were newly copied and can be safely - # cleaned up - unpacked_images.append(unpackedfilename) - tex.image.filepath_raw=unpackedfilename - tex.image.save() - image_filename = unpackedfilename.replace("\\","/") - # .replace("\\","/") to get only forward slashes as it's what POV prefers, - # even on windows - tex.image.filepath_raw=orig_image_filename - #FILE - else: - image_filename = path_image(tex.image) - # IMAGE SEQUENCE BEGINS - if image_filename: - if bpy.data.images[tex.image.name].source == 'SEQUENCE': - korvaa = "." + str(tex.image_user.frame_offset + 1).zfill(3) + "." - image_filename = image_filename.replace(".001.", korvaa) - print(" seq debug ") - print(image_filename) - # IMAGE SEQUENCE ENDS - imgGamma = "" - if image_filename: - texdata = bpy.data.textures[t.texture] - if t.use_map_color_diffuse: - texturesDif = image_filename - # colvalue = t.default_value # UNUSED - t_dif = t - print (texdata) - if texdata.pov.tex_gamma_enable: - imgGamma = (" gamma %.3g " % t_dif.texture.pov.tex_gamma_value) - if t.use_map_specular or t.use_map_raymir: - texturesSpec = image_filename - # colvalue = t.default_value # UNUSED - t_spec = t - if t.use_map_normal: - texturesNorm = image_filename - # colvalue = t.normal_factor/10 # UNUSED - #textNormName=tex.image.name + ".normal" - #was the above used? --MR - t_nor = t - if t.use_map_alpha: - texturesAlpha = image_filename - # colvalue = t.alpha_factor * 10.0 # UNUSED - #textDispName=tex.image.name + ".displ" - #was the above used? --MR - t_alpha = t - - #################################################################################### - - - tabWrite("\n") - # THIS AREA NEEDS TO LEAVE THE TEXTURE OPEN UNTIL ALL MAPS ARE WRITTEN DOWN. - # --MR - currentMatName = string_strip_hyphen(materialNames[mater.name]) - LocalMaterialNames.append(currentMatName) - tabWrite("\n#declare MAT_%s = \ntexture{\n" % currentMatName) - ################################################################################ - - if mater.pov.replacement_text != "": - tabWrite("%s\n" % mater.pov.replacement_text) - ################################################################################# - # XXX TODO: replace by new POV MINNAERT rather than aoi - if mater.pov.diffuse_shader == 'MINNAERT': - tabWrite("\n") - tabWrite("aoi\n") - tabWrite("texture_map {\n") - tabWrite("[%.3g finish {diffuse %.3g}]\n" % \ - (mater.darkness / 2.0, 2.0 - mater.darkness)) - tabWrite("[%.3g\n" % (1.0 - (mater.darkness / 2.0))) - - if mater.pov.diffuse_shader == 'FRESNEL': - # For FRESNEL diffuse in POV, we'll layer slope patterned textures - # with lamp vector as the slope vector and nest one slope per lamp - # into each texture map's entry. - - c = 1 - while (c <= lampCount): - tabWrite("slope { lampTarget%s }\n" % (c)) - tabWrite("texture_map {\n") - # Diffuse Fresnel value and factor go up to five, - # other kind of values needed: used the number 5 below to remap - tabWrite("[%.3g finish {diffuse %.3g}]\n" % \ - ((5.0 - mater.diffuse_fresnel) / 5, - (mater.diffuse_intensity * - ((5.0 - mater.diffuse_fresnel_factor) / 5)))) - tabWrite("[%.3g\n" % ((mater.diffuse_fresnel_factor / 5) * - (mater.diffuse_fresnel / 5.0))) - c += 1 - - # if shader is a 'FRESNEL' or 'MINNAERT': slope pigment pattern or aoi - # and texture map above, the rest below as one of its entry - - if texturesSpec != "" or texturesAlpha != "": - if texturesSpec != "": - # tabWrite("\n") - tabWrite("pigment_pattern {\n") - - mappingSpec =imgMapTransforms(t_spec) - if texturesSpec and texturesSpec.startswith("PAT_"): - tabWrite("function{f%s(x,y,z).grey}\n" %texturesSpec) - tabWrite("%s\n" % mappingSpec) - else: - - tabWrite("uv_mapping image_map{%s \"%s\" %s}\n" % \ - (imageFormat(texturesSpec), texturesSpec, imgMap(t_spec))) - tabWrite("%s\n" % mappingSpec) - tabWrite("}\n") - tabWrite("texture_map {\n") - tabWrite("[0 \n") - - if texturesDif == "": - if texturesAlpha != "": - tabWrite("\n") + text_strg += "[0 color rgbf<0,0,0,1>]\n" + text_strg += "[1 color rgbf<1,1,1,0>]\n" + if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}: + text_strg += "} \n" + if pat.tex_pattern_type == "brick": + text_strg += "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n" % ( + pat.brick_size_x, + pat.brick_size_y, + pat.brick_size_z, + pat.brick_mortar, + ) + text_strg += "%s \n" % mapping_dif + text_strg += "rotate <%.4g,%.4g,%.4g> \n" % (pat.tex_rot_x, pat.tex_rot_y, pat.tex_rot_z) + text_strg += "turbulence <%.4g,%.4g,%.4g> \n" % ( + pat.warp_turbulence_x, + pat.warp_turbulence_y, + pat.warp_turbulence_z, + ) + text_strg += "octaves %s \n" % pat.modifier_octaves + text_strg += "lambda %.4g \n" % pat.modifier_lambda + text_strg += "omega %.4g \n" % pat.modifier_omega + text_strg += "frequency %.4g \n" % pat.modifier_frequency + text_strg += "phase %.4g \n" % pat.modifier_phase + text_strg += "}\n\n" + text_strg += "#declare f%s=\n" % pat_name + text_strg += "function{pigment{%s}}\n" % pat_name + text_strg += "\n" + return text_strg - mappingAlpha = imgMapTransforms(t_alpha) - - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("function{f%s(x,y,z).transmit}%s\n" %(texturesAlpha, mappingAlpha)) - else: - - tabWrite("pigment {pigment_pattern {uv_mapping image_map" \ - "{%s \"%s\" %s}%s" % \ - (imageFormat(texturesAlpha), texturesAlpha, - imgMap(t_alpha), mappingAlpha)) - tabWrite("}\n") - tabWrite("pigment_map {\n") - tabWrite("[0 color rgbft<0,0,0,1,1>]\n") - tabWrite("[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n" % \ - (col[0], col[1], col[2], povFilter, trans)) - tabWrite("}\n") - tabWrite("}\n") - - else: - - tabWrite("pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n" % \ - (col[0], col[1], col[2], povFilter, trans)) - - if texturesSpec != "": - # Level 1 is no specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=1))) - - else: - # Level 2 is translated spec - tabWrite("finish {%s}\n" % (safety(material_finish, Level=2))) - - else: - mappingDif = imgMapTransforms(t_dif) - - if texturesAlpha != "": - mappingAlpha = imgMapTransforms(t_alpha) - - tabWrite("pigment {\n") - tabWrite("pigment_pattern {\n") - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("function{f%s(x,y,z).transmit}%s\n" %(texturesAlpha, mappingAlpha)) - else: - tabWrite("uv_mapping image_map{%s \"%s\" %s}%s}\n" % \ - (imageFormat(texturesAlpha), texturesAlpha, - imgMap(t_alpha), mappingAlpha)) - tabWrite("pigment_map {\n") - tabWrite("[0 color rgbft<0,0,0,1,1>]\n") - #if texturesAlpha and texturesAlpha.startswith("PAT_"): - #tabWrite("[1 pigment{%s}]\n" %texturesDif) - if texturesDif and not texturesDif.startswith("PAT_"): - tabWrite("[1 uv_mapping image_map {%s \"%s\" %s} %s]\n" % \ - (imageFormat(texturesDif), texturesDif, - (imgGamma + imgMap(t_dif)), mappingDif)) - elif texturesDif and texturesDif.startswith("PAT_"): - tabWrite("[1 %s]\n" %texturesDif) - tabWrite("}\n") - tabWrite("}\n") - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("}\n") - - else: - if texturesDif and texturesDif.startswith("PAT_"): - tabWrite("pigment{%s}\n" %texturesDif) - else: - tabWrite("pigment {uv_mapping image_map {%s \"%s\" %s}%s}\n" % \ - (imageFormat(texturesDif), texturesDif, - (imgGamma + imgMap(t_dif)), mappingDif)) - - if texturesSpec != "": - # Level 1 is no specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=1))) - - else: - # Level 2 is translated specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=2))) - - ## scale 1 rotate y*0 - #imageMap = ("{image_map {%s \"%s\" %s }\n" % \ - # (imageFormat(textures),textures,imgMap(t_dif))) - #tabWrite("uv_mapping pigment %s} %s finish {%s}\n" % \ - # (imageMap,mapping,safety(material_finish))) - #tabWrite("pigment {uv_mapping image_map {%s \"%s\" %s}%s} " \ - # "finish {%s}\n" % \ - # (imageFormat(texturesDif), texturesDif, imgMap(t_dif), - # mappingDif, safety(material_finish))) - if texturesNorm != "": - ## scale 1 rotate y*0 - - mappingNor =imgMapTransforms(t_nor) - - if texturesNorm and texturesNorm.startswith("PAT_"): - tabWrite("normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n" %(texturesNorm, ( - t_nor.normal_factor * 9.5), mappingNor)) - else: - tabWrite("normal {\n") - # XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials - if (mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov): - tabWrite("average\n") - tabWrite("normal_map{\n") - # 0.5 for entries below means a 50 percent mix - # between the micro normal and user bump map - # order seems indifferent as commutative - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(10/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[1.0 ") # Proceed with user bump... - tabWrite("uv_mapping bump_map " \ - "{%s \"%s\" %s bump_size %.4g }%s" % \ - (imageFormat(texturesNorm), texturesNorm, imgMap(t_nor), - ( - t_nor.normal_factor * 9.5), mappingNor)) - # ...Then close its last entry and the the normal_map itself - if (mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov): - tabWrite("]}}\n") - else: - tabWrite("]}\n") - if texturesSpec != "": - tabWrite("]\n") - ##################Second index for mapping specular max value############### - tabWrite("[1 \n") - - if texturesDif == "" and mater.pov.replacement_text == "": - if texturesAlpha != "": - mappingAlpha = imgMapTransforms(t_alpha) - - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("function{f%s(x,y,z).transmit %s}\n" %(texturesAlpha, mappingAlpha)) - else: - tabWrite("pigment {pigment_pattern {uv_mapping image_map" \ - "{%s \"%s\" %s}%s}\n" % \ - (imageFormat(texturesAlpha), texturesAlpha, imgMap(t_alpha), - mappingAlpha)) - tabWrite("pigment_map {\n") - tabWrite("[0 color rgbft<0,0,0,1,1>]\n") - tabWrite("[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n" % \ - (col[0], col[1], col[2], povFilter, trans)) - tabWrite("}\n") - tabWrite("}\n") - - else: - tabWrite("pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n" % \ - (col[0], col[1], col[2], povFilter, trans)) - - - if texturesSpec != "": - # Level 3 is full specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=3))) - - if mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov: - tabWrite("normal {\n") - tabWrite("average\n") - tabWrite("normal_map{\n") - # 0.5 for entries below means a 50 percent mix - # between the micro normal and user bump map - # order seems indifferent as commutative - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(10/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - #XXX IF USER BUMP_MAP - if texturesNorm != "": - tabWrite("[1.0 ") # Blurry reflection or not Proceed with user bump in either case... - tabWrite("uv_mapping bump_map " \ - "{%s \"%s\" %s bump_size %.4g }%s]\n" % \ - (imageFormat(texturesNorm), texturesNorm, imgMap(t_nor), - ( - t_nor.normal_factor * 9.5), mappingNor)) - # ...Then close the normal_map itself if blurry reflection - if mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov: - tabWrite("}}\n") - else: - tabWrite("}\n") - elif colored_specular_found: - # Level 1 is no specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=1))) - - else: - # Level 2 is translated specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=2))) - - elif mater.pov.replacement_text == "": - mappingDif = imgMapTransforms(t_dif) - - if texturesAlpha != "": - - mappingAlpha = imgMapTransforms(t_alpha) - - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("pigment{pigment_pattern {function{f%s(x,y,z).transmit}%s}\n" %(texturesAlpha, mappingAlpha)) - else: - tabWrite("pigment {pigment_pattern {uv_mapping image_map" \ - "{%s \"%s\" %s}%s}\n" % \ - (imageFormat(texturesAlpha), texturesAlpha, imgMap(t_alpha), - mappingAlpha)) - tabWrite("pigment_map {\n") - tabWrite("[0 color rgbft<0,0,0,1,1>]\n") - if texturesAlpha and texturesAlpha.startswith("PAT_"): - tabWrite("[1 function{f%s(x,y,z).transmit}%s]\n" %(texturesAlpha, mappingAlpha)) - elif texturesDif and not texturesDif.startswith("PAT_"): - tabWrite("[1 uv_mapping image_map {%s \"%s\" %s} %s]\n" % \ - (imageFormat(texturesDif), texturesDif, - (imgMap(t_dif) + imgGamma), mappingDif)) - elif texturesDif and texturesDif.startswith("PAT_"): - tabWrite("[1 %s %s]\n" %(texturesDif, mappingDif)) - tabWrite("}\n") - tabWrite("}\n") - - else: - if texturesDif and texturesDif.startswith("PAT_"): - tabWrite("pigment{%s %s}\n" %(texturesDif, mappingDif)) - else: - tabWrite("pigment {\n") - tabWrite("uv_mapping image_map {\n") - #tabWrite("%s \"%s\" %s}%s\n" % \ - # (imageFormat(texturesDif), texturesDif, - # (imgGamma + imgMap(t_dif)),mappingDif)) - tabWrite("%s \"%s\" \n" % (imageFormat(texturesDif), texturesDif)) - tabWrite("%s\n" % (imgGamma + imgMap(t_dif))) - tabWrite("}\n") - tabWrite("%s\n" % mappingDif) - tabWrite("}\n") - - if texturesSpec != "": - # Level 3 is full specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=3))) - else: - # Level 2 is translated specular - tabWrite("finish {%s}\n" % (safety(material_finish, Level=2))) - - ## scale 1 rotate y*0 - #imageMap = ("{image_map {%s \"%s\" %s }" % \ - # (imageFormat(textures), textures,imgMap(t_dif))) - #tabWrite("\n\t\t\tuv_mapping pigment %s} %s finish {%s}" % \ - # (imageMap, mapping, safety(material_finish))) - #tabWrite("\n\t\t\tpigment {uv_mapping image_map " \ - # "{%s \"%s\" %s}%s} finish {%s}" % \ - # (imageFormat(texturesDif), texturesDif,imgMap(t_dif), - # mappingDif, safety(material_finish))) - if texturesNorm != "" and mater.pov.replacement_text == "": - - - mappingNor =imgMapTransforms(t_nor) - - if texturesNorm and texturesNorm.startswith("PAT_"): - tabWrite("normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n" %(texturesNorm, ( - t_nor.normal_factor * 9.5), mappingNor)) - else: - tabWrite("normal {\n") - # XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials - if mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov: - tabWrite("average\n") - tabWrite("normal_map{\n") - # 0.5 for entries below means a 50 percent mix - # between the micro normal and user bump map - # order seems indifferent as commutative - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(10/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" %((10/(mater.pov_raytrace_mirror.gloss_factor+0.01)),(1/(mater.pov_raytrace_mirror.gloss_samples+0.001)))) # micronormals blurring - tabWrite("[1.0 ") # Blurry reflection or not Proceed with user bump in either case... - tabWrite("uv_mapping bump_map " \ - "{%s \"%s\" %s bump_size %.4g }%s]\n" % \ - (imageFormat(texturesNorm), texturesNorm, imgMap(t_nor), - ( - t_nor.normal_factor * 9.5), mappingNor)) - # ...Then close the normal_map itself if blurry reflection - if mater.pov_raytrace_mirror.use and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov: - tabWrite("}}\n") - else: - tabWrite("}\n") - if texturesSpec != "" and mater.pov.replacement_text == "": - tabWrite("]\n") - - tabWrite("}\n") - - #End of slope/ior texture_map - if mater.pov.diffuse_shader == 'MINNAERT' and mater.pov.replacement_text == "": - tabWrite("]\n") - tabWrite("}\n") - if mater.pov.diffuse_shader == 'FRESNEL' and mater.pov.replacement_text == "": - c = 1 - while (c <= lampCount): - tabWrite("]\n") - tabWrite("}\n") - c += 1 - - - - # Close first layer of POV "texture" (Blender material) - tabWrite("}\n") - - if ((mater.pov.specular_color.s > 0.0) and (mater.pov.diffuse_shader != 'MINNAERT')): - - colored_specular_found = True - else: - colored_specular_found = False - - # Write another layered texture using invisible diffuse and metallic trick - # to emulate colored specular highlights - special_texture_found = False - tmpidx = -1 - for t in mater.pov_texture_slots: - tmpidx += 1 - # index = mater.pov.active_texture_index - slot = mater.pov_texture_slots[tmpidx] # [index] - povtex = slot.texture # slot.name - tex = bpy.data.textures[povtex] - if(t and t.use and ((tex.type == 'IMAGE' and tex.image) or tex.type != 'IMAGE') and - (t.use_map_specular or t.use_map_raymir)): - # Specular mapped textures would conflict with colored specular - # because POV can't layer over or under pigment patterned textures - special_texture_found = True - - if colored_specular_found and not special_texture_found: - if comments: - tabWrite(" // colored highlights with a stransparent metallic layer\n") - else: - tabWrite("\n") - - tabWrite("texture {\n") - tabWrite("pigment {rgbft<%.3g, %.3g, %.3g, 0, 1>}\n" % \ - (mater.pov.specular_color[0], mater.pov.specular_color[1], mater.pov.specular_color[2])) - tabWrite("finish {%s}\n" % (safety(material_finish, Level=2))) # Level 2 is translated spec - - texturesNorm = "" - for t in mater.pov_texture_slots: - - if t and tex.pov.tex_pattern_type != 'emulator': - proceduralFlag=True - image_filename = string_strip_hyphen(bpy.path.clean_name(tex.name)) - if (t and tex.type == 'IMAGE' and - t.use and tex.image and - tex.pov.tex_pattern_type == 'emulator'): - proceduralFlag=False - image_filename = path_image(tex.image) - imgGamma = "" - if image_filename: - if t.use_map_normal: - texturesNorm = image_filename - # colvalue = t.normal_factor/10 # UNUSED XXX *-9.5 ! - #textNormName=tex.image.name + ".normal" - #was the above used? --MR - t_nor = t - if proceduralFlag: - tabWrite("normal{function" \ - "{f%s(x,y,z).grey} bump_size %.4g}\n" % \ - (texturesNorm, - ( - t_nor.normal_factor * 9.5))) - else: - tabWrite("normal {uv_mapping bump_map " \ - "{%s \"%s\" %s bump_size %.4g }%s}\n" % \ - (imageFormat(texturesNorm), - texturesNorm, imgMap(t_nor), - ( - t_nor.normal_factor * 9.5), - mappingNor)) - - tabWrite("}\n") # THEN IT CAN CLOSE LAST LAYER OF TEXTURE def string_strip_hyphen(name): + """POV naming schemes like to conform to most restrictive charsets.""" return name.replace("-", "") + + # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -def write_nodes(scene,povMatName,ntree,file): - """translate Blender node trees to pov and write them to file""" - declareNodes=[] - scene=bpy.context.scene +def write_nodes(scene, pov_mat_name, ntree, file): + """Translate Blender node trees to pov and write them to file.""" + # such function local inlined import are official guidelines + # of Blender Foundation to lighten addons footprint at startup + from os import path + + declare_nodes = [] + scene = bpy.context.scene for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayFinishNode" and node.outputs["Finish"].is_linked: - file.write('#declare %s = finish {\n'%povNodeName) - emission=node.inputs["Emission"].default_value + file.write("#declare %s = finish {\n" % pov_node_name) + emission = node.inputs["Emission"].default_value if node.inputs["Emission"].is_linked: pass - file.write(' emission %.4g\n'%emission) + file.write(" emission %.4g\n" % emission) for link in ntree.links: if link.to_node == node: - if link.from_node.bl_idname == 'PovrayDiffuseNode': - intensity=0 - albedo="" - brilliance=0 - crand=0 + if link.from_node.bl_idname == "PovrayDiffuseNode": + intensity = 0 + albedo = "" + brilliance = 0 + crand = 0 if link.from_node.inputs["Intensity"].is_linked: pass else: - intensity=link.from_node.inputs["Intensity"].default_value + intensity = link.from_node.inputs["Intensity"].default_value if link.from_node.inputs["Albedo"].is_linked: pass else: - if link.from_node.inputs["Albedo"].default_value == True: + if link.from_node.inputs["Albedo"].default_value: albedo = "albedo" - file.write(' diffuse %s %.4g\n'%(albedo,intensity)) + file.write(" diffuse %s %.4g\n" % (albedo, intensity)) if link.from_node.inputs["Brilliance"].is_linked: pass else: - brilliance=link.from_node.inputs["Brilliance"].default_value - file.write(' brilliance %.4g\n'%brilliance) + brilliance = link.from_node.inputs["Brilliance"].default_value + file.write(" brilliance %.4g\n" % brilliance) if link.from_node.inputs["Crand"].is_linked: pass else: - crand=link.from_node.inputs["Crand"].default_value + crand = link.from_node.inputs["Crand"].default_value if crand > 0: - file.write(' crand %.4g\n'%crand) - + file.write(" crand %.4g\n" % crand) - if link.from_node.bl_idname == 'PovraySubsurfaceNode': + if link.from_node.bl_idname == "PovraySubsurfaceNode": if scene.povray.sslt_enable: energy = 0 r = g = b = 0 if link.from_node.inputs["Translucency"].is_linked: pass else: - r,g,b,a=link.from_node.inputs["Translucency"].default_value[:] + r, g, b, a = link.from_node.inputs["Translucency"].default_value[:] if link.from_node.inputs["Energy"].is_linked: pass else: - energy=link.from_node.inputs["Energy"].default_value - file.write(' subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n'%(r,g,b,energy)) - - - - if link.from_node.bl_idname in {'PovraySpecularNode','PovrayPhongNode'}: - intensity=0 - albedo="" - roughness=0 - metallic=0 - phong_size=0 - highlight="specular" + energy = link.from_node.inputs["Energy"].default_value + file.write( + " subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n" + % (r, g, b, energy) + ) + + if link.from_node.bl_idname in {"PovraySpecularNode", "PovrayPhongNode"}: + intensity = 0 + albedo = "" + roughness = 0 + metallic = 0 + phong_size = 0 + highlight = "specular" if link.from_node.inputs["Intensity"].is_linked: pass else: - intensity=link.from_node.inputs["Intensity"].default_value + intensity = link.from_node.inputs["Intensity"].default_value if link.from_node.inputs["Albedo"].is_linked: pass else: - if link.from_node.inputs["Albedo"].default_value == True: + if link.from_node.inputs["Albedo"].default_value: albedo = "albedo" - if link.from_node.bl_idname in {'PovrayPhongNode'}: - highlight="phong" - file.write(' %s %s %.4g\n'%(highlight,albedo,intensity)) + if link.from_node.bl_idname in {"PovrayPhongNode"}: + highlight = "phong" + file.write(" %s %s %.4g\n" % (highlight, albedo, intensity)) - if link.from_node.bl_idname in {'PovraySpecularNode'}: + if link.from_node.bl_idname in {"PovraySpecularNode"}: if link.from_node.inputs["Roughness"].is_linked: pass else: - roughness=link.from_node.inputs["Roughness"].default_value - file.write(' roughness %.6g\n'%roughness) + roughness = link.from_node.inputs["Roughness"].default_value + file.write(" roughness %.6g\n" % roughness) - if link.from_node.bl_idname in {'PovrayPhongNode'}: + if link.from_node.bl_idname in {"PovrayPhongNode"}: if link.from_node.inputs["Size"].is_linked: pass else: - phong_size=link.from_node.inputs["Size"].default_value - file.write(' phong_size %s\n'%phong_size) + phong_size = link.from_node.inputs["Size"].default_value + file.write(" phong_size %s\n" % phong_size) if link.from_node.inputs["Metallic"].is_linked: pass else: - metallic=link.from_node.inputs["Metallic"].default_value - file.write(' metallic %.4g\n'%metallic) - - if link.from_node.bl_idname in {'PovrayMirrorNode'}: - file.write(' reflection {\n') - color=None - exponent=0 - metallic=0 - falloff=0 - fresnel="" - conserve="" + metallic = link.from_node.inputs["Metallic"].default_value + file.write(" metallic %.4g\n" % metallic) + + if link.from_node.bl_idname in {"PovrayMirrorNode"}: + file.write(" reflection {\n") + color = None + exponent = 0 + metallic = 0 + falloff = 0 + fresnel = "" + conserve = "" if link.from_node.inputs["Color"].is_linked: pass else: - color=link.from_node.inputs["Color"].default_value[:] - file.write(' <%.4g,%.4g,%.4g>\n'%color) + color = link.from_node.inputs["Color"].default_value[:] + file.write(" <%.4g,%.4g,%.4g>\n" % color) if link.from_node.inputs["Exponent"].is_linked: pass else: - exponent=link.from_node.inputs["Exponent"].default_value - file.write(' exponent %.4g\n'%exponent) + exponent = link.from_node.inputs["Exponent"].default_value + file.write(" exponent %.4g\n" % exponent) if link.from_node.inputs["Falloff"].is_linked: pass else: - falloff=link.from_node.inputs["Falloff"].default_value - file.write(' falloff %.4g\n'%falloff) + falloff = link.from_node.inputs["Falloff"].default_value + file.write(" falloff %.4g\n" % falloff) if link.from_node.inputs["Metallic"].is_linked: pass else: - metallic=link.from_node.inputs["Metallic"].default_value - file.write(' metallic %.4g'%metallic) + metallic = link.from_node.inputs["Metallic"].default_value + file.write(" metallic %.4g" % metallic) if link.from_node.inputs["Fresnel"].is_linked: pass else: - if link.from_node.inputs["Fresnel"].default_value==True: - fresnel="fresnel" + if link.from_node.inputs["Fresnel"].default_value: + fresnel = "fresnel" if link.from_node.inputs["Conserve energy"].is_linked: pass else: - if link.from_node.inputs["Conserve energy"].default_value==True: - conserve="conserve_energy" + if link.from_node.inputs["Conserve energy"].default_value: + conserve = "conserve_energy" - file.write(' %s}\n %s\n'%(fresnel,conserve)) + file.write(" %s}\n %s\n" % (fresnel, conserve)) - if link.from_node.bl_idname == 'PovrayAmbientNode': - ambient=(0,0,0) + if link.from_node.bl_idname == "PovrayAmbientNode": + ambient = (0, 0, 0) if link.from_node.inputs["Ambient"].is_linked: pass else: - ambient=link.from_node.inputs["Ambient"].default_value[:] - file.write(' ambient <%.4g,%.4g,%.4g>\n'%ambient) - - if link.from_node.bl_idname in {'PovrayIridescenceNode'}: - file.write(' irid {\n') - amount=0 - thickness=0 - turbulence=0 + ambient = link.from_node.inputs["Ambient"].default_value[:] + file.write(" ambient <%.4g,%.4g,%.4g>\n" % ambient) + + if link.from_node.bl_idname in {"PovrayIridescenceNode"}: + file.write(" irid {\n") + amount = 0 + thickness = 0 + turbulence = 0 if link.from_node.inputs["Amount"].is_linked: pass else: - amount=link.from_node.inputs["Amount"].default_value - file.write(' %.4g\n'%amount) + amount = link.from_node.inputs["Amount"].default_value + file.write(" %.4g\n" % amount) if link.from_node.inputs["Thickness"].is_linked: pass else: - exponent=link.from_node.inputs["Thickness"].default_value - file.write(' thickness %.4g\n'%thickness) + exponent = link.from_node.inputs["Thickness"].default_value + file.write(" thickness %.4g\n" % thickness) if link.from_node.inputs["Turbulence"].is_linked: pass else: - falloff=link.from_node.inputs["Turbulence"].default_value - file.write(' turbulence %.4g}\n'%turbulence) + falloff = link.from_node.inputs["Turbulence"].default_value + file.write(" turbulence %.4g}\n" % turbulence) - file.write('}\n') + file.write("}\n") for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayTransformNode" and node.outputs["Transform"].is_linked: - tx=node.inputs["Translate x"].default_value - ty=node.inputs["Translate y"].default_value - tz=node.inputs["Translate z"].default_value - rx=node.inputs["Rotate x"].default_value - ry=node.inputs["Rotate y"].default_value - rz=node.inputs["Rotate z"].default_value - sx=node.inputs["Scale x"].default_value - sy=node.inputs["Scale y"].default_value - sz=node.inputs["Scale z"].default_value - file.write('#declare %s = transform {\n translate<%.4g,%.4g,%.4g>\n rotate<%.4g,%.4g,%.4g>\n scale<%.4g,%.4g,%.4g>}\n'%(povNodeName,tx,ty,tz,rx,ry,rz,sx,sy,sz)) + tx = node.inputs["Translate x"].default_value + ty = node.inputs["Translate y"].default_value + tz = node.inputs["Translate z"].default_value + rx = node.inputs["Rotate x"].default_value + ry = node.inputs["Rotate y"].default_value + rz = node.inputs["Rotate z"].default_value + sx = node.inputs["Scale x"].default_value + sy = node.inputs["Scale y"].default_value + sz = node.inputs["Scale z"].default_value + file.write( + "#declare %s = transform {\n" + " translate<%.4g,%.4g,%.4g>\n" + " rotate<%.4g,%.4g,%.4g>\n" + " scale<%.4g,%.4g,%.4g>}\n" % (pov_node_name, tx, ty, tz, rx, ry, rz, sx, sy, sz) + ) for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayColorImageNode" and node.outputs["Pigment"].is_linked: - declareNodes.append(node.name) + declare_nodes.append(node.name) if node.image == "": - file.write('#declare %s = pigment { color rgb 0.8}\n'%(povNodeName)) + file.write("#declare %s = pigment { color rgb 0.8}\n" % (pov_node_name)) else: - im=bpy.data.images[node.image] - if im.filepath and os.path.exists(bpy.path.abspath(im.filepath)): + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): # (os.path) transform = "" for link in ntree.links: - if link.from_node.bl_idname=='PovrayTransformNode' and link.to_node==node: - povTransName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - transform="transform {%s}"%povTransName - uv="" - if node.map_type=="uv_mapping": - uv="uv_mapping" - filepath=bpy.path.abspath(im.filepath) - file.write('#declare %s = pigment {%s image_map {\n'%(povNodeName,uv)) - premul="off" + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#declare %s = pigment {%s image_map {\n" % (pov_node_name, uv)) + premul = "off" if node.premultiplied: - premul="on" - once="" + premul = "on" + once = "" if node.once: - once="once" - file.write(' "%s"\n gamma %.6g\n premultiplied %s\n'%(filepath,node.inputs["Gamma"].default_value,premul)) - file.write(' %s\n'%once) - if node.map_type!="uv_mapping": - file.write(' map_type %s\n'%(node.map_type)) - file.write(' interpolate %s\n filter all %.4g\n transmit all %.4g\n'% - (node.interpolate,node.inputs["Filter"].default_value,node.inputs["Transmit"].default_value)) - file.write(' }\n') - file.write(' %s\n'%transform) - file.write(' }\n') + once = "once" + file.write( + ' "%s"\n gamma %.6g\n premultiplied %s\n' + % (filepath, node.inputs["Gamma"].default_value, premul) + ) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % (node.map_type)) + file.write( + " interpolate %s\n filter all %.4g\n transmit all %.4g\n" + % ( + node.interpolate, + node.inputs["Filter"].default_value, + node.inputs["Transmit"].default_value, + ) + ) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write(" }\n") for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayImagePatternNode" and node.outputs["Pattern"].is_linked: - declareNodes.append(node.name) + declare_nodes.append(node.name) if node.image != "": - im=bpy.data.images[node.image] - if im.filepath and os.path.exists(bpy.path.abspath(im.filepath)): + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): transform = "" for link in ntree.links: - if link.from_node.bl_idname=='PovrayTransformNode' and link.to_node==node: - povTransName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - transform="transform {%s}"%povTransName - uv="" - if node.map_type=="uv_mapping": - uv="uv_mapping" - filepath=bpy.path.abspath(im.filepath) - file.write('#macro %s() %s image_pattern {\n'%(povNodeName,uv)) - premul="off" + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#macro %s() %s image_pattern {\n" % (pov_node_name, uv)) + premul = "off" if node.premultiplied: - premul="on" - once="" + premul = "on" + once = "" if node.once: - once="once" - file.write(' "%s"\n gamma %.6g\n premultiplied %s\n'%(filepath,node.inputs["Gamma"].default_value,premul)) - file.write(' %s\n'%once) - if node.map_type!="uv_mapping": - file.write(' map_type %s\n'%(node.map_type)) - file.write(' interpolate %s\n'%node.interpolate) - file.write(' }\n') - file.write(' %s\n'%transform) - file.write('#end\n') + once = "once" + file.write( + ' "%s"\n gamma %.6g\n premultiplied %s\n' + % (filepath, node.inputs["Gamma"].default_value, premul) + ) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % (node.map_type)) + file.write(" interpolate %s\n" % node.interpolate) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write("#end\n") for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayBumpMapNode" and node.outputs["Normal"].is_linked: if node.image != "": - im=bpy.data.images[node.image] - if im.filepath and os.path.exists(bpy.path.abspath(im.filepath)): + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): transform = "" for link in ntree.links: - if link.from_node.bl_idname=='PovrayTransformNode' and link.to_node==node: - povTransName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - transform="transform {%s}"%povTransName - uv="" - if node.map_type=="uv_mapping": - uv="uv_mapping" - filepath=bpy.path.abspath(im.filepath) - file.write('#declare %s = normal {%s bump_map {\n'%(povNodeName,uv)) - once="" + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#declare %s = normal {%s bump_map {\n" % (pov_node_name, uv)) + once = "" if node.once: - once="once" - file.write(' "%s"\n'%filepath) - file.write(' %s\n'%once) - if node.map_type!="uv_mapping": - file.write(' map_type %s\n'%(node.map_type)) - bump_size=node.inputs["Normal"].default_value + once = "once" + file.write(' "%s"\n' % filepath) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % (node.map_type)) + bump_size = node.inputs["Normal"].default_value if node.inputs["Normal"].is_linked: pass - file.write(' interpolate %s\n bump_size %.4g\n'%(node.interpolate,bump_size)) - file.write(' }\n') - file.write(' %s\n'%transform) - file.write(' }\n') - declareNodes.append(node.name) - - + file.write( + " interpolate %s\n bump_size %.4g\n" % (node.interpolate, bump_size) + ) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write(" }\n") + declare_nodes.append(node.name) for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayPigmentNode" and node.outputs["Pigment"].is_linked: - declareNodes.append(node.name) - r,g,b=node.inputs["Color"].default_value[:] - f=node.inputs["Filter"].default_value - t=node.inputs["Transmit"].default_value + declare_nodes.append(node.name) + r, g, b = node.inputs["Color"].default_value[:] + f = node.inputs["Filter"].default_value + t = node.inputs["Transmit"].default_value if node.inputs["Color"].is_linked: pass - file.write('#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n'%(povNodeName,r,g,b,f,t)) - + file.write( + "#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n" + % (pov_node_name, r, g, b, f, t) + ) for node in ntree.nodes: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name if node.bl_idname == "PovrayTextureNode" and node.outputs["Texture"].is_linked: - declareNodes.append(node.name) - r,g,b=node.inputs["Pigment"].default_value[:] - povColName="color rgb <%.4g,%.4g,%.4g>"%(r,g,b) + declare_nodes.append(node.name) + r, g, b = node.inputs["Pigment"].default_value[:] + pov_col_name = "color rgb <%.4g,%.4g,%.4g>" % (r, g, b) if node.inputs["Pigment"].is_linked: for link in ntree.links: - if link.to_node==node and link.to_socket.name=="Pigment": - povColName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - file.write('#declare %s = texture{\n pigment{%s}\n'%(povNodeName,povColName)) + if link.to_node == node and link.to_socket.name == "Pigment": + pov_col_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write("#declare %s = texture{\n pigment{%s}\n" % (pov_node_name, pov_col_name)) if node.inputs["Normal"].is_linked: for link in ntree.links: - if link.to_node==node and link.to_socket.name=="Normal" and link.from_node.name in declareNodes: - povNorName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - file.write(' normal{%s}\n'%povNorName) + if ( + link.to_node == node + and link.to_socket.name == "Normal" + and link.from_node.name in declare_nodes + ): + pov_nor_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write(" normal{%s}\n" % pov_nor_name) if node.inputs["Finish"].is_linked: for link in ntree.links: - if link.to_node==node and link.to_socket.name=="Finish": - povFinName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - file.write(' finish{%s}\n'%povFinName) - file.write('}\n') - declareNodes.append(node.name) + if link.to_node == node and link.to_socket.name == "Finish": + pov_fin_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write(" finish{%s}\n" % pov_fin_name) + file.write("}\n") + declare_nodes.append(node.name) - for i in range(0,len(ntree.nodes)): + for i in range(0, len(ntree.nodes)): for node in ntree.nodes: - if node.bl_idname in {"ShaderNodeGroup","ShaderTextureMapNode"}: + if node.bl_idname in {"ShaderNodeGroup", "ShaderTextureMapNode"}: for output in node.outputs: - if output.name=="Texture" and output.is_linked and (node.name not in declareNodes): - declare=True + if ( + output.name == "Texture" + and output.is_linked + and (node.name not in declare_nodes) + ): + declare = True for link in ntree.links: - if link.to_node==node and link.to_socket.name not in {"","Color ramp","Mapping","Transform","Modifier"}: - if link.from_node.name not in declareNodes: - declare=False + if link.to_node == node and link.to_socket.name not in { + "", + "Color ramp", + "Mapping", + "Transform", + "Modifier", + }: + if link.from_node.name not in declare_nodes: + declare = False if declare: - povNodeName=string_strip_hyphen(bpy.path.clean_name(node.name))+"_%s"%povMatName - uv="" - warp="" + pov_node_name = ( + string_strip_hyphen(bpy.path.clean_name(node.name)) + + "_%s" % pov_mat_name + ) + uv = "" + warp = "" for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=='PovrayMappingNode' and link.from_node.warp_type!="NONE": + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayMappingNode" + and link.from_node.warp_type != "NONE" + ): w_type = link.from_node.warp_type - if w_type=="uv_mapping": - uv="uv_mapping" + if w_type == "uv_mapping": + uv = "uv_mapping" else: - tor="" - if w_type=="toroidal": - tor="major_radius %.4g"%link.from_node.warp_tor_major_radius - orient=link.from_node.warp_orientation - exp=link.from_node.warp_dist_exp - warp="warp{%s orientation %s dist_exp %.4g %s}"%(w_type,orient,exp,tor) - if link.from_node.warp_type=="planar": - warp="warp{%s %s %.4g}"%(w_type,orient,exp) - if link.from_node.warp_type=="cubic": - warp="warp{%s}"%w_type - file.write('#declare %s = texture {%s\n'%(povNodeName,uv)) - pattern=node.inputs[0].default_value - advanced="" + tor = "" + if w_type == "toroidal": + tor = ( + "major_radius %.4g" + % link.from_node.warp_tor_major_radius + ) + orient = link.from_node.warp_orientation + exp = link.from_node.warp_dist_exp + warp = "warp{%s orientation %s dist_exp %.4g %s}" % ( + w_type, + orient, + exp, + tor, + ) + if link.from_node.warp_type == "planar": + warp = "warp{%s %s %.4g}" % (w_type, orient, exp) + if link.from_node.warp_type == "cubic": + warp = "warp{%s}" % w_type + file.write("#declare %s = texture {%s\n" % (pov_node_name, uv)) + pattern = node.inputs[0].default_value + advanced = "" if node.inputs[0].is_linked: for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=='ShaderPatternNode': + if ( + link.to_node == node + and link.from_node.bl_idname == "ShaderPatternNode" + ): ########### advanced ############################################### - lfn=link.from_node - pattern=lfn.pattern - if pattern == 'agate': - advanced = 'agate_turb %.4g'%lfn.agate_turb - if pattern == 'crackle': - advanced="form <%.4g,%.4g,%.4g>"%(lfn.crackle_form_x,lfn.crackle_form_y,lfn.crackle_form_z) - advanced+=" metric %.4g"%lfn.crackle_metric + lfn = link.from_node + pattern = lfn.pattern + if pattern == "agate": + advanced = "agate_turb %.4g" % lfn.agate_turb + if pattern == "crackle": + advanced = "form <%.4g,%.4g,%.4g>" % ( + lfn.crackle_form_x, + lfn.crackle_form_y, + lfn.crackle_form_z, + ) + advanced += " metric %.4g" % lfn.crackle_metric if lfn.crackle_solid: - advanced+=" solid" - if pattern in {'spiral1', 'spiral2'}: - advanced='%.4g'%lfn.spiral_arms - if pattern in {'tiling'}: - advanced='%.4g'%lfn.tiling_number - if pattern in {'gradient'}: - advanced='%s'%lfn.gradient_orient - if link.to_node==node and link.from_node.bl_idname=='PovrayImagePatternNode': - povMacroName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - pattern = "%s()"%povMacroName - file.write(' %s %s %s\n'%(pattern,advanced,warp)) - - repeat="" + advanced += " solid" + if pattern in {"spiral1", "spiral2"}: + advanced = "%.4g" % lfn.spiral_arms + if pattern in {"tiling"}: + advanced = "%.4g" % lfn.tiling_number + if pattern in {"gradient"}: + advanced = "%s" % lfn.gradient_orient + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayImagePatternNode" + ): + pov_macro_name = ( + string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + + "_%s" % pov_mat_name + ) + pattern = "%s()" % pov_macro_name + file.write(" %s %s %s\n" % (pattern, advanced, warp)) + + repeat = "" for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=='PovrayMultiplyNode': + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayMultiplyNode" + ): if link.from_node.amount_x > 1: - repeat+="warp{repeat %.4g * x}"%link.from_node.amount_x + repeat += "warp{repeat %.4g * x}" % link.from_node.amount_x if link.from_node.amount_y > 1: - repeat+=" warp{repeat %.4g * y}"%link.from_node.amount_y + repeat += " warp{repeat %.4g * y}" % link.from_node.amount_y if link.from_node.amount_z > 1: - repeat+=" warp{repeat %.4g * z}"%link.from_node.amount_z + repeat += " warp{repeat %.4g * z}" % link.from_node.amount_z - transform="" + transform = "" for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=='PovrayTransformNode': - povTransName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - transform="transform {%s}"%povTransName - x=0 - y=0 - z=0 - d=0 - e=0 - f=0 - g=0 - h=0 - modifier=False + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayTransformNode" + ): + pov_trans_name = ( + string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + x = 0 + y = 0 + z = 0 + d = 0 + e = 0 + f = 0 + g = 0 + h = 0 + modifier = False for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=='PovrayModifierNode': - modifier=True + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayModifierNode" + ): + modifier = True if link.from_node.inputs["Turb X"].is_linked: pass else: @@ -1800,66 +1604,135 @@ def write_nodes(scene,povMatName,ntree,file): else: h = link.from_node.inputs["Phase"].default_value - turb = "turbulence <%.4g,%.4g,%.4g>"%(x,y,z) - octv = "octaves %s"%d - lmbd = "lambda %.4g"%e - omg = "omega %.4g"%f - freq = "frequency %.4g"%g - pha = "phase %.4g"%h - - - file.write('\n') - if pattern not in {'checker', 'hexagon', 'square', 'triangular', 'brick'}: - file.write(' texture_map {\n') + turb = "turbulence <%.4g,%.4g,%.4g>" % (x, y, z) + octv = "octaves %s" % d + lmbd = "lambda %.4g" % e + omg = "omega %.4g" % f + freq = "frequency %.4g" % g + pha = "phase %.4g" % h + + file.write("\n") + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write(" texture_map {\n") if node.inputs["Color ramp"].is_linked: for link in ntree.links: - if link.to_node==node and link.from_node.bl_idname=="ShaderNodeValToRGB": + if ( + link.to_node == node + and link.from_node.bl_idname == "ShaderNodeValToRGB" + ): els = link.from_node.color_ramp.elements - n=-1 + n = -1 for el in els: - n+=1 - povInMatName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s_%s"%(n,povMatName) - default=True + n += 1 + pov_in_mat_name = string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + "_%s_%s" % (n, pov_mat_name) + default = True for ilink in ntree.links: - if ilink.to_node==node and ilink.to_socket.name == str(n): - default=False - povInMatName=string_strip_hyphen(bpy.path.clean_name(ilink.from_node.name))+"_%s"%povMatName + if ( + ilink.to_node == node + and ilink.to_socket.name == str(n) + ): + default = False + pov_in_mat_name = ( + string_strip_hyphen( + bpy.path.clean_name( + ilink.from_node.name + ) + ) + + "_%s" % pov_mat_name + ) if default: - r,g,b,a=el.color[:] - file.write(' #declare %s = texture{pigment{color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n'%(povInMatName,r,g,b,1-a)) - file.write(' [%s %s]\n'%(el.position,povInMatName)) + r, g, b, a = el.color[:] + file.write( + " #declare %s = texture{" + "pigment{" + "color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n" + % (pov_in_mat_name, r, g, b, 1 - a) + ) + file.write( + " [%s %s]\n" % (el.position, pov_in_mat_name) + ) else: - els=[[0,0,0,0],[1,1,1,1]] - for i in range(0,2): - povInMatName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s_%s"%(i,povMatName) - default=True + els = [[0, 0, 0, 0], [1, 1, 1, 1]] + for t in range(0, 2): + pov_in_mat_name = string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + "_%s_%s" % (t, pov_mat_name) + default = True for ilink in ntree.links: - if ilink.to_node==node and ilink.to_socket.name == str(i): - default=False - povInMatName=string_strip_hyphen(bpy.path.clean_name(ilink.from_node.name))+"_%s"%povMatName + if ilink.to_node == node and ilink.to_socket.name == str(t): + default = False + pov_in_mat_name = ( + string_strip_hyphen( + bpy.path.clean_name(ilink.from_node.name) + ) + + "_%s" % pov_mat_name + ) if default: - r,g,b=els[i][1],els[i][2],els[i][3] - if pattern not in {'checker', 'hexagon', 'square', 'triangular', 'brick'}: - file.write(' #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n'%(povInMatName,r,g,b)) + r, g, b = els[t][1], els[t][2], els[t][3] + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write( + " #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n" + % (pov_in_mat_name, r, g, b) + ) else: - file.write(' texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n'%(r,g,b)) - if pattern not in {'checker', 'hexagon', 'square', 'triangular', 'brick'}: - file.write(' [%s %s]\n'%(els[i][0],povInMatName)) + file.write( + " texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n" + % (r, g, b) + ) + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write(" [%s %s]\n" % (els[t][0], pov_in_mat_name)) else: - if default==False: - file.write(' texture{%s}\n'%povInMatName) - if pattern not in {'checker', 'hexagon', 'square', 'triangular', 'brick'}: - file.write('}\n') - if pattern == 'brick': - file.write("brick_size <%.4g, %.4g, %.4g> mortar %.4g \n"%(node.brick_size_x, - node.brick_size_y, node.brick_size_z, node.brick_mortar)) - file.write(' %s %s'%(repeat,transform)) + if not default: + file.write(" texture{%s}\n" % pov_in_mat_name) + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write("}\n") + if pattern == "brick": + file.write( + "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n" + % ( + node.brick_size_x, + node.brick_size_y, + node.brick_size_z, + node.brick_mortar, + ) + ) + file.write(" %s %s" % (repeat, transform)) if modifier: - file.write(' %s %s %s %s %s %s'%(turb,octv,lmbd,omg,freq,pha)) - file.write('}\n') - declareNodes.append(node.name) + file.write( + " %s %s %s %s %s %s" % (turb, octv, lmbd, omg, freq, pha) + ) + file.write("}\n") + declare_nodes.append(node.name) for link in ntree.links: - if link.to_node.bl_idname == "PovrayOutputNode" and link.from_node.name in declareNodes: - povMatNodeName=string_strip_hyphen(bpy.path.clean_name(link.from_node.name))+"_%s"%povMatName - file.write('#declare %s = %s\n'%(povMatName,povMatNodeName)) + if link.to_node.bl_idname == "PovrayOutputNode" and link.from_node.name in declare_nodes: + pov_mat_node_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + "_%s" % pov_mat_name + ) + file.write("#declare %s = %s\n" % (pov_mat_name, pov_mat_node_name)) diff --git a/render_povray/shading_gui.py b/render_povray/shading_gui.py new file mode 100755 index 000000000..3df7f4a96 --- /dev/null +++ b/render_povray/shading_gui.py @@ -0,0 +1,688 @@ +# ##### 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 ##### + +# <pep8 compliant> +""""User interface for shaders exported to POV textures.""" + +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import Operator, Menu, Panel +from bl_operators.presets import AddPresetBase + +# Example of wrapping every class 'as is' except some +from bl_ui import properties_material + +for member in dir(properties_material): + subclass = getattr(properties_material, member) + try: + # mat=bpy.context.active_object.active_material + # if (mat and mat.pov.type == "SURFACE" + # and not (mat.pov.material_use_nodes or mat.use_nodes)): + # and (engine in cls.COMPAT_ENGINES)) if subclasses were sorted + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +del properties_material + +from .shading_properties import check_material + + +def simple_material(mat): + """Test if a material uses nodes""" + if (mat is not None) and (not mat.use_nodes): + return True + return False + + +class MaterialButtonsPanel: + """Use this class to define buttons from the material tab of + properties window.""" + + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + rd = context.scene.render + return mat and (rd.engine in cls.COMPAT_ENGINES) + + +class MATERIAL_MT_POV_sss_presets(Menu): + """Use this class to define pov sss preset menu.""" + + bl_label = "SSS Presets" + preset_subdir = "pov/material/sss" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class MATERIAL_OT_POV_sss_add_preset(AddPresetBase, Operator): + """Add an SSS Preset""" + + bl_idname = "material.sss_preset_add" + bl_label = "Add SSS Preset" + preset_menu = "MATERIAL_MT_POV_sss_presets" + + # variable used for all preset values + preset_defines = ["material = bpy.context.material"] + + # properties to store in the preset + preset_values = [ + "material.pov_subsurface_scattering.radius", + "material.pov_subsurface_scattering.color", + ] + + # where to store the preset + preset_subdir = "pov/material/sss" + + +class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel): + """Use this class to define pov sss buttons panel.""" + + bl_label = "Subsurface Scattering" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return ( + check_material(mat) + and (mat.pov.type in {'SURFACE', 'WIRE'}) + and (engine in cls.COMPAT_ENGINES) + ) + + def draw_header(self, context): + mat = context.material # FORMERLY : #active_node_mat(context.material) + sss = mat.pov_subsurface_scattering + + self.layout.active = not mat.pov.use_shadeless + self.layout.prop(sss, "use", text="") + + def draw(self, context): + layout = self.layout + + mat = context.material # FORMERLY : #active_node_mat(context.material) + sss = mat.pov_subsurface_scattering + + layout.active = (sss.use) and (not mat.pov.use_shadeless) + + row = layout.row().split() + sub = row.row(align=True).split(align=True, factor=0.75) + sub.menu(MATERIAL_MT_POV_sss_presets.__name__, text=MATERIAL_MT_POV_sss_presets.bl_label) + sub.operator(MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='ADD') + sub.operator( + MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='REMOVE' + ).remove_active = True + + split = layout.split() + + col = split.column() + col.prop(sss, "ior") + col.prop(sss, "scale") + col.prop(sss, "color", text="") + col.prop(sss, "radius", text="RGB Radius", expand=True) + + col = split.column() + sub = col.column(align=True) + sub.label(text="Blend:") + sub.prop(sss, "color_factor", text="Color") + sub.prop(sss, "texture_factor", text="Texture") + sub.label(text="Scattering Weight:") + sub.prop(sss, "front") + sub.prop(sss, "back") + col.separator() + col.prop(sss, "error_threshold", text="Error") + + +class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel): + """Use this class to define an activate pov nodes button.""" + + bl_label = "Activate Node Settings" + bl_context = "material" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat = context.material + return ( + mat + and mat.pov.type == "SURFACE" + and (engine in cls.COMPAT_ENGINES) + and not (mat.pov.material_use_nodes or mat.use_nodes) + ) + + def draw(self, context): + layout = self.layout + # layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE') + # the above replaced with a context hook below: + layout.operator( + "WM_OT_context_toggle", text="Use POV-Ray Nodes", icon='NODETREE' + ).data_path = "material.pov.material_use_nodes" + + +class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel): + """Use this class to show pov active node properties buttons.""" + + bl_label = "Active Node Settings" + bl_context = "material" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat = context.material + return ( + mat + and mat.pov.type == "SURFACE" + and (engine in cls.COMPAT_ENGINES) + and mat.pov.material_use_nodes + ) + + def draw(self, context): + layout = self.layout + mat = context.material + node_tree = mat.node_tree + if node_tree: + node = node_tree.nodes.active + if mat.use_nodes: + if node: + layout.prop(mat.pov, "material_active_node") + if node.bl_idname == "PovrayMaterialNode": + layout.context_pointer_set("node", node) + if hasattr(node, "draw_buttons_ext"): + node.draw_buttons_ext(context, layout) + elif hasattr(node, "draw_buttons"): + node.draw_buttons(context, layout) + value_inputs = [ + socket + for socket in node.inputs + if socket.enabled and not socket.is_linked + ] + if value_inputs: + layout.separator() + layout.label(text="Inputs:") + for socket in value_inputs: + row = layout.row() + socket.draw(context, row, node, socket.name) + else: + layout.context_pointer_set("node", node) + if hasattr(node, "draw_buttons_ext"): + node.draw_buttons_ext(context, layout) + elif hasattr(node, "draw_buttons"): + node.draw_buttons(context, layout) + value_inputs = [ + socket + for socket in node.inputs + if socket.enabled and not socket.is_linked + ] + if value_inputs: + layout.separator() + layout.label(text="Inputs:") + for socket in value_inputs: + row = layout.row() + socket.draw(context, row, node, socket.name) + else: + layout.label(text="No active nodes!") + + +class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel): + """Use this class to define standard material specularity (highlights) buttons.""" + + bl_label = "Specular" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return ( + check_material(mat) + and (mat.pov.type in {'SURFACE', 'WIRE'}) + and (engine in cls.COMPAT_ENGINES) + ) + + def draw(self, context): + layout = self.layout + + mat = context.material.pov + + layout.active = not mat.use_shadeless + + split = layout.split() + + col = split.column() + col.prop(mat, "specular_color", text="") + col.prop(mat, "specular_intensity", text="Intensity") + + col = split.column() + col.prop(mat, "specular_shader", text="") + col.prop(mat, "use_specular_ramp", text="Ramp") + + col = layout.column() + if mat.specular_shader in {'COOKTORR', 'PHONG'}: + col.prop(mat, "specular_hardness", text="Hardness") + elif mat.specular_shader == 'BLINN': + row = col.row() + row.prop(mat, "specular_hardness", text="Hardness") + row.prop(mat, "specular_ior", text="IOR") + elif mat.specular_shader == 'WARDISO': + col.prop(mat, "specular_slope", text="Slope") + elif mat.specular_shader == 'TOON': + row = col.row() + row.prop(mat, "specular_toon_size", text="Size") + row.prop(mat, "specular_toon_smooth", text="Smooth") + + if mat.use_specular_ramp: + layout.separator() + layout.template_color_ramp(mat, "specular_ramp", expand=True) + layout.separator() + + row = layout.row() + row.prop(mat, "specular_ramp_input", text="Input") + row.prop(mat, "specular_ramp_blend", text="Blend") + + layout.prop(mat, "specular_ramp_factor", text="Factor") + + +class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel): + """Use this class to define standard material reflectivity (mirror) buttons.""" + + bl_label = "Mirror" + bl_options = {'DEFAULT_CLOSED'} + bl_idname = "MATERIAL_PT_POV_raytrace_mirror" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return ( + check_material(mat) + and (mat.pov.type in {'SURFACE', 'WIRE'}) + and (engine in cls.COMPAT_ENGINES) + ) + + def draw_header(self, context): + mat = context.material + raym = mat.pov_raytrace_mirror + + self.layout.prop(raym, "use", text="") + + def draw(self, context): + layout = self.layout + + mat = context.material # Formerly : #mat = active_node_mat(context.material) + raym = mat.pov_raytrace_mirror + + layout.active = raym.use + + split = layout.split() + + col = split.column() + col.prop(raym, "reflect_factor") + col.prop(raym, "mirror_color", text="") + + col = split.column() + col.prop(raym, "fresnel") + sub = col.column() + sub.active = raym.fresnel > 0.0 + sub.prop(raym, "fresnel_factor", text="Blend") + + split = layout.split() + + col = split.column() + col.separator() + col.prop(raym, "depth") + col.prop(raym, "distance", text="Max Dist") + col.separator() + sub = col.split(factor=0.4) + sub.active = raym.distance > 0.0 + sub.label(text="Fade To:") + sub.prop(raym, "fade_to", text="") + + col = split.column() + col.label(text="Gloss:") + col.prop(raym, "gloss_factor", text="Amount") + sub = col.column() + sub.active = raym.gloss_factor < 1.0 + sub.prop(raym, "gloss_threshold", text="Threshold") + sub.prop(raym, "gloss_samples", text="Noise") + sub.prop(raym, "gloss_anisotropic", text="Anisotropic") + + +class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel): + """Use this class to define pov material transparency (alpha) buttons.""" + + bl_label = "Transparency" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return ( + check_material(mat) + and (mat.pov.type in {'SURFACE', 'WIRE'}) + and (engine in cls.COMPAT_ENGINES) + ) + + def draw_header(self, context): + mat = context.material + + if simple_material(mat): + self.layout.prop(mat.pov, "use_transparency", text="") + + def draw(self, context): + layout = self.layout + + base_mat = context.material + mat = context.material # FORMERLY active_node_mat(context.material) + rayt = mat.pov_raytrace_transparency + + if simple_material(base_mat): + row = layout.row() + row.active = mat.pov.use_transparency + row.prop(mat.pov, "transparency_method", expand=True) + + split = layout.split() + split.active = base_mat.pov.use_transparency + + col = split.column() + col.prop(mat.pov, "alpha") + row = col.row() + row.active = (base_mat.pov.transparency_method != 'MASK') and (not mat.pov.use_shadeless) + row.prop(mat.pov, "specular_alpha", text="Specular") + + col = split.column() + col.active = not mat.pov.use_shadeless + col.prop(rayt, "fresnel") + sub = col.column() + sub.active = rayt.fresnel > 0.0 + sub.prop(rayt, "fresnel_factor", text="Blend") + + if base_mat.pov.transparency_method == 'RAYTRACE': + layout.separator() + split = layout.split() + split.active = base_mat.pov.use_transparency + + col = split.column() + col.prop(rayt, "ior") + col.prop(rayt, "filter") + col.prop(rayt, "falloff") + col.prop(rayt, "depth_max") + col.prop(rayt, "depth") + + col = split.column() + col.label(text="Gloss:") + col.prop(rayt, "gloss_factor", text="Amount") + sub = col.column() + sub.active = rayt.gloss_factor < 1.0 + sub.prop(rayt, "gloss_threshold", text="Threshold") + sub.prop(rayt, "gloss_samples", text="Samples") + + +class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel): + """Use this class to define more pov specific reflectivity buttons.""" + + bl_label = "POV-Ray Reflection" + bl_parent_id = "MATERIAL_PT_POV_raytrace_mirror" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat = context.material + return ( + mat + and mat.pov.type == "SURFACE" + and (engine in cls.COMPAT_ENGINES) + and not (mat.pov.material_use_nodes or mat.use_nodes) + ) + + def draw(self, context): + layout = self.layout + mat = context.material + col = layout.column() + col.prop(mat.pov, "irid_enable") + if mat.pov.irid_enable: + col = layout.column() + col.prop(mat.pov, "irid_amount", slider=True) + col.prop(mat.pov, "irid_thickness", slider=True) + col.prop(mat.pov, "irid_turbulence", slider=True) + col.prop(mat.pov, "conserve_energy") + col2 = col.split().column() + + if not mat.pov_raytrace_mirror.use: + col2.label(text="Please Check Mirror settings :") + col2.active = mat.pov_raytrace_mirror.use + col2.prop(mat.pov, "mirror_use_IOR") + if mat.pov.mirror_use_IOR: + col2.alignment = 'CENTER' + col2.label(text="The current Raytrace ") + col2.label(text="Transparency IOR is: " + str(mat.pov_raytrace_transparency.ior)) + col2.prop(mat.pov, "mirror_metallic") + + +''' +#group some native Blender (SSS) and POV (Fade)settings under such a parent panel? +class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel): + bl_label = "POV-Ray Interior" + bl_idname = "material.pov_interior" + #bl_parent_id = "material.absorption" + COMPAT_ENGINES = {'POVRAY_RENDER'} + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat=context.material + return mat and mat.pov.type == "SURFACE" and (engine in cls.COMPAT_ENGINES) and not (mat.pov.material_use_nodes or mat.use_nodes) + + + def draw_header(self, context): + mat = context.material +''' + + +class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel, Panel): + """Use this class to define pov fading (absorption) color buttons.""" + + bl_label = "POV-Ray Absorption" + COMPAT_ENGINES = {'POVRAY_RENDER'} + # bl_parent_id = "material.pov_interior" + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat = context.material + return ( + mat + and mat.pov.type == "SURFACE" + and (engine in cls.COMPAT_ENGINES) + and not (mat.pov.material_use_nodes or mat.use_nodes) + ) + + def draw_header(self, context): + mat = context.material + + self.layout.prop(mat.pov, "interior_fade_color", text="") + + def draw(self, context): + layout = self.layout + mat = context.material + # layout.active = mat.pov.interior_fade_color + if mat.pov.interior_fade_color != (0.0, 0.0, 0.0): + layout.label(text="Raytrace transparency") + layout.label(text="depth max Limit needs") + layout.label(text="to be non zero to fade") + + +class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel): + """Use this class to define pov caustics buttons.""" + + bl_label = "Caustics" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + mat = context.material + return ( + mat + and mat.pov.type == "SURFACE" + and (engine in cls.COMPAT_ENGINES) + and not (mat.pov.material_use_nodes or mat.use_nodes) + ) + + def draw_header(self, context): + mat = context.material + if mat.pov.caustics_enable: + self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER_SEL") + else: + self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER") + + def draw(self, context): + + layout = self.layout + + mat = context.material + layout.active = mat.pov.caustics_enable + col = layout.column() + if mat.pov.caustics_enable: + col.prop(mat.pov, "refraction_caustics") + if mat.pov.refraction_caustics: + + col.prop(mat.pov, "refraction_type", text="") + + if mat.pov.refraction_type == "1": + col.prop(mat.pov, "fake_caustics_power", slider=True) + elif mat.pov.refraction_type == "2": + col.prop(mat.pov, "photons_dispersion", slider=True) + col.prop(mat.pov, "photons_dispersion_samples", slider=True) + col.prop(mat.pov, "photons_reflection") + + if not mat.pov.refraction_caustics and not mat.pov.photons_reflection: + col = layout.column() + col.alignment = 'CENTER' + col.label(text="Caustics override is on, ") + col.label(text="but you didn't chose any !") + + +class MATERIAL_PT_strand(MaterialButtonsPanel, Panel): + """Use this class to define Blender strand antialiasing buttons.""" + + bl_label = "Strand" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return ( + mat and (mat.pov.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES) + ) + + def draw(self, context): + layout = self.layout + + mat = context.material # don't use node material + tan = mat.strand + + split = layout.split() + + col = split.column() + sub = col.column(align=True) + sub.label(text="Size:") + sub.prop(tan, "root_size", text="Root") + sub.prop(tan, "tip_size", text="Tip") + sub.prop(tan, "size_min", text="Minimum") + sub.prop(tan, "use_blender_units") + sub = col.column() + sub.active = not mat.pov.use_shadeless + sub.prop(tan, "use_tangent_shading") + col.prop(tan, "shape") + + col = split.column() + col.label(text="Shading:") + col.prop(tan, "width_fade") + ob = context.object + if ob and ob.type == 'MESH': + col.prop_search(tan, "uv_layer", ob.data, "uv_layers", text="") + else: + col.prop(tan, "uv_layer", text="") + col.separator() + sub = col.column() + sub.active = not mat.pov.use_shadeless + sub.label(text="Surface diffuse:") + sub = col.column() + sub.prop(tan, "blend_distance", text="Distance") + + +class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel): + """Use this class to define pov custom code declared name field.""" + + bl_label = "Custom POV Code" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + layout = self.layout + mat = context.material + col = layout.column() + col.alignment = 'RIGHT' + col.label(text="Override properties with this") + col.label(text="text editor {pov code} block") + layout.prop(mat.pov, "replacement_text", text="#declare name", icon='SYNTAX_ON') + + +classes = ( + MATERIAL_PT_POV_sss, + MATERIAL_MT_POV_sss_presets, + MATERIAL_OT_POV_sss_add_preset, + MATERIAL_PT_strand, + MATERIAL_PT_POV_activate_node, + MATERIAL_PT_POV_active_node, + MATERIAL_PT_POV_specular, + MATERIAL_PT_POV_mirror, + MATERIAL_PT_POV_transp, + MATERIAL_PT_POV_reflection, + ## MATERIAL_PT_POV_interior, + MATERIAL_PT_POV_fade_color, + MATERIAL_PT_POV_caustics, + MATERIAL_PT_POV_replacement_text, +) + + +def register(): + + for cls in classes: + register_class(cls) + + +def unregister(): + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/shading_nodes.py b/render_povray/shading_nodes.py new file mode 100755 index 000000000..2e8484f9c --- /dev/null +++ b/render_povray/shading_nodes.py @@ -0,0 +1,2011 @@ +# ##### 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 ##### + +# <pep8 compliant> +""""Nodes based User interface for shaders exported to POV textures.""" +import bpy + +from bpy.utils import register_class, unregister_class +from bpy.types import Menu, Node, NodeSocket, CompositorNodeTree, TextureNodeTree, Operator +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + EnumProperty, +) +import nodeitems_utils +from nodeitems_utils import NodeCategory, NodeItem + + +############################################################################### +# Pov Nodes init +############################################################################### + + +class PovraySocketUniversal(NodeSocket): + bl_idname = "PovraySocketUniversal" + bl_label = "Povray Socket" + value_unlimited: bpy.props.FloatProperty(default=0.0) + value_0_1: bpy.props.FloatProperty(min=0.0, max=1.0, default=0.0) + value_0_10: bpy.props.FloatProperty(min=0.0, max=10.0, default=0.0) + value_000001_10: bpy.props.FloatProperty(min=0.000001, max=10.0, default=0.0) + value_1_9: bpy.props.IntProperty(min=1, max=9, default=1) + value_0_255: bpy.props.IntProperty(min=0, max=255, default=0) + percent: bpy.props.FloatProperty(min=0.0, max=100.0, default=0.0) + + def draw(self, context, layout, node, text): + space = context.space_data + tree = space.edit_tree + links = tree.links + if self.is_linked: + value = [] + for link in links: + if link.from_node == node: + inps = link.to_node.inputs + for inp in inps: + if inp.bl_idname == "PovraySocketFloat_0_1" and inp.is_linked: + prop = "value_0_1" + if prop not in value: + value.append(prop) + if inp.bl_idname == "PovraySocketFloat_000001_10" and inp.is_linked: + prop = "value_000001_10" + if prop not in value: + value.append(prop) + if inp.bl_idname == "PovraySocketFloat_0_10" and inp.is_linked: + prop = "value_0_10" + if prop not in value: + value.append(prop) + if inp.bl_idname == "PovraySocketInt_1_9" and inp.is_linked: + prop = "value_1_9" + if prop not in value: + value.append(prop) + if inp.bl_idname == "PovraySocketInt_0_255" and inp.is_linked: + prop = "value_0_255" + if prop not in value: + value.append(prop) + if inp.bl_idname == "PovraySocketFloatUnlimited" and inp.is_linked: + prop = "value_unlimited" + if prop not in value: + value.append(prop) + if len(value) == 1: + layout.prop(self, "%s" % value[0], text=text) + else: + layout.prop(self, "percent", text="Percent") + else: + layout.prop(self, "percent", text=text) + + def draw_color(self, context, node): + return (1, 0, 0, 1) + + +class PovraySocketFloat_0_1(NodeSocket): + bl_idname = "PovraySocketFloat_0_1" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty( + description="Input node Value_0_1", min=0, max=1, default=0 + ) + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (0.5, 0.7, 0.7, 1) + + +class PovraySocketFloat_0_10(NodeSocket): + bl_idname = "PovraySocketFloat_0_10" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty( + description="Input node Value_0_10", min=0, max=10, default=0 + ) + + def draw(self, context, layout, node, text): + if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked: + layout.label(text="") + self.hide_value = True + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (0.65, 0.65, 0.65, 1) + + +class PovraySocketFloat_10(NodeSocket): + bl_idname = "PovraySocketFloat_10" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty( + description="Input node Value_10", min=-10, max=10, default=0 + ) + + def draw(self, context, layout, node, text): + if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked: + layout.label(text="") + self.hide_value = True + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (0.65, 0.65, 0.65, 1) + + +class PovraySocketFloatPositive(NodeSocket): + bl_idname = "PovraySocketFloatPositive" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty( + description="Input Node Value Positive", min=0.0, default=0 + ) + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (0.045, 0.005, 0.136, 1) + + +class PovraySocketFloat_000001_10(NodeSocket): + bl_idname = "PovraySocketFloat_000001_10" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty(min=0.000001, max=10, default=0.000001) + + def draw(self, context, layout, node, text): + if self.is_output or self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (1, 0, 0, 1) + + +class PovraySocketFloatUnlimited(NodeSocket): + bl_idname = "PovraySocketFloatUnlimited" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty(default=0.0) + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text, slider=True) + + def draw_color(self, context, node): + return (0.7, 0.7, 1, 1) + + +class PovraySocketInt_1_9(NodeSocket): + bl_idname = "PovraySocketInt_1_9" + bl_label = "Povray Socket" + default_value: bpy.props.IntProperty( + description="Input node Value_1_9", min=1, max=9, default=6 + ) + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text) + + def draw_color(self, context, node): + return (1, 0.7, 0.7, 1) + + +class PovraySocketInt_0_256(NodeSocket): + bl_idname = "PovraySocketInt_0_256" + bl_label = "Povray Socket" + default_value: bpy.props.IntProperty(min=0, max=255, default=0) + + def draw(self, context, layout, node, text): + if self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text) + + def draw_color(self, context, node): + return (0.5, 0.5, 0.5, 1) + + +class PovraySocketPattern(NodeSocket): + bl_idname = "PovraySocketPattern" + bl_label = "Povray Socket" + + default_value: bpy.props.EnumProperty( + name="Pattern", + description="Select the pattern", + items=( + ("boxed", "Boxed", ""), + ("brick", "Brick", ""), + ("cells", "Cells", ""), + ("checker", "Checker", ""), + ("granite", "Granite", ""), + ("leopard", "Leopard", ""), + ("marble", "Marble", ""), + ("onion", "Onion", ""), + ("planar", "Planar", ""), + ("quilted", "Quilted", ""), + ("ripples", "Ripples", ""), + ("radial", "Radial", ""), + ("spherical", "Spherical", ""), + ("spotted", "Spotted", ""), + ("waves", "Waves", ""), + ("wood", "Wood", ""), + ("wrinkles", "Wrinkles", ""), + ), + default="granite", + ) + + def draw(self, context, layout, node, text): + if self.is_output or self.is_linked: + layout.label(text="Pattern") + else: + layout.prop(self, "default_value", text=text) + + def draw_color(self, context, node): + return (1, 1, 1, 1) + + +class PovraySocketColor(NodeSocket): + bl_idname = "PovraySocketColor" + bl_label = "Povray Socket" + + default_value: FloatVectorProperty( + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + def draw(self, context, layout, node, text): + if self.is_output or self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text) + + def draw_color(self, context, node): + return (1, 1, 0, 1) + + +class PovraySocketColorRGBFT(NodeSocket): + bl_idname = "PovraySocketColorRGBFT" + bl_label = "Povray Socket" + + default_value: FloatVectorProperty( + precision=4, + step=0.01, + min=0, + soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + f: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0) + t: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0) + + def draw(self, context, layout, node, text): + if self.is_output or self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text=text) + + def draw_color(self, context, node): + return (1, 1, 0, 1) + + +class PovraySocketTexture(NodeSocket): + bl_idname = "PovraySocketTexture" + bl_label = "Povray Socket" + default_value: bpy.props.IntProperty() + + def draw(self, context, layout, node, text): + layout.label(text=text) + + def draw_color(self, context, node): + return (0, 1, 0, 1) + + +class PovraySocketTransform(NodeSocket): + bl_idname = "PovraySocketTransform" + bl_label = "Povray Socket" + default_value: bpy.props.IntProperty(min=0, max=255, default=0) + + def draw(self, context, layout, node, text): + layout.label(text=text) + + def draw_color(self, context, node): + return (99 / 255, 99 / 255, 199 / 255, 1) + + +class PovraySocketNormal(NodeSocket): + bl_idname = "PovraySocketNormal" + bl_label = "Povray Socket" + default_value: bpy.props.IntProperty(min=0, max=255, default=0) + + def draw(self, context, layout, node, text): + layout.label(text=text) + + def draw_color(self, context, node): + return (0.65, 0.65, 0.65, 1) + + +class PovraySocketSlope(NodeSocket): + bl_idname = "PovraySocketSlope" + bl_label = "Povray Socket" + default_value: bpy.props.FloatProperty(min=0.0, max=1.0) + height: bpy.props.FloatProperty(min=0.0, max=10.0) + slope: bpy.props.FloatProperty(min=-10.0, max=10.0) + + def draw(self, context, layout, node, text): + if self.is_output or self.is_linked: + layout.label(text=text) + else: + layout.prop(self, "default_value", text="") + layout.prop(self, "height", text="") + layout.prop(self, "slope", text="") + + def draw_color(self, context, node): + return (0, 0, 0, 1) + + +class PovraySocketMap(NodeSocket): + bl_idname = "PovraySocketMap" + bl_label = "Povray Socket" + default_value: bpy.props.StringProperty() + + def draw(self, context, layout, node, text): + layout.label(text=text) + + def draw_color(self, context, node): + return (0.2, 0, 0.2, 1) + + +class PovrayShaderNodeCategory(NodeCategory): + @classmethod + def poll(cls, context): + return context.space_data.tree_type == "ObjectNodeTree" + + +class PovrayTextureNodeCategory(NodeCategory): + @classmethod + def poll(cls, context): + return context.space_data.tree_type == "TextureNodeTree" + + +class PovraySceneNodeCategory(NodeCategory): + @classmethod + def poll(cls, context): + return context.space_data.tree_type == "CompositorNodeTree" + + +node_categories = [ + PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items=[NodeItem("PovrayOutputNode")]), + PovrayShaderNodeCategory("SIMPLE", "Simple texture", items=[NodeItem("PovrayTextureNode")]), + PovrayShaderNodeCategory( + "MAPS", + "Maps", + items=[ + NodeItem("PovrayBumpMapNode"), + NodeItem("PovrayColorImageNode"), + NodeItem("ShaderNormalMapNode"), + NodeItem("PovraySlopeNode"), + NodeItem("ShaderTextureMapNode"), + NodeItem("ShaderNodeValToRGB"), + ], + ), + PovrayShaderNodeCategory( + "OTHER", + "Other patterns", + items=[NodeItem("PovrayImagePatternNode"), NodeItem("ShaderPatternNode")], + ), + PovrayShaderNodeCategory("COLOR", "Color", items=[NodeItem("PovrayPigmentNode")]), + PovrayShaderNodeCategory( + "TRANSFORM", + "Transform", + items=[ + NodeItem("PovrayMappingNode"), + NodeItem("PovrayMultiplyNode"), + NodeItem("PovrayModifierNode"), + NodeItem("PovrayTransformNode"), + NodeItem("PovrayValueNode"), + ], + ), + PovrayShaderNodeCategory( + "FINISH", + "Finish", + items=[ + NodeItem("PovrayFinishNode"), + NodeItem("PovrayDiffuseNode"), + NodeItem("PovraySpecularNode"), + NodeItem("PovrayPhongNode"), + NodeItem("PovrayAmbientNode"), + NodeItem("PovrayMirrorNode"), + NodeItem("PovrayIridescenceNode"), + NodeItem("PovraySubsurfaceNode"), + ], + ), + PovrayShaderNodeCategory( + "CYCLES", + "Cycles", + items=[ + NodeItem("ShaderNodeAddShader"), + NodeItem("ShaderNodeAmbientOcclusion"), + NodeItem("ShaderNodeAttribute"), + NodeItem("ShaderNodeBackground"), + NodeItem("ShaderNodeBlackbody"), + NodeItem("ShaderNodeBrightContrast"), + NodeItem("ShaderNodeBsdfAnisotropic"), + NodeItem("ShaderNodeBsdfDiffuse"), + NodeItem("ShaderNodeBsdfGlass"), + NodeItem("ShaderNodeBsdfGlossy"), + NodeItem("ShaderNodeBsdfHair"), + NodeItem("ShaderNodeBsdfRefraction"), + NodeItem("ShaderNodeBsdfToon"), + NodeItem("ShaderNodeBsdfTranslucent"), + NodeItem("ShaderNodeBsdfTransparent"), + NodeItem("ShaderNodeBsdfVelvet"), + NodeItem("ShaderNodeBump"), + NodeItem("ShaderNodeCameraData"), + NodeItem("ShaderNodeCombineHSV"), + NodeItem("ShaderNodeCombineRGB"), + NodeItem("ShaderNodeCombineXYZ"), + NodeItem("ShaderNodeEmission"), + NodeItem("ShaderNodeExtendedMaterial"), + NodeItem("ShaderNodeFresnel"), + NodeItem("ShaderNodeGamma"), + NodeItem("ShaderNodeGeometry"), + NodeItem("ShaderNodeGroup"), + NodeItem("ShaderNodeHairInfo"), + NodeItem("ShaderNodeHoldout"), + NodeItem("ShaderNodeHueSaturation"), + NodeItem("ShaderNodeInvert"), + NodeItem("ShaderNodeLampData"), + NodeItem("ShaderNodeLayerWeight"), + NodeItem("ShaderNodeLightFalloff"), + NodeItem("ShaderNodeLightPath"), + NodeItem("ShaderNodeMapping"), + NodeItem("ShaderNodeMaterial"), + NodeItem("ShaderNodeMath"), + NodeItem("ShaderNodeMixRGB"), + NodeItem("ShaderNodeMixShader"), + NodeItem("ShaderNodeNewGeometry"), + NodeItem("ShaderNodeNormal"), + NodeItem("ShaderNodeNormalMap"), + NodeItem("ShaderNodeObjectInfo"), + NodeItem("ShaderNodeOutput"), + NodeItem("ShaderNodeOutputLamp"), + NodeItem("ShaderNodeOutputLineStyle"), + NodeItem("ShaderNodeOutputMaterial"), + NodeItem("ShaderNodeOutputWorld"), + NodeItem("ShaderNodeParticleInfo"), + NodeItem("ShaderNodeRGB"), + NodeItem("ShaderNodeRGBCurve"), + NodeItem("ShaderNodeRGBToBW"), + NodeItem("ShaderNodeScript"), + NodeItem("ShaderNodeSeparateHSV"), + NodeItem("ShaderNodeSeparateRGB"), + NodeItem("ShaderNodeSeparateXYZ"), + NodeItem("ShaderNodeSqueeze"), + NodeItem("ShaderNodeSubsurfaceScattering"), + NodeItem("ShaderNodeTangent"), + NodeItem("ShaderNodeTexBrick"), + NodeItem("ShaderNodeTexChecker"), + NodeItem("ShaderNodeTexCoord"), + NodeItem("ShaderNodeTexEnvironment"), + NodeItem("ShaderNodeTexGradient"), + NodeItem("ShaderNodeTexImage"), + NodeItem("ShaderNodeTexMagic"), + NodeItem("ShaderNodeTexMusgrave"), + NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexPointDensity"), + NodeItem("ShaderNodeTexSky"), + NodeItem("ShaderNodeTexVoronoi"), + NodeItem("ShaderNodeTexWave"), + NodeItem("ShaderNodeTexture"), + NodeItem("ShaderNodeUVAlongStroke"), + NodeItem("ShaderNodeUVMap"), + NodeItem("ShaderNodeValToRGB"), + NodeItem("ShaderNodeValue"), + NodeItem("ShaderNodeVectorCurve"), + NodeItem("ShaderNodeVectorMath"), + NodeItem("ShaderNodeVectorTransform"), + NodeItem("ShaderNodeVolumeAbsorption"), + NodeItem("ShaderNodeVolumeScatter"), + NodeItem("ShaderNodeWavelength"), + NodeItem("ShaderNodeWireframe"), + ], + ), + PovrayTextureNodeCategory( + "TEXTUREOUTPUT", + "Output", + items=[NodeItem("TextureNodeValToRGB"), NodeItem("TextureOutputNode")], + ), + PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items=[NodeItem("IsoPropsNode")]), + PovraySceneNodeCategory("FOG", "Fog", items=[NodeItem("PovrayFogNode")]), +] +############### end nodes init +############### nodes ui +##############Nodes + +# def find_node_input(node, name): +# for input in node.inputs: +# if input.name == name: +# return input + +# def panel_node_draw(layout, id_data, output_type, input_name): +# if not id_data.use_nodes: +# #layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE') +# #layout.operator("pov.use_shading_nodes", icon='NODETREE') +# layout.operator("WM_OT_context_toggle", icon='NODETREE').data_path = \ +# "material.pov.material_use_nodes" +# return False + +# ntree = id_data.node_tree + +# node = find_node(id_data, output_type) +# if not node: +# layout.label(text="No output node") +# else: +# input = find_node_input(node, input_name) +# layout.template_node_view(ntree, node, input) + +# return True + + +class NODE_MT_POV_map_create(Menu): + """Create maps""" + + bl_idname = "POVRAY_MT_node_map_create" + bl_label = "Create map" + + def draw(self, context): + layout = self.layout + layout.operator("node.map_create") + + +def menu_func_nodes(self, context): + ob = context.object + if hasattr(ob, 'active_material'): + mat = context.object.active_material + if mat and context.space_data.tree_type == 'ObjectNodeTree': + self.layout.prop(mat.pov, "material_use_nodes") + self.layout.menu(NODE_MT_POV_map_create.bl_idname) + self.layout.operator("wm.updatepreviewkey") + if hasattr(mat, 'active_texture') and context.scene.render.engine == 'POVRAY_RENDER': + tex = mat.active_texture + if tex and context.space_data.tree_type == 'TextureNodeTree': + self.layout.prop(tex.pov, "texture_use_nodes") + + +############### object + + +class ObjectNodeTree(bpy.types.NodeTree): + '''Povray Material Nodes''' + + bl_idname = 'ObjectNodeTree' + bl_label = 'Povray Object Nodes' + bl_icon = 'PLUGIN' + + @classmethod + def poll(cls, context): + return context.scene.render.engine == 'POVRAY_RENDER' + + @classmethod + def get_from_context(cls, context): + ob = context.active_object + if ob and ob.type not in {'LIGHT'}: + ma = ob.active_material + if ma is not None: + nt_name = ma.node_tree + if nt_name != '': + return nt_name, ma, ma + return (None, None, None) + + def update(self): + self.refresh = True + + +################### output ############################################################################################# + + +class PovrayOutputNode(Node, ObjectNodeTree): + '''Output''' + + bl_idname = 'PovrayOutputNode' + bl_label = 'Output' + bl_icon = 'SHADING_TEXTURE' + + def init(self, context): + + self.inputs.new('PovraySocketTexture', "Texture") + + def draw_buttons(self, context, layout): + + ob = context.object + layout.prop(ob.pov, "object_ior", slider=True) + + def draw_buttons_ext(self, context, layout): + + ob = context.object + layout.prop(ob.pov, "object_ior", slider=True) + + def draw_label(self): + return "Output" + + +################### material ########################################################################################### +class PovrayTextureNode(Node, ObjectNodeTree): + '''Texture''' + + bl_idname = 'PovrayTextureNode' + bl_label = 'Simple texture' + bl_icon = 'SHADING_TEXTURE' + + def init(self, context): + + color = self.inputs.new('PovraySocketColor', "Pigment") + color.default_value = (1, 1, 1) + normal = self.inputs.new('NodeSocketFloat', "Normal") + normal.hide_value = True + finish = self.inputs.new('NodeSocketVector', "Finish") + finish.hide_value = True + + self.outputs.new('PovraySocketTexture', "Texture") + + def draw_label(self): + return "Simple texture" + + +class PovrayFinishNode(Node, ObjectNodeTree): + '''Finish''' + + bl_idname = 'PovrayFinishNode' + bl_label = 'Finish' + bl_icon = 'SHADING_TEXTURE' + + def init(self, context): + + self.inputs.new('PovraySocketFloat_0_1', "Emission") + ambient = self.inputs.new('NodeSocketVector', "Ambient") + ambient.hide_value = True + diffuse = self.inputs.new('NodeSocketVector', "Diffuse") + diffuse.hide_value = True + specular = self.inputs.new('NodeSocketVector', "Highlight") + specular.hide_value = True + mirror = self.inputs.new('NodeSocketVector', "Mirror") + mirror.hide_value = True + iridescence = self.inputs.new('NodeSocketVector', "Iridescence") + iridescence.hide_value = True + subsurface = self.inputs.new('NodeSocketVector', "Translucency") + subsurface.hide_value = True + self.outputs.new('NodeSocketVector', "Finish") + + def draw_label(self): + return "Finish" + + +class PovrayDiffuseNode(Node, ObjectNodeTree): + '''Diffuse''' + + bl_idname = 'PovrayDiffuseNode' + bl_label = 'Diffuse' + bl_icon = 'MATSPHERE' + + def init(self, context): + + intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity") + intensity.default_value = 0.8 + albedo = self.inputs.new('NodeSocketBool', "Albedo") + albedo.default_value = False + brilliance = self.inputs.new('PovraySocketFloat_0_10', "Brilliance") + brilliance.default_value = 1.8 + self.inputs.new('PovraySocketFloat_0_1', "Crand") + self.outputs.new('NodeSocketVector', "Diffuse") + + def draw_label(self): + return "Diffuse" + + +class PovrayPhongNode(Node, ObjectNodeTree): + '''Phong''' + + bl_idname = 'PovrayPhongNode' + bl_label = 'Phong' + bl_icon = 'MESH_UVSPHERE' + + def init(self, context): + + albedo = self.inputs.new('NodeSocketBool', "Albedo") + intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity") + intensity.default_value = 0.8 + phong_size = self.inputs.new('PovraySocketInt_0_256', "Size") + phong_size.default_value = 60 + metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic") + + self.outputs.new('NodeSocketVector', "Phong") + + def draw_label(self): + return "Phong" + + +class PovraySpecularNode(Node, ObjectNodeTree): + '''Specular''' + + bl_idname = 'PovraySpecularNode' + bl_label = 'Specular' + bl_icon = 'MESH_UVSPHERE' + + def init(self, context): + + albedo = self.inputs.new('NodeSocketBool', "Albedo") + intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity") + intensity.default_value = 0.8 + roughness = self.inputs.new('PovraySocketFloat_0_1', "Roughness") + roughness.default_value = 0.02 + metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic") + + self.outputs.new('NodeSocketVector', "Specular") + + def draw_label(self): + return "Specular" + + +class PovrayMirrorNode(Node, ObjectNodeTree): + '''Mirror''' + + bl_idname = 'PovrayMirrorNode' + bl_label = 'Mirror' + bl_icon = 'SHADING_TEXTURE' + + def init(self, context): + + color = self.inputs.new('PovraySocketColor', "Color") + color.default_value = (1, 1, 1) + metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic") + metallic.default_value = 1.0 + exponent = self.inputs.new('PovraySocketFloat_0_1', "Exponent") + exponent.default_value = 1.0 + self.inputs.new('PovraySocketFloat_0_1', "Falloff") + self.inputs.new('NodeSocketBool', "Fresnel") + self.inputs.new('NodeSocketBool', "Conserve energy") + self.outputs.new('NodeSocketVector', "Mirror") + + def draw_label(self): + return "Mirror" + + +class PovrayAmbientNode(Node, ObjectNodeTree): + '''Ambient''' + + bl_idname = 'PovrayAmbientNode' + bl_label = 'Ambient' + bl_icon = 'SHADING_SOLID' + + def init(self, context): + + self.inputs.new('PovraySocketColor', "Ambient") + + self.outputs.new('NodeSocketVector', "Ambient") + + def draw_label(self): + return "Ambient" + + +class PovrayIridescenceNode(Node, ObjectNodeTree): + '''Iridescence''' + + bl_idname = 'PovrayIridescenceNode' + bl_label = 'Iridescence' + bl_icon = 'MESH_UVSPHERE' + + def init(self, context): + + amount = self.inputs.new('NodeSocketFloat', "Amount") + amount.default_value = 0.25 + thickness = self.inputs.new('NodeSocketFloat', "Thickness") + thickness.default_value = 1 + self.inputs.new('NodeSocketFloat', "Turbulence") + + self.outputs.new('NodeSocketVector', "Iridescence") + + def draw_label(self): + return "Iridescence" + + +class PovraySubsurfaceNode(Node, ObjectNodeTree): + '''Subsurface''' + + bl_idname = 'PovraySubsurfaceNode' + bl_label = 'Subsurface' + bl_icon = 'MESH_UVSPHERE' + + def init(self, context): + + translucency = self.inputs.new('NodeSocketColor', "Translucency") + translucency.default_value = (0, 0, 0, 1) + energy = self.inputs.new('PovraySocketInt_0_256', "Energy") + energy.default_value = 20 + self.outputs.new('NodeSocketVector', "Translucency") + + def draw_buttons(self, context, layout): + scene = context.scene + layout.prop(scene.pov, "sslt_enable", text="SSLT") + + def draw_buttons_ext(self, context, layout): + scene = context.scene + layout.prop(scene.pov, "sslt_enable", text="SSLT") + + def draw_label(self): + return "Subsurface" + + +##################################################################################################### + + +class PovrayMappingNode(Node, ObjectNodeTree): + '''Mapping''' + + bl_idname = 'PovrayMappingNode' + bl_label = 'Mapping' + bl_icon = 'NODE_TEXTURE' + + warp_type: EnumProperty( + name="Warp Types", + description="Select the type of warp", + items=( + ('cubic', "Cubic", ""), + ('cylindrical', "Cylindrical", ""), + ('planar', "Planar", ""), + ('spherical', "Spherical", ""), + ('toroidal', "Toroidal", ""), + ('uv_mapping', "UV", ""), + ('NONE', "None", "No indentation"), + ), + default='NONE', + ) + + warp_orientation: EnumProperty( + name="Warp Orientation", + description="Select the orientation of warp", + items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")), + default='y', + ) + + warp_dist_exp: FloatProperty( + name="Distance exponent", description="Distance exponent", min=0.0, max=100.0, default=1.0 + ) + + warp_tor_major_radius: FloatProperty( + name="Major radius", + description="Torus is distance from major radius", + min=0.0, + max=5.0, + default=1.0, + ) + + def init(self, context): + self.outputs.new('NodeSocketVector', "Mapping") + + def draw_buttons(self, context, layout): + + column = layout.column() + column.prop(self, "warp_type", text="Warp type") + if self.warp_type in {'toroidal', 'spherical', 'cylindrical', 'planar'}: + column.prop(self, "warp_orientation", text="Orientation") + column.prop(self, "warp_dist_exp", text="Exponent") + if self.warp_type == 'toroidal': + column.prop(self, "warp_tor_major_radius", text="Major R") + + def draw_buttons_ext(self, context, layout): + + column = layout.column() + column.prop(self, "warp_type", text="Warp type") + if self.warp_type in {'toroidal', 'spherical', 'cylindrical', 'planar'}: + column.prop(self, "warp_orientation", text="Orientation") + column.prop(self, "warp_dist_exp", text="Exponent") + if self.warp_type == 'toroidal': + column.prop(self, "warp_tor_major_radius", text="Major R") + + def draw_label(self): + return "Mapping" + + +class PovrayMultiplyNode(Node, ObjectNodeTree): + '''Multiply''' + + bl_idname = 'PovrayMultiplyNode' + bl_label = 'Multiply' + bl_icon = 'SHADING_SOLID' + + amount_x: FloatProperty( + name="X", description="Number of repeats", min=1.0, max=10000.0, default=1.0 + ) + + amount_y: FloatProperty( + name="Y", description="Number of repeats", min=1.0, max=10000.0, default=1.0 + ) + + amount_z: FloatProperty( + name="Z", description="Number of repeats", min=1.0, max=10000.0, default=1.0 + ) + + def init(self, context): + self.outputs.new('NodeSocketVector', "Amount") + + def draw_buttons(self, context, layout): + + column = layout.column() + column.label(text="Amount") + row = column.row(align=True) + row.prop(self, "amount_x") + row.prop(self, "amount_y") + row.prop(self, "amount_z") + + def draw_buttons_ext(self, context, layout): + + column = layout.column() + column.label(text="Amount") + row = column.row(align=True) + row.prop(self, "amount_x") + row.prop(self, "amount_y") + row.prop(self, "amount_z") + + def draw_label(self): + return "Multiply" + + +class PovrayTransformNode(Node, ObjectNodeTree): + '''Transform''' + + bl_idname = 'PovrayTransformNode' + bl_label = 'Transform' + bl_icon = 'NODE_TEXTURE' + + def init(self, context): + + self.inputs.new('PovraySocketFloatUnlimited', "Translate x") + self.inputs.new('PovraySocketFloatUnlimited', "Translate y") + self.inputs.new('PovraySocketFloatUnlimited', "Translate z") + self.inputs.new('PovraySocketFloatUnlimited', "Rotate x") + self.inputs.new('PovraySocketFloatUnlimited', "Rotate y") + self.inputs.new('PovraySocketFloatUnlimited', "Rotate z") + sX = self.inputs.new('PovraySocketFloatUnlimited', "Scale x") + sX.default_value = 1.0 + sY = self.inputs.new('PovraySocketFloatUnlimited', "Scale y") + sY.default_value = 1.0 + sZ = self.inputs.new('PovraySocketFloatUnlimited', "Scale z") + sZ.default_value = 1.0 + + self.outputs.new('NodeSocketVector', "Transform") + + def draw_label(self): + return "Transform" + + +class PovrayValueNode(Node, ObjectNodeTree): + '''Value''' + + bl_idname = 'PovrayValueNode' + bl_label = 'Value' + bl_icon = 'SHADING_SOLID' + + def init(self, context): + + self.outputs.new('PovraySocketUniversal', "Value") + + def draw_label(self): + return "Value" + + +class PovrayModifierNode(Node, ObjectNodeTree): + '''Modifier''' + + bl_idname = 'PovrayModifierNode' + bl_label = 'Modifier' + bl_icon = 'NODE_TEXTURE' + + def init(self, context): + + turb_x = self.inputs.new('PovraySocketFloat_0_10', "Turb X") + turb_x.default_value = 0.1 + turb_y = self.inputs.new('PovraySocketFloat_0_10', "Turb Y") + turb_y.default_value = 0.1 + turb_z = self.inputs.new('PovraySocketFloat_0_10', "Turb Z") + turb_z.default_value = 0.1 + octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves") + octaves.default_value = 1 + lambat = self.inputs.new('PovraySocketFloat_0_10', "Lambda") + lambat.default_value = 2.0 + omega = self.inputs.new('PovraySocketFloat_0_10', "Omega") + omega.default_value = 0.5 + freq = self.inputs.new('PovraySocketFloat_0_10', "Frequency") + freq.default_value = 2.0 + self.inputs.new('PovraySocketFloat_0_10', "Phase") + + self.outputs.new('NodeSocketVector', "Modifier") + + def draw_label(self): + return "Modifier" + + +class PovrayPigmentNode(Node, ObjectNodeTree): + '''Pigment''' + + bl_idname = 'PovrayPigmentNode' + bl_label = 'Color' + bl_icon = 'SHADING_SOLID' + + def init(self, context): + + color = self.inputs.new('PovraySocketColor', "Color") + color.default_value = (1, 1, 1) + pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter") + transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit") + self.outputs.new('NodeSocketColor', "Pigment") + + def draw_label(self): + return "Color" + + +class PovrayColorImageNode(Node, ObjectNodeTree): + '''ColorImage''' + + bl_idname = 'PovrayColorImageNode' + bl_label = 'Image map' + + map_type: bpy.props.EnumProperty( + name="Map type", + description="", + items=( + ('uv_mapping', "UV", ""), + ('0', "Planar", "Default planar mapping"), + ('1', "Spherical", "Spherical mapping"), + ('2', "Cylindrical", "Cylindrical mapping"), + ('5', "Torroidal", "Torus or donut shaped mapping"), + ), + default='0', + ) + image: StringProperty(maxlen=1024) # , subtype="FILE_PATH" + interpolate: EnumProperty( + name="Interpolate", + description="Adding the interpolate keyword can smooth the jagged look of a bitmap", + items=( + ('2', "Bilinear", "Gives bilinear interpolation"), + ('4', "Normalized", "Gives normalized distance"), + ), + default='2', + ) + premultiplied: BoolProperty(default=False) + once: BoolProperty(description="Not to repeat", default=False) + + def init(self, context): + + gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma") + gamma.default_value = 2.0 + transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit") + pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter") + mapping = self.inputs.new('NodeSocketVector', "Mapping") + mapping.hide_value = True + transform = self.inputs.new('NodeSocketVector', "Transform") + transform.hide_value = True + modifier = self.inputs.new('NodeSocketVector', "Modifier") + modifier.hide_value = True + + self.outputs.new('NodeSocketColor', "Pigment") + + def draw_buttons(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + row = column.row() + row.prop(self, "premultiplied", text="Premul") + row.prop(self, "once", text="Once") + + def draw_buttons_ext(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + row = column.row() + row.prop(self, "premultiplied", text="Premul") + row.prop(self, "once", text="Once") + + def draw_label(self): + return "Image map" + + +class PovrayBumpMapNode(Node, ObjectNodeTree): + '''BumpMap''' + + bl_idname = 'PovrayBumpMapNode' + bl_label = 'Bump map' + bl_icon = 'TEXTURE' + + map_type: bpy.props.EnumProperty( + name="Map type", + description="", + items=( + ('uv_mapping', "UV", ""), + ('0', "Planar", "Default planar mapping"), + ('1', "Spherical", "Spherical mapping"), + ('2', "Cylindrical", "Cylindrical mapping"), + ('5', "Torroidal", "Torus or donut shaped mapping"), + ), + default='0', + ) + image: StringProperty(maxlen=1024) # , subtype="FILE_PATH" + interpolate: EnumProperty( + name="Interpolate", + description="Adding the interpolate keyword can smooth the jagged look of a bitmap", + items=( + ('2', "Bilinear", "Gives bilinear interpolation"), + ('4', "Normalized", "Gives normalized distance"), + ), + default='2', + ) + once: BoolProperty(description="Not to repeat", default=False) + + def init(self, context): + + self.inputs.new('PovraySocketFloat_0_10', "Normal") + mapping = self.inputs.new('NodeSocketVector', "Mapping") + mapping.hide_value = True + transform = self.inputs.new('NodeSocketVector', "Transform") + transform.hide_value = True + modifier = self.inputs.new('NodeSocketVector', "Modifier") + modifier.hide_value = True + + normal = self.outputs.new('NodeSocketFloat', "Normal") + normal.hide_value = True + + def draw_buttons(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + column.prop(self, "once", text="Once") + + def draw_buttons_ext(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + column.prop(self, "once", text="Once") + + def draw_label(self): + return "Bump Map" + + +class PovrayImagePatternNode(Node, ObjectNodeTree): + '''ImagePattern''' + + bl_idname = 'PovrayImagePatternNode' + bl_label = 'Image pattern' + bl_icon = 'NODE_TEXTURE' + + map_type: bpy.props.EnumProperty( + name="Map type", + description="", + items=( + ('uv_mapping', "UV", ""), + ('0', "Planar", "Default planar mapping"), + ('1', "Spherical", "Spherical mapping"), + ('2', "Cylindrical", "Cylindrical mapping"), + ('5', "Torroidal", "Torus or donut shaped mapping"), + ), + default='0', + ) + image: StringProperty(maxlen=1024) # , subtype="FILE_PATH" + interpolate: EnumProperty( + name="Interpolate", + description="Adding the interpolate keyword can smooth the jagged look of a bitmap", + items=( + ('2', "Bilinear", "Gives bilinear interpolation"), + ('4', "Normalized", "Gives normalized distance"), + ), + default='2', + ) + premultiplied: BoolProperty(default=False) + once: BoolProperty(description="Not to repeat", default=False) + use_alpha: BoolProperty(default=True) + + def init(self, context): + + gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma") + gamma.default_value = 2.0 + + self.outputs.new('PovraySocketPattern', "Pattern") + + def draw_buttons(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + row = column.row() + row.prop(self, "premultiplied", text="Premul") + row.prop(self, "once", text="Once") + column.prop(self, "use_alpha", text="Use alpha") + + def draw_buttons_ext(self, context, layout): + + column = layout.column() + im = None + for image in bpy.data.images: + if image.name == self.image: + im = image + split = column.split(factor=0.8, align=True) + split.prop_search(self, "image", context.blend_data, "images", text="") + split.operator("pov.imageopen", text="", icon="FILEBROWSER") + if im is not None: + column.prop(im, "source", text="") + column.prop(self, "map_type", text="") + column.prop(self, "interpolate", text="") + row = column.row() + row.prop(self, "premultiplied", text="Premul") + row.prop(self, "once", text="Once") + + def draw_label(self): + return "Image pattern" + + +class ShaderPatternNode(Node, ObjectNodeTree): + '''Pattern''' + + bl_idname = 'ShaderPatternNode' + bl_label = 'Other patterns' + + pattern: EnumProperty( + name="Pattern", + description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling", + items=( + ('agate', "Agate", ""), + ('crackle', "Crackle", ""), + ('gradient', "Gradient", ""), + ('pavement', "Pavement", ""), + ('spiral1', "Spiral 1", ""), + ('spiral2', "Spiral 2", ""), + ('tiling', "Tiling", ""), + ), + default='agate', + ) + + agate_turb: FloatProperty( + name="Agate turb", description="Agate turbulence", min=0.0, max=100.0, default=0.5 + ) + + crackle_form_x: FloatProperty( + name="X", description="Form vector X", min=-150.0, max=150.0, default=-1 + ) + + crackle_form_y: FloatProperty( + name="Y", description="Form vector Y", min=-150.0, max=150.0, default=1 + ) + + crackle_form_z: FloatProperty( + name="Z", description="Form vector Z", min=-150.0, max=150.0, default=0 + ) + + crackle_metric: FloatProperty( + name="Metric", description="Crackle metric", min=0.0, max=150.0, default=1 + ) + + crackle_solid: BoolProperty(name="Solid", description="Crackle solid", default=False) + + spiral_arms: FloatProperty(name="Number", description="", min=0.0, max=256.0, default=2.0) + + tiling_number: IntProperty(name="Number", description="", min=1, max=27, default=1) + + gradient_orient: EnumProperty( + name="Orient", + description="", + items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")), + default='x', + ) + + def init(self, context): + + pat = self.outputs.new('PovraySocketPattern', "Pattern") + + def draw_buttons(self, context, layout): + + layout.prop(self, "pattern", text="") + if self.pattern == 'agate': + layout.prop(self, "agate_turb") + if self.pattern == 'crackle': + layout.prop(self, "crackle_metric") + layout.prop(self, "crackle_solid") + layout.label(text="Form:") + layout.prop(self, "crackle_form_x") + layout.prop(self, "crackle_form_y") + layout.prop(self, "crackle_form_z") + if self.pattern in {"spiral1", "spiral2"}: + layout.prop(self, "spiral_arms") + if self.pattern in {'tiling'}: + layout.prop(self, "tiling_number") + if self.pattern in {'gradient'}: + layout.prop(self, "gradient_orient") + + def draw_buttons_ext(self, context, layout): + pass + + def draw_label(self): + return "Other patterns" + + +class ShaderTextureMapNode(Node, ObjectNodeTree): + '''Texture Map''' + + bl_idname = 'ShaderTextureMapNode' + bl_label = 'Texture map' + + brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500) + + brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525) + + brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250) + + brick_mortar: FloatProperty( + name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01 + ) + + def init(self, context): + mat = bpy.context.object.active_material + self.inputs.new('PovraySocketPattern', "") + color = self.inputs.new('NodeSocketColor', "Color ramp") + color.hide_value = True + for i in range(0, 4): + transform = self.inputs.new('PovraySocketTransform', "Transform") + transform.hide_value = True + number = mat.pov.inputs_number + for i in range(number): + self.inputs.new('PovraySocketTexture', "%s" % i) + + self.outputs.new('PovraySocketTexture', "Texture") + + def draw_buttons(self, context, layout): + + if self.inputs[0].default_value == 'brick': + layout.prop(self, "brick_mortar") + layout.label(text="Brick size:") + layout.prop(self, "brick_size_x") + layout.prop(self, "brick_size_y") + layout.prop(self, "brick_size_z") + + def draw_buttons_ext(self, context, layout): + + if self.inputs[0].default_value == 'brick': + layout.prop(self, "brick_mortar") + layout.label(text="Brick size:") + layout.prop(self, "brick_size_x") + layout.prop(self, "brick_size_y") + layout.prop(self, "brick_size_z") + + def draw_label(self): + return "Texture map" + + +class ShaderNormalMapNode(Node, ObjectNodeTree): + '''Normal Map''' + + bl_idname = 'ShaderNormalMapNode' + bl_label = 'Normal map' + + brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500) + + brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525) + + brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250) + + brick_mortar: FloatProperty( + name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01 + ) + + def init(self, context): + self.inputs.new('PovraySocketPattern', "") + normal = self.inputs.new('PovraySocketFloat_10', "Normal") + slope = self.inputs.new('PovraySocketMap', "Slope map") + for i in range(0, 4): + transform = self.inputs.new('PovraySocketTransform', "Transform") + transform.hide_value = True + self.outputs.new('PovraySocketNormal', "Normal") + + def draw_buttons(self, context, layout): + # for i, inp in enumerate(self.inputs): + + if self.inputs[0].default_value == 'brick': + layout.prop(self, "brick_mortar") + layout.label(text="Brick size:") + layout.prop(self, "brick_size_x") + layout.prop(self, "brick_size_y") + layout.prop(self, "brick_size_z") + + def draw_buttons_ext(self, context, layout): + + if self.inputs[0].default_value == 'brick': + layout.prop(self, "brick_mortar") + layout.label(text="Brick size:") + layout.prop(self, "brick_size_x") + layout.prop(self, "brick_size_y") + layout.prop(self, "brick_size_z") + + def draw_label(self): + return "Normal map" + + +class ShaderNormalMapEntryNode(Node, ObjectNodeTree): + '''Normal Map Entry''' + + bl_idname = 'ShaderNormalMapEntryNode' + bl_label = 'Normal map entry' + + def init(self, context): + self.inputs.new('PovraySocketFloat_0_1', "Stop") + self.inputs.new('PovraySocketFloat_0_1', "Gray") + + def draw_label(self): + return "Normal map entry" + + +class IsoPropsNode(Node, CompositorNodeTree): + '''ISO Props''' + + bl_idname = 'IsoPropsNode' + bl_label = 'Iso' + node_label: StringProperty(maxlen=1024) + + def init(self, context): + ob = bpy.context.object + self.node_label = ob.name + text_name = ob.pov.function_text + if text_name: + text = bpy.data.texts[text_name] + for line in text.lines: + split = line.body.split() + if split[0] == "#declare": + socket = self.inputs.new('NodeSocketFloat', "%s" % split[1]) + value = split[3].split(";") + value = value[0] + socket.default_value = float(value) + + def draw_label(self): + return self.node_label + + +class PovrayFogNode(Node, CompositorNodeTree): + '''Fog settings''' + + bl_idname = 'PovrayFogNode' + bl_label = 'Fog' + + def init(self, context): + color = self.inputs.new('NodeSocketColor', "Color") + color.default_value = (0.7, 0.7, 0.7, 0.25) + self.inputs.new('PovraySocketFloat_0_1', "Filter") + distance = self.inputs.new('NodeSocketInt', "Distance") + distance.default_value = 150 + self.inputs.new('NodeSocketBool', "Ground") + fog_offset = self.inputs.new('NodeSocketFloat', "Offset") + fog_alt = self.inputs.new('NodeSocketFloat', "Altitude") + turb = self.inputs.new('NodeSocketVector', "Turbulence") + turb_depth = self.inputs.new('PovraySocketFloat_0_10', "Depth") + turb_depth.default_value = 0.5 + octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves") + octaves.default_value = 5 + lambdat = self.inputs.new('PovraySocketFloat_0_10', "Lambda") + lambdat.default_value = 1.25 + omega = self.inputs.new('PovraySocketFloat_0_10', "Omega") + omega.default_value = 0.35 + translate = self.inputs.new('NodeSocketVector', "Translate") + rotate = self.inputs.new('NodeSocketVector', "Rotate") + scale = self.inputs.new('NodeSocketVector', "Scale") + scale.default_value = (1, 1, 1) + + def draw_label(self): + return "Fog" + + +class PovraySlopeNode(Node, TextureNodeTree): + '''Output''' + + bl_idname = 'PovraySlopeNode' + bl_label = 'Slope Map' + + def init(self, context): + self.use_custom_color = True + self.color = (0, 0.2, 0) + slope = self.inputs.new('PovraySocketSlope', "0") + slope = self.inputs.new('PovraySocketSlope', "1") + slopemap = self.outputs.new('PovraySocketMap', "Slope map") + output.hide_value = True + + def draw_buttons(self, context, layout): + + layout.operator("pov.nodeinputadd") + row = layout.row() + row.label(text='Value') + row.label(text='Height') + row.label(text='Slope') + + def draw_buttons_ext(self, context, layout): + + layout.operator("pov.nodeinputadd") + row = layout.row() + row.label(text='Value') + row.label(text='Height') + row.label(text='Slope') + + def draw_label(self): + return "Slope Map" + + +######################################## Texture nodes ############################### +class TextureOutputNode(Node, TextureNodeTree): + '''Output''' + + bl_idname = 'TextureOutputNode' + bl_label = 'Color Map' + + def init(self, context): + tex = bpy.context.object.active_material.active_texture + num_sockets = int(tex.pov.density_lines / 32) + for i in range(num_sockets): + color = self.inputs.new('NodeSocketColor', "%s" % i) + color.hide_value = True + + def draw_buttons(self, context, layout): + + layout.label(text="Color Ramps:") + + def draw_label(self): + return "Color Map" + + +################################################################################## +#################################Operators######################################## +################################################################################## + + +class NODE_OT_iso_add(Operator): + bl_idname = "pov.nodeisoadd" + bl_label = "Create iso props" + + def execute(self, context): + ob = bpy.context.object + if bpy.context.scene.use_nodes == False: + bpy.context.scene.use_nodes = True + tree = bpy.context.scene.node_tree + for node in tree.nodes: + if node.bl_idname == "IsoPropsNode" and node.label == ob.name: + tree.nodes.remove(node) + isonode = tree.nodes.new('IsoPropsNode') + isonode.location = (0, 0) + isonode.label = ob.name + return {'FINISHED'} + + +class NODE_OT_map_create(Operator): + bl_idname = "node.map_create" + bl_label = "Create map" + + def execute(self, context): + x = y = 0 + space = context.space_data + tree = space.edit_tree + for node in tree.nodes: + if node.select == True: + x, y = node.location + node.select = False + tmap = tree.nodes.new('ShaderTextureMapNode') + tmap.location = (x - 200, y) + return {'FINISHED'} + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_props_dialog(self) + + def draw(self, context): + layout = self.layout + mat = context.object.active_material + layout.prop(mat.pov, "inputs_number") + + +class NODE_OT_povray_node_texture_map_add(Operator): + bl_idname = "pov.nodetexmapadd" + bl_label = "Texture map" + + def execute(self, context): + tree = bpy.context.object.active_material.node_tree + tmap = tree.nodes.active + bpy.context.object.active_material.node_tree.nodes.active = tmap + el = tmap.color_ramp.elements.new(0.5) + for el in tmap.color_ramp.elements: + el.color = (0, 0, 0, 1) + for inp in tmap.inputs: + tmap.inputs.remove(inp) + for outp in tmap.outputs: + tmap.outputs.remove(outp) + pattern = tmap.inputs.new('NodeSocketVector', "Pattern") + pattern.hide_value = True + for i in range(0, 3): + tmap.inputs.new('NodeSocketColor', "Shader") + tmap.outputs.new('NodeSocketShader', "BSDF") + tmap.label = "Texture Map" + return {'FINISHED'} + + +class NODE_OT_povray_node_output_add(Operator): + bl_idname = "pov.nodeoutputadd" + bl_label = "Output" + + def execute(self, context): + tree = bpy.context.object.active_material.node_tree + tmap = tree.nodes.new('ShaderNodeOutputMaterial') + bpy.context.object.active_material.node_tree.nodes.active = tmap + for inp in tmap.inputs: + tmap.inputs.remove(inp) + tmap.inputs.new('NodeSocketShader', "Surface") + tmap.label = "Output" + return {'FINISHED'} + + +class NODE_OT_povray_node_layered_add(Operator): + bl_idname = "pov.nodelayeredadd" + bl_label = "Layered material" + + def execute(self, context): + tree = bpy.context.object.active_material.node_tree + tmap = tree.nodes.new('ShaderNodeAddShader') + bpy.context.object.active_material.node_tree.nodes.active = tmap + tmap.label = "Layered material" + return {'FINISHED'} + + +class NODE_OT_povray_input_add(Operator): + bl_idname = "pov.nodeinputadd" + bl_label = "Add entry" + + def execute(self, context): + node = bpy.context.object.active_material.node_tree.nodes.active + if node.type in {'VALTORGB'}: + number = 1 + for inp in node.inputs: + if inp.type == 'SHADER': + number += 1 + node.inputs.new('NodeSocketShader', "%s" % number) + els = node.color_ramp.elements + pos1 = els[len(els) - 1].position + pos2 = els[len(els) - 2].position + pos = (pos1 - pos2) / 2 + pos2 + el = els.new(pos) + + if node.bl_idname == 'PovraySlopeNode': + number = len(node.inputs) + node.inputs.new('PovraySocketSlope', "%s" % number) + + return {'FINISHED'} + + +class NODE_OT_povray_input_remove(Operator): + bl_idname = "pov.nodeinputremove" + bl_label = "Remove input" + + def execute(self, context): + node = bpy.context.object.active_material.node_tree.nodes.active + if node.type in {'VALTORGB', 'ADD_SHADER'}: + number = len(node.inputs) - 1 + if number > 5: + inp = node.inputs[number] + node.inputs.remove(inp) + if node.type in {'VALTORGB'}: + els = node.color_ramp.elements + number = len(els) - 2 + el = els[number] + els.remove(el) + return {'FINISHED'} + + +class NODE_OT_povray_image_open(Operator): + bl_idname = "pov.imageopen" + bl_label = "Open" + + filepath: StringProperty( + name="File Path", description="Open image", maxlen=1024, subtype='FILE_PATH' + ) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + im = bpy.data.images.load(self.filepath) + node = context.object.active_material.node_tree.nodes.active + node.image = im.name + return {'FINISHED'} + + +# class TEXTURE_OT_povray_open_image(Operator): +# bl_idname = "pov.openimage" +# bl_label = "Open Image" + +# filepath = StringProperty( +# name="File Path", +# description="Open image", +# maxlen=1024, +# subtype='FILE_PATH', +# ) + +# def invoke(self, context, event): +# context.window_manager.fileselect_add(self) +# return {'RUNNING_MODAL'} + +# def execute(self, context): +# im=bpy.data.images.load(self.filepath) +# tex = context.texture +# tex.pov.image = im.name +# view_layer = context.view_layer +# view_layer.update() +# return {'FINISHED'} + + +class PovrayPatternNode(Operator): + bl_idname = "pov.patternnode" + bl_label = "Pattern" + + add = True + + def execute(self, context): + space = context.space_data + tree = space.edit_tree + for node in tree.nodes: + node.select = False + if self.add == True: + tmap = tree.nodes.new('ShaderNodeValToRGB') + tmap.label = "Pattern" + for inp in tmap.inputs: + tmap.inputs.remove(inp) + for outp in tmap.outputs: + tmap.outputs.remove(outp) + pattern = tmap.inputs.new('PovraySocketPattern', "Pattern") + pattern.hide_value = True + mapping = tmap.inputs.new('NodeSocketVector', "Mapping") + mapping.hide_value = True + transform = tmap.inputs.new('NodeSocketVector', "Transform") + transform.hide_value = True + modifier = tmap.inputs.new('NodeSocketVector', "Modifier") + modifier.hide_value = True + for i in range(0, 2): + tmap.inputs.new('NodeSocketShader', "%s" % (i + 1)) + tmap.outputs.new('NodeSocketShader', "Material") + tmap.outputs.new('NodeSocketColor', "Color") + tree.nodes.active = tmap + self.add = False + aNode = tree.nodes.active + aNode.select = True + v2d = context.region.view2d + x, y = v2d.region_to_view(self.x, self.y) + aNode.location = (x, y) + + def modal(self, context, event): + if event.type == 'MOUSEMOVE': + self.x = event.mouse_region_x + self.y = event.mouse_region_y + self.execute(context) + return {'RUNNING_MODAL'} + elif event.type == 'LEFTMOUSE': + return {'FINISHED'} + elif event.type in ('RIGHTMOUSE', 'ESC'): + return {'CANCELLED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + +class UpdatePreviewMaterial(Operator): + '''Operator update preview material''' + + bl_idname = "node.updatepreview" + bl_label = "Update preview" + + def execute(self, context): + scene = context.view_layer + ob = context.object + for obj in scene.objects: + if obj != ob: + scene.objects.active = ob + break + scene.objects.active = ob + + def modal(self, context, event): + if event.type == 'RIGHTMOUSE': + self.execute(context) + return {'FINISHED'} + return {'PASS_THROUGH'} + + def invoke(self, context, event): + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + + +class UpdatePreviewKey(Operator): + '''Operator update preview keymap''' + + bl_idname = "wm.updatepreviewkey" + bl_label = "Activate RMB" + + @classmethod + def poll(cls, context): + conf = context.window_manager.keyconfigs.active + mapstr = "Node Editor" + map = conf.keymaps[mapstr] + try: + map.keymap_items["node.updatepreview"] + return False + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + return True + + def execute(self, context): + conf = context.window_manager.keyconfigs.active + mapstr = "Node Editor" + map = conf.keymaps[mapstr] + map.keymap_items.new("node.updatepreview", type='RIGHTMOUSE', value="PRESS") + return {'FINISHED'} + + +classes = ( + PovraySocketUniversal, + PovraySocketFloat_0_1, + PovraySocketFloat_0_10, + PovraySocketFloat_10, + PovraySocketFloatPositive, + PovraySocketFloat_000001_10, + PovraySocketFloatUnlimited, + PovraySocketInt_1_9, + PovraySocketInt_0_256, + PovraySocketPattern, + PovraySocketColor, + PovraySocketColorRGBFT, + PovraySocketTexture, + PovraySocketTransform, + PovraySocketNormal, + PovraySocketSlope, + PovraySocketMap, + # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ? + # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ? + # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ? + NODE_MT_POV_map_create, + ObjectNodeTree, + PovrayOutputNode, + PovrayTextureNode, + PovrayFinishNode, + PovrayDiffuseNode, + PovrayPhongNode, + PovraySpecularNode, + PovrayMirrorNode, + PovrayAmbientNode, + PovrayIridescenceNode, + PovraySubsurfaceNode, + PovrayMappingNode, + PovrayMultiplyNode, + PovrayTransformNode, + PovrayValueNode, + PovrayModifierNode, + PovrayPigmentNode, + PovrayColorImageNode, + PovrayBumpMapNode, + PovrayImagePatternNode, + ShaderPatternNode, + ShaderTextureMapNode, + ShaderNormalMapNode, + ShaderNormalMapEntryNode, + IsoPropsNode, + PovrayFogNode, + PovraySlopeNode, + TextureOutputNode, + NODE_OT_iso_add, + NODE_OT_map_create, + NODE_OT_povray_node_texture_map_add, + NODE_OT_povray_node_output_add, + NODE_OT_povray_node_layered_add, + NODE_OT_povray_input_add, + NODE_OT_povray_input_remove, + NODE_OT_povray_image_open, + PovrayPatternNode, + UpdatePreviewMaterial, + UpdatePreviewKey, +) + + +def register(): + # from bpy.utils import register_class + bpy.types.NODE_HT_header.append(menu_func_nodes) + nodeitems_utils.register_node_categories("POVRAYNODES", node_categories) + for cls in classes: + register_class(cls) + + +def unregister(): + # from bpy.utils import unregister_class + + for cls in reversed(classes): + unregister_class(cls) + nodeitems_utils.unregister_node_categories("POVRAYNODES") + bpy.types.NODE_HT_header.remove(menu_func_nodes) diff --git a/render_povray/shading_properties.py b/render_povray/shading_properties.py new file mode 100755 index 000000000..18895eba1 --- /dev/null +++ b/render_povray/shading_properties.py @@ -0,0 +1,2290 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Declare shading properties exported to POV textures.""" +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import ( + FloatVectorProperty, + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, +) + + +def check_material(mat): + """Check that material node tree is not empty if use node button is on""" + if mat is not None: + if mat.use_nodes: + if not mat.node_tree: # FORMERLY : #mat.active_node_material is not None: + return True + return False + return True + return False + + +def pov_context_tex_datablock(context): + """Texture context type recreated as deprecated in blender 2.8""" + + idblock = context.brush + if idblock and context.scene.texture_context == 'OTHER': + return idblock + + # idblock = bpy.context.active_object.active_material + idblock = context.view_layer.objects.active.active_material + if idblock and context.scene.texture_context == 'MATERIAL': + return idblock + + idblock = context.scene.world + if idblock and context.scene.texture_context == 'WORLD': + return idblock + + idblock = context.light + if idblock and context.scene.texture_context == 'LIGHT': + return idblock + + if context.particle_system and context.scene.texture_context == 'PARTICLES': + idblock = context.particle_system.settings + + return idblock + + idblock = context.line_style + if idblock and context.scene.texture_context == 'LINESTYLE': + return idblock + + +def active_texture_name_from_uilist(self, context): + """Name created texture slots the same as created texture""" + idblock = pov_context_tex_datablock(context) + # mat = context.view_layer.objects.active.active_material + if idblock is not None: + index = idblock.pov.active_texture_index + name = idblock.pov_texture_slots[index].name + newname = idblock.pov_texture_slots[index].texture + tex = bpy.data.textures[name] + tex.name = newname + idblock.pov_texture_slots[index].name = newname + + +def active_texture_name_from_search(self, context): + """Texture rolldown to change the data linked by an existing texture""" + idblock = pov_context_tex_datablock(context) + # mat = context.view_layer.objects.active.active_material + if idblock is not None: + index = idblock.pov.active_texture_index + slot = idblock.pov_texture_slots[index] + name = slot.texture_search + + try: + # tex = bpy.data.textures[name] + slot.name = name + slot.texture = name + # Switch paint brush to this texture so settings remain contextual + # bpy.context.tool_settings.image_paint.brush.texture = tex + # bpy.context.tool_settings.image_paint.brush.mask_texture = tex + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass + + +def brush_texture_update(self, context): + + """Brush texture rolldown must show active slot texture props""" + idblock = pov_context_tex_datablock(context) + if idblock is not None: + # mat = context.view_layer.objects.active.active_material + idblock = pov_context_tex_datablock(context) + slot = idblock.pov_texture_slots[idblock.pov.active_texture_index] + tex = slot.texture + + if tex: + # Switch paint brush to active texture so slot and settings remain contextual + bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex] + bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex] + + +class RenderPovSettingsMaterial(PropertyGroup): + """Declare material level properties controllable in UI and translated to POV.""" + + ######################Begin Old Blender Internal Props######################### + # former Space properties from removed Blender Internal + use_limited_texture_context: BoolProperty( + name="", + description="Use the limited version of texture user (for â€old shading’ mode)", + default=True, + ) + texture_context: EnumProperty( + name="Texture context", + description="Type of texture data to display and edit", + items=( + ("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures" + ("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures" + ("LAMP", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures" + ( + "PARTICLES", + "", + "Show particles textures", + "PARTICLES", + 3, + ), # "Show particles textures" + ( + "LINESTYLE", + "", + "Show linestyle textures", + "LINE_DATA", + 4, + ), # "Show linestyle textures" + ( + "OTHER", + "", + "Show other data textures", + "TEXTURE_DATA", + 5, + ), # "Show other data textures" + ), + default="MATERIAL", + ) + + active_texture_index: IntProperty( + name="Index for texture_slots", default=0, update=brush_texture_update + ) + + transparency_method: EnumProperty( + name="Specular Shader Model", + description="Method to use for rendering transparency", + items=( + ("MASK", "Mask", "Mask the background"), + ("Z_TRANSPARENCY", "Z Transparency", "Use alpha buffer for transparent faces"), + ("RAYTRACE", "Raytrace", "Use raytracing for transparent refraction rendering"), + ), + default="MASK", + ) + + use_transparency: BoolProperty( + name="Transparency", description="Render material as transparent", default=False + ) + + alpha: FloatProperty( + name="Alpha", + description="Alpha transparency of the material", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + specular_alpha: FloatProperty( + name="Specular alpha", + description="Alpha transparency for specular areas", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + ambient: FloatProperty( + name="Ambient", + description="Amount of global ambient color the material receives", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + diffuse_color: FloatVectorProperty( + name="Diffuse color", + description=("Diffuse color of the material"), + precision=4, + step=0.01, + min=0, # max=inf, soft_max=1, + default=(0.6, 0.6, 0.6), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + darkness: FloatProperty( + name="Darkness", + description="Minnaert darkness", + min=0.0, + max=2.0, + soft_min=0.0, + soft_max=2.0, + default=1.0, + precision=3, + ) + + diffuse_fresnel: FloatProperty( + name="Diffuse fresnel", + description="Power of Fresnel", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=1.0, + precision=3, + ) + + diffuse_fresnel_factor: FloatProperty( + name="Diffuse fresnel factor", + description="Blending factor of Fresnel", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=0.5, + precision=3, + ) + + diffuse_intensity: FloatProperty( + name="Diffuse intensity", + description="Amount of diffuse reflection multiplying color", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.8, + precision=3, + ) + + diffuse_ramp_blend: EnumProperty( + name="Diffuse ramp blend", + description="Blending method of the ramp and the diffuse color", + items=( + ("MIX", "Mix", ""), + ("ADD", "Add", ""), + ("MULTIPLY", "Multiply", ""), + ("SUBTRACT", "Subtract", ""), + ("SCREEN", "Screen", ""), + ("DIVIDE", "Divide", ""), + ("DIFFERENCE", "Difference", ""), + ("DARKEN", "Darken", ""), + ("LIGHTEN", "Lighten", ""), + ("OVERLAY", "Overlay", ""), + ("DODGE", "Dodge", ""), + ("BURN", "Burn", ""), + ("HUE", "Hue", ""), + ("SATURATION", "Saturation", ""), + ("VALUE", "Value", ""), + ("COLOR", "Color", ""), + ("SOFT_LIGHT", "Soft light", ""), + ("LINEAR_LIGHT", "Linear light", ""), + ), + default="MIX", + ) + + diffuse_ramp_factor: FloatProperty( + name="Factor", + description="Blending factor (also uses alpha in Colorband)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + diffuse_ramp_input: EnumProperty( + name="Input", + description="How the ramp maps on the surface", + items=( + ("SHADER", "Shader", ""), + ("ENERGY", "Energy", ""), + ("NORMAL", "Normal", ""), + ("RESULT", "Result", ""), + ), + default="SHADER", + ) + + diffuse_shader: EnumProperty( + name="Diffuse Shader Model", + description="How the ramp maps on the surface", + items=( + ("LAMBERT", "Lambert", "Use a Lambertian shader"), + ("OREN_NAYAR", "Oren-Nayar", "Use an Oren-Nayar shader"), + ("MINNAERT", "Minnaert", "Use a Minnaert shader"), + ("FRESNEL", "Fresnel", "Use a Fresnel shader"), + ), + default="LAMBERT", + ) + + diffuse_toon_size: FloatProperty( + name="Size", + description="Size of diffuse toon area", + min=0.0, + max=3.14, + soft_min=0.0, + soft_max=3.14, + default=0.5, + precision=3, + ) + + diffuse_toon_smooth: FloatProperty( + name="Smooth", + description="Smoothness of diffuse toon area", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.1, + precision=3, + ) + + emit: FloatProperty( + name="Emit", + description="Amount of light to emit", + min=0.0, + soft_min=0.0, # max=inf, soft_max=inf, + default=0.0, + precision=3, + ) + + mirror_color: FloatVectorProperty( + name="Mirror color", + description=("Mirror color of the material"), + precision=4, + step=0.01, + min=0, # max=inf, soft_max=1, + default=(0.6, 0.6, 0.6), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + roughness: FloatProperty( + name="Roughness", + description="Oren-Nayar Roughness", + min=0.0, + max=3.14, + soft_min=0.0, + soft_max=3.14, + precision=3, + default=0.5, + ) + + halo: BoolProperty(name="Halo", description=" Halo settings for the material", default=False) + # (was readonly in Blender2.79, never None) + + line_color: FloatVectorProperty( + name="Line color", + description=("Line color used for Freestyle line rendering"), + precision=4, + step=0.01, + min=0, # max=inf, soft_max=1, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + # diffuse_ramp: + ## Color ramp used to affect diffuse shading + ## Type: ColorRamp, (readonly) + + # cr_node = bpy.data.materials['Material'].node_tree.nodes['ColorRamp'] + # layout.template_color_ramp(cr_node, "color_ramp", expand=True) + + # ou + + # class bpy.types.ColorRamp(bpy_struct) + + line_priority: IntProperty( + name="Recursion Limit", + description="The line color of a higher priority is used at material boundaries", + min=0, + max=32767, + default=0, + ) + + specular_color: FloatVectorProperty( + name="Specular color", + description=("Specular color of the material "), + precision=4, + step=0.01, + min=0, # max=inf, soft_max=1, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + specular_hardness: IntProperty( + name="Hardness", + description="How hard (sharp) the specular reflection is", + min=1, + max=511, + default=30, + ) + + specular_intensity: FloatProperty( + name="Intensity", + description="How intense (bright) the specular reflection is", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.1, + precision=3, + ) + + specular_ior: FloatProperty( + name="IOR", + description="Specular index of refraction", + min=-10.0, + max=10.0, + soft_min=0.0, + soft_max=10.0, + default=1.0, + precision=3, + ) + + ior: FloatProperty( + name="IOR", + description="Index of refraction", + min=-10.0, + max=10.0, + soft_min=0.0, + soft_max=10.0, + default=1.0, + precision=3, + ) + + specular_shader: EnumProperty( + name="Specular Shader Model", + description="How the ramp maps on the surface", + items=( + ("COOKTORR", "CookTorr", "Use a Cook-Torrance shader"), + ("PHONG", "Phong", "Use a Phong shader"), + ("BLINN", "Blinn", "Use a Blinn shader"), + ("TOON", "Toon", "Use a Toon shader"), + ("WARDISO", "WardIso", "Use a Ward anisotropic shader"), + ), + default="COOKTORR", + ) + + specular_slope: FloatProperty( + name="Slope", + description="The standard deviation of surface slope", + min=0.0, + max=0.4, + soft_min=0.0, + soft_max=0.4, + default=0.1, + precision=3, + ) + + specular_toon_size: FloatProperty( + name="Size", + description="Size of specular toon area", + min=0.0, + max=0.53, + soft_min=0.0, + soft_max=0.53, + default=0.5, + precision=3, + ) + + specular_toon_smooth: FloatProperty( + name="Smooth", + description="Smoothness of specular toon area", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.1, + precision=3, + ) + + translucency: FloatProperty( + name="Translucency", + description="Amount of diffuse shading on the back side", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.0, + precision=3, + ) + + transparency_method: EnumProperty( + name="Specular Shader Model", + description="Method to use for rendering transparency", + items=( + ("MASK", "Mask", "Mask the background"), + ("Z_TRANSPARENCY", "Z Transparency", "Use an ior of 1 for transparent faces"), + ("RAYTRACE", "Raytrace", "Use raytracing for transparent refraction rendering"), + ), + default="MASK", + ) + + type: EnumProperty( + name="Type", + description="Material type defining how the object is rendered", + items=( + ("SURFACE", "Surface", "Render object as a surface"), + # TO UPDATE > USE wire MACRO AND CHANGE DESCRIPTION + ("WIRE", "Wire", "Render the edges of faces as wires (not supported in raytracing)"), + ("VOLUME", "Volume", "Render object as a volume"), + # TO UPDATE > USE halo MACRO AND CHANGE DESCRIPTION + ("HALO", "Halo", "Render object as halo particles"), + ), + default="SURFACE", + ) + + use_cast_shadows: BoolProperty( + name="Cast", description="Allow this material to cast shadows", default=True + ) + + use_cast_shadows_only: BoolProperty( + name="Cast Only", + description="Make objects with this material " + "appear invisible (not rendered), only " + "casting shadows", + default=False, + ) + + use_cubic: BoolProperty( + name="Cubic Interpolation", + description="Use cubic interpolation for diffuse " "values, for smoother transitions", + default=False, + ) + + use_diffuse_ramp: BoolProperty( + name="Ramp", description="Toggle diffuse ramp operations", default=False + ) + + use_light_group_exclusive: BoolProperty( + name="Exclusive", + description="Material uses the light group exclusively" + "- these lamps are excluded from other " + "scene lighting", + default=False, + ) + + use_light_group_local: BoolProperty( + name="Local", + description="When linked in, material uses local light" " group with the same name", + default=False, + ) + + use_mist: BoolProperty( + name="Use Mist", + description="Use mist with this material " "(in world settings)", + default=True, + ) + + use_nodes: BoolProperty( + name="Nodes", + # Add Icon in UI or here? icon='NODES' + description="Use shader nodes to render the material", + default=False, + ) + + use_object_color: BoolProperty( + name="Object Color", + description="Modulate the result with a per-object color", + default=False, + ) + + use_only_shadow: BoolProperty( + name="Shadows Only", + description="Render shadows as the material’s alpha " + "value, making the material transparent " + "except for shadowed areas", + default=False, + ) + + use_shadeless: BoolProperty( + name="Shadeless", + description="Make this material insensitive to " "light or shadow", + default=False, + ) + + use_shadows: BoolProperty( + name="Receive", description="Allow this material to receive shadows", default=True + ) + + use_sky: BoolProperty( + name="Sky", + description="Render this material with zero alpha, " + "with sky background in place (scanline only)", + default=False, + ) + + use_specular_ramp: BoolProperty( + name="Ramp", description="Toggle specular ramp operations", default=False + ) + + use_tangent_shading: BoolProperty( + name="Tangent Shading", + description="Use the material’s tangent vector instead" + "of the normal for shading - for " + "anisotropic shading effects", + default=False, + ) + + use_transparent_shadows: BoolProperty( + name="Receive Transparent", + description="Allow this object to receive transparent " "shadows cast through other object", + default=False, + ) # linked to fake caustics + + use_vertex_color_light: BoolProperty( + name="Vertex Color Light", + description="Add vertex colors as additional lighting", + default=False, + ) + + use_vertex_color_paint: BoolProperty( + name="Vertex Color Paint", + description="Replace object base color with vertex " + "colors (multiply with â€texture face’ " + "face assigned textures)", + default=False, + ) + + specular_ramp_blend: EnumProperty( + name="Specular ramp blend", + description="Blending method of the ramp and the specular color", + items=( + ("MIX", "Mix", ""), + ("ADD", "Add", ""), + ("MULTIPLY", "Multiply", ""), + ("SUBTRACT", "Subtract", ""), + ("SCREEN", "Screen", ""), + ("DIVIDE", "Divide", ""), + ("DIFFERENCE", "Difference", ""), + ("DARKEN", "Darken", ""), + ("LIGHTEN", "Lighten", ""), + ("OVERLAY", "Overlay", ""), + ("DODGE", "Dodge", ""), + ("BURN", "Burn", ""), + ("HUE", "Hue", ""), + ("SATURATION", "Saturation", ""), + ("VALUE", "Value", ""), + ("COLOR", "Color", ""), + ("SOFT_LIGHT", "Soft light", ""), + ("LINEAR_LIGHT", "Linear light", ""), + ), + default="MIX", + ) + + specular_ramp_factor: FloatProperty( + name="Factor", + description="Blending factor (also uses alpha in Colorband)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + specular_ramp_input: EnumProperty( + name="Input", + description="How the ramp maps on the surface", + items=( + ("SHADER", "Shader", ""), + ("ENERGY", "Energy", ""), + ("NORMAL", "Normal", ""), + ("RESULT", "Result", ""), + ), + default="SHADER", + ) + + irid_enable: BoolProperty( + name="Iridescence coating", + description="Newton's thin film interference (like an oil slick on a puddle of " + "water or the rainbow hues of a soap bubble.)", + default=False, + ) + + mirror_use_IOR: BoolProperty( + name="Correct Reflection", + description="Use same IOR as raytrace transparency to calculate mirror reflections. " + "More physically correct", + default=False, + ) + + mirror_metallic: BoolProperty( + name="Metallic Reflection", + description="mirror reflections get colored as diffuse (for metallic materials)", + default=False, + ) + + conserve_energy: BoolProperty( + name="Conserve Energy", + description="Light transmitted is more correctly reduced by mirror reflections, " + "also the sum of diffuse and translucency gets reduced below one ", + default=True, + ) + + irid_amount: FloatProperty( + name="amount", + description="Contribution of the iridescence effect to the overall surface color. " + "As a rule of thumb keep to around 0.25 (25% contribution) or less, " + "but experiment. If the surface is coming out too white, try lowering " + "the diffuse and possibly the ambient values of the surface", + min=0.0, + max=1.0, + soft_min=0.01, + soft_max=1.0, + default=0.25, + ) + + irid_thickness: FloatProperty( + name="thickness", + description="A very thin film will have a high frequency of color changes while a " + "thick film will have large areas of color", + min=0.0, + max=1000.0, + soft_min=0.1, + soft_max=10.0, + default=1, + ) + + irid_turbulence: FloatProperty( + name="turbulence", + description="This parameter varies the thickness", + min=0.0, + max=10.0, + soft_min=0.000, + soft_max=1.0, + default=0, + ) + + interior_fade_color: FloatVectorProperty( + name="Interior Fade Color", + description="Color of filtered attenuation for transparent " "materials", + precision=4, + step=0.01, + min=0.0, + soft_max=1.0, + default=(0, 0, 0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + caustics_enable: BoolProperty( + name="Caustics", + description="use only fake refractive caustics (default) or photon based " + "reflective/refractive caustics", + default=True, + ) + + fake_caustics: BoolProperty( + name="Fake Caustics", description="use only (Fast) fake refractive caustics", default=True + ) + + fake_caustics_power: FloatProperty( + name="Fake caustics power", + description="Values typically range from 0.0 to 1.0 or higher. Zero is no caustics. " + "Low, non-zero values give broad hot-spots while higher values give " + "tighter, smaller simulated focal points", + min=0.00, + max=10.0, + soft_min=0.00, + soft_max=5.0, + default=0.15, + ) + + refraction_caustics: BoolProperty( + name="Refractive Caustics", + description="hotspots of light focused when going through the material", + default=True, + ) + + photons_dispersion: FloatProperty( + name="Chromatic Dispersion", + description="Light passing through will be separated according to wavelength. " + "This ratio of refractive indices for violet to red controls how much " + "the colors are spread out 1 = no dispersion, good values are 1.01 to 1.1", + min=1.0000, + max=10.000, + soft_min=1.0000, + soft_max=1.1000, + precision=4, + default=1.0000, + ) + + photons_dispersion_samples: IntProperty( + name="Dispersion Samples", + description="Number of color-steps for dispersion", + min=2, + max=128, + default=7, + ) + + photons_reflection: BoolProperty( + name="Reflective Photon Caustics", + description="Use this to make your Sauron's ring ;-P", + default=False, + ) + + refraction_type: EnumProperty( + items=[ + ("1", "Z Transparency Fake Caustics", "use fake caustics"), + ("2", "Raytrace Photons Caustics", "use photons for refractive caustics"), + ], + name="Refraction Type:", + description="use fake caustics (fast) or true photons for refractive Caustics", + default="1", + ) + + ##################################CustomPOV Code############################ + replacement_text: StringProperty( + name="Declared name:", + description="Type the variable name as declared either directly inlined " + "in your custom POV code from the text editor datablock (checked as a " + "source to render in it's side property panel), or this declaration can be " + "from an external .inc it points at. Here, name = texture {} expected", + default="", + ) + + # NODES + + def use_material_nodes_callback(self, context): + """Identify if node has been added and if it is used yet or default""" + + if hasattr(context.space_data, "tree_type"): + context.space_data.tree_type = "ObjectNodeTree" + mat = context.object.active_material + if mat.pov.material_use_nodes: + mat.use_nodes = True + tree = mat.node_tree + # tree.name = mat.name # XXX READONLY + links = tree.links + default = True + if len(tree.nodes) == 2: + o = 0 + m = 0 + for node in tree.nodes: + if node.type in {"OUTPUT", "MATERIAL"}: + tree.nodes.remove(node) + default = True + for node in tree.nodes: + if node.bl_idname == "PovrayOutputNode": + o += 1 + if node.bl_idname == "PovrayTextureNode": + m += 1 + if o == 1 and m == 1: + default = False + elif len(tree.nodes) == 0: + default = True + else: + default = False + if default: + output = tree.nodes.new("PovrayOutputNode") + output.location = 200, 200 + tmap = tree.nodes.new("PovrayTextureNode") + tmap.location = 0, 200 + links.new(tmap.outputs[0], output.inputs[0]) + tmap.select = True + tree.nodes.active = tmap + else: + mat.use_nodes = False + + def use_texture_nodes_callback(self, context): + """Identify texture nodes by filtering out output and composite ones""" + + tex = context.object.active_material.active_texture + if tex.pov.texture_use_nodes: + tex.use_nodes = True + if len(tex.node_tree.nodes) == 2: + for node in tex.node_tree.nodes: + if node.type in {"OUTPUT", "CHECKER"}: + tex.node_tree.nodes.remove(node) + else: + tex.use_nodes = False + + def node_active_callback(self, context): + """Synchronize active node with material before getting it""" + + items = [] # XXX comment out > remove? + mat = context.material + mat.node_tree.nodes # XXX comment out > remove? + for node in mat.node_tree.nodes: + node.select = False + for node in mat.node_tree.nodes: + if node.name == mat.pov.material_active_node: + node.select = True + mat.node_tree.nodes.active = node + + return node + + def node_enum_callback(self, context): + items = [] + mat = context.material + nodes = mat.node_tree.nodes + for node in nodes: + items.append(("%s" % node.name, "%s" % node.name, "")) + return items + + def pigment_normal_callback(self, context): + render = context.scene.pov.render # XXX comment out > remove? + items = [("pigment", "Pigment", ""), ("normal", "Normal", "")] + # XXX Find any other such traces of hgpovray experiment > remove or deploy ? + if render == "hgpovray": + items = [ + ("pigment", "Pigment", ""), + ("normal", "Normal", ""), + ("modulation", "Modulation", ""), + ] + return items + + def glow_callback(self, context): + scene = context.scene + ob = context.object + ob.pov.mesh_write_as_old = ob.pov.mesh_write_as + if scene.pov.render == "uberpov" and ob.pov.glow: + ob.pov.mesh_write_as = "NONE" + else: + ob.pov.mesh_write_as = ob.pov.mesh_write_as_old + + material_use_nodes: BoolProperty( + name="Use nodes", description="", update=use_material_nodes_callback, default=False + ) + + material_active_node: EnumProperty( + name="Active node", description="", items=node_enum_callback, update=node_active_callback + ) + + preview_settings: BoolProperty(name="Preview Settings", description="", default=False) + + object_preview_transform: BoolProperty(name="Transform object", description="", default=False) + + object_preview_scale: FloatProperty(name="XYZ", min=0.5, max=2.0, default=1.0) + + object_preview_rotate: FloatVectorProperty( + name="Rotate", description="", min=-180.0, max=180.0, default=(0.0, 0.0, 0.0), subtype="XYZ" + ) + + object_preview_bgcontrast: FloatProperty(name="Contrast", min=0.0, max=1.0, default=0.5) + + +class MaterialRaytraceTransparency(PropertyGroup): + """Declare transparency panel properties controllable in UI and translated to POV.""" + + depth: IntProperty( + name="Depth", + description="Maximum allowed number of light inter-refractions", + min=0, + max=32767, + default=2, + ) + + depth_max: FloatProperty( + name="Depth", + description="Maximum depth for light to travel through the " + "transparent material before becoming fully filtered (0.0 is disabled)", + min=0, + max=100, + default=0.0, + ) + + falloff: FloatProperty( + name="Falloff", + description="Falloff power for transmissivity filter effect (1.0 is linear)", + min=0.1, + max=10.0, + default=1.0, + precision=3, + ) + + filter: FloatProperty( + name="Filter", + description="Amount to blend in the material’s diffuse color in raytraced " + "transparency (simulating absorption)", + min=0.0, + max=1.0, + default=0.0, + precision=3, + ) + + fresnel: FloatProperty( + name="Fresnel", + description="Power of Fresnel for transparency (Ray or ZTransp)", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=0.0, + precision=3, + ) + + fresnel_factor: FloatProperty( + name="Blend", + description="Blending factor for Fresnel", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=1.250, + precision=3, + ) + + gloss_factor: FloatProperty( + name="Amount", + description="The clarity of the refraction. " + "(values < 1.0 give diffuse, blurry refractions)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + gloss_samples: IntProperty( + name="Samples", + description="frequency of the noise sample used for blurry refractions", + min=0, + max=1024, + default=18, + ) + + gloss_threshold: FloatProperty( + name="Threshold", + description="Threshold for adaptive sampling (if a sample " + "contributes less than this amount [as a percentage], " + "sampling is stopped)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.005, + precision=3, + ) + + ior: FloatProperty( + name="IOR", + description="Sets angular index of refraction for raytraced refraction", + min=-0.0, + max=10.0, + soft_min=0.25, + soft_max=4.0, + default=1.3, + ) + + +class MaterialRaytraceMirror(PropertyGroup): + """Declare reflection panel properties controllable in UI and translated to POV.""" + + bl_description = ("Raytraced reflection settings for the Material",) + + use: BoolProperty(name="Mirror", description="Enable raytraced reflections", default=False) + + depth: IntProperty( + name="Depth", + description="Maximum allowed number of light inter-reflections", + min=0, + max=32767, + default=2, + ) + + distance: FloatProperty( + name="Max Dist", + description="Maximum distance of reflected rays " + "(reflections further than this range " + "fade to sky color or material color)", + min=0.0, + max=100000.0, + soft_min=0.0, + soft_max=10000.0, + default=0.0, + precision=3, + ) + + fade_to: EnumProperty( + items=[ + ("FADE_TO_SKY", "Fade to sky", ""), + ("FADE_TO_MATERIAL", "Fade to material color", ""), + ], + name="Fade-out Color", + description="The color that rays with no intersection within the " + "Max Distance take (material color can be best for " + "indoor scenes, sky color for outdoor)", + default="FADE_TO_SKY", + ) + + fresnel: FloatProperty( + name="Fresnel", + description="Power of Fresnel for mirror reflection", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=0.0, + precision=3, + ) + + fresnel_factor: FloatProperty( + name="Blend", + description="Blending factor for Fresnel", + min=0.0, + max=5.0, + soft_min=0.0, + soft_max=5.0, + default=1.250, + precision=3, + ) + + gloss_anisotropic: FloatProperty( + name="Anisotropic", + description="The shape of the reflection, from 0.0 (circular) " + "to 1.0 (fully stretched along the tangent", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + gloss_factor: FloatProperty( + name="Amount", + description="The shininess of the reflection " + "(values < 1.0 give diffuse, blurry reflections)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + gloss_samples: IntProperty( + name="Noise", + description="Frequency of the noise pattern bumps averaged for blurry reflections", + min=0, + max=1024, + default=18, + ) + + gloss_threshold: FloatProperty( + name="Threshold", + description="Threshold for adaptive sampling (if a sample " + "contributes less than this amount [as a percentage], " + "sampling is stopped)", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.005, + precision=3, + ) + + mirror_color: FloatVectorProperty( + name="Mirror color", + description=("Mirror color of the material"), + precision=4, + step=0.01, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + reflect_factor: FloatProperty( + name="Reflectivity", + description="Amount of mirror reflection for raytrace", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + +class MaterialSubsurfaceScattering(PropertyGroup): + r"""Declare SSS/SSTL properties controllable in UI and translated to POV.""" + + bl_description = ("Subsurface scattering settings for the material",) + + use: BoolProperty( + name="Subsurface Scattering", + description="Enable diffuse subsurface scatting " "effects in a material", + default=False, + ) + + back: FloatProperty( + name="Back", + description="Back scattering weight", + min=0.0, + max=10.0, + soft_min=0.0, + soft_max=10.0, + default=1.0, + precision=3, + ) + + color: FloatVectorProperty( + name="Scattering color", + description=("Scattering color"), + precision=4, + step=0.01, + default=(0.604, 0.604, 0.604), + options={"ANIMATABLE"}, + subtype="COLOR", + ) + + color_factor: FloatProperty( + name="Color", + description="Blend factor for SSS colors", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=1.0, + precision=3, + ) + + error_threshold: FloatProperty( + name="Error", + description="Error tolerance (low values are slower and higher quality)", + default=0.050, + precision=3, + ) + + front: FloatProperty( + name="Front", + description="Front scattering weight", + min=0.0, + max=2.0, + soft_min=0.0, + soft_max=2.0, + default=1.0, + precision=3, + ) + + ior: FloatProperty( + name="IOR", + description="Index of refraction (higher values are denser)", + min=-0.0, + max=10.0, + soft_min=0.1, + soft_max=2.0, + default=1.3, + ) + + radius: FloatVectorProperty( + name="RGB Radius", + description=("Mean red/green/blue scattering path length"), + precision=4, + step=0.01, + min=0.001, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + ) + + scale: FloatProperty( + name="Scale", description="Object scale factor", default=0.100, precision=3 + ) + + texture_factor: FloatProperty( + name="Texture", + description="Texture scattering blend factor", + min=0.0, + max=1.0, + soft_min=0.0, + soft_max=1.0, + default=0.0, + precision=3, + ) + + +class MaterialStrandSettings(PropertyGroup): + """Declare strand properties controllable in UI and translated to POV.""" + + bl_description = ("Strand settings for the material",) + + blend_distance: FloatProperty( + name="Distance", + description="Worldspace distance over which to blend in the surface normal", + min=0.0, + max=10.0, + soft_min=0.0, + soft_max=10.0, + default=0.0, + precision=3, + ) + + root_size: FloatProperty( + name="Root", + description="Start size of strands in pixels or Blender units", + min=0.25, + default=1.0, + precision=5, + ) + + shape: FloatProperty( + name="Shape", + description="Positive values make strands rounder, negative ones make strands spiky", + min=-0.9, + max=0.9, + default=0.0, + precision=3, + ) + + size_min: FloatProperty( + name="Minimum", + description="Minimum size of strands in pixels", + min=0.001, + max=10.0, + default=1.0, + precision=3, + ) + + tip_size: FloatProperty( + name="Tip", + description="End size of strands in pixels or Blender units", + min=0.0, + default=1.0, + precision=5, + ) + + use_blender_units: BoolProperty( + name="Blender Units", + description="Use Blender units for widths instead of pixels", + default=False, + ) + + use_surface_diffuse: BoolProperty( + name="Surface diffuse", + description="Make diffuse shading more similar to shading the surface", + default=False, + ) + + use_tangent_shading: BoolProperty( + name="Tangent Shading", + description="Use direction of strands as normal for tangent-shading", + default=True, + ) + + uv_layer: StringProperty( + name="UV Layer", + # icon="GROUP_UVS", + description="Name of UV map to override", + default="", + ) + + width_fade: FloatProperty( + name="Width Fade", + description="Transparency along the width of the strand", + min=0.0, + max=2.0, + default=0.0, + precision=3, + ) + + # halo + + # Halo settings for the material + # Type: MaterialHalo, (readonly, never None) + + # ambient + + # Amount of global ambient color the material receives + # Type: float in [0, 1], default 0.0 + + # darkness + + # Minnaert darkness + # Type: float in [0, 2], default 0.0 + + # diffuse_color + + # Diffuse color of the material + # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) + + # diffuse_fresnel + + # Power of Fresnel + # Type: float in [0, 5], default 0.0 + + # diffuse_fresnel_factor + + # Blending factor of Fresnel + # Type: float in [0, 5], default 0.0 + + # diffuse_intensity + + # Amount of diffuse reflection + # Type: float in [0, 1], default 0.0 + + # diffuse_ramp + + # Color ramp used to affect diffuse shading + # Type: ColorRamp, (readonly) + + # diffuse_ramp_blend + + # Blending method of the ramp and the diffuse color + # Type: enum in [â€MIX’, â€ADD’, â€MULTIPLY’, â€SUBTRACT’, â€SCREEN’, â€DIVIDE’, â€DIFFERENCE’, â€DARKEN’, â€LIGHTEN’, â€OVERLAY’, â€DODGE’, â€BURN’, â€HUE’, â€SATURATION’, â€VALUE’, â€COLOR’, â€SOFT_LIGHT’, â€LINEAR_LIGHT’], default â€MIX’ + + # diffuse_ramp_factor + + # Blending factor (also uses alpha in Colorband) + # Type: float in [0, 1], default 0.0 + + # diffuse_ramp_input + + # How the ramp maps on the surface + # Type: enum in [â€SHADER’, â€ENERGY’, â€NORMAL’, â€RESULT’], default â€SHADER’ + + # diffuse_shader + + # LAMBERT Lambert, Use a Lambertian shader. + # OREN_NAYAR Oren-Nayar, Use an Oren-Nayar shader. + # TOON Toon, Use a toon shader. + # MINNAERT Minnaert, Use a Minnaert shader. + # FRESNEL Fresnel, Use a Fresnel shader. + + # Type: enum in [â€LAMBERT’, â€OREN_NAYAR’, â€TOON’, â€MINNAERT’, â€FRESNEL’], default â€LAMBERT’ + + # diffuse_toon_size + + # Size of diffuse toon area + # Type: float in [0, 3.14], default 0.0 + + # diffuse_toon_smooth + + # Smoothness of diffuse toon area + # Type: float in [0, 1], default 0.0 + + # emit + + # Amount of light to emit + # Type: float in [0, inf], default 0.0 + + # game_settings + + # Game material settings + # Type: MaterialGameSettings, (readonly, never None) + + # halo + + # Halo settings for the material + # Type: MaterialHalo, (readonly, never None) + + # invert_z + + # Render material’s faces with an inverted Z buffer (scanline only) + # Type: boolean, default False + + # light_group + + # Limit lighting to lamps in this Group + # Type: Group + + # line_color + + # Line color used for Freestyle line rendering + # Type: float array of 4 items in [0, inf], default (0.0, 0.0, 0.0, 0.0) + + # line_priority + + # The line color of a higher priority is used at material boundaries + # Type: int in [0, 32767], default 0 + + # mirror_color + + # Mirror color of the material + # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) + + # node_tree + + # Node tree for node based materials + # Type: NodeTree, (readonly) + + # offset_z + + # Give faces an artificial offset in the Z buffer for Z transparency + # Type: float in [-inf, inf], default 0.0 + + # paint_active_slot + + # Index of active texture paint slot + # Type: int in [0, 32767], default 0 + + # paint_clone_slot + + # Index of clone texture paint slot + # Type: int in [0, 32767], default 0 + + # pass_index + + # Index number for the “Material Index” render pass + # Type: int in [0, 32767], default 0 + + # physics + + # Game physics settings + # Type: MaterialPhysics, (readonly, never None) + + # preview_render_type + + # Type of preview render + + # FLAT Flat, Flat XY plane. + # SPHERE Sphere, Sphere. + # CUBE Cube, Cube. + # MONKEY Monkey, Monkey. + # HAIR Hair, Hair strands. + # SPHERE_A World Sphere, Large sphere with sky. + + # Type: enum in [â€FLAT’, â€SPHERE’, â€CUBE’, â€MONKEY’, â€HAIR’, â€SPHERE_A’], default â€FLAT’ + + # raytrace_mirror + + # Raytraced reflection settings for the material + # Type: MaterialRaytraceMirror, (readonly, never None) + + # raytrace_transparency + + # Raytraced transparency settings for the material + # Type: MaterialRaytraceTransparency, (readonly, never None) + + # roughness + + # Oren-Nayar Roughness + # Type: float in [0, 3.14], default 0.0 + + # shadow_buffer_bias + + # Factor to multiply shadow buffer bias with (0 is ignore) + # Type: float in [0, 10], default 0.0 + + # shadow_cast_alpha + + # Shadow casting alpha, in use for Irregular and Deep shadow buffer + # Type: float in [0.001, 1], default 0.0 + + # shadow_only_type + + # How to draw shadows + + # SHADOW_ONLY_OLD Shadow and Distance, Old shadow only method. + # SHADOW_ONLY Shadow Only, Improved shadow only method. + # SHADOW_ONLY_SHADED Shadow and Shading, Improved shadow only method which also renders lightless areas as shadows. + + # Type: enum in [â€SHADOW_ONLY_OLD’, â€SHADOW_ONLY’, â€SHADOW_ONLY_SHADED’], default â€SHADOW_ONLY_OLD’ + + # shadow_ray_bias + + # Shadow raytracing bias to prevent terminator problems on shadow boundary + # Type: float in [0, 0.25], default 0.0 + + # specular_color + + # Specular color of the material + # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0) + + # specular_hardness + + # How hard (sharp) the specular reflection is + # Type: int in [1, 511], default 0 + + # specular_intensity + + # How intense (bright) the specular reflection is + # Type: float in [0, 1], default 0.0 + + # specular_ior + + # Specular index of refraction + # Type: float in [1, 10], default 0.0 + + # specular_ramp + + # Color ramp used to affect specular shading + # Type: ColorRamp, (readonly) + + # specular_ramp_blend + + # Blending method of the ramp and the specular color + # Type: enum in [â€MIX’, â€ADD’, â€MULTIPLY’, â€SUBTRACT’, â€SCREEN’, â€DIVIDE’, â€DIFFERENCE’, â€DARKEN’, â€LIGHTEN’, â€OVERLAY’, â€DODGE’, â€BURN’, â€HUE’, â€SATURATION’, â€VALUE’, â€COLOR’, â€SOFT_LIGHT’, â€LINEAR_LIGHT’], default â€MIX’ + + # specular_ramp_factor + + # Blending factor (also uses alpha in Colorband) + # Type: float in [0, 1], default 0.0 + + # specular_ramp_input + + # How the ramp maps on the surface + # Type: enum in [â€SHADER’, â€ENERGY’, â€NORMAL’, â€RESULT’], default â€SHADER’ + # specular_shader + + # COOKTORR CookTorr, Use a Cook-Torrance shader. + # PHONG Phong, Use a Phong shader. + # BLINN Blinn, Use a Blinn shader. + # TOON Toon, Use a toon shader. + # WARDISO WardIso, Use a Ward anisotropic shader. + + # Type: enum in [â€COOKTORR’, â€PHONG’, â€BLINN’, â€TOON’, â€WARDISO’], default â€COOKTORR’ + + # specular_slope + + # The standard deviation of surface slope + # Type: float in [0, 0.4], default 0.0 + + # specular_toon_size + + # Size of specular toon area + # Type: float in [0, 1.53], default 0.0 + + # specular_toon_smooth + + # Smoothness of specular toon area + # Type: float in [0, 1], default 0.0 + + # strand + + # Strand settings for the material + # Type: MaterialStrand, (readonly, never None) + + # subsurface_scattering + + # Subsurface scattering settings for the material + # Type: MaterialSubsurfaceScattering, (readonly, never None) + + # texture_paint_images + + # Texture images used for texture painting + # Type: bpy_prop_collection of Image, (readonly) + + # texture_paint_slots + + # Texture slots defining the mapping and influence of textures + # Type: bpy_prop_collection of TexPaintSlot, (readonly) + + # texture_slots + + # Texture slots defining the mapping and influence of textures + # Type: MaterialTextureSlots bpy_prop_collection of MaterialTextureSlot, (readonly) + + # translucency + + # Amount of diffuse shading on the back side + # Type: float in [0, 1], default 0.0 + + # transparency_method + + # Method to use for rendering transparency + + # MASK Mask, Mask the background. + # Z_TRANSPARENCY Z Transparency, Use alpha buffer for transparent faces. + # RAYTRACE Raytrace, Use raytracing for transparent refraction rendering. + + # Type: enum in [â€MASK’, â€Z_TRANSPARENCY’, â€RAYTRACE’], default â€MASK’ + + # type + + # Material type defining how the object is rendered + + # SURFACE Surface, Render object as a surface. + # WIRE Wire, Render the edges of faces as wires (not supported in raytracing). + # VOLUME Volume, Render object as a volume. + # HALO Halo, Render object as halo particles. + + # Type: enum in [â€SURFACE’, â€WIRE’, â€VOLUME’, â€HALO’], default â€SURFACE’ + + # use_cast_approximate + + # Allow this material to cast shadows when using approximate ambient occlusion + # Type: boolean, default False + + # use_cast_buffer_shadows + + # Allow this material to cast shadows from shadow buffer lamps + # Type: boolean, default False + + # use_cast_shadows + + # Allow this material to cast shadows + # Type: boolean, default False + + # use_cast_shadows_only + + # Make objects with this material appear invisible (not rendered), only casting shadows + # Type: boolean, default False + + # use_cubic + + # Use cubic interpolation for diffuse values, for smoother transitions + # Type: boolean, default False + + # use_diffuse_ramp + + # Toggle diffuse ramp operations + # Type: boolean, default False + + # use_face_texture + + # Replace the object’s base color with color from UV map image textures + # Type: boolean, default False + + # use_face_texture_alpha + + # Replace the object’s base alpha value with alpha from UV map image textures + # Type: boolean, default False + + # use_full_oversampling + + # Force this material to render full shading/textures for all anti-aliasing samples + # Type: boolean, default False + + # use_light_group_exclusive + + # Material uses the light group exclusively - these lamps are excluded from other scene lighting + # Type: boolean, default False + + # use_light_group_local + + # When linked in, material uses local light group with the same name + # Type: boolean, default False + + # use_mist + + # Use mist with this material (in world settings) + # Type: boolean, default False + + # use_nodes + + # Use shader nodes to render the material + # Type: boolean, default False + + # use_object_color + + # Modulate the result with a per-object color + # Type: boolean, default False + + # use_only_shadow + + # Render shadows as the material’s alpha value, making the material transparent except for shadowed areas + # Type: boolean, default False + + # use_ray_shadow_bias + + # Prevent raytraced shadow errors on surfaces with smooth shaded normals (terminator problem) + # Type: boolean, default False + + # use_raytrace + + # Include this material and geometry that uses it in raytracing calculations + # Type: boolean, default False + + # use_shadeless + + # Make this material insensitive to light or shadow + # Type: boolean, default False + + # use_shadows + + # Allow this material to receive shadows + # Type: boolean, default False + + # use_sky + + # Render this material with zero alpha, with sky background in place (scanline only) + # Type: boolean, default False + + # use_specular_ramp + + # Toggle specular ramp operations + # Type: boolean, default False + + # use_tangent_shading + + # Use the material’s tangent vector instead of the normal for shading - for anisotropic shading effects + # Type: boolean, default False + + # use_textures + + # Enable/Disable each texture + # Type: boolean array of 18 items, default (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False) + + # use_transparency + + # Render material as transparent + # Type: boolean, default False + + # use_transparent_shadows + + # Allow this object to receive transparent shadows cast through other objects + # Type: boolean, default False + + # use_uv_project + + # Use to ensure UV interpolation is correct for camera projections (use with UV project modifier) + # Type: boolean, default False + + # use_vertex_color_light + + # Add vertex colors as additional lighting + # Type: boolean, default False + + # use_vertex_color_paint + + # Replace object base color with vertex colors (multiply with â€texture face’ face assigned textures) + # Type: boolean, default False + + # volume + + # Volume settings for the material + # Type: MaterialVolume, (readonly, never None) + """ + (mat.type in {'SURFACE', 'WIRE', 'VOLUME'}) + "use_transparency") + + + + mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY' + + + + + col.prop(mat, "use_raytrace") + col.prop(mat, "use_full_oversampling") + + sub.prop(mat, "use_sky") + + + col.prop(mat, "use_cast_shadows", text="Cast") + col.prop(mat, "use_cast_shadows_only", text="Cast Only") + col.prop(mat, "use_cast_buffer_shadows") + + sub.active = mat.use_cast_buffer_shadows + sub.prop(mat, "shadow_cast_alpha", text="Casting Alpha") + col.prop(mat, "use_cast_approximate") + + + + col.prop(mat, "diffuse_color", text="") + + sub.active = (not mat.use_shadeless) + + sub.prop(mat, "diffuse_intensity", text="Intensity") + + + col.prop(mat, "diffuse_shader", text="") + col.prop(mat, "use_diffuse_ramp", text="Ramp") + + + if mat.diffuse_shader == 'OREN_NAYAR': + col.prop(mat, "roughness") + elif mat.diffuse_shader == 'MINNAERT': + col.prop(mat, "darkness") + elif mat.diffuse_shader == 'TOON': + + row.prop(mat, "diffuse_toon_size", text="Size") + row.prop(mat, "diffuse_toon_smooth", text="Smooth") + elif mat.diffuse_shader == 'FRESNEL': + + row.prop(mat, "diffuse_fresnel", text="Fresnel") + row.prop(mat, "diffuse_fresnel_factor", text="Factor") + + if mat.use_diffuse_ramp: + + col.template_color_ramp(mat, "diffuse_ramp", expand=True) + + + + row.prop(mat, "diffuse_ramp_input", text="Input") + row.prop(mat, "diffuse_ramp_blend", text="Blend") + + col.prop(mat, "diffuse_ramp_factor", text="Factor") + + + + + col.prop(mat, "specular_color", text="") + col.prop(mat, "specular_intensity", text="Intensity") + + col.prop(mat, "specular_shader", text="") + col.prop(mat, "use_specular_ramp", text="Ramp") + + if mat.specular_shader in {'COOKTORR', 'PHONG'}: + col.prop(mat, "specular_hardness", text="Hardness") + elif mat.specular_shader == 'BLINN': + + row.prop(mat, "specular_hardness", text="Hardness") + row.prop(mat, "specular_ior", text="IOR") + elif mat.specular_shader == 'WARDISO': + col.prop(mat, "specular_slope", text="Slope") + elif mat.specular_shader == 'TOON': + + row.prop(mat, "specular_toon_size", text="Size") + row.prop(mat, "specular_toon_smooth", text="Smooth") + + if mat.use_specular_ramp: + layout.separator() + layout.template_color_ramp(mat, "specular_ramp", expand=True) + layout.separator() + + row = layout.row() + row.prop(mat, "specular_ramp_input", text="Input") + row.prop(mat, "specular_ramp_blend", text="Blend") + + layout.prop(mat, "specular_ramp_factor", text="Factor") + + + class MATERIAL_PT_shading(MaterialButtonsPanel, Panel): + bl_label = "Shading" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = active_node_mat(context.material) + + if mat.type in {'SURFACE', 'WIRE'}: + split = layout.split() + + col = split.column() + sub = col.column() + sub.active = not mat.use_shadeless + sub.prop(mat, "emit") + sub.prop(mat, "ambient") + sub = col.column() + sub.prop(mat, "translucency") + + col = split.column() + col.prop(mat, "use_shadeless") + sub = col.column() + sub.active = not mat.use_shadeless + sub.prop(mat, "use_tangent_shading") + sub.prop(mat, "use_cubic") + + + class MATERIAL_PT_transp(MaterialButtonsPanel, Panel): + bl_label = "Transparency" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + mat = context.material + + if simple_material(mat): + self.layout.prop(mat, "use_transparency", text="") + + def draw(self, context): + layout = self.layout + + base_mat = context.material + mat = active_node_mat(context.material) + rayt = mat.raytrace_transparency + + if simple_material(base_mat): + row = layout.row() + row.active = mat.use_transparency + row.prop(mat, "transparency_method", expand=True) + + split = layout.split() + split.active = base_mat.use_transparency + + col = split.column() + col.prop(mat, "alpha") + row = col.row() + row.active = (base_mat.transparency_method != 'MASK') and (not mat.use_shadeless) + row.prop(mat, "specular_alpha", text="Specular") + + col = split.column() + col.active = (not mat.use_shadeless) + col.prop(rayt, "fresnel") + sub = col.column() + sub.active = (rayt.fresnel > 0.0) + sub.prop(rayt, "fresnel_factor", text="Blend") + + if base_mat.transparency_method == 'RAYTRACE': + layout.separator() + split = layout.split() + split.active = base_mat.use_transparency + + col = split.column() + col.prop(rayt, "ior") + col.prop(rayt, "filter") + col.prop(rayt, "falloff") + col.prop(rayt, "depth_max") + col.prop(rayt, "depth") + + col = split.column() + col.label(text="Gloss:") + col.prop(rayt, "gloss_factor", text="Amount") + sub = col.column() + sub.active = rayt.gloss_factor < 1.0 + sub.prop(rayt, "gloss_threshold", text="Threshold") + sub.prop(rayt, "gloss_samples", text="Samples") + + + class MATERIAL_PT_mirror(MaterialButtonsPanel, Panel): + bl_label = "Mirror" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + raym = active_node_mat(context.material).raytrace_mirror + + self.layout.prop(raym, "use", text="") + + def draw(self, context): + layout = self.layout + + mat = active_node_mat(context.material) + raym = mat.raytrace_mirror + + layout.active = raym.use + + split = layout.split() + + col = split.column() + col.prop(raym, "reflect_factor") + col.prop(mat, "mirror_color", text="") + + col = split.column() + col.prop(raym, "fresnel") + sub = col.column() + sub.active = (raym.fresnel > 0.0) + sub.prop(raym, "fresnel_factor", text="Blend") + + split = layout.split() + + col = split.column() + col.separator() + col.prop(raym, "depth") + col.prop(raym, "distance", text="Max Dist") + col.separator() + sub = col.split(percentage=0.4) + sub.active = (raym.distance > 0.0) + sub.label(text="Fade To:") + sub.prop(raym, "fade_to", text="") + + col = split.column() + col.label(text="Gloss:") + col.prop(raym, "gloss_factor", text="Amount") + sub = col.column() + sub.active = (raym.gloss_factor < 1.0) + sub.prop(raym, "gloss_threshold", text="Threshold") + sub.prop(raym, "gloss_samples", text="Samples") + sub.prop(raym, "gloss_anisotropic", text="Anisotropic") + + + class MATERIAL_PT_sss(MaterialButtonsPanel, Panel): + bl_label = "Subsurface Scattering" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + mat = active_node_mat(context.material) + sss = mat.subsurface_scattering + + self.layout.active = (not mat.use_shadeless) + self.layout.prop(sss, "use", text="") + + def draw(self, context): + layout = self.layout + + mat = active_node_mat(context.material) + sss = mat.subsurface_scattering + + layout.active = (sss.use) and (not mat.use_shadeless) + + row = layout.row().split() + sub = row.row(align=True).split(align=True, percentage=0.75) + sub.menu("MATERIAL_MT_sss_presets", text=bpy.types.MATERIAL_MT_sss_presets.bl_label) + sub.operator("material.sss_preset_add", text="", icon='ZOOMIN') + sub.operator("material.sss_preset_add", text="", icon='ZOOMOUT').remove_active = True + + split = layout.split() + + col = split.column() + col.prop(sss, "ior") + col.prop(sss, "scale") + col.prop(sss, "color", text="") + col.prop(sss, "radius", text="RGB Radius", expand=True) + + col = split.column() + sub = col.column(align=True) + sub.label(text="Blend:") + sub.prop(sss, "color_factor", text="Color") + sub.prop(sss, "texture_factor", text="Texture") + sub.label(text="Scattering Weight:") + sub.prop(sss, "front") + sub.prop(sss, "back") + col.separator() + col.prop(sss, "error_threshold", text="Error") + + + class MATERIAL_PT_halo(MaterialButtonsPanel, Panel): + bl_label = "Halo" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + mat = context.material # don't use node material + halo = mat.halo + + def number_but(layout, toggle, number, name, color): + row = layout.row(align=True) + row.prop(halo, toggle, text="") + sub = row.column(align=True) + sub.active = getattr(halo, toggle) + sub.prop(halo, number, text=name, translate=False) + if not color == "": + sub.prop(mat, color, text="") + + split = layout.split() + + col = split.column() + col.prop(mat, "alpha") + col.prop(mat, "diffuse_color", text="") + col.prop(halo, "seed") + + col = split.column() + col.prop(halo, "size") + col.prop(halo, "hardness") + col.prop(halo, "add") + + layout.label(text="Options:") + + split = layout.split() + col = split.column() + col.prop(halo, "use_texture") + col.prop(halo, "use_vertex_normal") + col.prop(halo, "use_extreme_alpha") + col.prop(halo, "use_shaded") + col.prop(halo, "use_soft") + + col = split.column() + number_but(col, "use_ring", "ring_count", iface_("Rings"), "mirror_color") + number_but(col, "use_lines", "line_count", iface_("Lines"), "specular_color") + number_but(col, "use_star", "star_tip_count", iface_("Star Tips"), "") + + + class MATERIAL_PT_flare(MaterialButtonsPanel, Panel): + bl_label = "Flare" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + mat = context.material + engine = context.scene.render.engine + return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + halo = context.material.halo + + self.layout.prop(halo, "use_flare_mode", text="") + + def draw(self, context): + layout = self.layout + + mat = context.material # don't use node material + halo = mat.halo + + layout.active = halo.use_flare_mode + + split = layout.split() + + col = split.column() + col.prop(halo, "flare_size", text="Size") + col.prop(halo, "flare_boost", text="Boost") + col.prop(halo, "flare_seed", text="Seed") + + col = split.column() + col.prop(halo, "flare_subflare_count", text="Subflares") + col.prop(halo, "flare_subflare_size", text="Subsize") + + """ + + +#######################End Old Blender Internal Props########################## + + +classes = ( + RenderPovSettingsMaterial, + MaterialRaytraceTransparency, + MaterialRaytraceMirror, + MaterialSubsurfaceScattering, + MaterialStrandSettings, +) + + +def register(): + for cls in classes: + register_class(cls) + + bpy.types.Material.pov = PointerProperty(type=RenderPovSettingsMaterial) + bpy.types.Material.pov_raytrace_transparency = PointerProperty( + type=MaterialRaytraceTransparency + ) + bpy.types.Material.pov_subsurface_scattering = PointerProperty( + type=MaterialSubsurfaceScattering + ) + bpy.types.Material.strand = PointerProperty(type=MaterialStrandSettings) + bpy.types.Material.pov_raytrace_mirror = PointerProperty(type=MaterialRaytraceMirror) + + +def unregister(): + del bpy.types.Material.pov_subsurface_scattering + del bpy.types.Material.strand + del bpy.types.Material.pov_raytrace_mirror + del bpy.types.Material.pov_raytrace_transparency + del bpy.types.Material.pov + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/texturing.py b/render_povray/texturing.py new file mode 100755 index 000000000..e070dbe97 --- /dev/null +++ b/render_povray/texturing.py @@ -0,0 +1,902 @@ +# ***** 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 #**** + +# <pep8 compliant> + +"""Translate blender texture influences into POV.""" +import os +import bpy + + +def write_texture_influence( + using_uberpov, + mater, + material_names_dictionary, + local_material_names, + path_image, + exported_lights_count, + image_format, + img_map, + img_map_transforms, + tab_write, + comments, + string_strip_hyphen, + safety, + col, + preview_dir, + unpacked_images, +): + """Translate Blender texture influences to various POV texture tricks and write to pov file.""" + material_finish = material_names_dictionary[mater.name] + if mater.pov.use_transparency: + trans = 1.0 - mater.pov.alpha + else: + trans = 0.0 + if (mater.pov.specular_color.s == 0.0) or (mater.pov.diffuse_shader == "MINNAERT"): + # No layered texture because of aoi pattern used for minnaert and pov can't layer patterned + colored_specular_found = False + else: + colored_specular_found = True + + if mater.pov.use_transparency and mater.pov.transparency_method == "RAYTRACE": + pov_filter = mater.pov_raytrace_transparency.filter * (1.0 - mater.pov.alpha) + trans = (1.0 - mater.pov.alpha) - pov_filter + else: + pov_filter = 0.0 + + ##############SF + texture_dif = "" + texture_spec = "" + texture_norm = "" + texture_alpha = "" + # procedural_flag=False + tmpidx = -1 + for t in mater.pov_texture_slots: + + tmpidx += 1 + # index = mater.pov.active_texture_index + slot = mater.pov_texture_slots[tmpidx] # [index] + povtex = slot.texture # slot.name + tex = bpy.data.textures[povtex] + + if t and (t.use and (tex is not None)): + # 'NONE' ('NONE' type texture is different from no texture covered above) + if tex.type == "NONE" and tex.pov.tex_pattern_type == "emulator": + continue # move to next slot + + # Implicit else-if (as not skipped by previous "continue") + # PROCEDURAL + if tex.type != "IMAGE" and tex.type != "NONE": + procedural_flag = True + image_filename = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name)) + if image_filename: + if t.use_map_color_diffuse: + texture_dif = image_filename + # colvalue = t.default_value # UNUSED + t_dif = t + if t_dif.texture.pov.tex_gamma_enable: + img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value + if t.use_map_specular or t.use_map_raymir: + texture_spec = image_filename + # colvalue = t.default_value # UNUSED + t_spec = t + if t.use_map_normal: + texture_norm = image_filename + # colvalue = t.normal_factor/10 # UNUSED + # textNormName=tex.image.name + ".normal" + # was the above used? --MR + t_nor = t + if t.use_map_alpha: + texture_alpha = image_filename + # colvalue = t.alpha_factor * 10.0 # UNUSED + # textDispName=tex.image.name + ".displ" + # was the above used? --MR + t_alpha = t + + # RASTER IMAGE + elif tex.type == "IMAGE" and tex.image and tex.pov.tex_pattern_type == "emulator": + procedural_flag = False + # PACKED + if tex.image.packed_file: + orig_image_filename = tex.image.filepath_raw + unpackedfilename = os.path.join( + preview_dir, + ("unpacked_img_" + (string_strip_hyphen(bpy.path.clean_name(tex.name)))), + ) + if not os.path.exists(unpackedfilename): + # record which images that were newly copied and can be safely + # cleaned up + unpacked_images.append(unpackedfilename) + tex.image.filepath_raw = unpackedfilename + tex.image.save() + image_filename = unpackedfilename.replace("\\", "/") + # .replace("\\","/") to get only forward slashes as it's what POV prefers, + # even on windows + tex.image.filepath_raw = orig_image_filename + # FILE + else: + image_filename = path_image(tex.image) + # IMAGE SEQUENCE BEGINS + if image_filename: + if bpy.data.images[tex.image.name].source == "SEQUENCE": + korvaa = "." + str(tex.image_user.frame_offset + 1).zfill(3) + "." + image_filename = image_filename.replace(".001.", korvaa) + print(" seq debug ") + print(image_filename) + # IMAGE SEQUENCE ENDS + img_gamma = "" + if image_filename: + texdata = bpy.data.textures[t.texture] + if t.use_map_color_diffuse: + texture_dif = image_filename + # colvalue = t.default_value # UNUSED + t_dif = t + print(texdata) + if texdata.pov.tex_gamma_enable: + img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value + if t.use_map_specular or t.use_map_raymir: + texture_spec = image_filename + # colvalue = t.default_value # UNUSED + t_spec = t + if t.use_map_normal: + texture_norm = image_filename + # colvalue = t.normal_factor/10 # UNUSED + # textNormName=tex.image.name + ".normal" + # was the above used? --MR + t_nor = t + if t.use_map_alpha: + texture_alpha = image_filename + # colvalue = t.alpha_factor * 10.0 # UNUSED + # textDispName=tex.image.name + ".displ" + # was the above used? --MR + t_alpha = t + + #################################################################################### + + tab_write("\n") + # THIS AREA NEEDS TO LEAVE THE TEXTURE OPEN UNTIL ALL MAPS ARE WRITTEN DOWN. + # --MR + current_material_name = string_strip_hyphen(material_names_dictionary[mater.name]) + local_material_names.append(current_material_name) + tab_write("\n#declare MAT_%s = \ntexture{\n" % current_material_name) + ################################################################################ + + if mater.pov.replacement_text != "": + tab_write("%s\n" % mater.pov.replacement_text) + ################################################################################# + # XXX TODO: replace by new POV MINNAERT rather than aoi + if mater.pov.diffuse_shader == "MINNAERT": + tab_write("\n") + tab_write("aoi\n") + tab_write("texture_map {\n") + tab_write("[%.3g finish {diffuse %.3g}]\n" % (mater.darkness / 2.0, 2.0 - mater.darkness)) + tab_write("[%.3g\n" % (1.0 - (mater.darkness / 2.0))) + + if mater.pov.diffuse_shader == "FRESNEL": + # For FRESNEL diffuse in POV, we'll layer slope patterned textures + # with lamp vector as the slope vector and nest one slope per lamp + # into each texture map's entry. + + c = 1 + while c <= exported_lights_count: + tab_write("slope { lampTarget%s }\n" % (c)) + tab_write("texture_map {\n") + # Diffuse Fresnel value and factor go up to five, + # other kind of values needed: used the number 5 below to remap + tab_write( + "[%.3g finish {diffuse %.3g}]\n" + % ( + (5.0 - mater.diffuse_fresnel) / 5, + (mater.diffuse_intensity * ((5.0 - mater.diffuse_fresnel_factor) / 5)), + ) + ) + tab_write( + "[%.3g\n" % ((mater.diffuse_fresnel_factor / 5) * (mater.diffuse_fresnel / 5.0)) + ) + c += 1 + + # if shader is a 'FRESNEL' or 'MINNAERT': slope pigment pattern or aoi + # and texture map above, the rest below as one of its entry + + if texture_spec != "" or texture_alpha != "": + if texture_spec != "": + # tab_write("\n") + tab_write("pigment_pattern {\n") + + mapping_spec = img_map_transforms(t_spec) + if texture_spec and texture_spec.startswith("PAT_"): + tab_write("function{f%s(x,y,z).grey}\n" % texture_spec) + tab_write("%s\n" % mapping_spec) + else: + + tab_write( + 'uv_mapping image_map{%s "%s" %s}\n' + % (image_format(texture_spec), texture_spec, img_map(t_spec)) + ) + tab_write("%s\n" % mapping_spec) + tab_write("}\n") + tab_write("texture_map {\n") + tab_write("[0 \n") + + if texture_dif == "": + if texture_alpha != "": + tab_write("\n") + + mapping_alpha = img_map_transforms(t_alpha) + + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha)) + else: + + tab_write( + "pigment {pigment_pattern {uv_mapping image_map" + '{%s "%s" %s}%s' + % ( + image_format(texture_alpha), + texture_alpha, + img_map(t_alpha), + mapping_alpha, + ) + ) + tab_write("}\n") + tab_write("pigment_map {\n") + tab_write("[0 color rgbft<0,0,0,1,1>]\n") + tab_write( + "[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n" + % (col[0], col[1], col[2], pov_filter, trans) + ) + tab_write("}\n") + tab_write("}\n") + + else: + + tab_write( + "pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n" + % (col[0], col[1], col[2], pov_filter, trans) + ) + + if texture_spec != "": + # ref_level_bound 1 is no specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1))) + + else: + # ref_level_bound 2 is translated spec + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2))) + + else: + mapping_dif = img_map_transforms(t_dif) + + if texture_alpha != "": + mapping_alpha = img_map_transforms(t_alpha) + + tab_write("pigment {\n") + tab_write("pigment_pattern {\n") + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha)) + else: + tab_write( + 'uv_mapping image_map{%s "%s" %s}%s}\n' + % ( + image_format(texture_alpha), + texture_alpha, + img_map(t_alpha), + mapping_alpha, + ) + ) + tab_write("pigment_map {\n") + tab_write("[0 color rgbft<0,0,0,1,1>]\n") + # if texture_alpha and texture_alpha.startswith("PAT_"): + # tab_write("[1 pigment{%s}]\n" %texture_dif) + if texture_dif and not texture_dif.startswith("PAT_"): + tab_write( + '[1 uv_mapping image_map {%s "%s" %s} %s]\n' + % ( + image_format(texture_dif), + texture_dif, + (img_gamma + img_map(t_dif)), + mapping_dif, + ) + ) + elif texture_dif and texture_dif.startswith("PAT_"): + tab_write("[1 %s]\n" % texture_dif) + tab_write("}\n") + tab_write("}\n") + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write("}\n") + + else: + if texture_dif and texture_dif.startswith("PAT_"): + tab_write("pigment{%s}\n" % texture_dif) + else: + tab_write( + 'pigment {uv_mapping image_map {%s "%s" %s}%s}\n' + % ( + image_format(texture_dif), + texture_dif, + (img_gamma + img_map(t_dif)), + mapping_dif, + ) + ) + + if texture_spec != "": + # ref_level_bound 1 is no specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1))) + + else: + # ref_level_bound 2 is translated specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2))) + + ## scale 1 rotate y*0 + # imageMap = ("{image_map {%s \"%s\" %s }\n" % \ + # (image_format(textures),textures,img_map(t_dif))) + # tab_write("uv_mapping pigment %s} %s finish {%s}\n" % \ + # (imageMap,mapping,safety(material_finish))) + # tab_write("pigment {uv_mapping image_map {%s \"%s\" %s}%s} " \ + # "finish {%s}\n" % \ + # (image_format(texture_dif), texture_dif, img_map(t_dif), + # mapping_dif, safety(material_finish))) + if texture_norm != "": + ## scale 1 rotate y*0 + + mapping_normal = img_map_transforms(t_nor) + + if texture_norm and texture_norm.startswith("PAT_"): + tab_write( + "normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n" + % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal) + ) + else: + tab_write("normal {\n") + # XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("average\n") + tab_write("normal_map{\n") + # 0.5 for entries below means a 50 percent mix + # between the micro normal and user bump map + # order seems indifferent as commutative + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write("[1.0 ") # Proceed with user bump... + tab_write( + "uv_mapping bump_map " + '{%s "%s" %s bump_size %.4g }%s' + % ( + image_format(texture_norm), + texture_norm, + img_map(t_nor), + (-t_nor.normal_factor * 9.5), + mapping_normal, + ) + ) + # ...Then close its last entry and the the normal_map itself + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("]}}\n") + else: + tab_write("]}\n") + if texture_spec != "": + tab_write("]\n") + ##################Second index for mapping specular max value############### + tab_write("[1 \n") + + if texture_dif == "" and mater.pov.replacement_text == "": + if texture_alpha != "": + mapping_alpha = img_map_transforms(t_alpha) + + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write("function{f%s(x,y,z).transmit %s}\n" % (texture_alpha, mapping_alpha)) + else: + tab_write( + "pigment {pigment_pattern {uv_mapping image_map" + '{%s "%s" %s}%s}\n' + % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha) + ) + tab_write("pigment_map {\n") + tab_write("[0 color rgbft<0,0,0,1,1>]\n") + tab_write( + "[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n" + % (col[0], col[1], col[2], pov_filter, trans) + ) + tab_write("}\n") + tab_write("}\n") + + else: + tab_write( + "pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n" + % (col[0], col[1], col[2], pov_filter, trans) + ) + + if texture_spec != "": + # ref_level_bound 3 is full specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3))) + + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("normal {\n") + tab_write("average\n") + tab_write("normal_map{\n") + # 0.5 for entries below means a 50 percent mix + # between the micro normal and user bump map + # order seems indifferent as commutative + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + # XXX IF USER BUMP_MAP + if texture_norm != "": + tab_write( + "[1.0 " + ) # Blurry reflection or not Proceed with user bump in either case... + tab_write( + "uv_mapping bump_map " + '{%s "%s" %s bump_size %.4g }%s]\n' + % ( + image_format(texture_norm), + texture_norm, + img_map(t_nor), + (-t_nor.normal_factor * 9.5), + mapping_normal, + ) + ) + # ...Then close the normal_map itself if blurry reflection + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("}}\n") + else: + tab_write("}\n") + elif colored_specular_found: + # ref_level_bound 1 is no specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1))) + + else: + # ref_level_bound 2 is translated specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2))) + + elif mater.pov.replacement_text == "": + mapping_dif = img_map_transforms(t_dif) + + if texture_alpha != "": + + mapping_alpha = img_map_transforms(t_alpha) + + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write( + "pigment{pigment_pattern {function{f%s(x,y,z).transmit}%s}\n" + % (texture_alpha, mapping_alpha) + ) + else: + tab_write( + "pigment {pigment_pattern {uv_mapping image_map" + '{%s "%s" %s}%s}\n' + % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha) + ) + tab_write("pigment_map {\n") + tab_write("[0 color rgbft<0,0,0,1,1>]\n") + if texture_alpha and texture_alpha.startswith("PAT_"): + tab_write("[1 function{f%s(x,y,z).transmit}%s]\n" % (texture_alpha, mapping_alpha)) + elif texture_dif and not texture_dif.startswith("PAT_"): + tab_write( + '[1 uv_mapping image_map {%s "%s" %s} %s]\n' + % ( + image_format(texture_dif), + texture_dif, + (img_map(t_dif) + img_gamma), + mapping_dif, + ) + ) + elif texture_dif and texture_dif.startswith("PAT_"): + tab_write("[1 %s %s]\n" % (texture_dif, mapping_dif)) + tab_write("}\n") + tab_write("}\n") + + else: + if texture_dif and texture_dif.startswith("PAT_"): + tab_write("pigment{%s %s}\n" % (texture_dif, mapping_dif)) + else: + tab_write("pigment {\n") + tab_write("uv_mapping image_map {\n") + # tab_write("%s \"%s\" %s}%s\n" % \ + # (image_format(texture_dif), texture_dif, + # (img_gamma + img_map(t_dif)),mapping_dif)) + tab_write('%s "%s" \n' % (image_format(texture_dif), texture_dif)) + tab_write("%s\n" % (img_gamma + img_map(t_dif))) + tab_write("}\n") + tab_write("%s\n" % mapping_dif) + tab_write("}\n") + + if texture_spec != "": + # ref_level_bound 3 is full specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3))) + else: + # ref_level_bound 2 is translated specular + tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2))) + + ## scale 1 rotate y*0 + # imageMap = ("{image_map {%s \"%s\" %s }" % \ + # (image_format(textures), textures,img_map(t_dif))) + # tab_write("\n\t\t\tuv_mapping pigment %s} %s finish {%s}" % \ + # (imageMap, mapping, safety(material_finish))) + # tab_write("\n\t\t\tpigment {uv_mapping image_map " \ + # "{%s \"%s\" %s}%s} finish {%s}" % \ + # (image_format(texture_dif), texture_dif,img_map(t_dif), + # mapping_dif, safety(material_finish))) + if texture_norm != "" and mater.pov.replacement_text == "": + + mapping_normal = img_map_transforms(t_nor) + + if texture_norm and texture_norm.startswith("PAT_"): + tab_write( + "normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n" + % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal) + ) + else: + tab_write("normal {\n") + # XXX TODO: fix and propagate the micro normals reflection blur below to non textured materials + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("average\n") + tab_write("normal_map{\n") + # 0.5 for entries below means a 50 percent mix + # between the micro normal and user bump map + # order seems indifferent as commutative + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n" + % ( + (10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)), + (1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)), + ) + ) # micronormals blurring + tab_write( + "[1.0 " + ) # Blurry reflection or not Proceed with user bump in either case... + tab_write( + "uv_mapping bump_map " + '{%s "%s" %s bump_size %.4g }%s]\n' + % ( + image_format(texture_norm), + texture_norm, + img_map(t_nor), + (-t_nor.normal_factor * 9.5), + mapping_normal, + ) + ) + # ...Then close the normal_map itself if blurry reflection + if ( + mater.pov_raytrace_mirror.use + and mater.pov_raytrace_mirror.gloss_factor < 1.0 + and not using_uberpov + ): + tab_write("}}\n") + else: + tab_write("}\n") + if texture_spec != "" and mater.pov.replacement_text == "": + tab_write("]\n") + + tab_write("}\n") + + # End of slope/ior texture_map + if mater.pov.diffuse_shader == "MINNAERT" and mater.pov.replacement_text == "": + tab_write("]\n") + tab_write("}\n") + if mater.pov.diffuse_shader == "FRESNEL" and mater.pov.replacement_text == "": + c = 1 + while c <= exported_lights_count: + tab_write("]\n") + tab_write("}\n") + c += 1 + + # Close first layer of POV "texture" (Blender material) + tab_write("}\n") + + colored_specular_found = bool( + (mater.pov.specular_color.s > 0.0) and (mater.pov.diffuse_shader != "MINNAERT") + ) + + # Write another layered texture using invisible diffuse and metallic trick + # to emulate colored specular highlights + special_texture_found = False + tmpidx = -1 + for t in mater.pov_texture_slots: + tmpidx += 1 + # index = mater.pov.active_texture_index + slot = mater.pov_texture_slots[tmpidx] # [index] + povtex = slot.texture # slot.name + tex = bpy.data.textures[povtex] + # Specular mapped textures would conflict with colored specular + # because POV can't layer over or under pigment patterned textures + special_texture_found = bool( + t + and t.use + and ((tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE") + and (t.use_map_specular or t.use_map_raymir) + ) + if colored_specular_found and not special_texture_found: + if comments: + tab_write(" // colored highlights with a stransparent metallic layer\n") + else: + tab_write("\n") + + tab_write("texture {\n") + tab_write( + "pigment {rgbft<%.3g, %.3g, %.3g, 0, 1>}\n" + % ( + mater.pov.specular_color[0], + mater.pov.specular_color[1], + mater.pov.specular_color[2], + ) + ) + tab_write( + "finish {%s}\n" % (safety(material_finish, ref_level_bound=2)) + ) # ref_level_bound 2 is translated spec + + texture_norm = "" + for t in mater.pov_texture_slots: + + if t and tex.pov.tex_pattern_type != "emulator": + procedural_flag = True + image_filename = string_strip_hyphen(bpy.path.clean_name(tex.name)) + if ( + t + and tex.type == "IMAGE" + and t.use + and tex.image + and tex.pov.tex_pattern_type == "emulator" + ): + procedural_flag = False + image_filename = path_image(tex.image) + img_gamma = "" + if image_filename: + if t.use_map_normal: + texture_norm = image_filename + # colvalue = t.normal_factor/10 # UNUSED XXX *-9.5 ! + # textNormName=tex.image.name + ".normal" + # was the above used? --MR + t_nor = t + if procedural_flag: + tab_write( + "normal{function" + "{f%s(x,y,z).grey} bump_size %.4g}\n" + % (texture_norm, (-t_nor.normal_factor * 9.5)) + ) + else: + tab_write( + "normal {uv_mapping bump_map " + '{%s "%s" %s bump_size %.4g }%s}\n' + % ( + image_format(texture_norm), + texture_norm, + img_map(t_nor), + (-t_nor.normal_factor * 9.5), + mapping_normal, + ) + ) + + tab_write("}\n") # THEN IT CAN CLOSE LAST LAYER OF TEXTURE diff --git a/render_povray/texturing_gui.py b/render_povray/texturing_gui.py new file mode 100755 index 000000000..6d12fb620 --- /dev/null +++ b/render_povray/texturing_gui.py @@ -0,0 +1,1255 @@ +# ##### 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 ##### + +# <pep8 compliant> + +"""User interface for texturing tools.""" + +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import ( + Operator, + Menu, + UIList, + Panel, + Brush, + Material, + Light, + World, + ParticleSettings, + FreestyleLineStyle, +) + +# from .ui import TextureButtonsPanel + +from .shading_properties import pov_context_tex_datablock +from bl_ui.properties_paint_common import brush_texture_settings + +# Example of wrapping every class 'as is' +from bl_ui import properties_texture + +# unused, replaced by pov_context_tex_datablock (no way to use?): +# from bl_ui.properties_texture import context_tex_datablock +# from bl_ui.properties_texture import texture_filter_common #unused yet? + +for member in dir(properties_texture): + subclass = getattr(properties_texture, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass +del properties_texture + + +class TextureButtonsPanel: + """Use this class to define buttons from the texture tab properties.""" + + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "texture" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + tex = context.texture + rd = context.scene.render + return tex and (rd.engine in cls.COMPAT_ENGINES) + + +class TEXTURE_MT_POV_specials(Menu): + """Use this class to define pov texture slot operations buttons.""" + + bl_label = "Texture Specials" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + layout = self.layout + + layout.operator("texture.slot_copy", icon='COPYDOWN') + layout.operator("texture.slot_paste", icon='PASTEDOWN') + + +class WORLD_TEXTURE_SLOTS_UL_POV_layerlist(UIList): + """Use this class to show pov texture slots list.""" # XXX Not used yet + + index: bpy.props.IntProperty(name='index') + # should active_propname be index or..? + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + world = context.scene.world # .pov + active_data = world.pov + # tex = context.texture #may be needed later? + + # We could write some code to decide which icon to use here... + # custom_icon = 'TEXTURE' + + # ob = data + slot = item + # ma = slot.name + # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. + if self.layout_type in {'DEFAULT', 'COMPACT'}: + # You should always start your row layout by a label (icon + text), or a non-embossed text field, + # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. + # We use icon_value of label, as our given icon is an integer value, not an enum ID. + # Note "data" names should never be translated! + if slot: + layout.prop(item, "texture", text="", emboss=False, icon='TEXTURE') + else: + layout.label(text="New", translate=False, icon_value=icon) + # 'GRID' layout type should be as compact as possible (typically a single icon!). + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(UIList): + """Use this class to show pov texture slots list.""" + + # texture_slots: + index: bpy.props.IntProperty(name='index') + # foo = random prop + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + # ob = data + slot = item + # ma = slot.name + # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. + if self.layout_type in {'DEFAULT', 'COMPACT'}: + # You should always start your row layout by a label (icon + text), or a non-embossed text field, + # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. + # We use icon_value of label, as our given icon is an integer value, not an enum ID. + # Note "data" names should never be translated! + if slot: + layout.prop(item, "texture", text="", emboss=False, icon='TEXTURE') + else: + layout.label(text="New", translate=False, icon_value=icon) + # 'GRID' layout type should be as compact as possible (typically a single icon!). + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class TEXTURE_PT_context(TextureButtonsPanel, Panel): + """Rewrite of this existing class to modify it.""" + + bl_label = "" + bl_context = "texture" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'POVRAY_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + # register but not unregistered because + # the modified parts concern only POVRAY_RENDER + @classmethod + def poll(cls, context): + return ( + context.scene.texture_context + not in ('MATERIAL', 'WORLD', 'LIGHT', 'PARTICLES', 'LINESTYLE') + or context.scene.render.engine != 'POVRAY_RENDER' + ) + + def draw(self, context): + layout = self.layout + tex = context.texture + space = context.space_data + pin_id = space.pin_id + use_pin_id = space.use_pin_id + user = context.texture_user + + col = layout.column() + + if not (use_pin_id and isinstance(pin_id, bpy.types.Texture)): + pin_id = None + + if not pin_id: + col.template_texture_user() + + if user or pin_id: + col.separator() + + if pin_id: + col.template_ID(space, "pin_id") + else: + propname = context.texture_user_property.identifier + col.template_ID(user, propname, new="texture.new") + + if tex: + col.separator() + + split = col.split(factor=0.2) + split.label(text="Type") + split.prop(tex, "type", text="") + + +class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel): + """Use this class to show pov texture context buttons.""" + + bl_label = "" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return engine in cls.COMPAT_ENGINES + # if not (hasattr(context, "pov_texture_slot") or hasattr(context, "texture_node")): + # return False + return ( + context.material + or context.scene.world + or context.light + or context.texture + or context.line_style + or context.particle_system + or isinstance(context.space_data.pin_id, ParticleSettings) + or context.texture_user + ) and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + scene = context.scene + mat = context.view_layer.objects.active.active_material + wld = context.scene.world + + layout.prop(scene, "texture_context", expand=True) + if scene.texture_context == 'MATERIAL' and mat is not None: + + row = layout.row() + row.template_list( + "MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist", + "", + mat, + "pov_texture_slots", + mat.pov, + "active_texture_index", + rows=2, + maxrows=16, + type="DEFAULT", + ) + col = row.column(align=True) + col.operator("pov.textureslotadd", icon='ADD', text='') + col.operator("pov.textureslotremove", icon='REMOVE', text='') + # XXX todo: recreate for pov_texture_slots? + # col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' + # col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' + col.separator() + + if mat.pov_texture_slots: + index = mat.pov.active_texture_index + slot = mat.pov_texture_slots[index] + povtex = slot.texture # slot.name + tex = bpy.data.textures[povtex] + col.prop(tex, 'use_fake_user', text='') + # layout.label(text='Linked Texture data browser:') + # propname = slot.texture_search + # if slot.texture was a pointer to texture data rather than just a name string: + # layout.template_ID(povtex, "texture", new="texture.new") + + layout.prop_search( + slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE' + ) + try: + bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[ + slot.texture_search + ] + bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[ + slot.texture_search + ] + except KeyError: + # texture not hand-linked by user + pass + + if tex: + layout.separator() + split = layout.split(factor=0.2) + split.label(text="Type") + split.prop(tex, "type", text="") + + # else: + # for i in range(18): # length of material texture slots + # mat.pov_texture_slots.add() + elif scene.texture_context == 'WORLD' and wld is not None: + + row = layout.row() + row.template_list( + "WORLD_TEXTURE_SLOTS_UL_POV_layerlist", + "", + wld, + "pov_texture_slots", + wld.pov, + "active_texture_index", + rows=2, + maxrows=16, + type="DEFAULT", + ) + col = row.column(align=True) + col.operator("pov.textureslotadd", icon='ADD', text='') + col.operator("pov.textureslotremove", icon='REMOVE', text='') + + # todo: recreate for pov_texture_slots? + # col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' + # col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' + col.separator() + + if wld.pov_texture_slots: + index = wld.pov.active_texture_index + slot = wld.pov_texture_slots[index] + povtex = slot.texture # slot.name + tex = bpy.data.textures[povtex] + col.prop(tex, 'use_fake_user', text='') + # layout.label(text='Linked Texture data browser:') + propname = slot.texture_search + # if slot.texture was a pointer to texture data rather than just a name string: + # layout.template_ID(povtex, "texture", new="texture.new") + + layout.prop_search( + slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE' + ) + try: + bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[ + slot.texture_search + ] + bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[ + slot.texture_search + ] + except KeyError: + # texture not hand-linked by user + pass + + if tex: + layout.separator() + split = layout.split(factor=0.2) + split.label(text="Type") + split.prop(tex, "type", text="") + + +# Commented out below is a reminder of what existed in Blender Internal +# attributes need to be recreated +''' + slot = getattr(context, "texture_slot", None) + node = getattr(context, "texture_node", None) + space = context.space_data + + #attempt at replacing removed space_data + mtl = getattr(context, "material", None) + if mtl != None: + spacedependant = mtl + wld = getattr(context, "world", None) + if wld != None: + spacedependant = wld + lgt = getattr(context, "light", None) + if lgt != None: + spacedependant = lgt + + + #idblock = context.particle_system.settings + + tex = getattr(context, "texture", None) + if tex != None: + spacedependant = tex + + + + scene = context.scene + idblock = scene.pov#pov_context_tex_datablock(context) + pin_id = space.pin_id + + #spacedependant.use_limited_texture_context = True + + if space.use_pin_id and not isinstance(pin_id, Texture): + idblock = id_tex_datablock(pin_id) + pin_id = None + + if not space.use_pin_id: + layout.row().prop(spacedependant, "texture_context", expand=True) + pin_id = None + + if spacedependant.texture_context == 'OTHER': + if not pin_id: + layout.template_texture_user() + user = context.texture_user + if user or pin_id: + layout.separator() + + row = layout.row() + + if pin_id: + row.template_ID(space, "pin_id") + else: + propname = context.texture_user_property.identifier + row.template_ID(user, propname, new="texture.new") + + if tex: + split = layout.split(factor=0.2) + if tex.use_nodes: + if slot: + split.label(text="Output:") + split.prop(slot, "output_node", text="") + else: + split.label(text="Type:") + split.prop(tex, "type", text="") + return + + tex_collection = (pin_id is None) and (node is None) and (spacedependant.texture_context not in ('LINESTYLE','OTHER')) + + if tex_collection: + + pov = getattr(context, "pov", None) + active_texture_index = getattr(spacedependant, "active_texture_index", None) + print (pov) + print(idblock) + print(active_texture_index) + row = layout.row() + + row.template_list("TEXTURE_UL_texslots", "", idblock, "texture_slots", + idblock, "active_texture_index", rows=2, maxrows=16, type="DEFAULT") + + # row.template_list("WORLD_TEXTURE_SLOTS_UL_List", "texture_slots", world, + # world.texture_slots, world, "active_texture_index", rows=2) + + col = row.column(align=True) + col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' + col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' + col.menu("TEXTURE_MT_POV_specials", icon='DOWNARROW_HLT', text="") + + if tex_collection: + layout.template_ID(idblock, "active_texture", new="texture.new") + elif node: + layout.template_ID(node, "texture", new="texture.new") + elif idblock: + layout.template_ID(idblock, "texture", new="texture.new") + + if pin_id: + layout.template_ID(space, "pin_id") + + if tex: + split = layout.split(factor=0.2) + if tex.use_nodes: + if slot: + split.label(text="Output:") + split.prop(slot, "output_node", text="") + else: + split.label(text="Type:") +''' + + +class TEXTURE_PT_colors(TextureButtonsPanel, Panel): + """Use this class to show pov color ramps.""" + + bl_label = "Colors" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + layout = self.layout + + tex = context.texture + + layout.prop(tex, "use_color_ramp", text="Ramp") + if tex.use_color_ramp: + layout.template_color_ramp(tex, "color_ramp", expand=True) + + split = layout.split() + + col = split.column() + col.label(text="RGB Multiply:") + sub = col.column(align=True) + sub.prop(tex, "factor_red", text="R") + sub.prop(tex, "factor_green", text="G") + sub.prop(tex, "factor_blue", text="B") + + col = split.column() + col.label(text="Adjust:") + col.prop(tex, "intensity") + col.prop(tex, "contrast") + col.prop(tex, "saturation") + + col = layout.column() + col.prop(tex, "use_clamp", text="Clamp") + + +# Texture Slot Panels # + + +class TEXTURE_OT_POV_texture_slot_add(Operator): + """Use this class for the add texture slot button.""" + + bl_idname = "pov.textureslotadd" + bl_label = "Add" + bl_description = "Add texture_slot" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def execute(self, context): + idblock = pov_context_tex_datablock(context) + tex = bpy.data.textures.new(name='Texture', type='IMAGE') + # tex.use_fake_user = True + # mat = context.view_layer.objects.active.active_material + slot = idblock.pov_texture_slots.add() + slot.name = tex.name + slot.texture = tex.name + slot.texture_search = tex.name + # Switch paint brush and paint brush mask + # to this texture so settings remain contextual + bpy.context.tool_settings.image_paint.brush.texture = tex + bpy.context.tool_settings.image_paint.brush.mask_texture = tex + idblock.pov.active_texture_index = len(idblock.pov_texture_slots) - 1 + + # for area in bpy.context.screen.areas: + # if area.type in ['PROPERTIES']: + # area.tag_redraw() + + return {'FINISHED'} + + +class TEXTURE_OT_POV_texture_slot_remove(Operator): + """Use this class for the remove texture slot button.""" + + bl_idname = "pov.textureslotremove" + bl_label = "Remove" + bl_description = "Remove texture_slot" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def execute(self, context): + idblock = pov_context_tex_datablock(context) + # mat = context.view_layer.objects.active.active_material + # tex_slot = idblock.pov_texture_slots.remove(idblock.pov.active_texture_index) # not used + if idblock.pov.active_texture_index > 0: + idblock.pov.active_texture_index -= 1 + try: + tex = idblock.pov_texture_slots[idblock.pov.active_texture_index].texture + except IndexError: + # No more slots + return {'FINISHED'} + # Switch paint brush to previous texture so settings remain contextual + # if 'tex' in locals(): # Would test is the tex variable is assigned / exists + bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex] + bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex] + + return {'FINISHED'} + + +class TextureSlotPanel(TextureButtonsPanel): + """Use this class to show pov texture slots panel.""" + + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + if not hasattr(context, "pov_texture_slot"): + return False + + engine = context.scene.render.engine + # return TextureButtonsPanel.poll(cls, context) and (engine in cls.COMPAT_ENGINES) + return TextureButtonsPanel.poll(context) and (engine in cls.COMPAT_ENGINES) + + +class TEXTURE_PT_POV_type(TextureButtonsPanel, Panel): + """Use this class to define pov texture type buttons.""" + + bl_label = "POV Textures" + COMPAT_ENGINES = {'POVRAY_RENDER'} + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + # world = context.world # unused + tex = context.texture + + split = layout.split(factor=0.2) + split.label(text="Pattern") + split.prop(tex.pov, "tex_pattern_type", text="") + + # row = layout.row() + # row.template_list("WORLD_TEXTURE_SLOTS_UL_List", "texture_slots", world, + # world.texture_slots, world, "active_texture_index") + + +class TEXTURE_PT_POV_preview(TextureButtonsPanel, Panel): + """Use this class to define pov texture preview panel.""" + + bl_label = "Preview" + COMPAT_ENGINES = {'POVRAY_RENDER'} + bl_options = {'HIDE_HEADER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + if not hasattr(context, "pov_texture_slot"): + return False + tex = context.texture + # mat = bpy.context.active_object.active_material #unused + return tex and (tex.pov.tex_pattern_type != 'emulator') and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + tex = context.texture + slot = getattr(context, "pov_texture_slot", None) + # idblock = pov_context_tex_datablock(context) # unused + layout = self.layout + # if idblock: + # layout.template_preview(tex, parent=idblock, slot=slot) + if tex.pov.tex_pattern_type != 'emulator': + layout.operator("tex.preview_update") + else: + layout.template_preview(tex, slot=slot) + + +class TEXTURE_PT_POV_parameters(TextureButtonsPanel, Panel): + """Use this class to define pov texture pattern buttons.""" + + bl_label = "POV Pattern Options" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw(self, context): + # mat = bpy.context.active_object.active_material # Unused + layout = self.layout + tex = context.texture + align = True + if tex is not None and tex.pov.tex_pattern_type != 'emulator': + if tex.pov.tex_pattern_type == 'agate': + layout.prop(tex.pov, "modifier_turbulence", text="Agate Turbulence") + if tex.pov.tex_pattern_type in {'spiral1', 'spiral2'}: + layout.prop(tex.pov, "modifier_numbers", text="Number of arms") + if tex.pov.tex_pattern_type == 'tiling': + layout.prop(tex.pov, "modifier_numbers", text="Pattern number") + if tex.pov.tex_pattern_type == 'magnet': + layout.prop(tex.pov, "magnet_style", text="Magnet style") + if tex.pov.tex_pattern_type == 'quilted': + row = layout.row(align=align) + row.prop(tex.pov, "modifier_control0", text="Control0") + row.prop(tex.pov, "modifier_control1", text="Control1") + if tex.pov.tex_pattern_type == 'brick': + col = layout.column(align=align) + row = col.row() + row.prop(tex.pov, "brick_size_x", text="Brick size X") + row.prop(tex.pov, "brick_size_y", text="Brick size Y") + row = col.row() + row.prop(tex.pov, "brick_size_z", text="Brick size Z") + row.prop(tex.pov, "brick_mortar", text="Brick mortar") + if tex.pov.tex_pattern_type in {'julia', 'mandel', 'magnet'}: + col = layout.column(align=align) + if tex.pov.tex_pattern_type == 'julia': + row = col.row() + row.prop(tex.pov, "julia_complex_1", text="Complex 1") + row.prop(tex.pov, "julia_complex_2", text="Complex 2") + if tex.pov.tex_pattern_type == 'magnet' and tex.pov.magnet_style == 'julia': + row = col.row() + row.prop(tex.pov, "julia_complex_1", text="Complex 1") + row.prop(tex.pov, "julia_complex_2", text="Complex 2") + row = col.row() + if tex.pov.tex_pattern_type in {'julia', 'mandel'}: + row.prop(tex.pov, "f_exponent", text="Exponent") + if tex.pov.tex_pattern_type == 'magnet': + row.prop(tex.pov, "magnet_type", text="Type") + row.prop(tex.pov, "f_iter", text="Iterations") + row = col.row() + row.prop(tex.pov, "f_ior", text="Interior") + row.prop(tex.pov, "f_ior_fac", text="Factor I") + row = col.row() + row.prop(tex.pov, "f_eor", text="Exterior") + row.prop(tex.pov, "f_eor_fac", text="Factor E") + if tex.pov.tex_pattern_type == 'gradient': + layout.label(text="Gradient orientation:") + column_flow = layout.column_flow(columns=3, align=True) + column_flow.prop(tex.pov, "grad_orient_x", text="X") + column_flow.prop(tex.pov, "grad_orient_y", text="Y") + column_flow.prop(tex.pov, "grad_orient_z", text="Z") + if tex.pov.tex_pattern_type == 'pavement': + layout.prop(tex.pov, "pave_sides", text="Pavement:number of sides") + col = layout.column(align=align) + column_flow = col.column_flow(columns=3, align=True) + column_flow.prop(tex.pov, "pave_tiles", text="Tiles") + if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 6: + column_flow.prop(tex.pov, "pave_pat_35", text="Pattern") + if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 5: + column_flow.prop(tex.pov, "pave_pat_22", text="Pattern") + if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 5: + column_flow.prop(tex.pov, "pave_pat_12", text="Pattern") + if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 6: + column_flow.prop(tex.pov, "pave_pat_12", text="Pattern") + if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 4: + column_flow.prop(tex.pov, "pave_pat_7", text="Pattern") + if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 4: + column_flow.prop(tex.pov, "pave_pat_5", text="Pattern") + if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 5: + column_flow.prop(tex.pov, "pave_pat_4", text="Pattern") + if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 3: + column_flow.prop(tex.pov, "pave_pat_3", text="Pattern") + if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 4: + column_flow.prop(tex.pov, "pave_pat_3", text="Pattern") + if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 3: + column_flow.prop(tex.pov, "pave_pat_2", text="Pattern") + if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 6: + column_flow.label(text="!!! 5 tiles!") + column_flow.prop(tex.pov, "pave_form", text="Form") + if tex.pov.tex_pattern_type == 'function': + layout.prop(tex.pov, "func_list", text="Functions") + if tex.pov.tex_pattern_type == 'function' and tex.pov.func_list != "NONE": + func = None + if tex.pov.func_list in {"f_noise3d", "f_ph", "f_r", "f_th"}: + func = 0 + if tex.pov.func_list in { + "f_comma", + "f_crossed_trough", + "f_cubic_saddle", + "f_cushion", + "f_devils_curve", + "f_enneper", + "f_glob", + "f_heart", + "f_hex_x", + "f_hex_y", + "f_hunt_surface", + "f_klein_bottle", + "f_kummer_surface_v1", + "f_lemniscate_of_gerono", + "f_mitre", + "f_nodal_cubic", + "f_noise_generator", + "f_odd", + "f_paraboloid", + "f_pillow", + "f_piriform", + "f_quantum", + "f_quartic_paraboloid", + "f_quartic_saddle", + "f_sphere", + "f_steiners_roman", + "f_torus_gumdrop", + "f_umbrella", + }: + func = 1 + if tex.pov.func_list in { + "f_bicorn", + "f_bifolia", + "f_boy_surface", + "f_superellipsoid", + "f_torus", + }: + func = 2 + if tex.pov.func_list in { + "f_ellipsoid", + "f_folium_surface", + "f_hyperbolic_torus", + "f_kampyle_of_eudoxus", + "f_parabolic_torus", + "f_quartic_cylinder", + "f_torus2", + }: + func = 3 + if tex.pov.func_list in { + "f_blob2", + "f_cross_ellipsoids", + "f_flange_cover", + "f_isect_ellipsoids", + "f_kummer_surface_v2", + "f_ovals_of_cassini", + "f_rounded_box", + "f_spikes_2d", + "f_strophoid", + }: + func = 4 + if tex.pov.func_list in { + "f_algbr_cyl1", + "f_algbr_cyl2", + "f_algbr_cyl3", + "f_algbr_cyl4", + "f_blob", + "f_mesh1", + "f_poly4", + "f_spikes", + }: + func = 5 + if tex.pov.func_list in { + "f_devils_curve_2d", + "f_dupin_cyclid", + "f_folium_surface_2d", + "f_hetero_mf", + "f_kampyle_of_eudoxus_2d", + "f_lemniscate_of_gerono_2d", + "f_polytubes", + "f_ridge", + "f_ridged_mf", + "f_spiral", + "f_witch_of_agnesi", + }: + func = 6 + if tex.pov.func_list in {"f_helix1", "f_helix2", "f_piriform_2d", "f_strophoid_2d"}: + func = 7 + if tex.pov.func_list == "f_helical_torus": + func = 8 + column_flow = layout.column_flow(columns=3, align=True) + column_flow.label(text="X") + column_flow.prop(tex.pov, "func_plus_x", text="") + column_flow.prop(tex.pov, "func_x", text="Value") + column_flow = layout.column_flow(columns=3, align=True) + column_flow.label(text="Y") + column_flow.prop(tex.pov, "func_plus_y", text="") + column_flow.prop(tex.pov, "func_y", text="Value") + column_flow = layout.column_flow(columns=3, align=True) + column_flow.label(text="Z") + column_flow.prop(tex.pov, "func_plus_z", text="") + column_flow.prop(tex.pov, "func_z", text="Value") + row = layout.row(align=align) + if func > 0: + row.prop(tex.pov, "func_P0", text="P0") + if func > 1: + row.prop(tex.pov, "func_P1", text="P1") + row = layout.row(align=align) + if func > 2: + row.prop(tex.pov, "func_P2", text="P2") + if func > 3: + row.prop(tex.pov, "func_P3", text="P3") + row = layout.row(align=align) + if func > 4: + row.prop(tex.pov, "func_P4", text="P4") + if func > 5: + row.prop(tex.pov, "func_P5", text="P5") + row = layout.row(align=align) + if func > 6: + row.prop(tex.pov, "func_P6", text="P6") + if func > 7: + row.prop(tex.pov, "func_P7", text="P7") + row = layout.row(align=align) + row.prop(tex.pov, "func_P8", text="P8") + row.prop(tex.pov, "func_P9", text="P9") + ###################################################End Patterns############################ + + layout.prop(tex.pov, "warp_types", text="Warp types") # warp + if tex.pov.warp_types == "TOROIDAL": + layout.prop(tex.pov, "warp_tor_major_radius", text="Major radius") + if tex.pov.warp_types not in {"CUBIC", "NONE"}: + layout.prop(tex.pov, "warp_orientation", text="Warp orientation") + col = layout.column(align=align) + row = col.row() + row.prop(tex.pov, "warp_dist_exp", text="Distance exponent") + row = col.row() + row.prop(tex.pov, "modifier_frequency", text="Frequency") + row.prop(tex.pov, "modifier_phase", text="Phase") + + row = layout.row() + + row.label(text="Offset:") + row.label(text="Scale:") + row.label(text="Rotate:") + col = layout.column(align=align) + row = col.row() + row.prop(tex.pov, "tex_mov_x", text="X") + row.prop(tex.pov, "tex_scale_x", text="X") + row.prop(tex.pov, "tex_rot_x", text="X") + row = col.row() + row.prop(tex.pov, "tex_mov_y", text="Y") + row.prop(tex.pov, "tex_scale_y", text="Y") + row.prop(tex.pov, "tex_rot_y", text="Y") + row = col.row() + row.prop(tex.pov, "tex_mov_z", text="Z") + row.prop(tex.pov, "tex_scale_z", text="Z") + row.prop(tex.pov, "tex_rot_z", text="Z") + row = layout.row() + + row.label(text="Turbulence:") + col = layout.column(align=align) + row = col.row() + row.prop(tex.pov, "warp_turbulence_x", text="X") + row.prop(tex.pov, "modifier_octaves", text="Octaves") + row = col.row() + row.prop(tex.pov, "warp_turbulence_y", text="Y") + row.prop(tex.pov, "modifier_lambda", text="Lambda") + row = col.row() + row.prop(tex.pov, "warp_turbulence_z", text="Z") + row.prop(tex.pov, "modifier_omega", text="Omega") + + +class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel): + """Use this class to define POV texture mapping buttons.""" + + bl_label = "Mapping" + COMPAT_ENGINES = {'POVRAY_RENDER'} + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + + @classmethod + def poll(cls, context): + idblock = pov_context_tex_datablock(context) + if isinstance(idblock, Brush) and not context.sculpt_object: + return False + + if not getattr(context, "texture_slot", None): + return False + + engine = context.scene.render.engine + return engine in cls.COMPAT_ENGINES + + def draw(self, context): + layout = self.layout + + idblock = pov_context_tex_datablock(context) + mat = bpy.context.active_object.active_material + # tex = context.texture_slot + tex = mat.pov_texture_slots[mat.active_texture_index] + if not isinstance(idblock, Brush): + split = layout.split(percentage=0.3) + col = split.column() + col.label(text="Coordinates:") + col = split.column() + col.prop(tex, "texture_coords", text="") + + if tex.texture_coords == 'ORCO': + """ + ob = context.object + if ob and ob.type == 'MESH': + split = layout.split(percentage=0.3) + split.label(text="Mesh:") + split.prop(ob.data, "texco_mesh", text="") + """ + elif tex.texture_coords == 'UV': + split = layout.split(percentage=0.3) + split.label(text="Map:") + ob = context.object + if ob and ob.type == 'MESH': + split.prop_search(tex, "uv_layer", ob.data, "uv_textures", text="") + else: + split.prop(tex, "uv_layer", text="") + + elif tex.texture_coords == 'OBJECT': + split = layout.split(percentage=0.3) + split.label(text="Object:") + split.prop(tex, "object", text="") + + elif tex.texture_coords == 'ALONG_STROKE': + split = layout.split(percentage=0.3) + split.label(text="Use Tips:") + split.prop(tex, "use_tips", text="") + + if isinstance(idblock, Brush): + if context.sculpt_object or context.image_paint_object: + brush_texture_settings(layout, idblock, context.sculpt_object) + else: + if isinstance(idblock, FreestyleLineStyle): + split = layout.split(percentage=0.3) + split.label(text="Projection:") + split.prop(tex, "mapping", text="") + + split = layout.split(percentage=0.3) + split.separator() + row = split.row() + row.prop(tex, "mapping_x", text="") + row.prop(tex, "mapping_y", text="") + row.prop(tex, "mapping_z", text="") + + elif isinstance(idblock, Material): + split = layout.split(percentage=0.3) + split.label(text="Projection:") + split.prop(tex, "mapping", text="") + + split = layout.split() + + col = split.column() + if tex.texture_coords in {'ORCO', 'UV'}: + col.prop(tex, "use_from_dupli") + if idblock.type == 'VOLUME' and tex.texture_coords == 'ORCO': + col.prop(tex, "use_map_to_bounds") + elif tex.texture_coords == 'OBJECT': + col.prop(tex, "use_from_original") + if idblock.type == 'VOLUME': + col.prop(tex, "use_map_to_bounds") + else: + col.label() + + col = split.column() + row = col.row() + row.prop(tex, "mapping_x", text="") + row.prop(tex, "mapping_y", text="") + row.prop(tex, "mapping_z", text="") + + row = layout.row() + row.column().prop(tex, "offset") + row.column().prop(tex, "scale") + + +class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel): + """Use this class to define pov texture influence buttons.""" + + bl_label = "Influence" + COMPAT_ENGINES = {'POVRAY_RENDER'} + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + # bl_context = 'texture' + @classmethod + def poll(cls, context): + idblock = pov_context_tex_datablock(context) + if ( + # isinstance(idblock, Brush) and # Brush used for everything since 2.8 + context.scene.texture_context + == 'OTHER' + ): # XXX replace by isinstance(idblock, bpy.types.Brush) and ... + return False + + # Specify below also for pov_world_texture_slots, lights etc. + # to display for various types of slots but only when any + if not getattr(idblock, "pov_texture_slots", None): + return False + + engine = context.scene.render.engine + return engine in cls.COMPAT_ENGINES + + def draw(self, context): + + layout = self.layout + + idblock = pov_context_tex_datablock(context) + # tex = context.pov_texture_slot + # mat = bpy.context.active_object.active_material + texslot = idblock.pov_texture_slots[ + idblock.pov.active_texture_index + ] # bpy.data.textures[mat.active_texture_index] + # below tex is unused + tex = bpy.data.textures[idblock.pov_texture_slots[idblock.pov.active_texture_index].texture] + + def factor_but(layout, toggle, factor, name): + row = layout.row(align=True) + row.prop(texslot, toggle, text="") + sub = row.row(align=True) + sub.active = getattr(texslot, toggle) + sub.prop(texslot, factor, text=name, slider=True) + return sub # XXX, temp. use_map_normal needs to override. + + if isinstance(idblock, Material): + split = layout.split() + + col = split.column() + if idblock.pov.type in {'SURFACE', 'WIRE'}: + + split = layout.split() + + col = split.column() + col.label(text="Diffuse:") + factor_but(col, "use_map_diffuse", "diffuse_factor", "Intensity") + factor_but(col, "use_map_color_diffuse", "diffuse_color_factor", "Color") + factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") + factor_but(col, "use_map_translucency", "translucency_factor", "Translucency") + + col.label(text="Specular:") + factor_but(col, "use_map_specular", "specular_factor", "Intensity") + factor_but(col, "use_map_color_spec", "specular_color_factor", "Color") + factor_but(col, "use_map_hardness", "hardness_factor", "Hardness") + + col = split.column() + col.label(text="Shading:") + factor_but(col, "use_map_ambient", "ambient_factor", "Ambient") + factor_but(col, "use_map_emit", "emit_factor", "Emit") + factor_but(col, "use_map_mirror", "mirror_factor", "Mirror") + factor_but(col, "use_map_raymir", "raymir_factor", "Ray Mirror") + + col.label(text="Geometry:") + # XXX replace 'or' when displacement is fixed to not rely on normal influence value. + sub_tmp = factor_but(col, "use_map_normal", "normal_factor", "Normal") + sub_tmp.active = texslot.use_map_normal or texslot.use_map_displacement + # END XXX + + factor_but(col, "use_map_warp", "warp_factor", "Warp") + factor_but(col, "use_map_displacement", "displacement_factor", "Displace") + + elif idblock.pov.type == 'HALO': + layout.label(text="Halo:") + + split = layout.split() + + col = split.column() + factor_but(col, "use_map_color_diffuse", "diffuse_color_factor", "Color") + factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") + + col = split.column() + factor_but(col, "use_map_raymir", "raymir_factor", "Size") + factor_but(col, "use_map_hardness", "hardness_factor", "Hardness") + factor_but(col, "use_map_translucency", "translucency_factor", "Add") + elif idblock.pov.type == 'VOLUME': + layout.label(text="Volume:") + + split = layout.split() + + col = split.column() + factor_but(col, "use_map_density", "density_factor", "Density") + factor_but(col, "use_map_emission", "emission_factor", "Emission") + factor_but(col, "use_map_scatter", "scattering_factor", "Scattering") + factor_but(col, "use_map_reflect", "reflection_factor", "Reflection") + + col = split.column() + col.label(text=" ") + factor_but(col, "use_map_color_emission", "emission_color_factor", "Emission Color") + factor_but( + col, + "use_map_color_transmission", + "transmission_color_factor", + "Transmission Color", + ) + factor_but( + col, "use_map_color_reflection", "reflection_color_factor", "Reflection Color" + ) + + layout.label(text="Geometry:") + + split = layout.split() + + col = split.column() + factor_but(col, "use_map_warp", "warp_factor", "Warp") + + col = split.column() + factor_but(col, "use_map_displacement", "displacement_factor", "Displace") + + elif isinstance(idblock, Light): + split = layout.split() + + col = split.column() + factor_but(col, "use_map_color", "color_factor", "Color") + + col = split.column() + factor_but(col, "use_map_shadow", "shadow_factor", "Shadow") + + elif isinstance(idblock, World): + split = layout.split() + + col = split.column() + factor_but(col, "use_map_blend", "blend_factor", "Blend") + factor_but(col, "use_map_horizon", "horizon_factor", "Horizon") + + col = split.column() + factor_but(col, "use_map_zenith_up", "zenith_up_factor", "Zenith Up") + factor_but(col, "use_map_zenith_down", "zenith_down_factor", "Zenith Down") + elif isinstance(idblock, ParticleSettings): + split = layout.split() + + col = split.column() + col.label(text="General:") + factor_but(col, "use_map_time", "time_factor", "Time") + factor_but(col, "use_map_life", "life_factor", "Lifetime") + factor_but(col, "use_map_density", "density_factor", "Density") + factor_but(col, "use_map_size", "size_factor", "Size") + + col = split.column() + col.label(text="Physics:") + factor_but(col, "use_map_velocity", "velocity_factor", "Velocity") + factor_but(col, "use_map_damp", "damp_factor", "Damp") + factor_but(col, "use_map_gravity", "gravity_factor", "Gravity") + factor_but(col, "use_map_field", "field_factor", "Force Fields") + + layout.label(text="Hair:") + + split = layout.split() + + col = split.column() + factor_but(col, "use_map_length", "length_factor", "Length") + factor_but(col, "use_map_clump", "clump_factor", "Clump") + factor_but(col, "use_map_twist", "twist_factor", "Twist") + + col = split.column() + factor_but(col, "use_map_kink_amp", "kink_amp_factor", "Kink Amplitude") + factor_but(col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency") + factor_but(col, "use_map_rough", "rough_factor", "Rough") + + elif isinstance(idblock, FreestyleLineStyle): + split = layout.split() + + col = split.column() + factor_but(col, "use_map_color_diffuse", "diffuse_color_factor", "Color") + col = split.column() + factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") + + layout.separator() + + if not isinstance(idblock, ParticleSettings): + split = layout.split() + + col = split.column() + # col.prop(tex, "blend_type", text="Blend") #deprecated since 2.8 + # col.prop(tex, "use_rgb_to_intensity") #deprecated since 2.8 + # color is used on gray-scale textures even when use_rgb_to_intensity is disabled. + # col.prop(tex, "color", text="") #deprecated since 2.8 + + col = split.column() + # col.prop(tex, "invert", text="Negative") #deprecated since 2.8 + # col.prop(tex, "use_stencil") #deprecated since 2.8 + + # if isinstance(idblock, (Material, World)): + # col.prop(tex, "default_value", text="DVar", slider=True) + + +class TEXTURE_PT_POV_tex_gamma(TextureButtonsPanel, Panel): + """Use this class to define pov texture gamma buttons.""" + + bl_label = "Image Gamma" + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def draw_header(self, context): + tex = context.texture + + self.layout.prop(tex.pov, "tex_gamma_enable", text="", icon='SEQ_LUMA_WAVEFORM') + + def draw(self, context): + layout = self.layout + + tex = context.texture + + layout.active = tex.pov.tex_gamma_enable + layout.prop(tex.pov, "tex_gamma_value", text="Gamma Value") + + +# commented out below UI for texture only custom code inside exported material: +# class TEXTURE_PT_povray_replacement_text(TextureButtonsPanel, Panel): +# bl_label = "Custom POV Code" +# COMPAT_ENGINES = {'POVRAY_RENDER'} + +# def draw(self, context): +# layout = self.layout + +# tex = context.texture + +# col = layout.column() +# col.label(text="Replace properties with:") +# col.prop(tex.pov, "replacement_text", text="") + + +classes = ( + WORLD_TEXTURE_SLOTS_UL_POV_layerlist, + TEXTURE_MT_POV_specials, + TEXTURE_PT_POV_context_texture, + TEXTURE_PT_colors, + TEXTURE_PT_POV_type, + TEXTURE_PT_POV_preview, + TEXTURE_PT_POV_parameters, + TEXTURE_PT_POV_tex_gamma, + MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist, + TEXTURE_OT_POV_texture_slot_add, + TEXTURE_OT_POV_texture_slot_remove, + TEXTURE_PT_POV_influence, + TEXTURE_PT_POV_mapping, +) + + +def register(): + + for cls in classes: + register_class(cls) + + +def unregister(): + + for cls in reversed(classes): + if cls != TEXTURE_PT_context: + unregister_class(cls) diff --git a/render_povray/texturing_properties.py b/render_povray/texturing_properties.py new file mode 100755 index 000000000..bb89ee025 --- /dev/null +++ b/render_povray/texturing_properties.py @@ -0,0 +1,1137 @@ +# ##### 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 ##### + +# <pep8 compliant> +"""Declare texturing properties controllable in UI.""" + +import bpy +from bpy.utils import register_class, unregister_class +from bpy.types import PropertyGroup +from bpy.props import ( + FloatVectorProperty, + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + PointerProperty, + CollectionProperty, +) + +from .shading_properties import active_texture_name_from_uilist, active_texture_name_from_search + +############################################################################### +# Texture slots (Material context) exported as POV texture properties. +############################################################################### +class MaterialTextureSlot(PropertyGroup): + """Declare material texture slot level properties for UI and translated to POV.""" + + bl_idname = ("pov_texture_slots",) + bl_description = ("Texture_slots from Blender-2.79",) + + # Adding a "real" texture datablock as property is not possible + # (or at least not easy through a dynamically populated EnumProperty). + # That's why we'll use a prop_search() UILayout function in texturing_gui.py. + # So we'll assign the name of the needed texture datablock to the below StringProperty. + texture: StringProperty(update=active_texture_name_from_uilist) + # and use another temporary StringProperty to change the linked data + texture_search: StringProperty( + name="", update=active_texture_name_from_search, description="Browse Texture to be linked" + ) + + alpha_factor: FloatProperty( + name="Alpha", description="Amount texture affects alpha", default=1.0 + ) + + ambient_factor: FloatProperty( + name="", description="Amount texture affects ambient", default=1.0 + ) + + bump_method: EnumProperty( + name="", + description="Method to use for bump mapping", + items=( + ("BUMP_ORIGINAL", "Bump Original", ""), + ("BUMP_COMPATIBLE", "Bump Compatible", ""), + ("BUMP_DEFAULT", "Bump Default", ""), + ("BUMP_BEST_QUALITY", "Bump Best Quality", ""), + ), + default="BUMP_ORIGINAL", + ) + + bump_objectspace: EnumProperty( + name="", + description="Space to apply bump mapping in", + items=( + ("BUMP_VIEWSPACE", "Bump Viewspace", ""), + ("BUMP_OBJECTSPACE", "Bump Objectspace", ""), + ("BUMP_TEXTURESPACE", "Bump Texturespace", ""), + ), + default="BUMP_VIEWSPACE", + ) + + density_factor: FloatProperty( + name="", description="Amount texture affects density", default=1.0 + ) + + diffuse_color_factor: FloatProperty( + name="", description="Amount texture affects diffuse color", default=1.0 + ) + + diffuse_factor: FloatProperty( + name="", description="Amount texture affects diffuse reflectivity", default=1.0 + ) + + displacement_factor: FloatProperty( + name="", description="Amount texture displaces the surface", default=0.2 + ) + + emission_color_factor: FloatProperty( + name="", description="Amount texture affects emission color", default=1.0 + ) + + emission_factor: FloatProperty( + name="", description="Amount texture affects emission", default=1.0 + ) + + emit_factor: FloatProperty(name="", description="Amount texture affects emission", default=1.0) + + hardness_factor: FloatProperty( + name="", description="Amount texture affects hardness", default=1.0 + ) + + mapping: EnumProperty( + name="", + description="", + items=( + ("FLAT", "Flat", ""), + ("CUBE", "Cube", ""), + ("TUBE", "Tube", ""), + ("SPHERE", "Sphere", ""), + ), + default="FLAT", + ) + + mapping_x: EnumProperty( + name="", + description="", + items=(("NONE", "", ""), ("X", "", ""), ("Y", "", ""), ("Z", "", "")), + default="NONE", + ) + + mapping_y: EnumProperty( + name="", + description="", + items=(("NONE", "", ""), ("X", "", ""), ("Y", "", ""), ("Z", "", "")), + default="NONE", + ) + + mapping_z: EnumProperty( + name="", + description="", + items=(("NONE", "", ""), ("X", "", ""), ("Y", "", ""), ("Z", "", "")), + default="NONE", + ) + + mirror_factor: FloatProperty( + name="", description="Amount texture affects mirror color", default=1.0 + ) + + normal_factor: FloatProperty( + name="", description="Amount texture affects normal values", default=1.0 + ) + + normal_map_space: EnumProperty( + name="", + description="Sets space of normal map image", + items=( + ("CAMERA", "Camera", ""), + ("WORLD", "World", ""), + ("OBJECT", "Object", ""), + ("TANGENT", "Tangent", ""), + ), + default="CAMERA", + ) + + object: StringProperty( + name="Object", + description="Object to use for mapping with Object texture coordinates", + default="", + ) + + raymir_factor: FloatProperty( + name="", description="Amount texture affects ray mirror", default=1.0 + ) + + reflection_color_factor: FloatProperty( + name="", description="Amount texture affects color of out-scattered light", default=1.0 + ) + + reflection_factor: FloatProperty( + name="", description="Amount texture affects brightness of out-scattered light", default=1.0 + ) + + scattering_factor: FloatProperty( + name="", description="Amount texture affects scattering", default=1.0 + ) + + specular_color_factor: FloatProperty( + name="", description="Amount texture affects specular color", default=1.0 + ) + + specular_factor: FloatProperty( + name="", description="Amount texture affects specular reflectivity", default=1.0 + ) + + offset: FloatVectorProperty( + name="Offset", + description=("Fine tune of the texture mapping X, Y and Z locations "), + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="TRANSLATION", + ) + + scale: FloatVectorProperty( + name="Size", + subtype="XYZ", + size=3, + description="Set scaling for the texture’s X, Y and Z sizes ", + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + ) + + texture_coords: EnumProperty( + name="", + description="", + items=( + ("GLOBAL", "Global", ""), + ("OBJECT", "Object", ""), + ("UV", "UV", ""), + ("ORCO", "Original Coordinates", ""), + ("STRAND", "Strand", ""), + ("STICKY", "Sticky", ""), + ("WINDOW", "Window", ""), + ("NORMAL", "Normal", ""), + ("REFLECTION", "Reflection", ""), + ("STRESS", "Stress", ""), + ("TANGENT", "Tangent", ""), + ), + default="GLOBAL", + ) + + translucency_factor: FloatProperty( + name="", description="Amount texture affects translucency", default=1.0 + ) + + transmission_color_factor: FloatProperty( + name="", + description="Amount texture affects result color after light has been scattered/absorbed", + default=1.0, + ) + + use: BoolProperty(name="", description="Enable this material texture slot", default=True) + + use_from_dupli: BoolProperty( + name="", + description="Dupli’s instanced from verts, faces or particles, " + "inherit texture coordinate from their parent", + default=False, + ) + + use_from_original: BoolProperty( + name="", + description="Dupli’s derive their object coordinates from the " + "original objects transformation", + default=False, + ) + + use_interpolation: BoolProperty( + name="", description="Interpolates pixels using selected filter ", default=False + ) + + use_map_alpha: BoolProperty( + name="", description="Causes the texture to affect the alpha value", default=False + ) + + use_map_ambient: BoolProperty( + name="", description="Causes the texture to affect the value of ambient", default=False + ) + + use_map_color_diffuse: BoolProperty( + name="", + description="Causes the texture to affect basic color of the material", + default=True, + ) + + use_map_color_emission: BoolProperty( + name="", description="Causes the texture to affect the color of emission", default=False + ) + + use_map_color_reflection: BoolProperty( + name="", + description="Causes the texture to affect the color of scattered light", + default=False, + ) + + use_map_color_spec: BoolProperty( + name="", description="Causes the texture to affect the specularity color", default=False + ) + + use_map_color_transmission: BoolProperty( + name="", + description="Causes the texture to affect the result color after " + "other light has been scattered/absorbed", + default=False, + ) + + use_map_density: BoolProperty( + name="", description="Causes the texture to affect the volume’s density", default=False + ) + + use_map_diffuse: BoolProperty( + name="", + description="Causes the texture to affect the value of the materials diffuse reflectivity", + default=False, + ) + + use_map_displacement: BoolProperty( + name="", description="Let the texture displace the surface", default=False + ) + + use_map_emission: BoolProperty( + name="", description="Causes the texture to affect the volume’s emission", default=False + ) + + use_map_emit: BoolProperty( + name="", description="Causes the texture to affect the emit value", default=False + ) + + use_map_hardness: BoolProperty( + name="", description="Causes the texture to affect the hardness value", default=False + ) + + use_map_mirror: BoolProperty( + name="", description="Causes the texture to affect the mirror color", default=False + ) + + use_map_normal: BoolProperty( + name="", description="Causes the texture to affect the rendered normal", default=False + ) + + use_map_raymir: BoolProperty( + name="", description="Causes the texture to affect the ray-mirror value", default=False + ) + + use_map_reflect: BoolProperty( + name="", + description="Causes the texture to affect the reflected light’s brightness", + default=False, + ) + + use_map_scatter: BoolProperty( + name="", description="Causes the texture to affect the volume’s scattering", default=False + ) + + use_map_specular: BoolProperty( + name="", + description="Causes the texture to affect the value of specular reflectivity", + default=False, + ) + + use_map_translucency: BoolProperty( + name="", description="Causes the texture to affect the translucency value", default=False + ) + + use_map_warp: BoolProperty( + name="", + description="Let the texture warp texture coordinates of next channels", + default=False, + ) + + uv_layer: StringProperty( + name="", description="UV layer to use for mapping with UV texture coordinates", default="" + ) + + warp_factor: FloatProperty( + name="", + description="Amount texture affects texture coordinates of next channels", + default=0.0, + ) + + ####################################### + + blend_factor: FloatProperty( + name="Blend", + description="Amount texture affects color progression of the " "background", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + horizon_factor: FloatProperty( + name="Horizon", + description="Amount texture affects color of the horizon" "", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + object: StringProperty( + name="Object", + description="Object to use for mapping with Object texture coordinates", + default="", + ) + + texture_coords: EnumProperty( + name="Coordinates", + description="Texture coordinates used to map the texture onto the background", + items=( + ("VIEW", "View", "Use view vector for the texture coordinates"), + ( + "GLOBAL", + "Global", + "Use global coordinates for the texture coordinates (interior mist)", + ), + ( + "ANGMAP", + "AngMap", + "Use 360 degree angular coordinates, e.g. for spherical light probes", + ), + ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"), + ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"), + ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"), + ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates"), + ), + default="VIEW", + ) + + use_map_blend: BoolProperty( + name="Blend Map", description="Affect the color progression of the background", default=True + ) + + use_map_horizon: BoolProperty( + name="Horizon Map", description="Affect the color of the horizon", default=False + ) + + use_map_zenith_down: BoolProperty( + name="", description="Affect the color of the zenith below", default=False + ) + + use_map_zenith_up: BoolProperty( + name="Zenith Up Map", description="Affect the color of the zenith above", default=False + ) + + zenith_down_factor: FloatProperty( + name="Zenith Down", + description="Amount texture affects color of the zenith below", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + zenith_up_factor: FloatProperty( + name="Zenith Up", + description="Amount texture affects color of the zenith above", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + +############################################################################### +# Texture slots (World context) exported as POV texture properties. +############################################################################### +class WorldTextureSlot(PropertyGroup): + """Declare world texture slot level properties for UI and translated to POV.""" + + bl_idname = ("pov_texture_slots",) + bl_description = ("Texture_slots from Blender-2.79",) + + # Adding a "real" texture datablock as property is not possible + # (or at least not easy through a dynamically populated EnumProperty). + # That's why we'll use a prop_search() UILayout function in ui.py. + # So we'll assign the name of the needed texture datablock to the below StringProperty. + texture: StringProperty(update=active_texture_name_from_uilist) + # and use another temporary StringProperty to change the linked data + texture_search: StringProperty( + name="", update=active_texture_name_from_search, description="Browse Texture to be linked" + ) + + blend_factor: FloatProperty( + name="Blend", + description="Amount texture affects color progression of the " "background", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + horizon_factor: FloatProperty( + name="Horizon", + description="Amount texture affects color of the horizon", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + object: StringProperty( + name="Object", + description="Object to use for mapping with Object texture coordinates", + default="", + ) + + offset: FloatVectorProperty( + name="Offset", + description=("Fine tune of the texture mapping X, Y and Z locations "), + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(0.0, 0.0, 0.0), + options={"ANIMATABLE"}, + subtype="TRANSLATION", + ) + + scale: FloatVectorProperty( + name="Size", + subtype="XYZ", + size=3, + description="Set scaling for the texture’s X, Y and Z sizes ", + precision=4, + step=0.1, + soft_min=-100.0, + soft_max=100.0, + default=(1.0, 1.0, 1.0), + options={"ANIMATABLE"}, + ) + + texture_coords: EnumProperty( + name="Coordinates", + description="Texture coordinates used to map the texture onto the background", + items=( + ("VIEW", "View", "Use view vector for the texture coordinates"), + ( + "GLOBAL", + "Global", + "Use global coordinates for the texture coordinates (interior mist)", + ), + ( + "ANGMAP", + "AngMap", + "Use 360 degree angular coordinates, e.g. for spherical light probes", + ), + ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"), + ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"), + ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"), + ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates"), + ), + default="VIEW", + ) + + use_map_blend: BoolProperty( + name="Blend Map", description="Affect the color progression of the background", default=True + ) + + use_map_horizon: BoolProperty( + name="Horizon Map", description="Affect the color of the horizon", default=False + ) + + use_map_zenith_down: BoolProperty( + name="", description="Affect the color of the zenith below", default=False + ) + + use_map_zenith_up: BoolProperty( + name="Zenith Up Map", description="Affect the color of the zenith above", default=False + ) + + zenith_down_factor: FloatProperty( + name="Zenith Down", + description="Amount texture affects color of the zenith below", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + zenith_up_factor: FloatProperty( + name="Zenith Up", + description="Amount texture affects color of the zenith above", + soft_min=0.0, + soft_max=1.0, + default=1.0, + ) + + +############################################################################### +# Space properties from removed former Blender Internal +############################################################################### + +# added below at superclass level so as to be available in World, Material, +# and Light, for texture slots use + +bpy.types.ID.use_limited_texture_context = BoolProperty( + name="", + description="Use the limited version of texture user (for â€old shading’ mode)", + default=True, +) +bpy.types.ID.texture_context = EnumProperty( + name="Texture context", + description="Type of texture data to display and edit", + items=( + ("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures" + ("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures" + ("LIGHT", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures" + ("PARTICLES", "", "Show particles textures", "PARTICLES", 3), # "Show particles textures" + ("LINESTYLE", "", "Show linestyle textures", "LINE_DATA", 4), # "Show linestyle textures" + ("OTHER", "", "Show other data textures", "TEXTURE_DATA", 5), # "Show other data textures" + ), + default="MATERIAL", +) +# bpy.types.ID.active_texture_index = IntProperty( +# name = "Index for texture_slots", +# default = 0, +# ) + + +############################################################################### +# Texture POV properties. +############################################################################### + + +class RenderPovSettingsTexture(PropertyGroup): + """Declare texture level properties controllable in UI and translated to POV.""" + + # former Space properties from removed Blender Internal + active_texture_index: IntProperty(name="Index for texture_slots", min=0, max=17, default=0) + + use_limited_texture_context: BoolProperty( + name="", + description="Use the limited version of texture user (for â€old shading’ mode)", + default=True, + ) + + texture_context: EnumProperty( + name="Texture context", + description="Type of texture data to display and edit", + items=( + ("MATERIAL", "", "Show material textures", "MATERIAL", 0), # "Show material textures" + ("WORLD", "", "Show world textures", "WORLD", 1), # "Show world textures" + ("LAMP", "", "Show lamp textures", "LIGHT", 2), # "Show lamp textures" + ( + "PARTICLES", + "", + "Show particles textures", + "PARTICLES", + 3, + ), # "Show particles textures" + ( + "LINESTYLE", + "", + "Show linestyle textures", + "LINE_DATA", + 4, + ), # "Show linestyle textures" + ( + "OTHER", + "", + "Show other data textures", + "TEXTURE_DATA", + 5, + ), # "Show other data textures" + ), + default="MATERIAL", + ) + + # Custom texture gamma + tex_gamma_enable: BoolProperty( + name="Enable custom texture gamma", + description="Notify some custom gamma for which texture has been precorrected " + "without the file format carrying it and only if it differs from your " + "OS expected standard (see pov doc)", + default=False, + ) + + tex_gamma_value: FloatProperty( + name="Custom texture gamma", + description="value for which the file was issued e.g. a Raw photo is gamma 1.0", + min=0.45, + max=5.00, + soft_min=1.00, + soft_max=2.50, + default=1.00, + ) + + ##################################CustomPOV Code############################ + # commented out below if we wanted custom pov code in texture only, inside exported material: + # replacement_text = StringProperty( + # name="Declared name:", + # description="Type the declared name in custom POV code or an external .inc " + # "it points at. pigment {} expected", + # default="") + + tex_pattern_type: EnumProperty( + name="Texture_Type", + description="Choose between Blender or POV parameters to specify texture", + items=( + ("agate", "Agate", "", "PLUGIN", 0), + ("aoi", "Aoi", "", "PLUGIN", 1), + ("average", "Average", "", "PLUGIN", 2), + ("boxed", "Boxed", "", "PLUGIN", 3), + ("bozo", "Bozo", "", "PLUGIN", 4), + ("bumps", "Bumps", "", "PLUGIN", 5), + ("cells", "Cells", "", "PLUGIN", 6), + ("crackle", "Crackle", "", "PLUGIN", 7), + ("cubic", "Cubic", "", "PLUGIN", 8), + ("cylindrical", "Cylindrical", "", "PLUGIN", 9), + ("density_file", "Density", "(.df3)", "PLUGIN", 10), + ("dents", "Dents", "", "PLUGIN", 11), + ("fractal", "Fractal", "", "PLUGIN", 12), + ("function", "Function", "", "PLUGIN", 13), + ("gradient", "Gradient", "", "PLUGIN", 14), + ("granite", "Granite", "", "PLUGIN", 15), + ("image_pattern", "Image pattern", "", "PLUGIN", 16), + ("leopard", "Leopard", "", "PLUGIN", 17), + ("marble", "Marble", "", "PLUGIN", 18), + ("onion", "Onion", "", "PLUGIN", 19), + ("pigment_pattern", "pigment pattern", "", "PLUGIN", 20), + ("planar", "Planar", "", "PLUGIN", 21), + ("quilted", "Quilted", "", "PLUGIN", 22), + ("radial", "Radial", "", "PLUGIN", 23), + ("ripples", "Ripples", "", "PLUGIN", 24), + ("slope", "Slope", "", "PLUGIN", 25), + ("spherical", "Spherical", "", "PLUGIN", 26), + ("spiral1", "Spiral1", "", "PLUGIN", 27), + ("spiral2", "Spiral2", "", "PLUGIN", 28), + ("spotted", "Spotted", "", "PLUGIN", 29), + ("waves", "Waves", "", "PLUGIN", 30), + ("wood", "Wood", "", "PLUGIN", 31), + ("wrinkles", "Wrinkles", "", "PLUGIN", 32), + ("brick", "Brick", "", "PLUGIN", 33), + ("checker", "Checker", "", "PLUGIN", 34), + ("hexagon", "Hexagon", "", "PLUGIN", 35), + ("object", "Mesh", "", "PLUGIN", 36), + ("emulator", "Blender Type Emulator", "", "SCRIPTPLUGINS", 37), + ), + default="emulator", + ) + + magnet_style: EnumProperty( + name="Magnet style", + description="magnet or julia", + items=(("mandel", "Mandelbrot", ""), ("julia", "Julia", "")), + default="julia", + ) + + magnet_type: IntProperty(name="Magnet_type", description="1 or 2", min=1, max=2, default=2) + + warp_types: EnumProperty( + name="Warp Types", + description="Select the type of warp", + items=( + ("PLANAR", "Planar", ""), + ("CUBIC", "Cubic", ""), + ("SPHERICAL", "Spherical", ""), + ("TOROIDAL", "Toroidal", ""), + ("CYLINDRICAL", "Cylindrical", ""), + ("NONE", "None", "No indentation"), + ), + default="NONE", + ) + + warp_orientation: EnumProperty( + name="Warp Orientation", + description="Select the orientation of warp", + items=(("x", "X", ""), ("y", "Y", ""), ("z", "Z", "")), + default="y", + ) + + wave_type: EnumProperty( + name="Waves type", + description="Select the type of waves", + items=( + ("ramp", "Ramp", ""), + ("sine", "Sine", ""), + ("scallop", "Scallop", ""), + ("cubic", "Cubic", ""), + ("poly", "Poly", ""), + ("triangle", "Triangle", ""), + ), + default="ramp", + ) + + gen_noise: IntProperty( + name="Noise Generators", description="Noise Generators", min=1, max=3, default=1 + ) + + warp_dist_exp: FloatProperty( + name="Distance exponent", description="Distance exponent", min=0.0, max=100.0, default=1.0 + ) + + warp_tor_major_radius: FloatProperty( + name="Major radius", + description="Torus is distance from major radius", + min=0.0, + max=5.0, + default=1.0, + ) + + warp_turbulence_x: FloatProperty( + name="Turbulence X", description="Turbulence X", min=0.0, max=5.0, default=0.0 + ) + + warp_turbulence_y: FloatProperty( + name="Turbulence Y", description="Turbulence Y", min=0.0, max=5.0, default=0.0 + ) + + warp_turbulence_z: FloatProperty( + name="Turbulence Z", description="Turbulence Z", min=0.0, max=5.0, default=0.0 + ) + + modifier_octaves: IntProperty( + name="Turbulence octaves", description="Turbulence octaves", min=1, max=10, default=1 + ) + + modifier_lambda: FloatProperty( + name="Turbulence lambda", description="Turbulence lambda", min=0.0, max=5.0, default=1.00 + ) + + modifier_omega: FloatProperty( + name="Turbulence omega", description="Turbulence omega", min=0.0, max=10.0, default=1.00 + ) + + modifier_phase: FloatProperty( + name="Phase", + description="The phase value causes the map entries to be shifted so that the map " + "starts and ends at a different place", + min=0.0, + max=2.0, + default=0.0, + ) + + modifier_frequency: FloatProperty( + name="Frequency", + description="The frequency keyword adjusts the number of times that a color map " + "repeats over one cycle of a pattern", + min=0.0, + max=25.0, + default=2.0, + ) + + modifier_turbulence: FloatProperty( + name="Turbulence", description="Turbulence", min=0.0, max=5.0, default=2.0 + ) + + modifier_numbers: IntProperty(name="Numbers", description="Numbers", min=1, max=27, default=2) + + modifier_control0: IntProperty( + name="Control0", description="Control0", min=0, max=100, default=1 + ) + + modifier_control1: IntProperty( + name="Control1", description="Control1", min=0, max=100, default=1 + ) + + brick_size_x: FloatProperty( + name="Brick size x", description="", min=0.0000, max=1.0000, default=0.2500 + ) + + brick_size_y: FloatProperty( + name="Brick size y", description="", min=0.0000, max=1.0000, default=0.0525 + ) + + brick_size_z: FloatProperty( + name="Brick size z", description="", min=0.0000, max=1.0000, default=0.1250 + ) + + brick_mortar: FloatProperty( + name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01 + ) + + julia_complex_1: FloatProperty( + name="Julia Complex 1", description="", min=0.000, max=1.500, default=0.360 + ) + + julia_complex_2: FloatProperty( + name="Julia Complex 2", description="", min=0.000, max=1.500, default=0.250 + ) + + f_iter: IntProperty(name="Fractal Iteration", description="", min=0, max=100, default=20) + + f_exponent: IntProperty(name="Fractal Exponent", description="", min=2, max=33, default=2) + + f_ior: IntProperty(name="Fractal Interior", description="", min=1, max=6, default=1) + + f_ior_fac: FloatProperty( + name="Fractal Interior Factor", description="", min=0.0, max=10.0, default=1.0 + ) + + f_eor: IntProperty(name="Fractal Exterior", description="", min=1, max=8, default=1) + + f_eor_fac: FloatProperty( + name="Fractal Exterior Factor", description="", min=0.0, max=10.0, default=1.0 + ) + + grad_orient_x: IntProperty( + name="Gradient orientation X", description="", min=0, max=1, default=0 + ) + + grad_orient_y: IntProperty( + name="Gradient orientation Y", description="", min=0, max=1, default=1 + ) + + grad_orient_z: IntProperty( + name="Gradient orientation Z", description="", min=0, max=1, default=0 + ) + + pave_sides: EnumProperty( + name="Pavement sides", + description="", + items=(("3", "3", ""), ("4", "4", ""), ("6", "6", "")), + default="3", + ) + + pave_pat_2: IntProperty( + name="Pavement pattern 2", description="maximum: 2", min=1, max=2, default=2 + ) + + pave_pat_3: IntProperty( + name="Pavement pattern 3", description="maximum: 3", min=1, max=3, default=3 + ) + + pave_pat_4: IntProperty( + name="Pavement pattern 4", description="maximum: 4", min=1, max=4, default=4 + ) + + pave_pat_5: IntProperty( + name="Pavement pattern 5", description="maximum: 5", min=1, max=5, default=5 + ) + + pave_pat_7: IntProperty( + name="Pavement pattern 7", description="maximum: 7", min=1, max=7, default=7 + ) + + pave_pat_12: IntProperty( + name="Pavement pattern 12", description="maximum: 12", min=1, max=12, default=12 + ) + + pave_pat_22: IntProperty( + name="Pavement pattern 22", description="maximum: 22", min=1, max=22, default=22 + ) + + pave_pat_35: IntProperty( + name="Pavement pattern 35", description="maximum: 35", min=1, max=35, default=35 + ) + + pave_tiles: IntProperty( + name="Pavement tiles", + description="If sides = 6, maximum tiles 5!!!", + min=1, + max=6, + default=1, + ) + + pave_form: IntProperty(name="Pavement form", description="", min=0, max=4, default=0) + + #########FUNCTIONS############################################################################# + #########FUNCTIONS############################################################################# + + func_list: EnumProperty( + name="Functions", + description="Select the function for create pattern", + items=( + ("NONE", "None", "No indentation"), + ("f_algbr_cyl1", "Algbr cyl1", ""), + ("f_algbr_cyl2", "Algbr cyl2", ""), + ("f_algbr_cyl3", "Algbr cyl3", ""), + ("f_algbr_cyl4", "Algbr cyl4", ""), + ("f_bicorn", "Bicorn", ""), + ("f_bifolia", "Bifolia", ""), + ("f_blob", "Blob", ""), + ("f_blob2", "Blob2", ""), + ("f_boy_surface", "Boy surface", ""), + ("f_comma", "Comma", ""), + ("f_cross_ellipsoids", "Cross ellipsoids", ""), + ("f_crossed_trough", "Crossed trough", ""), + ("f_cubic_saddle", "Cubic saddle", ""), + ("f_cushion", "Cushion", ""), + ("f_devils_curve", "Devils curve", ""), + ("f_devils_curve_2d", "Devils curve 2d", ""), + ("f_dupin_cyclid", "Dupin cyclid", ""), + ("f_ellipsoid", "Ellipsoid", ""), + ("f_enneper", "Enneper", ""), + ("f_flange_cover", "Flange cover", ""), + ("f_folium_surface", "Folium surface", ""), + ("f_folium_surface_2d", "Folium surface 2d", ""), + ("f_glob", "Glob", ""), + ("f_heart", "Heart", ""), + ("f_helical_torus", "Helical torus", ""), + ("f_helix1", "Helix1", ""), + ("f_helix2", "Helix2", ""), + ("f_hex_x", "Hex x", ""), + ("f_hex_y", "Hex y", ""), + ("f_hetero_mf", "Hetero mf", ""), + ("f_hunt_surface", "Hunt surface", ""), + ("f_hyperbolic_torus", "Hyperbolic torus", ""), + ("f_isect_ellipsoids", "Isect ellipsoids", ""), + ("f_kampyle_of_eudoxus", "Kampyle of eudoxus", ""), + ("f_kampyle_of_eudoxus_2d", "Kampyle of eudoxus 2d", ""), + ("f_klein_bottle", "Klein bottle", ""), + ("f_kummer_surface_v1", "Kummer surface v1", ""), + ("f_kummer_surface_v2", "Kummer surface v2", ""), + ("f_lemniscate_of_gerono", "Lemniscate of gerono", ""), + ("f_lemniscate_of_gerono_2d", "Lemniscate of gerono 2d", ""), + ("f_mesh1", "Mesh1", ""), + ("f_mitre", "Mitre", ""), + ("f_nodal_cubic", "Nodal cubic", ""), + ("f_noise3d", "Noise3d", ""), + ("f_noise_generator", "Noise generator", ""), + ("f_odd", "Odd", ""), + ("f_ovals_of_cassini", "Ovals of cassini", ""), + ("f_paraboloid", "Paraboloid", ""), + ("f_parabolic_torus", "Parabolic torus", ""), + ("f_ph", "Ph", ""), + ("f_pillow", "Pillow", ""), + ("f_piriform", "Piriform", ""), + ("f_piriform_2d", "Piriform 2d", ""), + ("f_poly4", "Poly4", ""), + ("f_polytubes", "Polytubes", ""), + ("f_quantum", "Quantum", ""), + ("f_quartic_paraboloid", "Quartic paraboloid", ""), + ("f_quartic_saddle", "Quartic saddle", ""), + ("f_quartic_cylinder", "Quartic cylinder", ""), + ("f_r", "R", ""), + ("f_ridge", "Ridge", ""), + ("f_ridged_mf", "Ridged mf", ""), + ("f_rounded_box", "Rounded box", ""), + ("f_sphere", "Sphere", ""), + ("f_spikes", "Spikes", ""), + ("f_spikes_2d", "Spikes 2d", ""), + ("f_spiral", "Spiral", ""), + ("f_steiners_roman", "Steiners roman", ""), + ("f_strophoid", "Strophoid", ""), + ("f_strophoid_2d", "Strophoid 2d", ""), + ("f_superellipsoid", "Superellipsoid", ""), + ("f_th", "Th", ""), + ("f_torus", "Torus", ""), + ("f_torus2", "Torus2", ""), + ("f_torus_gumdrop", "Torus gumdrop", ""), + ("f_umbrella", "Umbrella", ""), + ("f_witch_of_agnesi", "Witch of agnesi", ""), + ("f_witch_of_agnesi_2d", "Witch of agnesi 2d", ""), + ), + default="NONE", + ) + + func_x: FloatProperty(name="FX", description="", min=0.0, max=25.0, default=1.0) + + func_plus_x: EnumProperty( + name="Func plus x", + description="", + items=(("NONE", "None", ""), ("increase", "*", ""), ("plus", "+", "")), + default="NONE", + ) + + func_y: FloatProperty(name="FY", description="", min=0.0, max=25.0, default=1.0) + + func_plus_y: EnumProperty( + name="Func plus y", + description="", + items=(("NONE", "None", ""), ("increase", "*", ""), ("plus", "+", "")), + default="NONE", + ) + + func_z: FloatProperty(name="FZ", description="", min=0.0, max=25.0, default=1.0) + + func_plus_z: EnumProperty( + name="Func plus z", + description="", + items=(("NONE", "None", ""), ("increase", "*", ""), ("plus", "+", "")), + default="NONE", + ) + + func_P0: FloatProperty(name="P0", description="", min=0.0, max=25.0, default=1.0) + + func_P1: FloatProperty(name="P1", description="", min=0.0, max=25.0, default=1.0) + + func_P2: FloatProperty(name="P2", description="", min=0.0, max=25.0, default=1.0) + + func_P3: FloatProperty(name="P3", description="", min=0.0, max=25.0, default=1.0) + + func_P4: FloatProperty(name="P4", description="", min=0.0, max=25.0, default=1.0) + + func_P5: FloatProperty(name="P5", description="", min=0.0, max=25.0, default=1.0) + + func_P6: FloatProperty(name="P6", description="", min=0.0, max=25.0, default=1.0) + + func_P7: FloatProperty(name="P7", description="", min=0.0, max=25.0, default=1.0) + + func_P8: FloatProperty(name="P8", description="", min=0.0, max=25.0, default=1.0) + + func_P9: FloatProperty(name="P9", description="", min=0.0, max=25.0, default=1.0) + + ######################################### + tex_rot_x: FloatProperty(name="Rotate X", description="", min=-180.0, max=180.0, default=0.0) + + tex_rot_y: FloatProperty(name="Rotate Y", description="", min=-180.0, max=180.0, default=0.0) + + tex_rot_z: FloatProperty(name="Rotate Z", description="", min=-180.0, max=180.0, default=0.0) + + tex_mov_x: FloatProperty( + name="Move X", description="", min=-100000.0, max=100000.0, default=0.0 + ) + + tex_mov_y: FloatProperty( + name="Move Y", description="", min=-100000.0, max=100000.0, default=0.0 + ) + + tex_mov_z: FloatProperty( + name="Move Z", description="", min=-100000.0, max=100000.0, default=0.0 + ) + + tex_scale_x: FloatProperty(name="Scale X", description="", min=0.0, max=10000.0, default=1.0) + + tex_scale_y: FloatProperty(name="Scale Y", description="", min=0.0, max=10000.0, default=1.0) + + tex_scale_z: FloatProperty(name="Scale Z", description="", min=0.0, max=10000.0, default=1.0) + + +classes = (MaterialTextureSlot, WorldTextureSlot, RenderPovSettingsTexture) + + +def register(): + for cls in classes: + register_class(cls) + + bpy.types.Material.pov_texture_slots = CollectionProperty(type=MaterialTextureSlot) + bpy.types.World.pov_texture_slots = CollectionProperty(type=WorldTextureSlot) + bpy.types.Texture.pov = PointerProperty(type=RenderPovSettingsTexture) + + +def unregister(): + del bpy.types.Texture.pov + del bpy.types.World.pov_texture_slots + del bpy.types.Material.pov_texture_slots + + for cls in reversed(classes): + unregister_class(cls) diff --git a/render_povray/ui.py b/render_povray/ui.py deleted file mode 100644 index 297c2e0d8..000000000 --- a/render_povray/ui.py +++ /dev/null @@ -1,4719 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> -"""User interface for the POV tools""" - -import bpy -import sys # really import here and in render.py? -import os # really import here and in render.py? -import addon_utils -from time import sleep -from os.path import isfile -from bpy.app.handlers import persistent -from bl_operators.presets import AddPresetBase -from bpy.utils import register_class, unregister_class -from bpy.types import ( - Operator, - Menu, - UIList, - Panel, - Brush, - Material, - Light, - World, - ParticleSettings, - FreestyleLineStyle, -) - -# Example of wrapping every class 'as is' -from bl_ui import properties_output - -for member in dir(properties_output): - subclass = getattr(properties_output, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_output - -from bl_ui import properties_freestyle -for member in dir(properties_freestyle): - subclass = getattr(properties_freestyle, member) - try: - if not (subclass.bl_space_type == 'PROPERTIES' - and subclass.bl_context == "render"): - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - #subclass.bl_parent_id = "RENDER_PT_POV_filter" - except: - pass -del properties_freestyle - -from bl_ui import properties_view_layer - -for member in dir(properties_view_layer): - subclass = getattr(properties_view_layer, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_view_layer - -# Use some of the existing buttons. -from bl_ui import properties_render - -# DEPRECATED#properties_render.RENDER_PT_render.COMPAT_ENGINES.add('POVRAY_RENDER') -# DEPRECATED#properties_render.RENDER_PT_dimensions.COMPAT_ENGINES.add('POVRAY_RENDER') -# properties_render.RENDER_PT_antialiasing.COMPAT_ENGINES.add('POVRAY_RENDER') -# TORECREATE##DEPRECATED#properties_render.RENDER_PT_shading.COMPAT_ENGINES.add('POVRAY_RENDER') -# DEPRECATED#properties_render.RENDER_PT_output.COMPAT_ENGINES.add('POVRAY_RENDER') -del properties_render - - -# 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') -# TORECREATE##DEPRECATED#properties_world.WORLD_PT_mist.COMPAT_ENGINES.add('POVRAY_RENDER') -del properties_world - - -# Example of wrapping every class 'as is' -from bl_ui import properties_texture -from bl_ui.properties_texture import context_tex_datablock -from bl_ui.properties_texture import texture_filter_common - -for member in dir(properties_texture): - subclass = getattr(properties_texture, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_texture - -# 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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_physics_rigidbody_constraint - -# Physics Smoke 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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_physics_softbody - -# Physics Fluid 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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_physics_fluid - -# 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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -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) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_physics_dynamicpaint - - -# Example of wrapping every class 'as is' -from bl_ui import properties_data_modifier - -for member in dir(properties_data_modifier): - subclass = getattr(properties_data_modifier, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_data_modifier - -# Example of wrapping every class 'as is' except some -from bl_ui import properties_material - -for member in dir(properties_material): - subclass = getattr(properties_material, member) - try: - # mat=bpy.context.active_object.active_material - # if (mat and mat.pov.type == "SURFACE" - # and not (mat.pov.material_use_nodes or mat.use_nodes)): - # and (engine in cls.COMPAT_ENGINES)) if subclasses were sorted - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_material - - -from bl_ui import properties_data_camera - -for member in dir(properties_data_camera): - subclass = getattr(properties_data_camera, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_data_camera - - -from bl_ui import properties_particle as properties_particle - -for member in dir( - properties_particle -): # add all "particle" panels from blender - subclass = getattr(properties_particle, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - except: - pass -del properties_particle - - -############# POV-Centric WORSPACE ############# -@persistent -def povCentricWorkspace(dummy): - """Set up a POV centric Workspace if addon was activated and saved as default renderer - - This would bring a ’_RestrictData’ error because UI needs to be fully loaded before - workspace changes so registering this function in bpy.app.handlers is needed. - By default handlers are freed when loading new files, but here we want the handler - to stay running across multiple files as part of this add-on. That is why the the - bpy.app.handlers.persistent decorator is used (@persistent) above. - """ - - wsp = bpy.data.workspaces.get('Scripting') - context = bpy.context - if wsp is not None and context.scene.render.engine == 'POVRAY_RENDER': - new_wsp = bpy.ops.workspace.duplicate({'workspace': wsp}) - bpy.data.workspaces['Scripting.001'].name='POV' - # Already done it would seem, but explicitly make this workspaces the active one - context.window.workspace = bpy.data.workspaces['POV'] - pov_screen = bpy.data.workspaces['POV'].screens[0] - pov_workspace = pov_screen.areas - - - override = bpy.context.copy() - - for area in pov_workspace: - if area.type == 'VIEW_3D': - for region in [r for r in area.regions if r.type == 'WINDOW']: - for space in area.spaces: - if space.type == 'VIEW_3D': - #override['screen'] = pov_screen - override['area'] = area - override['region']= region - #bpy.data.workspaces['POV'].screens[0].areas[6].spaces[0].width = 333 # Read only, how do we set ? - #This has a glitch: - #bpy.ops.screen.area_move(override, x=(area.x + area.width), y=(area.y + 5), delta=100) - #bpy.ops.screen.area_move(override, x=(area.x + 5), y=area.y, delta=-100) - - bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'TEXT_EDITOR') - space.show_region_ui = True - #bpy.ops.screen.region_scale(override) - #bpy.ops.screen.region_scale() - break - - elif area.type == 'CONSOLE': - for region in [r for r in area.regions if r.type == 'WINDOW']: - for space in area.spaces: - if space.type == 'CONSOLE': - #override['screen'] = pov_screen - override['area'] = area - override['region']= region - bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'INFO') - - break - elif area.type == 'INFO': - for region in [r for r in area.regions if r.type == 'WINDOW']: - for space in area.spaces: - if space.type == 'INFO': - #override['screen'] = pov_screen - override['area'] = area - override['region']= region - bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'CONSOLE') - - break - - elif area.type == 'TEXT_EDITOR': - for region in [r for r in area.regions if r.type == 'WINDOW']: - for space in area.spaces: - if space.type == 'TEXT_EDITOR': - #override['screen'] = pov_screen - override['area'] = area - override['region']= region - #bpy.ops.screen.space_type_set_or_cycle(space_type='VIEW_3D') - #space.type = 'VIEW_3D' - bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'VIEW_3D') - - #bpy.ops.screen.area_join(override, cursor=(area.x, area.y + area.height)) - - break - - - if area.type == 'VIEW_3D': - for region in [r for r in area.regions if r.type == 'WINDOW']: - for space in area.spaces: - if space.type == 'VIEW_3D': - #override['screen'] = pov_screen - override['area'] = area - override['region']= region - bpy.ops.screen.region_quadview(override) - space.region_3d.view_perspective = 'CAMERA' - #bpy.ops.screen.space_type_set_or_cycle(override, space_type = 'TEXT_EDITOR') - #bpy.ops.screen.region_quadview(override) - - - - - - - bpy.data.workspaces.update() - # Already outliners but invert both types - pov_workspace[1].spaces[0].display_mode = 'LIBRARIES' - pov_workspace[3].spaces[0].display_mode = 'VIEW_LAYER' - - ''' - for window in bpy.context.window_manager.windows: - for area in [a for a in window.screen.areas if a.type == 'VIEW_3D']: - for region in [r for r in area.regions if r.type == 'WINDOW']: - context_override = { - 'window': window, - 'screen': window.screen, - 'area': area, - 'region': region, - 'space_data': area.spaces.active, - 'scene': bpy.context.scene - } - bpy.ops.view3d.camera_to_view(context_override) - ''' - - - else: - print("default 'Scripting' workspace needed for POV centric Workspace") - - - - - - - -class WORLD_MT_POV_presets(Menu): - 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""" - - 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" - - -def check_material(mat): - if mat is not None: - if mat.use_nodes: - if ( - not mat.node_tree - ): # FORMERLY : #mat.active_node_material is not None: - return True - return False - return True - return False - - -def simple_material(mat): - """Test if a material uses nodes""" - if (mat is not None) and (not mat.use_nodes): - return True - return False - - -def check_add_mesh_extra_objects(): - """Test if Add mesh extra objects addon is activated - - This addon is currently used to generate the proxy for POV parametric - surface which is almost the same priciple as its Math xyz surface - """ - if "add_mesh_extra_objects" in bpy.context.preferences.addons.keys(): - return True - return False - -def check_render_freestyle_svg(): - """Test if Freestyle SVG Exporter addon is activated - - This addon is currently used to generate the SVG lines file - when Freestyle is enabled alongside POV - """ - if "render_freestyle_svg" in bpy.context.preferences.addons.keys(): - return True - return False - -def locate_docpath(): - """POV can be installed with some include files. - - Get their path as defined in user preferences or registry keys for - the user to be able to invoke them.""" - - addon_prefs = bpy.context.preferences.addons[__package__].preferences - # Use the system preference if its set. - pov_documents = addon_prefs.docpath_povray - if pov_documents: - if os.path.exists(pov_documents): - return pov_documents - else: - print( - "User Preferences path to povray documents %r NOT FOUND, checking $PATH" - % pov_documents - ) - - # Windows Only - if sys.platform[:3] == "win": - import winreg - - try: - win_reg_key = winreg.OpenKey( - winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows" - ) - win_docpath = winreg.QueryValueEx(win_reg_key, "DocPath")[0] - pov_documents = os.path.join(win_docpath, "Insert Menu") - if os.path.exists(pov_documents): - return pov_documents - except FileNotFoundError: - return "" - # search the path all os's - pov_documents_default = "include" - - os_path_ls = os.getenv("PATH").split(':') + [""] - - for dir_name in os_path_ls: - pov_documents = os.path.join(dir_name, pov_documents_default) - if os.path.exists(pov_documents): - return pov_documents - return "" - - -def pov_context_tex_datablock(context): - """Texture context type recreated as deprecated in blender 2.8""" - - idblock = context.brush - if idblock and context.scene.texture_context == 'OTHER': - return idblock - - # idblock = bpy.context.active_object.active_material - idblock = context.view_layer.objects.active.active_material - if idblock and context.scene.texture_context == 'MATERIAL': - return idblock - - idblock = context.scene.world - if idblock and context.scene.texture_context == 'WORLD': - return idblock - - idblock = context.light - if idblock and context.scene.texture_context == 'LIGHT': - return idblock - - if context.particle_system and context.scene.texture_context == 'PARTICLES': - idblock = context.particle_system.settings - - return idblock - - idblock = context.line_style - if idblock and context.scene.texture_context == 'LINESTYLE': - return idblock - - -class RenderButtonsPanel: - """Use this class to define buttons from the render tab of - properties window.""" - - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "render" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - rd = context.scene.render - return rd.engine in cls.COMPAT_ENGINES - - -class ModifierButtonsPanel: - """Use this class to define buttons from the modifier tab of - properties window.""" - - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "modifier" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - mods = context.object.modifiers - rd = context.scene.render - return mods and (rd.engine in cls.COMPAT_ENGINES) - - -class MaterialButtonsPanel: - """Use this class to define buttons from the material tab of - properties window.""" - - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "material" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - mat = context.material - rd = context.scene.render - return mat and (rd.engine in cls.COMPAT_ENGINES) - - -class TextureButtonsPanel: - """Use this class to define buttons from the texture tab of - properties window.""" - - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "texture" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - tex = context.texture - rd = context.scene.render - return tex and (rd.engine in cls.COMPAT_ENGINES) - - -# class TextureTypePanel(TextureButtonsPanel): - -# @classmethod -# def poll(cls, context): -# tex = context.texture -# engine = context.scene.render.engine -# return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES)) - - -class ObjectButtonsPanel: - """Use this class to define buttons from the object tab of - properties window.""" - - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "object" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - obj = context.object - rd = context.scene.render - return obj and (rd.engine in cls.COMPAT_ENGINES) - - -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 must be defined in each subclass, external engines can add themselves here - - @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 must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - wld = context.world - rd = context.scene.render - return wld and (rd.engine in cls.COMPAT_ENGINES) - - -class TextButtonsPanel: - """Use this class to define buttons from the side tab of - text window.""" - - bl_space_type = 'TEXT_EDITOR' - bl_region_type = 'UI' - bl_label = "POV-Ray" - # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here - - @classmethod - def poll(cls, context): - text = context.space_data - rd = context.scene.render - return text and (rd.engine in cls.COMPAT_ENGINES) - - -from bl_ui import properties_data_mesh - -# These panels are kept -properties_data_mesh.DATA_PT_custom_props_mesh.COMPAT_ENGINES.add( - 'POVRAY_RENDER' -) -properties_data_mesh.DATA_PT_context_mesh.COMPAT_ENGINES.add('POVRAY_RENDER') - -## make some native panels contextual to some object variable -## by recreating custom panels inheriting their properties - - -class PovDataButtonsPanel(properties_data_mesh.MeshButtonsPanel): - """Use this class to define buttons from the edit data tab of - properties window.""" - - COMPAT_ENGINES = {'POVRAY_RENDER'} - POV_OBJECT_TYPES = { - 'PLANE', - 'BOX', - 'SPHERE', - 'CYLINDER', - 'CONE', - 'TORUS', - 'BLOB', - 'ISOSURFACE', - 'SUPERELLIPSOID', - 'SUPERTORUS', - 'HEIGHT_FIELD', - 'PARAMETRIC', - 'POLYCIRCLE', - } - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - obj = context.object - # We use our parent class poll func too, avoids to re-define too much things... - return ( - super(PovDataButtonsPanel, 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. ;) -class DATA_PT_POV_normals(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_normals.bl_label - - draw = properties_data_mesh.DATA_PT_normals.draw - - -class DATA_PT_POV_texture_space(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_texture_space.bl_label - bl_options = properties_data_mesh.DATA_PT_texture_space.bl_options - - draw = properties_data_mesh.DATA_PT_texture_space.draw - - -class DATA_PT_POV_vertex_groups(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_vertex_groups.bl_label - - draw = properties_data_mesh.DATA_PT_vertex_groups.draw - - -class DATA_PT_POV_shape_keys(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_shape_keys.bl_label - - draw = properties_data_mesh.DATA_PT_shape_keys.draw - - -class DATA_PT_POV_uv_texture(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_uv_texture.bl_label - - draw = properties_data_mesh.DATA_PT_uv_texture.draw - - -class DATA_PT_POV_vertex_colors(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_vertex_colors.bl_label - - draw = properties_data_mesh.DATA_PT_vertex_colors.draw - - -class DATA_PT_POV_customdata(PovDataButtonsPanel, Panel): - bl_label = properties_data_mesh.DATA_PT_customdata.bl_label - bl_options = properties_data_mesh.DATA_PT_customdata.bl_options - draw = properties_data_mesh.DATA_PT_customdata.draw - - -del properties_data_mesh - - -################################################################################ -# 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: -# 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 PovLampButtonsPanel(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): - engine = context.scene.render.engine - obj = context.object - # We use our parent class poll func too, avoids to re-define too much things... - return ( - super(PovLampButtonsPanel, 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. ;) - - -class LIGHT_PT_POV_preview(PovLampButtonsPanel, Panel): - bl_label = properties_data_light.DATA_PT_preview.bl_label - - draw = properties_data_light.DATA_PT_preview.draw - - -class LIGHT_PT_POV_light(PovLampButtonsPanel, Panel): - bl_label = properties_data_light.DATA_PT_light.bl_label - - draw = properties_data_light.DATA_PT_light.draw - - -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): - """Use this class to define pov world buttons""" - - '''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): - 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(PovLampButtonsPanel, 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(PovLampButtonsPanel, Panel): - bl_label = "Shadow" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - lamp = context.lamp - engine = context.scene.render.engine - return lamp and (engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - lamp = context.lamp - - layout.row().prop(lamp, "shadow_method", expand=True) - - split = layout.split() - - col = split.column() - sub = col.column() - sub.prop(lamp, "spot_size", text="Size") - sub.prop(lamp, "spot_blend", text="Blend", slider=True) - col.prop(lamp, "use_square") - col.prop(lamp, "show_cone") - - col = split.column() - - col.active = ( - lamp.shadow_method != 'BUFFER_SHADOW' - or lamp.shadow_buffer_type != 'DEEP' - ) - col.prop(lamp, "use_halo") - sub = col.column(align=True) - sub.active = lamp.use_halo - sub.prop(lamp, "halo_intensity", text="Intensity") - if lamp.shadow_method == 'BUFFER_SHADOW': - sub.prop(lamp, "halo_step", text="Step") - if lamp.shadow_method == 'NOSHADOW' and lamp.type == 'AREA': - split = layout.split() - - col = split.column() - col.label(text="Form factor sampling:") - - sub = col.row(align=True) - - if lamp.shape == 'SQUARE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples") - elif lamp.shape == 'RECTANGLE': - sub.prop(lamp.pov, "shadow_ray_samples_x", text="Samples X") - sub.prop(lamp.pov, "shadow_ray_samples_y", text="Samples Y") - - if lamp.shadow_method != 'NOSHADOW': - split = layout.split() - - col = split.column() - col.prop(lamp, "shadow_color", text="") - - col = split.column() - col.prop(lamp, "use_shadow_layer", text="This Layer Only") - col.prop(lamp, "use_only_shadow") - - if lamp.shadow_method == 'RAY_SHADOW': - split = layout.split() - - col = split.column() - col.label(text="Sampling:") - - if lamp.type in {'POINT', 'SUN', 'SPOT'}: - sub = col.row() - - sub.prop(lamp, "shadow_ray_samples", text="Samples") - sub.prop(lamp, "shadow_soft_size", text="Soft Size") - - elif lamp.type == 'AREA': - sub = col.row(align=True) - - if lamp.shape == 'SQUARE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples") - elif lamp.shape == 'RECTANGLE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples X") - sub.prop(lamp, "shadow_ray_samples_y", text="Samples Y") - - -''' - if lamp.shadow_method == 'NOSHADOW' and lamp.type == 'AREA': - split = layout.split() - - col = split.column() - col.label(text="Form factor sampling:") - - sub = col.row(align=True) - - if lamp.shape == 'SQUARE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples") - elif lamp.shape == 'RECTANGLE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples X") - sub.prop(lamp, "shadow_ray_samples_y", text="Samples Y") - - if lamp.shadow_method != 'NOSHADOW': - split = layout.split() - - col = split.column() - col.prop(lamp, "shadow_color", text="") - - col = split.column() - col.prop(lamp, "use_shadow_layer", text="This Layer Only") - col.prop(lamp, "use_only_shadow") - - if lamp.shadow_method == 'RAY_SHADOW': - split = layout.split() - - col = split.column() - col.label(text="Sampling:") - - if lamp.type in {'POINT', 'SUN', 'SPOT'}: - sub = col.row() - - sub.prop(lamp, "shadow_ray_samples", text="Samples") - sub.prop(lamp, "shadow_soft_size", text="Soft Size") - - elif lamp.type == 'AREA': - sub = col.row(align=True) - - if lamp.shape == 'SQUARE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples") - elif lamp.shape == 'RECTANGLE': - sub.prop(lamp, "shadow_ray_samples_x", text="Samples X") - sub.prop(lamp, "shadow_ray_samples_y", text="Samples Y") - - col.row().prop(lamp, "shadow_ray_sample_method", expand=True) - - if lamp.shadow_ray_sample_method == 'ADAPTIVE_QMC': - layout.prop(lamp, "shadow_adaptive_threshold", text="Threshold") - - if lamp.type == 'AREA' and lamp.shadow_ray_sample_method == 'CONSTANT_JITTERED': - row = layout.row() - row.prop(lamp, "use_umbra") - row.prop(lamp, "use_dither") - row.prop(lamp, "use_jitter") - - elif lamp.shadow_method == 'BUFFER_SHADOW': - col = layout.column() - col.label(text="Buffer Type:") - col.row().prop(lamp, "shadow_buffer_type", expand=True) - - if lamp.shadow_buffer_type in {'REGULAR', 'HALFWAY', 'DEEP'}: - split = layout.split() - - col = split.column() - col.label(text="Filter Type:") - col.prop(lamp, "shadow_filter_type", text="") - sub = col.column(align=True) - sub.prop(lamp, "shadow_buffer_soft", text="Soft") - sub.prop(lamp, "shadow_buffer_bias", text="Bias") - - col = split.column() - col.label(text="Sample Buffers:") - col.prop(lamp, "shadow_sample_buffers", text="") - sub = col.column(align=True) - sub.prop(lamp, "shadow_buffer_size", text="Size") - sub.prop(lamp, "shadow_buffer_samples", text="Samples") - if lamp.shadow_buffer_type == 'DEEP': - col.prop(lamp, "compression_threshold") - - elif lamp.shadow_buffer_type == 'IRREGULAR': - layout.prop(lamp, "shadow_buffer_bias", text="Bias") - - split = layout.split() - - col = split.column() - col.prop(lamp, "use_auto_clip_start", text="Autoclip Start") - sub = col.column() - sub.active = not lamp.use_auto_clip_start - sub.prop(lamp, "shadow_buffer_clip_start", text="Clip Start") - - col = split.column() - col.prop(lamp, "use_auto_clip_end", text="Autoclip End") - sub = col.column() - sub.active = not lamp.use_auto_clip_end - sub.prop(lamp, "shadow_buffer_clip_end", text=" Clip End") -''' - - -class LIGHT_PT_POV_area(PovLampButtonsPanel, Panel): - bl_label = properties_data_light.DATA_PT_area.bl_label - - @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(PovLampButtonsPanel, Panel): - bl_label = properties_data_light.DATA_PT_spot.bl_label - - @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(PovLampButtonsPanel, 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(PovLampButtonsPanel, 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 obj.pov.unlock_parameters == False: - 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 -############################################################################### - - -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 RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel): - """Use this class to define pov ini settingss buttons.""" - bl_options = {'DEFAULT_CLOSED'} - bl_label = "Auto Start" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw_header(self, context): - scene = context.scene - if scene.pov.tempfiles_enable: - self.layout.prop( - scene.pov, "tempfiles_enable", text="", icon='AUTO' - ) - else: - self.layout.prop( - scene.pov, "tempfiles_enable", text="", icon='CONSOLE' - ) - - def draw(self, context): - - layout = self.layout - - scene = context.scene - - layout.active = scene.pov.max_trace_level != 0 - split = layout.split() - - col = split.column() - col.label(text="Command line switches:") - col.prop(scene.pov, "command_line_switches", text="") - split = layout.split() - - #layout.active = not scene.pov.tempfiles_enable - if not scene.pov.tempfiles_enable: - split.prop(scene.pov, "deletefiles_enable", text="Delete files") - split.prop(scene.pov, "pov_editor", text="POV Editor") - - col = layout.column() - col.prop(scene.pov, "scene_name", text="Name") - col.prop(scene.pov, "scene_path", text="Path to files") - # col.prop(scene.pov, "scene_path", text="Path to POV-file") - # col.prop(scene.pov, "renderimage_path", text="Path to image") - - split = layout.split() - split.prop(scene.pov, "indentation_character", text="Indent") - if scene.pov.indentation_character == 'SPACE': - split.prop(scene.pov, "indentation_spaces", text="Spaces") - - row = layout.row() - row.prop(scene.pov, "comments_enable", text="Comments") - row.prop(scene.pov, "list_lf_enable", text="Line breaks in lists") - - -class RENDER_PT_POV_render_settings(RenderButtonsPanel, Panel): - """Use this class to define pov render settings buttons.""" - - bl_label = "Global Settings" - bl_icon = 'SETTINGS' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw_header(self, context): - scene = context.scene - if scene.pov.global_settings_advanced: - self.layout.prop( - scene.pov, "global_settings_advanced", text="", icon='SETTINGS' - ) - else: - self.layout.prop( - scene.pov, - "global_settings_advanced", - text="", - icon='PREFERENCES', - ) - - def draw(self, context): - layout = self.layout - - scene = context.scene - rd = context.scene.render - # layout.active = (scene.pov.max_trace_level != 0) - - if sys.platform[:3] != "win": - layout.prop( - scene.pov, "sdl_window_enable", text="POV-Ray SDL Window" - ) - - col = layout.column() - col.label(text="Main Path Tracing:") - col.prop(scene.pov, "max_trace_level", text="Ray Depth") - align = True - layout.active = scene.pov.global_settings_advanced - # Deprecated (autodetected in pov3.8): - # layout.prop(scene.pov, "charset") - row = layout.row(align=align) - row.prop(scene.pov, "adc_bailout") - row = layout.row(align=align) - row.prop(scene.pov, "ambient_light") - row = layout.row(align=align) - row.prop(scene.pov, "irid_wavelength") - row = layout.row(align=align) - row.prop(scene.pov, "max_intersections") - row = layout.row(align=align) - row.prop(scene.pov, "number_of_waves") - row = layout.row(align=align) - row.prop(scene.pov, "noise_generator") - - split = layout.split() - split.label(text="Shading:") - split = layout.split() - - row = split.row(align=align) - row.prop(scene.pov, "use_shadows") - row.prop(scene.pov, "alpha_mode") - - -class RENDER_PT_POV_photons(RenderButtonsPanel, Panel): - """Use this class to define pov photons buttons.""" - - bl_label = "Photons" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - # def draw_header(self, context): - # self.layout.label(icon='SETTINGS') - - def draw_header(self, context): - scene = context.scene - if scene.pov.photon_enable: - self.layout.prop( - scene.pov, "photon_enable", text="", icon='PMARKER_ACT' - ) - else: - self.layout.prop( - scene.pov, "photon_enable", text="", icon='PMARKER' - ) - - def draw(self, context): - scene = context.scene - layout = self.layout - layout.active = scene.pov.photon_enable - col = layout.column() - # col.label(text="Global Photons:") - col.prop(scene.pov, "photon_max_trace_level", text="Photon Depth") - - split = layout.split() - - col = split.column() - col.prop(scene.pov, "photon_spacing", text="Spacing") - col.prop(scene.pov, "photon_gather_min") - - col = split.column() - col.prop(scene.pov, "photon_adc_bailout", text="Photon ADC") - col.prop(scene.pov, "photon_gather_max") - - box = layout.box() - box.label(text='Photon Map File:') - row = box.row() - row.prop(scene.pov, "photon_map_file_save_load", expand=True) - if scene.pov.photon_map_file_save_load in {'save'}: - box.prop(scene.pov, "photon_map_dir") - box.prop(scene.pov, "photon_map_filename") - if scene.pov.photon_map_file_save_load in {'load'}: - box.prop(scene.pov, "photon_map_file") - # end main photons - - -class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel): - """Use this class to define pov antialiasing buttons.""" - - bl_label = "Anti-Aliasing" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw_header(self, context): - prefs = bpy.context.preferences.addons[__package__].preferences - scene = context.scene - if ( - prefs.branch_feature_set_povray != 'uberpov' - and scene.pov.antialias_method == '2' - ): - self.layout.prop( - scene.pov, "antialias_enable", text="", icon='ERROR' - ) - elif scene.pov.antialias_enable: - self.layout.prop( - scene.pov, "antialias_enable", text="", icon='ANTIALIASED' - ) - else: - self.layout.prop( - scene.pov, "antialias_enable", text="", icon='ALIASED' - ) - - def draw(self, context): - prefs = bpy.context.preferences.addons[__package__].preferences - layout = self.layout - scene = context.scene - - layout.active = scene.pov.antialias_enable - - row = layout.row() - row.prop(scene.pov, "antialias_method", text="") - - if ( - prefs.branch_feature_set_povray != 'uberpov' - and scene.pov.antialias_method == '2' - ): - col = layout.column() - col.alignment = 'CENTER' - col.label(text="Stochastic Anti Aliasing is") - col.label(text="Only Available with UberPOV") - col.label(text="Feature Set in User Preferences.") - col.label(text="Using Type 2 (recursive) instead") - else: - row.prop(scene.pov, "jitter_enable", text="Jitter") - - split = layout.split() - col = split.column() - col.prop(scene.pov, "antialias_depth", text="AA Depth") - sub = split.column() - sub.prop(scene.pov, "jitter_amount", text="Jitter Amount") - if scene.pov.jitter_enable: - sub.enabled = True - else: - sub.enabled = False - - row = layout.row() - row.prop(scene.pov, "antialias_threshold", text="AA Threshold") - row.prop(scene.pov, "antialias_gamma", text="AA Gamma") - - if prefs.branch_feature_set_povray == 'uberpov': - row = layout.row() - row.prop( - scene.pov, "antialias_confidence", text="AA Confidence" - ) - if scene.pov.antialias_method == '2': - row.enabled = True - else: - row.enabled = False - - -class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel): - """Use this class to define pov radiosity buttons.""" - - bl_label = "Diffuse Radiosity" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw_header(self, context): - scene = context.scene - if scene.pov.radio_enable: - self.layout.prop( - scene.pov, - "radio_enable", - text="", - icon='OUTLINER_OB_LIGHTPROBE', - ) - else: - self.layout.prop( - scene.pov, "radio_enable", text="", icon='LIGHTPROBE_CUBEMAP' - ) - - def draw(self, context): - layout = self.layout - - scene = context.scene - - layout.active = scene.pov.radio_enable - - split = layout.split() - - col = split.column() - col.prop(scene.pov, "radio_count", text="Rays") - col.prop(scene.pov, "radio_recursion_limit", text="Recursions") - - split.prop(scene.pov, "radio_error_bound", text="Error Bound") - - layout.prop(scene.pov, "radio_display_advanced") - - if scene.pov.radio_display_advanced: - split = layout.split() - - col = split.column() - col.prop(scene.pov, "radio_adc_bailout", slider=True) - col.prop(scene.pov, "radio_minimum_reuse", text="Min Reuse") - col.prop(scene.pov, "radio_gray_threshold", slider=True) - col.prop(scene.pov, "radio_pretrace_start", slider=True) - col.prop(scene.pov, "radio_low_error_factor", slider=True) - - col = split.column() - col.prop(scene.pov, "radio_brightness") - col.prop(scene.pov, "radio_maximum_reuse", text="Max Reuse") - col.prop(scene.pov, "radio_nearest_count") - col.prop(scene.pov, "radio_pretrace_end", slider=True) - - col = layout.column() - col.label(text="Estimation Influence:") - col.prop(scene.pov, "radio_always_sample") - col.prop(scene.pov, "radio_normal") - col.prop(scene.pov, "radio_media") - col.prop(scene.pov, "radio_subsurface") - - -class POV_RADIOSITY_MT_presets(Menu): - """Use this class to define pov radiosity presets menu.""" - - bl_label = "Radiosity Presets" - preset_subdir = "pov/radiosity" - preset_operator = "script.execute_preset" - draw = bpy.types.Menu.draw_preset - - -class RENDER_OT_POV_radiosity_add_preset(AddPresetBase, Operator): - """Use this class to define pov radiosity add presets button""" - - '''Add a Radiosity Preset''' - bl_idname = "scene.radiosity_preset_add" - bl_label = "Add Radiosity Preset" - preset_menu = "POV_RADIOSITY_MT_presets" - - # variable used for all preset values - preset_defines = ["scene = bpy.context.scene"] - - # properties to store in the preset - preset_values = [ - "scene.pov.radio_display_advanced", - "scene.pov.radio_adc_bailout", - "scene.pov.radio_always_sample", - "scene.pov.radio_brightness", - "scene.pov.radio_count", - "scene.pov.radio_error_bound", - "scene.pov.radio_gray_threshold", - "scene.pov.radio_low_error_factor", - "scene.pov.radio_media", - "scene.pov.radio_subsurface", - "scene.pov.radio_minimum_reuse", - "scene.pov.radio_maximum_reuse", - "scene.pov.radio_nearest_count", - "scene.pov.radio_normal", - "scene.pov.radio_recursion_limit", - "scene.pov.radio_pretrace_start", - "scene.pov.radio_pretrace_end", - ] - - # where to store the preset - preset_subdir = "pov/radiosity" - - -# Draw into an existing panel -def rad_panel_func(self, context): - layout = self.layout - - row = layout.row(align=True) - row.menu( - POV_RADIOSITY_MT_presets.__name__, - text=POV_RADIOSITY_MT_presets.bl_label, - ) - row.operator( - RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='ADD' - ) - row.operator( - RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='REMOVE' - ).remove_active = True - - -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") - - -##class RENDER_PT_povray_baking(RenderButtonsPanel, Panel): -## bl_label = "Baking" -## COMPAT_ENGINES = {'POVRAY_RENDER'} -## -## def draw_header(self, context): -## scene = context.scene -## -## self.layout.prop(scene.pov, "baking_enable", text="") -## -## def draw(self, context): -## layout = self.layout -## -## scene = context.scene -## rd = scene.render -## -## layout.active = scene.pov.baking_enable - - -class MODIFIERS_PT_POV_modifiers(ModifierButtonsPanel, Panel): - """Use this class to define pov modifier buttons. (For booleans)""" - - bl_label = "POV-Ray" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - # def draw_header(self, context): - # scene = context.scene - # self.layout.prop(scene.pov, "boolean_mod", text="") - - def draw(self, context): - scene = context.scene - layout = self.layout - ob = context.object - mod = ob.modifiers - col = layout.column() - # Find Boolean Modifiers for displaying CSG option - onceCSG = 0 - for mod in ob.modifiers: - if onceCSG == 0: - if mod: - if mod.type == 'BOOLEAN': - col.prop(ob.pov, "boolean_mod") - onceCSG = 1 - - if ob.pov.boolean_mod == "POV": - split = layout.split() - col = layout.column() - # Inside Vector for CSG - col.prop(ob.pov, "inside_vector") - - -class MATERIAL_MT_POV_sss_presets(Menu): - """Use this class to define pov sss preset menu.""" - - bl_label = "SSS Presets" - preset_subdir = "pov/material/sss" - preset_operator = "script.execute_preset" - draw = bpy.types.Menu.draw_preset - - -class MATERIAL_OT_POV_sss_add_preset(AddPresetBase, Operator): - """Add an SSS Preset""" - - bl_idname = "material.sss_preset_add" - bl_label = "Add SSS Preset" - preset_menu = "MATERIAL_MT_POV_sss_presets" - - # variable used for all preset values - preset_defines = ["material = bpy.context.material"] - - # properties to store in the preset - preset_values = [ - "material.pov_subsurface_scattering.radius", - "material.pov_subsurface_scattering.color", - ] - - # where to store the preset - preset_subdir = "pov/material/sss" - - -class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel): - """Use this class to define pov sss buttons panel.""" - - bl_label = "Subsurface Scattering" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return ( - check_material(mat) - and (mat.pov.type in {'SURFACE', 'WIRE'}) - and (engine in cls.COMPAT_ENGINES) - ) - - def draw_header(self, context): - mat = context.material # FORMERLY : #active_node_mat(context.material) - sss = mat.pov_subsurface_scattering - - self.layout.active = not mat.pov.use_shadeless - self.layout.prop(sss, "use", text="") - - def draw(self, context): - layout = self.layout - - mat = context.material # FORMERLY : #active_node_mat(context.material) - sss = mat.pov_subsurface_scattering - - layout.active = (sss.use) and (not mat.pov.use_shadeless) - - row = layout.row().split() - sub = row.row(align=True).split(align=True, factor=0.75) - sub.menu( - MATERIAL_MT_POV_sss_presets.__name__, - text=MATERIAL_MT_POV_sss_presets.bl_label, - ) - sub.operator( - MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='ADD' - ) - sub.operator( - MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='REMOVE' - ).remove_active = True - - split = layout.split() - - col = split.column() - col.prop(sss, "ior") - col.prop(sss, "scale") - col.prop(sss, "color", text="") - col.prop(sss, "radius", text="RGB Radius", expand=True) - - col = split.column() - sub = col.column(align=True) - sub.label(text="Blend:") - sub.prop(sss, "color_factor", text="Color") - sub.prop(sss, "texture_factor", text="Texture") - sub.label(text="Scattering Weight:") - sub.prop(sss, "front") - sub.prop(sss, "back") - col.separator() - col.prop(sss, "error_threshold", text="Error") - - -class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel): - """Use this class to define an activate pov nodes button.""" - - bl_label = "Activate Node Settings" - bl_context = "material" - bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat = context.material - ob = context.object - return ( - mat - and mat.pov.type == "SURFACE" - and (engine in cls.COMPAT_ENGINES) - and not (mat.pov.material_use_nodes or mat.use_nodes) - ) - - def draw(self, context): - layout = self.layout - # layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE') - # the above replaced with a context hook below: - layout.operator( - "WM_OT_context_toggle", text="Use POV-Ray Nodes", icon='NODETREE' - ).data_path = "material.pov.material_use_nodes" - - -class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel): - """Use this class to show pov active node properties buttons.""" - - bl_label = "Active Node Settings" - bl_context = "material" - bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat = context.material - ob = context.object - return ( - mat - and mat.pov.type == "SURFACE" - and (engine in cls.COMPAT_ENGINES) - and mat.pov.material_use_nodes - ) - - def draw(self, context): - layout = self.layout - mat = context.material - node_tree = mat.node_tree - if node_tree: - node = node_tree.nodes.active - if mat.use_nodes: - if node: - layout.prop(mat.pov, "material_active_node") - if node.bl_idname == "PovrayMaterialNode": - layout.context_pointer_set("node", node) - if hasattr(node, "draw_buttons_ext"): - node.draw_buttons_ext(context, layout) - elif hasattr(node, "draw_buttons"): - node.draw_buttons(context, layout) - value_inputs = [ - socket - for socket in node.inputs - if socket.enabled and not socket.is_linked - ] - if value_inputs: - layout.separator() - layout.label(text="Inputs:") - for socket in value_inputs: - row = layout.row() - socket.draw(context, row, node, socket.name) - else: - layout.context_pointer_set("node", node) - if hasattr(node, "draw_buttons_ext"): - node.draw_buttons_ext(context, layout) - elif hasattr(node, "draw_buttons"): - node.draw_buttons(context, layout) - value_inputs = [ - socket - for socket in node.inputs - if socket.enabled and not socket.is_linked - ] - if value_inputs: - layout.separator() - layout.label(text="Inputs:") - for socket in value_inputs: - row = layout.row() - socket.draw(context, row, node, socket.name) - else: - layout.label(text="No active nodes!") - -class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel): - """Use this class to define standard material specularity (highlights) buttons.""" - - bl_label = "Specular" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return ( - check_material(mat) - and (mat.pov.type in {'SURFACE', 'WIRE'}) - and (engine in cls.COMPAT_ENGINES) - ) - def draw(self, context): - layout = self.layout - - mat = context.material.pov - - layout.active = (not mat.use_shadeless) - - split = layout.split() - - col = split.column() - col.prop(mat, "specular_color", text="") - col.prop(mat, "specular_intensity", text="Intensity") - - col = split.column() - col.prop(mat, "specular_shader", text="") - col.prop(mat, "use_specular_ramp", text="Ramp") - - col = layout.column() - if mat.specular_shader in {'COOKTORR', 'PHONG'}: - col.prop(mat, "specular_hardness", text="Hardness") - elif mat.specular_shader == 'BLINN': - row = col.row() - row.prop(mat, "specular_hardness", text="Hardness") - row.prop(mat, "specular_ior", text="IOR") - elif mat.specular_shader == 'WARDISO': - col.prop(mat, "specular_slope", text="Slope") - elif mat.specular_shader == 'TOON': - row = col.row() - row.prop(mat, "specular_toon_size", text="Size") - row.prop(mat, "specular_toon_smooth", text="Smooth") - - if mat.use_specular_ramp: - layout.separator() - layout.template_color_ramp(mat, "specular_ramp", expand=True) - layout.separator() - - row = layout.row() - row.prop(mat, "specular_ramp_input", text="Input") - row.prop(mat, "specular_ramp_blend", text="Blend") - - layout.prop(mat, "specular_ramp_factor", text="Factor") - -class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel): - """Use this class to define standard material reflectivity (mirror) buttons.""" - - bl_label = "Mirror" - bl_options = {'DEFAULT_CLOSED'} - bl_idname = "MATERIAL_PT_POV_raytrace_mirror" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return ( - check_material(mat) - and (mat.pov.type in {'SURFACE', 'WIRE'}) - and (engine in cls.COMPAT_ENGINES) - ) - - def draw_header(self, context): - mat = context.material - raym = mat.pov_raytrace_mirror - - self.layout.prop(raym, "use", text="") - - def draw(self, context): - layout = self.layout - - mat = ( - context.material - ) # Formerly : #mat = active_node_mat(context.material) - raym = mat.pov_raytrace_mirror - - layout.active = raym.use - - split = layout.split() - - col = split.column() - col.prop(raym, "reflect_factor") - col.prop(raym, "mirror_color", text="") - - col = split.column() - col.prop(raym, "fresnel") - sub = col.column() - sub.active = raym.fresnel > 0.0 - sub.prop(raym, "fresnel_factor", text="Blend") - - split = layout.split() - - col = split.column() - col.separator() - col.prop(raym, "depth") - col.prop(raym, "distance", text="Max Dist") - col.separator() - sub = col.split(factor=0.4) - sub.active = raym.distance > 0.0 - sub.label(text="Fade To:") - sub.prop(raym, "fade_to", text="") - - col = split.column() - col.label(text="Gloss:") - col.prop(raym, "gloss_factor", text="Amount") - sub = col.column() - sub.active = raym.gloss_factor < 1.0 - sub.prop(raym, "gloss_threshold", text="Threshold") - sub.prop(raym, "gloss_samples", text="Noise") - sub.prop(raym, "gloss_anisotropic", text="Anisotropic") - - -class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel): - """Use this class to define pov material transparency (alpha) buttons.""" - - bl_label = "Transparency" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return ( - check_material(mat) - and (mat.pov.type in {'SURFACE', 'WIRE'}) - and (engine in cls.COMPAT_ENGINES) - ) - - def draw_header(self, context): - mat = context.material - - if simple_material(mat): - self.layout.prop(mat.pov, "use_transparency", text="") - - def draw(self, context): - layout = self.layout - - base_mat = context.material - mat = context.material # FORMERLY active_node_mat(context.material) - rayt = mat.pov_raytrace_transparency - - if simple_material(base_mat): - row = layout.row() - row.active = mat.pov.use_transparency - row.prop(mat.pov, "transparency_method", expand=True) - - split = layout.split() - split.active = base_mat.pov.use_transparency - - col = split.column() - col.prop(mat.pov, "alpha") - row = col.row() - row.active = (base_mat.pov.transparency_method != 'MASK') and ( - not mat.pov.use_shadeless - ) - row.prop(mat.pov, "specular_alpha", text="Specular") - - col = split.column() - col.active = not mat.pov.use_shadeless - col.prop(rayt, "fresnel") - sub = col.column() - sub.active = rayt.fresnel > 0.0 - sub.prop(rayt, "fresnel_factor", text="Blend") - - if base_mat.pov.transparency_method == 'RAYTRACE': - layout.separator() - split = layout.split() - split.active = base_mat.pov.use_transparency - - col = split.column() - col.prop(rayt, "ior") - col.prop(rayt, "filter") - col.prop(rayt, "falloff") - col.prop(rayt, "depth_max") - col.prop(rayt, "depth") - - col = split.column() - col.label(text="Gloss:") - col.prop(rayt, "gloss_factor", text="Amount") - sub = col.column() - sub.active = rayt.gloss_factor < 1.0 - sub.prop(rayt, "gloss_threshold", text="Threshold") - sub.prop(rayt, "gloss_samples", text="Samples") - - -class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel): - """Use this class to define more pov specific reflectivity buttons.""" - - bl_label = "POV-Ray Reflection" - bl_parent_id = "MATERIAL_PT_POV_raytrace_mirror" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat = context.material - ob = context.object - return ( - mat - and mat.pov.type == "SURFACE" - and (engine in cls.COMPAT_ENGINES) - and not (mat.pov.material_use_nodes or mat.use_nodes) - ) - - def draw(self, context): - layout = self.layout - mat = context.material - col = layout.column() - col.prop(mat.pov, "irid_enable") - if mat.pov.irid_enable: - col = layout.column() - col.prop(mat.pov, "irid_amount", slider=True) - col.prop(mat.pov, "irid_thickness", slider=True) - col.prop(mat.pov, "irid_turbulence", slider=True) - col.prop(mat.pov, "conserve_energy") - col2 = col.split().column() - - if not mat.pov_raytrace_mirror.use: - col2.label(text="Please Check Mirror settings :") - col2.active = mat.pov_raytrace_mirror.use - col2.prop(mat.pov, "mirror_use_IOR") - if mat.pov.mirror_use_IOR: - col2.alignment = 'CENTER' - col2.label(text="The current Raytrace ") - col2.label(text="Transparency IOR is: " + str(mat.pov.ior)) - col2.prop(mat.pov, "mirror_metallic") - - -''' -#group some native Blender (SSS) and POV (Fade)settings under such a parent panel? -class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel): - bl_label = "POV-Ray Interior" - bl_idname = "material.pov_interior" - #bl_parent_id = "material.absorption" - COMPAT_ENGINES = {'POVRAY_RENDER'} - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat=context.material - ob = context.object - return mat and mat.pov.type == "SURFACE" and (engine in cls.COMPAT_ENGINES) and not (mat.pov.material_use_nodes or mat.use_nodes) - - - def draw_header(self, context): - mat = context.material -''' - - -class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel, Panel): - """Use this class to define pov fading (absorption) color buttons.""" - - bl_label = "POV-Ray Absorption" - COMPAT_ENGINES = {'POVRAY_RENDER'} - # bl_parent_id = "material.pov_interior" - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat = context.material - ob = context.object - return ( - mat - and mat.pov.type == "SURFACE" - and (engine in cls.COMPAT_ENGINES) - and not (mat.pov.material_use_nodes or mat.use_nodes) - ) - - def draw_header(self, context): - mat = context.material - - self.layout.prop(mat.pov, "interior_fade_color", text="") - - def draw(self, context): - layout = self.layout - mat = context.material - # layout.active = mat.pov.interior_fade_color - if mat.pov.interior_fade_color != (0.0, 0.0, 0.0): - layout.label(text="Raytrace transparency") - layout.label(text="depth max Limit needs") - layout.label(text="to be non zero to fade") - - pass - - -class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel): - """Use this class to define pov caustics buttons.""" - - bl_label = "Caustics" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - mat = context.material - ob = context.object - return ( - mat - and mat.pov.type == "SURFACE" - and (engine in cls.COMPAT_ENGINES) - and not (mat.pov.material_use_nodes or mat.use_nodes) - ) - - def draw_header(self, context): - mat = context.material - if mat.pov.caustics_enable: - self.layout.prop( - mat.pov, "caustics_enable", text="", icon="PMARKER_SEL" - ) - else: - self.layout.prop( - mat.pov, "caustics_enable", text="", icon="PMARKER" - ) - - def draw(self, context): - - layout = self.layout - - mat = context.material - layout.active = mat.pov.caustics_enable - col = layout.column() - if mat.pov.caustics_enable: - col.prop(mat.pov, "refraction_caustics") - if mat.pov.refraction_caustics: - - col.prop(mat.pov, "refraction_type", text="") - - if mat.pov.refraction_type == "1": - col.prop(mat.pov, "fake_caustics_power", slider=True) - elif mat.pov.refraction_type == "2": - col.prop(mat.pov, "photons_dispersion", slider=True) - col.prop(mat.pov, "photons_dispersion_samples", slider=True) - col.prop(mat.pov, "photons_reflection") - - if ( - not mat.pov.refraction_caustics - and not mat.pov.photons_reflection - ): - col = layout.column() - col.alignment = 'CENTER' - col.label(text="Caustics override is on, ") - col.label(text="but you didn't chose any !") - - -class MATERIAL_PT_strand(MaterialButtonsPanel, Panel): - """Use this class to define Blender strand antialiasing buttons.""" - - bl_label = "Strand" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - mat = context.material - engine = context.scene.render.engine - return ( - mat - and (mat.pov.type in {'SURFACE', 'WIRE', 'HALO'}) - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - mat = context.material # don't use node material - tan = mat.strand - - split = layout.split() - - col = split.column() - sub = col.column(align=True) - sub.label(text="Size:") - sub.prop(tan, "root_size", text="Root") - sub.prop(tan, "tip_size", text="Tip") - sub.prop(tan, "size_min", text="Minimum") - sub.prop(tan, "use_blender_units") - sub = col.column() - sub.active = not mat.pov.use_shadeless - sub.prop(tan, "use_tangent_shading") - col.prop(tan, "shape") - - col = split.column() - col.label(text="Shading:") - col.prop(tan, "width_fade") - ob = context.object - if ob and ob.type == 'MESH': - col.prop_search( - tan, "uv_layer", ob.data, "tessface_uv_textures", text="" - ) - else: - col.prop(tan, "uv_layer", text="") - col.separator() - sub = col.column() - sub.active = not mat.pov.use_shadeless - sub.label(text="Surface diffuse:") - sub = col.column() - sub.prop(tan, "blend_distance", text="Distance") - - -class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel): - """Use this class to define pov custom code declared name field.""" - - bl_label = "Custom POV Code" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - layout = self.layout - - mat = context.material - - col = layout.column() - col.label(text="Replace properties with:") - col.prop(mat.pov, "replacement_text", text="") - - -class TEXTURE_MT_POV_specials(Menu): - """Use this class to define pov texture slot operations buttons.""" - - bl_label = "Texture Specials" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - layout = self.layout - - layout.operator("texture.slot_copy", icon='COPYDOWN') - layout.operator("texture.slot_paste", icon='PASTEDOWN') - - -class WORLD_TEXTURE_SLOTS_UL_POV_layerlist(UIList): - """Use this class to show pov texture slots list.""" # XXX Not used yet - - index: bpy.props.IntProperty(name='index') - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname - ): - world = context.scene.world # .pov - active_data = world.pov - # tex = context.texture #may be needed later? - - # We could write some code to decide which icon to use here... - custom_icon = 'TEXTURE' - - ob = data - slot = item - # ma = slot.name - # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. - if self.layout_type in {'DEFAULT', 'COMPACT'}: - # You should always start your row layout by a label (icon + text), or a non-embossed text field, - # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. - # We use icon_value of label, as our given icon is an integer value, not an enum ID. - # Note "data" names should never be translated! - if slot: - layout.prop( - item, "texture", text="", emboss=False, icon='TEXTURE' - ) - else: - layout.label(text="New", translate=False, icon_value=icon) - # 'GRID' layout type should be as compact as possible (typically a single icon!). - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - -class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(UIList): - """Use this class to show pov texture slots list.""" - - # texture_slots: - index: bpy.props.IntProperty(name='index') - # foo = random prop - def draw_item( - self, context, layout, data, item, icon, active_data, active_propname - ): - ob = data - slot = item - # ma = slot.name - # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code. - if self.layout_type in {'DEFAULT', 'COMPACT'}: - # You should always start your row layout by a label (icon + text), or a non-embossed text field, - # this will also make the row easily selectable in the list! The later also enables ctrl-click rename. - # We use icon_value of label, as our given icon is an integer value, not an enum ID. - # Note "data" names should never be translated! - if slot: - layout.prop( - item, "texture", text="", emboss=False, icon='TEXTURE' - ) - else: - layout.label(text="New", translate=False, icon_value=icon) - # 'GRID' layout type should be as compact as possible (typically a single icon!). - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - -# Rewrite an existing class to modify. -# register but not unregistered because -# the modified parts concern only POVRAY_RENDER -class TEXTURE_PT_context(TextureButtonsPanel, Panel): - bl_label = "" - bl_context = "texture" - bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'POVRAY_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - return ( - (context.scene.texture_context - not in('MATERIAL','WORLD','LIGHT','PARTICLES','LINESTYLE') - or context.scene.render.engine != 'POVRAY_RENDER') - ) - def draw(self, context): - layout = self.layout - tex = context.texture - space = context.space_data - pin_id = space.pin_id - use_pin_id = space.use_pin_id - user = context.texture_user - - col = layout.column() - - if not (use_pin_id and isinstance(pin_id, bpy.types.Texture)): - pin_id = None - - if not pin_id: - col.template_texture_user() - - if user or pin_id: - col.separator() - - if pin_id: - col.template_ID(space, "pin_id") - else: - propname = context.texture_user_property.identifier - col.template_ID(user, propname, new="texture.new") - - if tex: - col.separator() - - split = col.split(factor=0.2) - split.label(text="Type") - split.prop(tex, "type", text="") - -class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel): - """Use this class to show pov texture context buttons.""" - - bl_label = "" - bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - return engine in cls.COMPAT_ENGINES - # if not (hasattr(context, "pov_texture_slot") or hasattr(context, "texture_node")): - # return False - return ( - context.material - or context.scene.world - or context.light - or context.texture - or context.line_style - or context.particle_system - or isinstance(context.space_data.pin_id, ParticleSettings) - or context.texture_user - ) and (engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - scene = context.scene - mat = context.view_layer.objects.active.active_material - wld = context.scene.world - - layout.prop(scene, "texture_context", expand=True) - if scene.texture_context == 'MATERIAL' and mat is not None: - - row = layout.row() - row.template_list( - "MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist", - "", - mat, - "pov_texture_slots", - mat.pov, - "active_texture_index", - rows=2, - maxrows=16, - type="DEFAULT" - ) - col = row.column(align=True) - col.operator("pov.textureslotadd", icon='ADD', text='') - col.operator("pov.textureslotremove", icon='REMOVE', text='') - #todo: recreate for pov_texture_slots? - #col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' - #col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' - col.separator() - - if mat.pov_texture_slots: - index = mat.pov.active_texture_index - slot = mat.pov_texture_slots[index] - povtex = slot.texture#slot.name - tex = bpy.data.textures[povtex] - col.prop(tex, 'use_fake_user', text='') - #layout.label(text='Linked Texture data browser:') - propname = slot.texture_search - # if slot.texture was a pointer to texture data rather than just a name string: - # layout.template_ID(povtex, "texture", new="texture.new") - - layout.prop_search( - slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE' - ) - try: - bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[slot.texture_search] - bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[slot.texture_search] - except KeyError: - # texture not hand-linked by user - pass - - if tex: - layout.separator() - split = layout.split(factor=0.2) - split.label(text="Type") - split.prop(tex, "type", text="") - - # else: - # for i in range(18): # length of material texture slots - # mat.pov_texture_slots.add() - elif scene.texture_context == 'WORLD' and wld is not None: - - row = layout.row() - row.template_list( - "WORLD_TEXTURE_SLOTS_UL_POV_layerlist", - "", - wld, - "pov_texture_slots", - wld.pov, - "active_texture_index", - rows=2, - maxrows=16, - type="DEFAULT" - ) - col = row.column(align=True) - col.operator("pov.textureslotadd", icon='ADD', text='') - col.operator("pov.textureslotremove", icon='REMOVE', text='') - - #todo: recreate for pov_texture_slots? - #col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' - #col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' - col.separator() - - if wld.pov_texture_slots: - index = wld.pov.active_texture_index - slot = wld.pov_texture_slots[index] - povtex = slot.texture#slot.name - tex = bpy.data.textures[povtex] - col.prop(tex, 'use_fake_user', text='') - #layout.label(text='Linked Texture data browser:') - propname = slot.texture_search - # if slot.texture was a pointer to texture data rather than just a name string: - # layout.template_ID(povtex, "texture", new="texture.new") - - layout.prop_search( - slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE' - ) - try: - bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[slot.texture_search] - bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[slot.texture_search] - except KeyError: - # texture not hand-linked by user - pass - - if tex: - layout.separator() - split = layout.split(factor=0.2) - split.label(text="Type") - split.prop(tex, "type", text="") - -# Commented out below is a reminder of what existed in Blender Internal -# attributes need to be recreated -''' - slot = getattr(context, "texture_slot", None) - node = getattr(context, "texture_node", None) - space = context.space_data - - #attempt at replacing removed space_data - mtl = getattr(context, "material", None) - if mtl != None: - spacedependant = mtl - wld = getattr(context, "world", None) - if wld != None: - spacedependant = wld - lgt = getattr(context, "light", None) - if lgt != None: - spacedependant = lgt - - - #idblock = context.particle_system.settings - - tex = getattr(context, "texture", None) - if tex != None: - spacedependant = tex - - - - scene = context.scene - idblock = scene.pov#pov_context_tex_datablock(context) - pin_id = space.pin_id - - #spacedependant.use_limited_texture_context = True - - if space.use_pin_id and not isinstance(pin_id, Texture): - idblock = id_tex_datablock(pin_id) - pin_id = None - - if not space.use_pin_id: - layout.row().prop(spacedependant, "texture_context", expand=True) - pin_id = None - - if spacedependant.texture_context == 'OTHER': - if not pin_id: - layout.template_texture_user() - user = context.texture_user - if user or pin_id: - layout.separator() - - row = layout.row() - - if pin_id: - row.template_ID(space, "pin_id") - else: - propname = context.texture_user_property.identifier - row.template_ID(user, propname, new="texture.new") - - if tex: - split = layout.split(factor=0.2) - if tex.use_nodes: - if slot: - split.label(text="Output:") - split.prop(slot, "output_node", text="") - else: - split.label(text="Type:") - split.prop(tex, "type", text="") - return - - tex_collection = (pin_id is None) and (node is None) and (spacedependant.texture_context not in ('LINESTYLE','OTHER')) - - if tex_collection: - - pov = getattr(context, "pov", None) - active_texture_index = getattr(spacedependant, "active_texture_index", None) - print (pov) - print(idblock) - print(active_texture_index) - row = layout.row() - - row.template_list("TEXTURE_UL_texslots", "", idblock, "texture_slots", - idblock, "active_texture_index", rows=2, maxrows=16, type="DEFAULT") - - # row.template_list("WORLD_TEXTURE_SLOTS_UL_List", "texture_slots", world, - # world.texture_slots, world, "active_texture_index", rows=2) - - col = row.column(align=True) - col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' - col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' - col.menu("TEXTURE_MT_POV_specials", icon='DOWNARROW_HLT', text="") - - if tex_collection: - layout.template_ID(idblock, "active_texture", new="texture.new") - elif node: - layout.template_ID(node, "texture", new="texture.new") - elif idblock: - layout.template_ID(idblock, "texture", new="texture.new") - - if pin_id: - layout.template_ID(space, "pin_id") - - if tex: - split = layout.split(factor=0.2) - if tex.use_nodes: - if slot: - split.label(text="Output:") - split.prop(slot, "output_node", text="") - else: - split.label(text="Type:") -''' - - -class TEXTURE_PT_colors(TextureButtonsPanel, Panel): - """Use this class to show pov color ramps.""" - - bl_label = "Colors" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - layout = self.layout - - tex = context.texture - - layout.prop(tex, "use_color_ramp", text="Ramp") - if tex.use_color_ramp: - layout.template_color_ramp(tex, "color_ramp", expand=True) - - split = layout.split() - - col = split.column() - col.label(text="RGB Multiply:") - sub = col.column(align=True) - sub.prop(tex, "factor_red", text="R") - sub.prop(tex, "factor_green", text="G") - sub.prop(tex, "factor_blue", text="B") - - col = split.column() - col.label(text="Adjust:") - col.prop(tex, "intensity") - col.prop(tex, "contrast") - col.prop(tex, "saturation") - - col = layout.column() - col.prop(tex, "use_clamp", text="Clamp") - - -# Texture Slot Panels # - - -class TEXTURE_OT_POV_texture_slot_add(Operator): - """Use this class for the add texture slot button.""" - - bl_idname = "pov.textureslotadd" - bl_label = "Add" - bl_description = "Add texture_slot" - bl_options = {'REGISTER', 'UNDO'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def execute(self, context): - idblock = pov_context_tex_datablock(context) - tex = bpy.data.textures.new(name='Texture', type='IMAGE') - #tex.use_fake_user = True - #mat = context.view_layer.objects.active.active_material - slot = idblock.pov_texture_slots.add() - slot.name = tex.name - slot.texture = tex.name - slot.texture_search = tex.name - # Switch paint brush and paint brush mask - # to this texture so settings remain contextual - bpy.context.tool_settings.image_paint.brush.texture = tex - bpy.context.tool_settings.image_paint.brush.mask_texture = tex - idblock.pov.active_texture_index = (len(idblock.pov_texture_slots)-1) - - #for area in bpy.context.screen.areas: - #if area.type in ['PROPERTIES']: - #area.tag_redraw() - - - return {'FINISHED'} - - -class TEXTURE_OT_POV_texture_slot_remove(Operator): - """Use this class for the remove texture slot button.""" - - bl_idname = "pov.textureslotremove" - bl_label = "Remove" - bl_description = "Remove texture_slot" - bl_options = {'REGISTER', 'UNDO'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def execute(self, context): - idblock = pov_context_tex_datablock(context) - #mat = context.view_layer.objects.active.active_material - tex_slot = idblock.pov_texture_slots.remove(idblock.pov.active_texture_index) - if idblock.pov.active_texture_index > 0: - idblock.pov.active_texture_index -= 1 - try: - tex = idblock.pov_texture_slots[idblock.pov.active_texture_index].texture - except IndexError: - # No more slots - return {'FINISHED'} - # Switch paint brush to previous texture so settings remain contextual - # if 'tex' in locals(): # Would test is the tex variable is assigned / exists - bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex] - bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex] - - return {'FINISHED'} - -class TextureSlotPanel(TextureButtonsPanel): - """Use this class to show pov texture slots panel.""" - - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - if not hasattr(context, "pov_texture_slot"): - return False - - engine = context.scene.render.engine - return TextureButtonsPanel.poll(cls, context) and ( - engine in cls.COMPAT_ENGINES - ) - - -class TEXTURE_PT_POV_type(TextureButtonsPanel, Panel): - """Use this class to define pov texture type buttons.""" - - bl_label = "POV Textures" - COMPAT_ENGINES = {'POVRAY_RENDER'} - bl_options = {'HIDE_HEADER'} - - def draw(self, context): - layout = self.layout - world = context.world - tex = context.texture - - split = layout.split(factor=0.2) - split.label(text="Pattern") - split.prop(tex.pov, "tex_pattern_type", text="") - - # row = layout.row() - # row.template_list("WORLD_TEXTURE_SLOTS_UL_List", "texture_slots", world, - # world.texture_slots, world, "active_texture_index") - - -class TEXTURE_PT_POV_preview(TextureButtonsPanel, Panel): - """Use this class to define pov texture preview panel.""" - - bl_label = "Preview" - COMPAT_ENGINES = {'POVRAY_RENDER'} - bl_options = {'HIDE_HEADER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - if not hasattr(context, "pov_texture_slot"): - return False - tex = context.texture - mat = bpy.context.active_object.active_material - return ( - tex - and (tex.pov.tex_pattern_type != 'emulator') - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - tex = context.texture - slot = getattr(context, "pov_texture_slot", None) - idblock = pov_context_tex_datablock(context) - layout = self.layout - # if idblock: - # layout.template_preview(tex, parent=idblock, slot=slot) - if tex.pov.tex_pattern_type != 'emulator': - layout.operator("tex.preview_update") - else: - layout.template_preview(tex, slot=slot) - - -class TEXTURE_PT_POV_parameters(TextureButtonsPanel, Panel): - """Use this class to define pov texture pattern buttons.""" - - bl_label = "POV Pattern Options" - bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - mat = bpy.context.active_object.active_material - layout = self.layout - tex = context.texture - align = True - if tex is not None and tex.pov.tex_pattern_type != 'emulator': - if tex.pov.tex_pattern_type == 'agate': - layout.prop( - tex.pov, "modifier_turbulence", text="Agate Turbulence" - ) - if tex.pov.tex_pattern_type in {'spiral1', 'spiral2'}: - layout.prop(tex.pov, "modifier_numbers", text="Number of arms") - if tex.pov.tex_pattern_type == 'tiling': - layout.prop(tex.pov, "modifier_numbers", text="Pattern number") - if tex.pov.tex_pattern_type == 'magnet': - layout.prop(tex.pov, "magnet_style", text="Magnet style") - if tex.pov.tex_pattern_type == 'quilted': - row = layout.row(align=align) - row.prop(tex.pov, "modifier_control0", text="Control0") - row.prop(tex.pov, "modifier_control1", text="Control1") - if tex.pov.tex_pattern_type == 'brick': - col = layout.column(align=align) - row = col.row() - row.prop(tex.pov, "brick_size_x", text="Brick size X") - row.prop(tex.pov, "brick_size_y", text="Brick size Y") - row = col.row() - row.prop(tex.pov, "brick_size_z", text="Brick size Z") - row.prop(tex.pov, "brick_mortar", text="Brick mortar") - if tex.pov.tex_pattern_type in {'julia', 'mandel', 'magnet'}: - col = layout.column(align=align) - if tex.pov.tex_pattern_type == 'julia': - row = col.row() - row.prop(tex.pov, "julia_complex_1", text="Complex 1") - row.prop(tex.pov, "julia_complex_2", text="Complex 2") - if ( - tex.pov.tex_pattern_type == 'magnet' - and tex.pov.magnet_style == 'julia' - ): - row = col.row() - row.prop(tex.pov, "julia_complex_1", text="Complex 1") - row.prop(tex.pov, "julia_complex_2", text="Complex 2") - row = col.row() - if tex.pov.tex_pattern_type in {'julia', 'mandel'}: - row.prop(tex.pov, "f_exponent", text="Exponent") - if tex.pov.tex_pattern_type == 'magnet': - row.prop(tex.pov, "magnet_type", text="Type") - row.prop(tex.pov, "f_iter", text="Iterations") - row = col.row() - row.prop(tex.pov, "f_ior", text="Interior") - row.prop(tex.pov, "f_ior_fac", text="Factor I") - row = col.row() - row.prop(tex.pov, "f_eor", text="Exterior") - row.prop(tex.pov, "f_eor_fac", text="Factor E") - if tex.pov.tex_pattern_type == 'gradient': - layout.label(text="Gradient orientation:") - column_flow = layout.column_flow(columns=3, align=True) - column_flow.prop(tex.pov, "grad_orient_x", text="X") - column_flow.prop(tex.pov, "grad_orient_y", text="Y") - column_flow.prop(tex.pov, "grad_orient_z", text="Z") - if tex.pov.tex_pattern_type == 'pavement': - layout.prop( - tex.pov, "pave_sides", text="Pavement:number of sides" - ) - col = layout.column(align=align) - column_flow = col.column_flow(columns=3, align=True) - column_flow.prop(tex.pov, "pave_tiles", text="Tiles") - if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 6: - column_flow.prop(tex.pov, "pave_pat_35", text="Pattern") - if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 5: - column_flow.prop(tex.pov, "pave_pat_22", text="Pattern") - if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 5: - column_flow.prop(tex.pov, "pave_pat_12", text="Pattern") - if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 6: - column_flow.prop(tex.pov, "pave_pat_12", text="Pattern") - if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 4: - column_flow.prop(tex.pov, "pave_pat_7", text="Pattern") - if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 4: - column_flow.prop(tex.pov, "pave_pat_5", text="Pattern") - if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 5: - column_flow.prop(tex.pov, "pave_pat_4", text="Pattern") - if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 3: - column_flow.prop(tex.pov, "pave_pat_3", text="Pattern") - if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 4: - column_flow.prop(tex.pov, "pave_pat_3", text="Pattern") - if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 3: - column_flow.prop(tex.pov, "pave_pat_2", text="Pattern") - if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 6: - column_flow.label(text="!!! 5 tiles!") - column_flow.prop(tex.pov, "pave_form", text="Form") - if tex.pov.tex_pattern_type == 'function': - layout.prop(tex.pov, "func_list", text="Functions") - if ( - tex.pov.tex_pattern_type == 'function' - and tex.pov.func_list != "NONE" - ): - func = None - if tex.pov.func_list in {"f_noise3d", "f_ph", "f_r", "f_th"}: - func = 0 - if tex.pov.func_list in { - "f_comma", - "f_crossed_trough", - "f_cubic_saddle", - "f_cushion", - "f_devils_curve", - "f_enneper", - "f_glob", - "f_heart", - "f_hex_x", - "f_hex_y", - "f_hunt_surface", - "f_klein_bottle", - "f_kummer_surface_v1", - "f_lemniscate_of_gerono", - "f_mitre", - "f_nodal_cubic", - "f_noise_generator", - "f_odd", - "f_paraboloid", - "f_pillow", - "f_piriform", - "f_quantum", - "f_quartic_paraboloid", - "f_quartic_saddle", - "f_sphere", - "f_steiners_roman", - "f_torus_gumdrop", - "f_umbrella", - }: - func = 1 - if tex.pov.func_list in { - "f_bicorn", - "f_bifolia", - "f_boy_surface", - "f_superellipsoid", - "f_torus", - }: - func = 2 - if tex.pov.func_list in { - "f_ellipsoid", - "f_folium_surface", - "f_hyperbolic_torus", - "f_kampyle_of_eudoxus", - "f_parabolic_torus", - "f_quartic_cylinder", - "f_torus2", - }: - func = 3 - if tex.pov.func_list in { - "f_blob2", - "f_cross_ellipsoids", - "f_flange_cover", - "f_isect_ellipsoids", - "f_kummer_surface_v2", - "f_ovals_of_cassini", - "f_rounded_box", - "f_spikes_2d", - "f_strophoid", - }: - func = 4 - if tex.pov.func_list in { - "f_algbr_cyl1", - "f_algbr_cyl2", - "f_algbr_cyl3", - "f_algbr_cyl4", - "f_blob", - "f_mesh1", - "f_poly4", - "f_spikes", - }: - func = 5 - if tex.pov.func_list in { - "f_devils_curve_2d", - "f_dupin_cyclid", - "f_folium_surface_2d", - "f_hetero_mf", - "f_kampyle_of_eudoxus_2d", - "f_lemniscate_of_gerono_2d", - "f_polytubes", - "f_ridge", - "f_ridged_mf", - "f_spiral", - "f_witch_of_agnesi", - }: - func = 6 - if tex.pov.func_list in { - "f_helix1", - "f_helix2", - "f_piriform_2d", - "f_strophoid_2d", - }: - func = 7 - if tex.pov.func_list == "f_helical_torus": - func = 8 - column_flow = layout.column_flow(columns=3, align=True) - column_flow.label(text="X") - column_flow.prop(tex.pov, "func_plus_x", text="") - column_flow.prop(tex.pov, "func_x", text="Value") - column_flow = layout.column_flow(columns=3, align=True) - column_flow.label(text="Y") - column_flow.prop(tex.pov, "func_plus_y", text="") - column_flow.prop(tex.pov, "func_y", text="Value") - column_flow = layout.column_flow(columns=3, align=True) - column_flow.label(text="Z") - column_flow.prop(tex.pov, "func_plus_z", text="") - column_flow.prop(tex.pov, "func_z", text="Value") - row = layout.row(align=align) - if func > 0: - row.prop(tex.pov, "func_P0", text="P0") - if func > 1: - row.prop(tex.pov, "func_P1", text="P1") - row = layout.row(align=align) - if func > 2: - row.prop(tex.pov, "func_P2", text="P2") - if func > 3: - row.prop(tex.pov, "func_P3", text="P3") - row = layout.row(align=align) - if func > 4: - row.prop(tex.pov, "func_P4", text="P4") - if func > 5: - row.prop(tex.pov, "func_P5", text="P5") - row = layout.row(align=align) - if func > 6: - row.prop(tex.pov, "func_P6", text="P6") - if func > 7: - row.prop(tex.pov, "func_P7", text="P7") - row = layout.row(align=align) - row.prop(tex.pov, "func_P8", text="P8") - row.prop(tex.pov, "func_P9", text="P9") - ###################################################End Patterns############################ - - layout.prop(tex.pov, "warp_types", text="Warp types") # warp - if tex.pov.warp_types == "TOROIDAL": - layout.prop( - tex.pov, "warp_tor_major_radius", text="Major radius" - ) - if tex.pov.warp_types not in {"CUBIC", "NONE"}: - layout.prop( - tex.pov, "warp_orientation", text="Warp orientation" - ) - col = layout.column(align=align) - row = col.row() - row.prop(tex.pov, "warp_dist_exp", text="Distance exponent") - row = col.row() - row.prop(tex.pov, "modifier_frequency", text="Frequency") - row.prop(tex.pov, "modifier_phase", text="Phase") - - row = layout.row() - - row.label(text="Offset:") - row.label(text="Scale:") - row.label(text="Rotate:") - col = layout.column(align=align) - row = col.row() - row.prop(tex.pov, "tex_mov_x", text="X") - row.prop(tex.pov, "tex_scale_x", text="X") - row.prop(tex.pov, "tex_rot_x", text="X") - row = col.row() - row.prop(tex.pov, "tex_mov_y", text="Y") - row.prop(tex.pov, "tex_scale_y", text="Y") - row.prop(tex.pov, "tex_rot_y", text="Y") - row = col.row() - row.prop(tex.pov, "tex_mov_z", text="Z") - row.prop(tex.pov, "tex_scale_z", text="Z") - row.prop(tex.pov, "tex_rot_z", text="Z") - row = layout.row() - - row.label(text="Turbulence:") - col = layout.column(align=align) - row = col.row() - row.prop(tex.pov, "warp_turbulence_x", text="X") - row.prop(tex.pov, "modifier_octaves", text="Octaves") - row = col.row() - row.prop(tex.pov, "warp_turbulence_y", text="Y") - row.prop(tex.pov, "modifier_lambda", text="Lambda") - row = col.row() - row.prop(tex.pov, "warp_turbulence_z", text="Z") - row.prop(tex.pov, "modifier_omega", text="Omega") - -class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel): - """Use this class to define POV texture mapping buttons""" - bl_label = "Mapping" - COMPAT_ENGINES = {'POVRAY_RENDER'} - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - - @classmethod - def poll(cls, context): - idblock = pov_context_tex_datablock(context) - if isinstance(idblock, Brush) and not context.sculpt_object: - return False - - if not getattr(context, "texture_slot", None): - return False - - engine = context.scene.render.engine - return (engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - idblock = pov_context_tex_datablock(context) - - #tex = context.texture_slot - tex = mat.pov_texture_slots[ - mat.active_texture_index - ] - if not isinstance(idblock, Brush): - split = layout.split(percentage=0.3) - col = split.column() - col.label(text="Coordinates:") - col = split.column() - col.prop(tex, "texture_coords", text="") - - if tex.texture_coords == 'ORCO': - """ - ob = context.object - if ob and ob.type == 'MESH': - split = layout.split(percentage=0.3) - split.label(text="Mesh:") - split.prop(ob.data, "texco_mesh", text="") - """ - elif tex.texture_coords == 'UV': - split = layout.split(percentage=0.3) - split.label(text="Map:") - ob = context.object - if ob and ob.type == 'MESH': - split.prop_search(tex, "uv_layer", ob.data, "uv_textures", text="") - else: - split.prop(tex, "uv_layer", text="") - - elif tex.texture_coords == 'OBJECT': - split = layout.split(percentage=0.3) - split.label(text="Object:") - split.prop(tex, "object", text="") - - elif tex.texture_coords == 'ALONG_STROKE': - split = layout.split(percentage=0.3) - split.label(text="Use Tips:") - split.prop(tex, "use_tips", text="") - - if isinstance(idblock, Brush): - if context.sculpt_object or context.image_paint_object: - brush_texture_settings(layout, idblock, context.sculpt_object) - else: - if isinstance(idblock, FreestyleLineStyle): - split = layout.split(percentage=0.3) - split.label(text="Projection:") - split.prop(tex, "mapping", text="") - - split = layout.split(percentage=0.3) - split.separator() - row = split.row() - row.prop(tex, "mapping_x", text="") - row.prop(tex, "mapping_y", text="") - row.prop(tex, "mapping_z", text="") - - elif isinstance(idblock, Material): - split = layout.split(percentage=0.3) - split.label(text="Projection:") - split.prop(tex, "mapping", text="") - - split = layout.split() - - col = split.column() - if tex.texture_coords in {'ORCO', 'UV'}: - col.prop(tex, "use_from_dupli") - if (idblock.type == 'VOLUME' and tex.texture_coords == 'ORCO'): - col.prop(tex, "use_map_to_bounds") - elif tex.texture_coords == 'OBJECT': - col.prop(tex, "use_from_original") - if (idblock.type == 'VOLUME'): - col.prop(tex, "use_map_to_bounds") - else: - col.label() - - col = split.column() - row = col.row() - row.prop(tex, "mapping_x", text="") - row.prop(tex, "mapping_y", text="") - row.prop(tex, "mapping_z", text="") - - row = layout.row() - row.column().prop(tex, "offset") - row.column().prop(tex, "scale") - -class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel): - """Use this class to define pov texture influence buttons.""" - - bl_label = "Influence" - COMPAT_ENGINES = {'POVRAY_RENDER'} - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - #bl_context = 'texture' - @classmethod - def poll(cls, context): - idblock = pov_context_tex_datablock(context) - if ( - # isinstance(idblock, Brush) and # Brush used for everything since 2.8 - context.scene.texture_context == 'OTHER' - ): # XXX replace by isinstance(idblock, bpy.types.Brush) and ... - return False - - # Specify below also for pov_world_texture_slots, lights etc. - # to display for various types of slots but only when any - if not getattr(idblock, "pov_texture_slots", None): - return False - - engine = context.scene.render.engine - return engine in cls.COMPAT_ENGINES - - def draw(self, context): - - layout = self.layout - - idblock = pov_context_tex_datablock(context) - # tex = context.pov_texture_slot - #mat = bpy.context.active_object.active_material - texslot = idblock.pov_texture_slots[ - idblock.pov.active_texture_index - ] # bpy.data.textures[mat.active_texture_index] - tex = bpy.data.textures[ - idblock.pov_texture_slots[idblock.pov.active_texture_index].texture - ] - - def factor_but(layout, toggle, factor, name): - row = layout.row(align=True) - row.prop(texslot, toggle, text="") - sub = row.row(align=True) - sub.active = getattr(texslot, toggle) - sub.prop(texslot, factor, text=name, slider=True) - return sub # XXX, temp. use_map_normal needs to override. - - if isinstance(idblock, Material): - split = layout.split() - - col = split.column() - if idblock.pov.type in {'SURFACE', 'WIRE'}: - - split = layout.split() - - col = split.column() - col.label(text="Diffuse:") - factor_but( - col, "use_map_diffuse", "diffuse_factor", "Intensity" - ) - factor_but( - col, - "use_map_color_diffuse", - "diffuse_color_factor", - "Color", - ) - factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") - factor_but( - col, - "use_map_translucency", - "translucency_factor", - "Translucency", - ) - - col.label(text="Specular:") - factor_but( - col, "use_map_specular", "specular_factor", "Intensity" - ) - factor_but( - col, "use_map_color_spec", "specular_color_factor", "Color" - ) - factor_but( - col, "use_map_hardness", "hardness_factor", "Hardness" - ) - - col = split.column() - col.label(text="Shading:") - factor_but(col, "use_map_ambient", "ambient_factor", "Ambient") - factor_but(col, "use_map_emit", "emit_factor", "Emit") - factor_but(col, "use_map_mirror", "mirror_factor", "Mirror") - factor_but(col, "use_map_raymir", "raymir_factor", "Ray Mirror") - - col.label(text="Geometry:") - # XXX replace 'or' when displacement is fixed to not rely on normal influence value. - sub_tmp = factor_but( - col, "use_map_normal", "normal_factor", "Normal" - ) - sub_tmp.active = ( - texslot.use_map_normal or texslot.use_map_displacement - ) - # END XXX - - factor_but(col, "use_map_warp", "warp_factor", "Warp") - factor_but( - col, - "use_map_displacement", - "displacement_factor", - "Displace", - ) - - # ~ sub = col.column() - # ~ sub.active = texslot.use_map_translucency or texslot.map_emit or texslot.map_alpha or texslot.map_raymir or texslot.map_hardness or texslot.map_ambient or texslot.map_specularity or texslot.map_reflection or texslot.map_mirror - # ~ sub.prop(texslot, "default_value", text="Amount", slider=True) - elif idblock.pov.type == 'HALO': - layout.label(text="Halo:") - - split = layout.split() - - col = split.column() - factor_but( - col, - "use_map_color_diffuse", - "diffuse_color_factor", - "Color", - ) - factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") - - col = split.column() - factor_but(col, "use_map_raymir", "raymir_factor", "Size") - factor_but( - col, "use_map_hardness", "hardness_factor", "Hardness" - ) - factor_but( - col, "use_map_translucency", "translucency_factor", "Add" - ) - elif idblock.pov.type == 'VOLUME': - layout.label(text="Volume:") - - split = layout.split() - - col = split.column() - factor_but(col, "use_map_density", "density_factor", "Density") - factor_but( - col, "use_map_emission", "emission_factor", "Emission" - ) - factor_but( - col, "use_map_scatter", "scattering_factor", "Scattering" - ) - factor_but( - col, "use_map_reflect", "reflection_factor", "Reflection" - ) - - col = split.column() - col.label(text=" ") - factor_but( - col, - "use_map_color_emission", - "emission_color_factor", - "Emission Color", - ) - factor_but( - col, - "use_map_color_transmission", - "transmission_color_factor", - "Transmission Color", - ) - factor_but( - col, - "use_map_color_reflection", - "reflection_color_factor", - "Reflection Color", - ) - - layout.label(text="Geometry:") - - split = layout.split() - - col = split.column() - factor_but(col, "use_map_warp", "warp_factor", "Warp") - - col = split.column() - factor_but( - col, - "use_map_displacement", - "displacement_factor", - "Displace", - ) - - elif isinstance(idblock, Light): - split = layout.split() - - col = split.column() - factor_but(col, "use_map_color", "color_factor", "Color") - - col = split.column() - factor_but(col, "use_map_shadow", "shadow_factor", "Shadow") - - elif isinstance(idblock, World): - split = layout.split() - - col = split.column() - factor_but(col, "use_map_blend", "blend_factor", "Blend") - factor_but(col, "use_map_horizon", "horizon_factor", "Horizon") - - col = split.column() - factor_but( - col, "use_map_zenith_up", "zenith_up_factor", "Zenith Up" - ) - factor_but( - col, "use_map_zenith_down", "zenith_down_factor", "Zenith Down" - ) - elif isinstance(idblock, ParticleSettings): - split = layout.split() - - col = split.column() - col.label(text="General:") - factor_but(col, "use_map_time", "time_factor", "Time") - factor_but(col, "use_map_life", "life_factor", "Lifetime") - factor_but(col, "use_map_density", "density_factor", "Density") - factor_but(col, "use_map_size", "size_factor", "Size") - - col = split.column() - col.label(text="Physics:") - factor_but(col, "use_map_velocity", "velocity_factor", "Velocity") - factor_but(col, "use_map_damp", "damp_factor", "Damp") - factor_but(col, "use_map_gravity", "gravity_factor", "Gravity") - factor_but(col, "use_map_field", "field_factor", "Force Fields") - - layout.label(text="Hair:") - - split = layout.split() - - col = split.column() - factor_but(col, "use_map_length", "length_factor", "Length") - factor_but(col, "use_map_clump", "clump_factor", "Clump") - factor_but(col, "use_map_twist", "twist_factor", "Twist") - - col = split.column() - factor_but( - col, "use_map_kink_amp", "kink_amp_factor", "Kink Amplitude" - ) - factor_but( - col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency" - ) - factor_but(col, "use_map_rough", "rough_factor", "Rough") - - elif isinstance(idblock, FreestyleLineStyle): - split = layout.split() - - col = split.column() - factor_but( - col, "use_map_color_diffuse", "diffuse_color_factor", "Color" - ) - col = split.column() - factor_but(col, "use_map_alpha", "alpha_factor", "Alpha") - - layout.separator() - - if not isinstance(idblock, ParticleSettings): - split = layout.split() - - col = split.column() - # col.prop(tex, "blend_type", text="Blend") #deprecated since 2.8 - # col.prop(tex, "use_rgb_to_intensity") #deprecated since 2.8 - # color is used on gray-scale textures even when use_rgb_to_intensity is disabled. - # col.prop(tex, "color", text="") #deprecated since 2.8 - - col = split.column() - # col.prop(tex, "invert", text="Negative") #deprecated since 2.8 - # col.prop(tex, "use_stencil") #deprecated since 2.8 - - # if isinstance(idblock, (Material, World)): - # col.prop(tex, "default_value", text="DVar", slider=True) - - -class TEXTURE_PT_POV_tex_gamma(TextureButtonsPanel, Panel): - """Use this class to define pov texture gamma buttons.""" - - bl_label = "Image Gamma" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw_header(self, context): - tex = context.texture - - self.layout.prop( - tex.pov, "tex_gamma_enable", text="", icon='SEQ_LUMA_WAVEFORM' - ) - - def draw(self, context): - layout = self.layout - - tex = context.texture - - layout.active = tex.pov.tex_gamma_enable - layout.prop(tex.pov, "tex_gamma_value", text="Gamma Value") - - -# commented out below UI for texture only custom code inside exported material: -# class TEXTURE_PT_povray_replacement_text(TextureButtonsPanel, Panel): -# bl_label = "Custom POV Code" -# COMPAT_ENGINES = {'POVRAY_RENDER'} - -# def draw(self, context): -# layout = self.layout - -# tex = context.texture - -# col = layout.column() -# col.label(text="Replace properties with:") -# col.prop(tex.pov, "replacement_text", text="") - - -class OBJECT_PT_POV_obj_parameters(ObjectButtonsPanel, Panel): - """Use this class to define pov specific object level options buttons.""" - - bl_label = "POV" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - - engine = context.scene.render.engine - return engine in cls.COMPAT_ENGINES - - def draw(self, context): - layout = self.layout - - obj = context.object - - split = layout.split() - - col = split.column(align=True) - - col.label(text="Radiosity:") - col.prop(obj.pov, "importance_value", text="Importance") - col.label(text="Photons:") - col.prop(obj.pov, "collect_photons", text="Receive Photon Caustics") - if obj.pov.collect_photons: - col.prop( - obj.pov, "spacing_multiplier", text="Photons Spacing Multiplier" - ) - - split = layout.split() - - col = split.column() - col.prop(obj.pov, "hollow") - col.prop(obj.pov, "double_illuminate") - - if obj.type == 'META' or obj.pov.curveshape == 'lathe': - # if obj.pov.curveshape == 'sor' - col.prop(obj.pov, "sturm") - col.prop(obj.pov, "no_shadow") - col.prop(obj.pov, "no_image") - col.prop(obj.pov, "no_reflection") - col.prop(obj.pov, "no_radiosity") - col.prop(obj.pov, "inverse") - col.prop(obj.pov, "hierarchy") - # col.prop(obj.pov,"boundorclip",text="Bound / Clip") - # if obj.pov.boundorclip != "none": - # col.prop_search(obj.pov,"boundorclipob",context.blend_data,"objects",text="Object") - # text = "Clipped by" - # if obj.pov.boundorclip == "clipped_by": - # text = "Bounded by" - # col.prop(obj.pov,"addboundorclip",text=text) - - -class OBJECT_PT_POV_obj_sphere(PovDataButtonsPanel, Panel): - """Use this class to define pov sphere primitive parameters buttons.""" - - bl_label = "POV Sphere" - 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 == 'SPHERE' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'SPHERE': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label(text="Sphere radius: " + str(obj.pov.sphere_radius)) - - 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.sphere_update", text="Update", icon="SHADING_RENDERED" - ) - - # col.label(text="Parameters:") - col.prop(obj.pov, "sphere_radius", text="Radius of Sphere") - - -class OBJECT_PT_POV_obj_cylinder(PovDataButtonsPanel, Panel): - """Use this class to define pov cylinder primitive parameters buttons.""" - - bl_label = "POV Cylinder" - 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 == 'CYLINDER' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'CYLINDER': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label( - text="Cylinder radius: " + str(obj.pov.cylinder_radius) - ) - col.label( - text="Cylinder cap location: " - + str(obj.pov.cylinder_location_cap) - ) - - 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.cylinder_update", text="Update", icon="MESH_CYLINDER" - ) - - # col.label(text="Parameters:") - col.prop(obj.pov, "cylinder_radius") - col.prop(obj.pov, "cylinder_location_cap") - - -class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel): - """Use this class to define pov cone primitive parameters buttons.""" - - bl_label = "POV Cone" - 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 == 'CONE' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'CONE': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label( - text="Cone base radius: " + str(obj.pov.cone_base_radius) - ) - col.label( - text="Cone cap radius: " + str(obj.pov.cone_cap_radius) - ) - col.label( - text="Cone proxy segments: " + str(obj.pov.cone_segments) - ) - col.label(text="Cone height: " + str(obj.pov.cone_height)) - 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.pov, "cone_base_radius", text="Radius of Cone Base" - ) - col.prop(obj.pov, "cone_cap_radius", text="Radius of Cone Cap") - col.prop( - obj.pov, "cone_segments", text="Segmentation of Cone proxy" - ) - col.prop(obj.pov, "cone_height", text="Height of the cone") - - -class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel): - """Use this class to define pov superellipsoid primitive parameters buttons.""" - - bl_label = "POV Superquadric ellipsoid" - 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 == 'SUPERELLIPSOID' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'SUPERELLIPSOID': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label(text="Radial segmentation: " + str(obj.pov.se_u)) - col.label(text="Lateral segmentation: " + str(obj.pov.se_v)) - col.label(text="Ring shape: " + str(obj.pov.se_n1)) - col.label(text="Cross-section shape: " + str(obj.pov.se_n2)) - col.label(text="Fill up and down: " + str(obj.pov.se_edit)) - 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.superellipsoid_update", - text="Update", - icon="MOD_SUBSURF", - ) - - # col.label(text="Parameters:") - col.prop(obj.pov, "se_u") - col.prop(obj.pov, "se_v") - col.prop(obj.pov, "se_n1") - col.prop(obj.pov, "se_n2") - col.prop(obj.pov, "se_edit") - - -class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel): - """Use this class to define pov torus primitive parameters buttons.""" - - bl_label = "POV Torus" - 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 == 'TORUS' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'TORUS': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label( - text="Torus major radius: " - + str(obj.pov.torus_major_radius) - ) - col.label( - text="Torus minor radius: " - + str(obj.pov.torus_minor_radius) - ) - col.label( - text="Torus major segments: " - + str(obj.pov.torus_major_segments) - ) - col.label( - text="Torus minor segments: " - + str(obj.pov.torus_minor_segments) - ) - 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.torus_update", text="Update", icon="MESH_TORUS" - ) - - # col.label(text="Parameters:") - col.prop(obj.pov, "torus_major_radius") - col.prop(obj.pov, "torus_minor_radius") - col.prop(obj.pov, "torus_major_segments") - col.prop(obj.pov, "torus_minor_segments") - - -class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel): - """Use this class to define pov supertorus primitive parameters buttons.""" - - bl_label = "POV SuperTorus" - 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 == 'SUPERTORUS' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'SUPERTORUS': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label( - text="SuperTorus major radius: " - + str(obj.pov.st_major_radius) - ) - col.label( - text="SuperTorus minor radius: " - + str(obj.pov.st_minor_radius) - ) - col.label( - text="SuperTorus major segments: " + str(obj.pov.st_u) - ) - col.label( - text="SuperTorus minor segments: " + str(obj.pov.st_v) - ) - - col.label( - text="SuperTorus Ring Manipulator: " + str(obj.pov.st_ring) - ) - col.label( - text="SuperTorus Cross Manipulator: " - + str(obj.pov.st_cross) - ) - col.label( - text="SuperTorus Internal And External radii: " - + str(obj.pov.st_ie) - ) - - col.label( - text="SuperTorus accuracy: " + str(ob.pov.st_accuracy) - ) - col.label( - text="SuperTorus max gradient: " - + str(ob.pov.st_max_gradient) - ) - - 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.supertorus_update", text="Update", icon="MESH_TORUS" - ) - - # col.label(text="Parameters:") - col.prop(obj.pov, "st_major_radius") - col.prop(obj.pov, "st_minor_radius") - col.prop(obj.pov, "st_u") - col.prop(obj.pov, "st_v") - col.prop(obj.pov, "st_ring") - col.prop(obj.pov, "st_cross") - col.prop(obj.pov, "st_ie") - # col.prop(obj.pov, "st_edit") #? - col.prop(obj.pov, "st_accuracy") - col.prop(obj.pov, "st_max_gradient") - - -class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel): - """Use this class to define pov parametric surface primitive parameters buttons.""" - - bl_label = "POV Parametric surface" - 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 == 'PARAMETRIC' - and (engine in cls.COMPAT_ENGINES) - ) - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - - if obj.pov.object_as == 'PARAMETRIC': - if obj.pov.unlock_parameters == False: - col.prop( - obj.pov, - "unlock_parameters", - text="Exported parameters below", - icon='LOCKED', - ) - col.label(text="Minimum U: " + str(obj.pov.u_min)) - col.label(text="Minimum V: " + str(obj.pov.v_min)) - col.label(text="Maximum U: " + str(obj.pov.u_max)) - col.label(text="Minimum V: " + str(obj.pov.v_min)) - col.label(text="X Function: " + str(obj.pov.x_eq)) - col.label(text="Y Function: " + str(obj.pov.y_eq)) - col.label(text="Z Function: " + str(obj.pov.x_eq)) - - 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.parametric_update", text="Update", icon="SCRIPTPLUGINS" - ) - - col.prop(obj.pov, "u_min", text="Minimum U") - col.prop(obj.pov, "v_min", text="Minimum V") - col.prop(obj.pov, "u_max", text="Maximum U") - col.prop(obj.pov, "v_max", text="Minimum V") - col.prop(obj.pov, "x_eq", text="X Function") - col.prop(obj.pov, "y_eq", text="Y Function") - col.prop(obj.pov, "z_eq", text="Z Function") - - -class OBJECT_PT_povray_replacement_text(ObjectButtonsPanel, Panel): - """Use this class to define pov object replacement field.""" - - bl_label = "Custom POV Code" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - layout = self.layout - - obj = context.object - - col = layout.column() - col.label(text="Replace properties with:") - col.prop(obj.pov, "replacement_text", text="") - - -############################################################################### -# Add Povray Objects -############################################################################### - - -class VIEW_MT_POV_primitives_add(Menu): - """Define the primitives menu with presets""" - - bl_idname = "VIEW_MT_POV_primitives_add" - bl_label = "Povray" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - engine = context.scene.render.engine - return engine == 'POVRAY_RENDER' - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.menu( - VIEW_MT_POV_Basic_Shapes.bl_idname, text="Primitives", icon="GROUP" - ) - layout.menu(VIEW_MT_POV_import.bl_idname, text="Import", icon="IMPORT") - - -class VIEW_MT_POV_Basic_Shapes(Menu): - """Use this class to sort simple primitives menu entries.""" - - bl_idname = "POVRAY_MT_basic_shape_tools" - bl_label = "Basic_shapes" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator( - "pov.addplane", text="Infinite Plane", icon='MESH_PLANE' - ) - layout.operator("pov.addbox", text="Box", icon='MESH_CUBE') - layout.operator("pov.addsphere", text="Sphere", icon='SHADING_RENDERED') - layout.operator( - "pov.addcylinder", text="Cylinder", icon="MESH_CYLINDER" - ) - layout.operator("pov.cone_add", text="Cone", icon="MESH_CONE") - layout.operator("pov.addtorus", text="Torus", icon='MESH_TORUS') - layout.separator() - layout.operator("pov.addrainbow", text="Rainbow", icon="COLOR") - layout.operator("pov.addlathe", text="Lathe", icon='MOD_SCREW') - layout.operator("pov.addprism", text="Prism", icon='MOD_SOLIDIFY') - layout.operator( - "pov.addsuperellipsoid", - text="Superquadric Ellipsoid", - icon='MOD_SUBSURF', - ) - layout.operator( - "pov.addheightfield", text="Height Field", icon="RNDCURVE" - ) - layout.operator( - "pov.addspheresweep", text="Sphere Sweep", icon='FORCE_CURVE' - ) - layout.separator() - layout.operator( - "pov.addblobsphere", text="Blob Sphere", icon='META_DATA' - ) - layout.separator() - layout.label(text="Isosurfaces") - layout.operator( - "pov.addisosurfacebox", text="Isosurface Box", icon="META_CUBE" - ) - layout.operator( - "pov.addisosurfacesphere", - text="Isosurface Sphere", - icon="META_BALL", - ) - layout.operator( - "pov.addsupertorus", text="Supertorus", icon="SURFACE_NTORUS" - ) - layout.separator() - layout.label(text="Macro based") - layout.operator( - "pov.addpolygontocircle", - text="Polygon To Circle Blending", - icon="MOD_CAST", - ) - layout.operator("pov.addloft", text="Loft", icon="SURFACE_NSURFACE") - layout.separator() - # Warning if the Add Advanced Objects addon containing - # Add mesh extra objects is not enabled - if not check_add_mesh_extra_objects(): - # col = box.column() - layout.label( - text="Please enable Add Mesh: Extra Objects addon", icon="INFO" - ) - # layout.separator() - layout.operator( - "preferences.addon_show", - text="Go to Add Mesh: Extra Objects addon", - icon="PREFERENCES", - ).module = "add_mesh_extra_objects" - - # layout.separator() - return - else: - layout.operator( - "pov.addparametric", text="Parametric", icon='SCRIPTPLUGINS' - ) - - -class VIEW_MT_POV_import(Menu): - """Use this class for the import menu.""" - - bl_idname = "POVRAY_MT_import_tools" - bl_label = "Import" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES") - - -def menu_func_add(self, context): - engine = context.scene.render.engine - if engine == 'POVRAY_RENDER': - self.layout.menu("VIEW_MT_POV_primitives_add", icon="PLUGIN") - - -def menu_func_import(self, context): - engine = context.scene.render.engine - if engine == 'POVRAY_RENDER': - self.layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES") - - -##############Nodes - -# def find_node_input(node, name): -# for input in node.inputs: -# if input.name == name: -# return input - -# def panel_node_draw(layout, id_data, output_type, input_name): -# if not id_data.use_nodes: -# #layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE') -# #layout.operator("pov.use_shading_nodes", icon='NODETREE') -# layout.operator("WM_OT_context_toggle", icon='NODETREE').data_path = \ -# "material.pov.material_use_nodes" -# return False - -# ntree = id_data.node_tree - -# node = find_node(id_data, output_type) -# if not node: -# layout.label(text="No output node") -# else: -# input = find_node_input(node, input_name) -# layout.template_node_view(ntree, node, input) - -# return True - - -class NODE_MT_POV_map_create(Menu): - """Create maps""" - - bl_idname = "POVRAY_MT_node_map_create" - bl_label = "Create map" - - def draw(self, context): - layout = self.layout - layout.operator("node.map_create") - - -def menu_func_nodes(self, context): - ob = context.object - if hasattr(ob, 'active_material'): - mat = context.object.active_material - if mat and context.space_data.tree_type == 'ObjectNodeTree': - self.layout.prop(mat.pov, "material_use_nodes") - self.layout.menu(NODE_MT_POV_map_create.bl_idname) - self.layout.operator("wm.updatepreviewkey") - if ( - hasattr(mat, 'active_texture') - and context.scene.render.engine == 'POVRAY_RENDER' - ): - tex = mat.active_texture - if tex and context.space_data.tree_type == 'TextureNodeTree': - self.layout.prop(tex.pov, "texture_use_nodes") - - -############################################################################### -# Camera Povray 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="") - - -############################################################################### -# Text Povray Settings -############################################################################### - - -class TEXT_OT_POV_insert(Operator): - """Use this class to create blender text editor operator to insert pov snippets like other pov IDEs""" - - bl_idname = "text.povray_insert" - bl_label = "Insert" - - filepath: bpy.props.StringProperty(name="Filepath", subtype='FILE_PATH') - - @classmethod - def poll(cls, context): - # context.area.type == 'TEXT_EDITOR' - return bpy.ops.text.insert.poll() - - def execute(self, context): - if self.filepath and isfile(self.filepath): - file = open(self.filepath, "r") - bpy.ops.text.insert(text=file.read()) - - # places the cursor at the end without scrolling -.- - # context.space_data.text.write(file.read()) - file.close() - return {'FINISHED'} - - -def validinsert(ext): - return ext in {".txt", ".inc", ".pov"} - - -class TEXT_MT_POV_insert(Menu): - """Use this class to create a menu launcher in text editor for the TEXT_OT_POV_insert operator .""" - - bl_label = "Insert" - bl_idname = "TEXT_MT_POV_insert" - - def draw(self, context): - pov_documents = locate_docpath() - prop = self.layout.operator( - "wm.path_open", text="Open folder", icon='FILE_FOLDER' - ) - prop.filepath = pov_documents - self.layout.separator() - - list = [] - for root, dirs, files in os.walk(pov_documents): - list.append(root) - print(list) - self.path_menu( - list, - "text.povray_insert", - # {"internal": True}, - filter_ext=validinsert, - ) - - -class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel): - """Use this class to create a panel in text editor for the user to decide if he renders text only or adds to 3d scene.""" - - bl_label = "POV" - COMPAT_ENGINES = {'POVRAY_RENDER'} - - def draw(self, context): - layout = self.layout - - text = context.space_data.text - - pov_documents = locate_docpath() - if not pov_documents: - layout.label(text="Please configure ", icon="INFO") - layout.label(text="default pov include path ") - layout.label(text="in addon preferences") - # layout.separator() - layout.operator( - "preferences.addon_show", - text="Go to Render: Persistence of Vision addon", - icon="PREFERENCES", - ).module = "render_povray" - - # layout.separator() - else: - # print(pov_documents) - layout.menu(TEXT_MT_POV_insert.bl_idname) - - if text: - box = layout.box() - box.label(text='Source to render:', icon='RENDER_STILL') - row = box.row() - row.prop(text.pov, "custom_code", expand=True) - if text.pov.custom_code in {'3dview'}: - box.operator("render.render", icon='OUTLINER_DATA_ARMATURE') - if text.pov.custom_code in {'text'}: - rtext = bpy.context.space_data.text - box.operator("text.run", icon='ARMATURE_DATA') - # layout.prop(text.pov, "custom_code") - elif text.pov.custom_code in {'both'}: - box.operator("render.render", icon='POSE_HLT') - layout.label(text="Please specify declared", icon="INFO") - layout.label(text="items in properties ") - # layout.label(text="") - layout.label(text="replacement fields") - - -############################################### -# Text editor templates from header menu - - -class TEXT_MT_POV_templates(Menu): - """Use this class to create a menu for the same pov templates scenes as other pov IDEs.""" - - bl_label = "POV" - - # We list templates on file evaluation, we can assume they are static data, - # and better avoid running this on every draw call. - import os - - template_paths = [os.path.join(os.path.dirname(__file__), "templates_pov")] - - def draw(self, context): - self.path_menu( - self.template_paths, "text.open", props_default={"internal": True} - ) - - -def menu_func_templates(self, context): - # Do not depend on POV being active renderer here... - self.layout.menu("TEXT_MT_POV_templates") - -############################################################################### -# Freestyle -############################################################################### -#import addon_utils -#addon_utils.paths()[0] -#addon_utils.modules() -#mod.bl_info['name'] == 'Freestyle SVG Exporter': -bpy.utils.script_paths("addons") -#render_freestyle_svg = os.path.join(bpy.utils.script_paths("addons"), "render_freestyle_svg.py") - -render_freestyle_svg = bpy.context.preferences.addons.get('render_freestyle_svg') - #mpath=addon_utils.paths()[0].render_freestyle_svg - #import mpath - #from mpath import render_freestyle_svg #= addon_utils.modules(['Freestyle SVG Exporter']) - #from scripts\\addons import render_freestyle_svg -if check_render_freestyle_svg(): - ''' - snippetsWIP - import myscript - import importlib - - importlib.reload(myscript) - myscript.main() - ''' - for member in dir(render_freestyle_svg): - subclass = getattr(render_freestyle_svg, member) - try: - subclass.COMPAT_ENGINES.add('POVRAY_RENDER') - if subclass.bl_idname == "RENDER_PT_SVGExporterPanel": - subclass.bl_parent_id = "RENDER_PT_POV_filter" - subclass.bl_options = {'HIDE_HEADER'} - #subclass.bl_order = 11 - print(subclass.bl_info) - except: - pass - - #del render_freestyle_svg.RENDER_PT_SVGExporterPanel.bl_parent_id - - -class RENDER_PT_POV_filter(RenderButtonsPanel, Panel): - """Use this class to invoke stuff like Freestyle UI.""" - - bl_label = "Freestyle" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'POVRAY_RENDER'} - - @classmethod - def poll(cls, context): - with_freestyle = bpy.app.build_options.freestyle - engine = context.scene.render.engine - return(with_freestyle and engine == 'POVRAY_RENDER') - def draw_header(self, context): - - #scene = context.scene - rd = context.scene.render - layout = self.layout - - if rd.use_freestyle: - layout.prop( - rd, "use_freestyle", text="", icon='LINE_DATA' - ) - - else: - layout.prop( - rd, "use_freestyle", text="", icon='OUTLINER_OB_IMAGE' - ) - - def draw(self, context): - rd = context.scene.render - layout = self.layout - layout.active = rd.use_freestyle - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - flow = layout.grid_flow( - row_major=True, - columns=0, - even_columns=True, - even_rows=False, - align=True, - ) - - flow.prop(rd, "line_thickness_mode", expand=True) - - if rd.line_thickness_mode == 'ABSOLUTE': - flow.prop(rd, "line_thickness") - - # Warning if the Freestyle SVG Exporter addon is not enabled - if not check_render_freestyle_svg(): - # col = box.column() - layout.label( - text="Please enable Freestyle SVG Exporter addon", icon="INFO" - ) - # layout.separator() - layout.operator( - "preferences.addon_show", - text="Go to Render: Freestyle SVG Exporter addon", - icon="PREFERENCES", - ).module = "render_freestyle_svg" - -classes = ( - WORLD_PT_POV_world, - WORLD_MT_POV_presets, - WORLD_OT_POV_add_preset, - WORLD_TEXTURE_SLOTS_UL_POV_layerlist, - #WORLD_TEXTURE_SLOTS_UL_List, - WORLD_PT_POV_mist, - # RenderButtonsPanel, - # ModifierButtonsPanel, - # MaterialButtonsPanel, - # TextureButtonsPanel, - # ObjectButtonsPanel, - # CameraDataButtonsPanel, - # WorldButtonsPanel, - # TextButtonsPanel, - # PovDataButtonsPanel, - DATA_PT_POV_normals, - DATA_PT_POV_texture_space, - DATA_PT_POV_vertex_groups, - DATA_PT_POV_shape_keys, - DATA_PT_POV_uv_texture, - DATA_PT_POV_vertex_colors, - DATA_PT_POV_customdata, - # PovLampButtonsPanel, - LIGHT_PT_POV_preview, - LIGHT_PT_POV_light, - LIGHT_MT_POV_presets, - LIGHT_OT_POV_add_preset, - OBJECT_PT_POV_rainbow, - RENDER_PT_POV_export_settings, - RENDER_PT_POV_render_settings, - RENDER_PT_POV_photons, - RENDER_PT_POV_antialias, - RENDER_PT_POV_radiosity, - RENDER_PT_POV_filter, - POV_RADIOSITY_MT_presets, - RENDER_OT_POV_radiosity_add_preset, - RENDER_PT_POV_media, - MODIFIERS_PT_POV_modifiers, - MATERIAL_PT_POV_sss, - MATERIAL_MT_POV_sss_presets, - MATERIAL_OT_POV_sss_add_preset, - MATERIAL_PT_strand, - MATERIAL_PT_POV_activate_node, - MATERIAL_PT_POV_active_node, - MATERIAL_PT_POV_specular, - MATERIAL_PT_POV_mirror, - MATERIAL_PT_POV_transp, - MATERIAL_PT_POV_reflection, - # MATERIAL_PT_POV_interior, - MATERIAL_PT_POV_fade_color, - MATERIAL_PT_POV_caustics, - MATERIAL_PT_POV_replacement_text, - TEXTURE_MT_POV_specials, - TEXTURE_PT_POV_context_texture, - TEXTURE_PT_POV_type, - TEXTURE_PT_POV_preview, - TEXTURE_PT_POV_parameters, - TEXTURE_PT_POV_tex_gamma, - OBJECT_PT_POV_obj_parameters, - OBJECT_PT_POV_obj_sphere, - OBJECT_PT_POV_obj_cylinder, - OBJECT_PT_POV_obj_cone, - OBJECT_PT_POV_obj_superellipsoid, - OBJECT_PT_POV_obj_torus, - OBJECT_PT_POV_obj_supertorus, - OBJECT_PT_POV_obj_parametric, - OBJECT_PT_povray_replacement_text, - VIEW_MT_POV_primitives_add, - VIEW_MT_POV_Basic_Shapes, - VIEW_MT_POV_import, - NODE_MT_POV_map_create, - CAMERA_PT_POV_cam_dof, - CAMERA_PT_POV_cam_nor, - CAMERA_PT_POV_replacement_text, - TEXT_OT_POV_insert, - TEXT_MT_POV_insert, - TEXT_PT_POV_custom_code, - TEXT_MT_POV_templates, - #TEXTURE_PT_POV_povray_texture_slots, - #TEXTURE_UL_POV_texture_slots, - MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist, - TEXTURE_OT_POV_texture_slot_add, - TEXTURE_OT_POV_texture_slot_remove, - TEXTURE_PT_POV_influence, - TEXTURE_PT_POV_mapping, -) - - -def register(): - # from bpy.utils import register_class - - for cls in classes: - register_class(cls) - - bpy.types.VIEW3D_MT_add.prepend(menu_func_add) - bpy.types.TOPBAR_MT_file_import.append(menu_func_import) - bpy.types.TEXT_MT_templates.append(menu_func_templates) - bpy.types.RENDER_PT_POV_radiosity.prepend(rad_panel_func) - bpy.types.LIGHT_PT_POV_light.prepend(light_panel_func) - # bpy.types.WORLD_PT_POV_world.prepend(world_panel_func) - # was used for parametric objects but made the other addon unreachable on - # unregister for other tools to use created a user action call instead - # addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True) - # bpy.types.TEXTURE_PT_context_texture.prepend(TEXTURE_PT_POV_type) - - if not povCentricWorkspace in bpy.app.handlers.load_post: - # print("Adding POV wentric workspace on load handlers list") - bpy.app.handlers.load_post.append(povCentricWorkspace) - -def unregister(): - if povCentricWorkspace in bpy.app.handlers.load_post: - # print("Removing POV wentric workspace from load handlers list") - bpy.app.handlers.load_post.remove(povCentricWorkspace) - - # from bpy.utils import unregister_class - - # bpy.types.TEXTURE_PT_context_texture.remove(TEXTURE_PT_POV_type) - # addon_utils.disable("add_mesh_extra_objects", default_set=False) - # bpy.types.WORLD_PT_POV_world.remove(world_panel_func) - bpy.types.LIGHT_PT_POV_light.remove(light_panel_func) - bpy.types.RENDER_PT_POV_radiosity.remove(rad_panel_func) - bpy.types.TEXT_MT_templates.remove(menu_func_templates) - bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) - bpy.types.VIEW3D_MT_add.remove(menu_func_add) - - for cls in reversed(classes): - if cls != TEXTURE_PT_context: - unregister_class(cls) diff --git a/render_povray/update_files.py b/render_povray/update_files.py old mode 100644 new mode 100755 index 416e40d37..985f84684 --- a/render_povray/update_files.py +++ b/render_povray/update_files.py @@ -33,8 +33,22 @@ from bpy.props import ( EnumProperty, ) +# Todo: +# *update this file to just cover 2.79 to 3.xx and ui it from a Blender internal to pov menu +# *as well as update from older pov > switch to QMC when pov 3.8 is out ? +# *filter if possible files built in pre 2.79 versions. tell user their file is too old and may +# be salvaged from older vesion of this operator from within latest stable blender 2.79 version. +# else if bpy.app.version[0] == 2 and bpy.app.version[1] <= 92 and and bpy.app.version[1] >= 79: +# warn users to update blender to 3.xx for creating their newer files then try to salvage +# using this script +# +# if bpy.app.version[0] >= 3: # just test file created a)there or b)before, a) do nothing +# "your version is relatively futureproof" > doing nothing +# b)"use this operator to salvage your blends from latest stable 2.79" + def update2_0_0_9(): + """Update properties from older Blender versions. The render API changed a lot up to 2.79.""" # Temporally register old props, so we can access their values. register() @@ -109,26 +123,16 @@ def update2_0_0_9(): "pov_refraction_type", "pov_replacement_text", ]: - old_mat_props[k] = getattr(bpy.types.Material, k)[1].get( - 'default', None - ) + old_mat_props[k] = getattr(bpy.types.Material, k)[1].get('default', None) # Get default values of pov texture props. old_tex_props = {} - for k in [ - "pov_tex_gamma_enable", - "pov_tex_gamma_value", - "pov_replacement_text", - ]: + for k in ["pov_tex_gamma_enable", "pov_tex_gamma_value", "pov_replacement_text"]: old_tex_props[k] = getattr(bpy.types.Texture, k)[1].get('default', None) # Get default values of pov object props. old_obj_props = {} - for k in [ - "pov_importance_value", - "pov_collect_photons", - "pov_replacement_text", - ]: + for k in ["pov_importance_value", "pov_collect_photons", "pov_replacement_text"]: old_obj_props[k] = getattr(bpy.types.Object, k)[1].get('default', None) # Get default values of pov camera props. @@ -189,7 +193,7 @@ def update2_0_0_9(): class RenderCopySettings(bpy.types.Operator): - """Update old POV properties to new ones""" + """Update old POV properties to new ones.""" bl_idname = "scene.pov_update_properties" bl_label = "PovRay render: Update to script v0.0.9" @@ -253,19 +257,13 @@ def register(): # Not a real pov option, just to know if we should write Scene.pov_radio_enable = BoolProperty( - name="Enable Radiosity", - description="Enable POV-Rays radiosity calculation", - default=False, + name="Enable Radiosity", description="Enable POV-Rays radiosity calculation", default=False ) Scene.pov_radio_display_advanced = BoolProperty( - name="Advanced Options", - description="Show advanced options", - default=False, + name="Advanced Options", description="Show advanced options", default=False ) Scene.pov_media_enable = BoolProperty( - name="Enable Media", - description="Enable POV-Rays atmospheric media", - default=False, + name="Enable Media", description="Enable POV-Rays atmospheric media", default=False ) Scene.pov_media_samples = IntProperty( name="Samples", @@ -288,9 +286,7 @@ def register(): ) Scene.pov_baking_enable = BoolProperty( - name="Enable Baking", - description="Enable POV-Rays texture baking", - default=False, + name="Enable Baking", description="Enable POV-Rays texture baking", default=False ) Scene.pov_indentation_character = EnumProperty( name="Indentation", @@ -311,9 +307,7 @@ def register(): ) Scene.pov_comments_enable = BoolProperty( - name="Enable Comments", - description="Add comments to pov file", - default=True, + name="Enable Comments", description="Add comments to pov file", default=True ) # Real pov options @@ -339,11 +333,7 @@ def register(): ) Scene.pov_antialias_depth = IntProperty( - name="Antialias Depth", - description="Depth of pixel for sampling", - min=1, - max=9, - default=3, + name="Antialias Depth", description="Depth of pixel for sampling", min=1, max=9, default=3 ) Scene.pov_antialias_threshold = FloatProperty( @@ -504,9 +494,7 @@ def register(): # max_sample - not available yet Scene.pov_radio_media = BoolProperty( - name="Media", - description="Radiosity estimation can be affected by media", - default=False, + name="Media", description="Radiosity estimation can be affected by media", default=False ) Scene.pov_radio_minimum_reuse = FloatProperty( @@ -529,9 +517,7 @@ def register(): ) Scene.pov_radio_normal = BoolProperty( - name="Normals", - description="Radiosity estimation can be affected by normals", - default=False, + name="Normals", description="Radiosity estimation can be affected by normals", default=False ) Scene.pov_radio_recursion_limit = IntProperty( @@ -638,9 +624,7 @@ def register(): ) Mat.pov_fake_caustics = BoolProperty( - name="Fake Caustics", - description="use only (Fast) fake refractive caustics", - default=True, + name="Fake Caustics", description="use only (Fast) fake refractive caustics", default=True ) Mat.pov_fake_caustics_power = FloatProperty( @@ -654,9 +638,7 @@ def register(): ) Mat.pov_photons_refraction = BoolProperty( - name="Refractive Photon Caustics", - description="more physically correct", - default=False, + name="Refractive Photon Caustics", description="more physically correct", default=False ) Mat.pov_photons_dispersion = FloatProperty( @@ -752,9 +734,7 @@ def register(): # DOF Toggle Cam.pov_dof_enable = BoolProperty( - name="Depth Of Field", - description="Enable POV-Ray Depth Of Field ", - default=True, + name="Depth Of Field", description="Enable POV-Ray Depth Of Field ", default=True ) # Aperture (Intensity of the Blur) @@ -816,12 +796,12 @@ def unregister(): Obj = bpy.types.Object Cam = bpy.types.Camera Text = bpy.types.Text - del Scene.pov_tempfiles_enable # CR - del Scene.pov_scene_name # CR - del Scene.pov_deletefiles_enable # CR - del Scene.pov_scene_path # CR - del Scene.pov_renderimage_path # CR - del Scene.pov_list_lf_enable # CR + del Scene.pov_tempfiles_enable + del Scene.pov_scene_name + del Scene.pov_deletefiles_enable + del Scene.pov_scene_path + del Scene.pov_renderimage_path + del Scene.pov_list_lf_enable del Scene.pov_radio_enable del Scene.pov_radio_display_advanced del Scene.pov_radio_adc_bailout @@ -836,56 +816,56 @@ def unregister(): del Scene.pov_radio_nearest_count del Scene.pov_radio_normal del Scene.pov_radio_recursion_limit - del Scene.pov_radio_pretrace_start # MR - del Scene.pov_radio_pretrace_end # MR - del Scene.pov_media_enable # MR - del Scene.pov_media_samples # MR - del Scene.pov_media_color # MR - del Scene.pov_baking_enable # MR - del Scene.pov_max_trace_level # MR - del Scene.pov_photon_spacing # MR - del Scene.pov_photon_max_trace_level # MR - del Scene.pov_photon_adc_bailout # MR - del Scene.pov_photon_gather_min # MR - del Scene.pov_photon_gather_max # MR - del Scene.pov_antialias_enable # CR - del Scene.pov_antialias_method # CR - del Scene.pov_antialias_depth # CR - del Scene.pov_antialias_threshold # CR - del Scene.pov_antialias_gamma # CR - del Scene.pov_jitter_enable # CR - del Scene.pov_jitter_amount # CR - del Scene.pov_command_line_switches # CR - del Scene.pov_indentation_character # CR - del Scene.pov_indentation_spaces # CR - del Scene.pov_comments_enable # CR - del Mat.pov_irid_enable # MR - del Mat.pov_mirror_use_IOR # MR - del Mat.pov_mirror_metallic # MR - del Mat.pov_conserve_energy # MR - del Mat.pov_irid_amount # MR - del Mat.pov_irid_thickness # MR - del Mat.pov_irid_turbulence # MR - del Mat.pov_interior_fade_color # MR - del Mat.pov_caustics_enable # MR - del Mat.pov_fake_caustics # MR - del Mat.pov_fake_caustics_power # MR - del Mat.pov_photons_refraction # MR - del Mat.pov_photons_dispersion # MR - del Mat.pov_photons_reflection # MR - del Mat.pov_refraction_type # MR - del Mat.pov_replacement_text # MR - del Tex.pov_tex_gamma_enable # MR - del Tex.pov_tex_gamma_value # MR - del Tex.pov_replacement_text # MR - del Obj.pov_importance_value # MR - del Obj.pov_collect_photons # MR - del Obj.pov_replacement_text # MR - del Cam.pov_dof_enable # MR - del Cam.pov_dof_aperture # MR - del Cam.pov_dof_samples_min # MR - del Cam.pov_dof_samples_max # MR - del Cam.pov_dof_variance # MR - del Cam.pov_dof_confidence # MR - del Cam.pov_replacement_text # MR - del Text.pov_custom_code # MR + del Scene.pov_radio_pretrace_start + del Scene.pov_radio_pretrace_end + del Scene.pov_media_enable + del Scene.pov_media_samples + del Scene.pov_media_color + del Scene.pov_baking_enable + del Scene.pov_max_trace_level + del Scene.pov_photon_spacing + del Scene.pov_photon_max_trace_level + del Scene.pov_photon_adc_bailout + del Scene.pov_photon_gather_min + del Scene.pov_photon_gather_max + del Scene.pov_antialias_enable + del Scene.pov_antialias_method + del Scene.pov_antialias_depth + del Scene.pov_antialias_threshold + del Scene.pov_antialias_gamma + del Scene.pov_jitter_enable + del Scene.pov_jitter_amount + del Scene.pov_command_line_switches + del Scene.pov_indentation_character + del Scene.pov_indentation_spaces + del Scene.pov_comments_enable + del Mat.pov_irid_enable + del Mat.pov_mirror_use_IOR + del Mat.pov_mirror_metallic + del Mat.pov_conserve_energy + del Mat.pov_irid_amount + del Mat.pov_irid_thickness + del Mat.pov_irid_turbulence + del Mat.pov_interior_fade_color + del Mat.pov_caustics_enable + del Mat.pov_fake_caustics + del Mat.pov_fake_caustics_power + del Mat.pov_photons_refraction + del Mat.pov_photons_dispersion + del Mat.pov_photons_reflection + del Mat.pov_refraction_type + del Mat.pov_replacement_text + del Tex.pov_tex_gamma_enable + del Tex.pov_tex_gamma_value + del Tex.pov_replacement_text + del Obj.pov_importance_value + del Obj.pov_collect_photons + del Obj.pov_replacement_text + del Cam.pov_dof_enable + del Cam.pov_dof_aperture + del Cam.pov_dof_samples_min + del Cam.pov_dof_samples_max + del Cam.pov_dof_variance + del Cam.pov_dof_confidence + del Cam.pov_replacement_text + del Text.pov_custom_code -- GitLab