diff options
Diffstat (limited to 'render_povray')
46 files changed, 2648 insertions, 1823 deletions
diff --git a/render_povray/__init__.py b/render_povray/__init__.py index fe0de21f..47291895 100755 --- a/render_povray/__init__.py +++ b/render_povray/__init__.py @@ -33,8 +33,8 @@ base_ui.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_gui.py : + Display cam/light/environment properties from scenography_properties.py for user to change them scenography.py Translate cam/light/environment properties to corresponding pov features @@ -57,25 +57,25 @@ object_particles.py : object_gui.py : Display properties from object_properties.py for user to change them -shading_properties.py +shading_properties.py : Initialize properties for translating Blender materials parameters to pov -shading_nodes.py +shading_nodes.py : Translate node trees to the pov file -shading_gui.py +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 -texturing_properties.py +texturing_properties.py : Initialize properties for translating Blender materials /world... texture influences to pov -texturing_gui.py +texturing_gui.py : Display properties from texturing_properties.py for user to change them -texturing.py +texturing.py : Translate blender texture influences into POV render_properties.py : @@ -96,10 +96,10 @@ scripting_gui.py : scripting.py : Insert POV native scene description elements into blender scene or to exported POV file -df3_library.py +df3_library.py : Render smoke to *.df3 files -update_files.py +update_files.py : Update new variables to values from older API. This file needs an update @@ -151,8 +151,8 @@ Blender stand up to other POV IDEs such as povwin or QTPOV bl_info = { - "name": "Persistence of Vision", - "author": "Campbell Barton, " + 'name': "Persistence of Vision", + 'author': "Campbell Barton, " "Maurice Raybaud, " "Leonid Desyatkov, " "Bastien Montagne, " @@ -160,12 +160,12 @@ bl_info = { "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": "Co-maintainers welcome", + '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': "Co-maintainers welcome", } # Other occasional contributors, more or less in chronological order: @@ -222,7 +222,7 @@ else: class POV_OT_update_addon(bpy.types.Operator): - """Update this add-on to the latest version""" + """Update this addon to the latest version""" bl_idname = "pov.update_addon" bl_label = "Update POV addon" @@ -250,8 +250,8 @@ class POV_OT_update_addon(bpy.types.Operator): else: shutil.copyfile(src, dest) - print('-' * 20) - print('Updating POV addon...') + print("-" * 20) + print("Updating POV addon...") with tempfile.TemporaryDirectory() as temp_dir_path: temp_zip_path = os.path.join(temp_dir_path, 'master.zip') @@ -261,19 +261,19 @@ class POV_OT_update_addon(bpy.types.Operator): # 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) + 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) + 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(): + print("Extracting ZIP archive") + with zipfile.ZipFile(temp_zip_path) as zip_archive: + for member in zip_archive.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 @@ -291,7 +291,7 @@ class POV_OT_update_addon(bpy.types.Operator): if not os.path.exists(target_path): os.makedirs(target_path) - source = zip.open(member) + source = zip_archive.open(member) target = open(os.path.join(target_path, filename), "wb") with source, target: @@ -301,26 +301,26 @@ class POV_OT_update_addon(bpy.types.Operator): 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.') + 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) + print("POV addon addon folder:", render_povray_dir) # TODO: Create backup # Delete old POV addon files # (only directories and *.py files, user might have other stuff in there!) - print('Deleting old POV addon files') + 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)) + for directory in next(os.walk(render_povray_dir))[DIRNAMES]: + shutil.rmtree(os.path.join(render_povray_dir, directory)) - print('Copying new POV addon files') + print("Copying new POV addon files") # copy new POV addon files # copy __init__.py shutil.copy2( @@ -330,9 +330,9 @@ class POV_OT_update_addon(bpy.types.Operator): recursive_overwrite(extracted_render_povray_path, render_povray_dir) 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!') + print("POV addon update finished, restart Blender for the changes to take effect.") + print("-" * 20) + self.report({'WARNING'}, "Restart Blender!") return {'FINISHED'} @@ -342,33 +342,33 @@ class POV_OT_update_addon(bpy.types.Operator): class PovrayPreferences(bpy.types.AddonPreferences): - """Declare preference variables to set up POV binary.""" + """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", + 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), + ('povray', 'Official POV-Ray', '', 'PLUGIN', 0), + ('uberpov', 'Unofficial UberPOV', '', 'PLUGIN', 1), ), - default="povray", + default='povray', ) filepath_povray: StringProperty( - name="Binary Location", description="Path to renderer executable", subtype="FILE_PATH" + 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" + 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", + name='Use Sound', + description='Signaling end of the render process at various' + 'stages can help if you\'re away from monitor', default=False, ) @@ -376,21 +376,21 @@ class PovrayPreferences(bpy.types.AddonPreferences): # 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", + 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", + 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", + name='Cancel Render Sound', + description='Path to cancelled or render time error sound file', + subtype='FILE_PATH', ) def draw(self, context): diff --git a/render_povray/base_ui.py b/render_povray/base_ui.py index 967080fa..e0bbd929 100755 --- a/render_povray/base_ui.py +++ b/render_povray/base_ui.py @@ -23,9 +23,10 @@ # import addon_utils # from time import sleep import bpy - +import os from bpy.app.handlers import persistent +from pathlib import Path # from bpy.utils import register_class, unregister_class # from bpy.types import ( @@ -70,169 +71,182 @@ def pov_centric_moray_like_workspace(dummy): # 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: + + available_workspaces = bpy.data.workspaces + + if all(tabs in available_workspaces for tabs in ['POV-Mo', 'POV-Ed']): + print("\nPOV-Mo and POV-Ed tabs respectively provide GUI and TEXT\n" + "oriented POV workspaces akin to Moray and POVWIN") + else: + if 'POV-Ed'not in available_workspaces: print( - "\nPOV centric workspace available if you set render option\n" - "and save it in default file with CTRL+U" + "\nTo use POV centric workspaces you can set POV render option\n" + "and save it with File > Defaults > Save Startup File menu" ) + try: + if all(othertabs not in available_workspaces for othertabs in ['Geometry Nodes', 'POV-Ed']): + bpy.ops.workspace.append_activate( + idname='Geometry Nodes', + filepath=os.path.join(bpy.utils.user_resource('CONFIG'), 'startup.blend') + ) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + try: + # Last resort: try to import from the blender templates + for p in Path(next(bpy.utils.app_template_paths())).rglob("startup.blend"): + bpy.ops.workspace.append_activate( + idname=self.targetWorkspace, + filepath=str(p)) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + # Giving up as prerequisites can't be found + print( + "\nFactory Geometry Nodes workspace needed for POV text centric" + "\nworkspace to activate when POV is set as default renderer" + ) + finally: + # Create POVWIN like editor (text oriented editing) + if 'POV-Ed' not in available_workspaces and 'Geometry Nodes' in available_workspaces: + wsp = available_workspaces.get('Geometry Nodes') + context = bpy.context + if context.scene.render.engine == 'POVRAY_RENDER' and wsp is not None: + bpy.ops.workspace.duplicate({'workspace': wsp}) + available_workspaces['Geometry Nodes.001'].name = 'POV-Ed' + # May be already done, but explicitly make this workspace the active one + context.window.workspace = available_workspaces['POV-Ed'] + pov_screen = available_workspaces['POV-Ed'].screens[0] + pov_workspace = pov_screen.areas + pov_window = context.window + # override = bpy.context.copy() # crashes + override = {} + properties_area = pov_workspace[0] + nodes_to_3dview_area = pov_workspace[1] + view3d_to_text_area = pov_workspace[2] + spreadsheet_to_console_area = pov_workspace[3] + + try: + nodes_to_3dview_area.ui_type = 'VIEW_3D' + override['window'] = pov_window + override['screen'] = bpy.context.screen + override['area'] = nodes_to_3dview_area + override['region'] = nodes_to_3dview_area.regions[-1] + bpy.ops.screen.space_type_set_or_cycle( + override, 'INVOKE_DEFAULT', space_type='VIEW_3D' + ) + space = nodes_to_3dview_area.spaces.active + space.region_3d.view_perspective = 'CAMERA' + + override['window'] = pov_window + override['screen'] = bpy.context.screen + override['area'] = view3d_to_text_area + override['region'] = view3d_to_text_area .regions[-1] + override['scene'] = bpy.context.scene + override['space_data'] = view3d_to_text_area .spaces.active + bpy.ops.screen.space_type_set_or_cycle( + override, 'INVOKE_DEFAULT', space_type='TEXT_EDITOR' + ) + view3d_to_text_area.spaces.active.show_region_ui = True + + spreadsheet_to_console_area.ui_type = 'CONSOLE' + override['window'] = pov_window + override['screen'] = bpy.context.screen + override['area'] = spreadsheet_to_console_area + override['region'] = spreadsheet_to_console_area.regions[-1] + bpy.ops.screen.space_type_set_or_cycle( + override, 'INVOKE_DEFAULT', space_type='CONSOLE' + ) + space = properties_area.spaces.active + space.context = 'RENDER' + bpy.ops.workspace.reorder_to_front({'workspace': available_workspaces['POV-Ed']}) + except AttributeError: + # In case necessary area types lack in existing blend files + pass + + if 'POV-Mo'not in available_workspaces: + try: + if all(tab not in available_workspaces for tab in ['Rendering', 'POV-Mo']): + bpy.ops.workspace.append_activate( + idname='Rendering', + filepath=os.path.join(bpy.utils.user_resource('CONFIG'), 'startup.blend') + ) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + try: + # Last resort: try to import from the blender templates + for p in Path(next(bpy.utils.app_template_paths())).rglob("startup.blend"): + bpy.ops.workspace.append_activate( + idname=self.targetWorkspace, + filepath=str(p)) + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + # Giving up + print( + "\nFactory 'Rendering' workspace needed for POV GUI centric" + "\nworkspace to activate when POV is set as default renderer" + ) + finally: + # Create Moray like workspace (GUI oriented editing) + if 'POV-Mo' not in available_workspaces and 'Rendering' in available_workspaces: + wsp1 = available_workspaces.get('Rendering') + context = bpy.context + if context.scene.render.engine == 'POVRAY_RENDER' and wsp1 is not None: + bpy.ops.workspace.duplicate({'workspace': wsp1}) + available_workspaces['Rendering.001'].name = 'POV-Mo' + # Already done it would seem, but explicitly make this workspace the active one + context.window.workspace = available_workspaces['POV-Mo'] + pov_screen = available_workspaces['POV-Mo'].screens[0] + pov_workspace = pov_screen.areas + pov_window = context.window + # override = bpy.context.copy() # crashes + override = {} + properties_area = pov_workspace[0] + image_editor_to_view3d_area = pov_workspace[2] + + try: + image_editor_to_view3d_area.ui_type = 'VIEW_3D' + override['window'] = pov_window + override['screen'] = bpy.context.screen + override['area'] = image_editor_to_view3d_area + override['region'] = image_editor_to_view3d_area.regions[-1] + bpy.ops.screen.space_type_set_or_cycle( + override, 'INVOKE_DEFAULT', space_type='VIEW_3D' + ) + space = image_editor_to_view3d_area.spaces.active # Uncomment For non quad view + space.region_3d.view_perspective = 'CAMERA' # Uncomment For non quad view + space.show_region_toolbar = True + # bpy.ops.view3d.camera_to_view(override) # Uncomment For non quad view ? + for num, reg in enumerate(image_editor_to_view3d_area.regions): + if reg.type != 'view3d': + override['region'] = image_editor_to_view3d_area.regions[num] + bpy.ops.screen.region_quadview(override) # Comment out for non quad + propspace = properties_area.spaces.active + propspace.context = 'MATERIAL' + bpy.ops.workspace.reorder_to_front({'workspace': available_workspaces['POV-Mo']}) + except (AttributeError, TypeError): + # In case necessary types lack in existing blend files + pass + # available_workspaces.update() - 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() + try: + bpy.ops.wm.blend_strings_utf8_validate() + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + pass 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 not mat.node_tree return True return False @@ -242,7 +256,7 @@ def simple_material(mat): return (mat is not None) and (not mat.use_nodes) -def pov_context_tex_datablock(context): +def pov_context_texblock(context): """Recreate texture context type as deprecated in blender 2.8.""" idblock = context.brush if idblock and context.scene.texture_context == 'OTHER': @@ -263,8 +277,7 @@ def pov_context_tex_datablock(context): if context.particle_system and context.scene.texture_context == 'PARTICLES': idblock = context.particle_system.settings - - return idblock + return idblock idblock = context.line_style if idblock and context.scene.texture_context == 'LINESTYLE': diff --git a/render_povray/icons/pov.add.blobcapsule.dat b/render_povray/icons/pov.add.blobcapsule.dat Binary files differnew file mode 100644 index 00000000..212b20c2 --- /dev/null +++ b/render_povray/icons/pov.add.blobcapsule.dat diff --git a/render_povray/icons/pov.add.blobcube.dat b/render_povray/icons/pov.add.blobcube.dat Binary files differnew file mode 100644 index 00000000..8860b9f7 --- /dev/null +++ b/render_povray/icons/pov.add.blobcube.dat diff --git a/render_povray/icons/pov.add.blobellipsoid.dat b/render_povray/icons/pov.add.blobellipsoid.dat Binary files differnew file mode 100644 index 00000000..6fb52834 --- /dev/null +++ b/render_povray/icons/pov.add.blobellipsoid.dat diff --git a/render_povray/icons/pov.add.blobplane.dat b/render_povray/icons/pov.add.blobplane.dat Binary files differnew file mode 100644 index 00000000..9de03c0f --- /dev/null +++ b/render_povray/icons/pov.add.blobplane.dat diff --git a/render_povray/icons/pov.add.blobsphere.dat b/render_povray/icons/pov.add.blobsphere.dat Binary files differnew file mode 100644 index 00000000..a9ba7317 --- /dev/null +++ b/render_povray/icons/pov.add.blobsphere.dat diff --git a/render_povray/icons/pov.add.box.dat b/render_povray/icons/pov.add.box.dat Binary files differnew file mode 100644 index 00000000..c6d27a5f --- /dev/null +++ b/render_povray/icons/pov.add.box.dat diff --git a/render_povray/icons/pov.add.cone.dat b/render_povray/icons/pov.add.cone.dat Binary files differnew file mode 100644 index 00000000..cf193bc2 --- /dev/null +++ b/render_povray/icons/pov.add.cone.dat diff --git a/render_povray/icons/pov.add.cylinder.dat b/render_povray/icons/pov.add.cylinder.dat Binary files differnew file mode 100644 index 00000000..9820412a --- /dev/null +++ b/render_povray/icons/pov.add.cylinder.dat diff --git a/render_povray/icons/pov.add.heightfield.dat b/render_povray/icons/pov.add.heightfield.dat Binary files differnew file mode 100644 index 00000000..d3c8fe3d --- /dev/null +++ b/render_povray/icons/pov.add.heightfield.dat diff --git a/render_povray/icons/pov.add.infinite_plane.dat b/render_povray/icons/pov.add.infinite_plane.dat Binary files differnew file mode 100644 index 00000000..932e0128 --- /dev/null +++ b/render_povray/icons/pov.add.infinite_plane.dat diff --git a/render_povray/icons/pov.add.isosurface.dat b/render_povray/icons/pov.add.isosurface.dat Binary files differnew file mode 100644 index 00000000..c7dd1443 --- /dev/null +++ b/render_povray/icons/pov.add.isosurface.dat diff --git a/render_povray/icons/pov.add.isosurfacebox.dat b/render_povray/icons/pov.add.isosurfacebox.dat Binary files differnew file mode 100644 index 00000000..62f1f87e --- /dev/null +++ b/render_povray/icons/pov.add.isosurfacebox.dat diff --git a/render_povray/icons/pov.add.isosurfacesphere.dat b/render_povray/icons/pov.add.isosurfacesphere.dat Binary files differnew file mode 100644 index 00000000..47b71fab --- /dev/null +++ b/render_povray/icons/pov.add.isosurfacesphere.dat diff --git a/render_povray/icons/pov.add.isosurfacesupertorus.dat b/render_povray/icons/pov.add.isosurfacesupertorus.dat Binary files differnew file mode 100644 index 00000000..dfbd98bd --- /dev/null +++ b/render_povray/icons/pov.add.isosurfacesupertorus.dat diff --git a/render_povray/icons/pov.add.lathe.dat b/render_povray/icons/pov.add.lathe.dat Binary files differnew file mode 100644 index 00000000..0fcdb2b2 --- /dev/null +++ b/render_povray/icons/pov.add.lathe.dat diff --git a/render_povray/icons/pov.add.loft.dat b/render_povray/icons/pov.add.loft.dat Binary files differnew file mode 100644 index 00000000..e340425a --- /dev/null +++ b/render_povray/icons/pov.add.loft.dat diff --git a/render_povray/icons/pov.add.parametric.dat b/render_povray/icons/pov.add.parametric.dat Binary files differnew file mode 100644 index 00000000..dfe8c0cf --- /dev/null +++ b/render_povray/icons/pov.add.parametric.dat diff --git a/render_povray/icons/pov.add.polytocircle.dat b/render_povray/icons/pov.add.polytocircle.dat Binary files differnew file mode 100644 index 00000000..bc8df737 --- /dev/null +++ b/render_povray/icons/pov.add.polytocircle.dat diff --git a/render_povray/icons/pov.add.prism.dat b/render_povray/icons/pov.add.prism.dat Binary files differnew file mode 100644 index 00000000..0459bdd3 --- /dev/null +++ b/render_povray/icons/pov.add.prism.dat diff --git a/render_povray/icons/pov.add.rainbow.dat b/render_povray/icons/pov.add.rainbow.dat Binary files differnew file mode 100644 index 00000000..fd72b434 --- /dev/null +++ b/render_povray/icons/pov.add.rainbow.dat diff --git a/render_povray/icons/pov.add.sphere.dat b/render_povray/icons/pov.add.sphere.dat Binary files differnew file mode 100644 index 00000000..b202e5ca --- /dev/null +++ b/render_povray/icons/pov.add.sphere.dat diff --git a/render_povray/icons/pov.add.spheresweep.dat b/render_povray/icons/pov.add.spheresweep.dat Binary files differnew file mode 100644 index 00000000..67551166 --- /dev/null +++ b/render_povray/icons/pov.add.spheresweep.dat diff --git a/render_povray/icons/pov.add.superellipsoid.dat b/render_povray/icons/pov.add.superellipsoid.dat Binary files differnew file mode 100644 index 00000000..0c04fdfb --- /dev/null +++ b/render_povray/icons/pov.add.superellipsoid.dat diff --git a/render_povray/icons/pov.add.torus.dat b/render_povray/icons/pov.add.torus.dat Binary files differnew file mode 100644 index 00000000..cc514dc1 --- /dev/null +++ b/render_povray/icons/pov.add.torus.dat diff --git a/render_povray/object_curve_topology.py b/render_povray/object_curve_topology.py index a51ef83e..1b30c618 100755 --- a/render_povray/object_curve_topology.py +++ b/render_povray/object_curve_topology.py @@ -30,8 +30,15 @@ from .shading import write_object_material_interior # -------- LOFT, ETC. -def export_curves(file, ob, string_strip_hyphen, global_matrix, tab_write): - """write all curves based POV primitives to exported file """ +def export_curves(file, ob, string_strip_hyphen, tab_write): + """write all curves based POV primitives to exported file + + Args: + file: The POV file being written + ob: The current curve object to export from Blender + string_strip_hyphen: Function to clean up names + tab_write: Function to write to POV file + """ # name_orig = "OB" + ob.name # XXX Unused, check instantiation dataname_orig = "DATA" + ob.data.name @@ -174,7 +181,7 @@ def export_curves(file, ob, string_strip_hyphen, global_matrix, tab_write): tab_write('#declare %s%s=spline {\n' % (dataname, n)) tab_write('cubic_spline\n') lp = len(spline.points) - delta = 1 / (lp) + delta = 1 / lp d = -delta point = spline.points[lp - 1] x, y, z, w = point.co[:] @@ -196,8 +203,8 @@ def export_curves(file, ob, string_strip_hyphen, global_matrix, tab_write): 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('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') @@ -796,7 +803,9 @@ def export_curves(file, ob, string_strip_hyphen, global_matrix, tab_write): 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" + "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 @@ -943,6 +952,7 @@ def export_curves(file, ob, string_strip_hyphen, global_matrix, tab_write): ) tab_write("}\n") if len(ob.data.splines) == 1: + p = 1 tab_write('#declare %s = object{\n' % dataname) tab_write( ' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n' diff --git a/render_povray/object_gui.py b/render_povray/object_gui.py index fa33fc65..ffedb650 100755 --- a/render_povray/object_gui.py +++ b/render_povray/object_gui.py @@ -21,14 +21,21 @@ import bpy -from bpy.utils import register_class, unregister_class +import os + +from bpy.utils import ( + register_class, + unregister_class, + register_tool, + unregister_tool +) from bpy.types import ( # Operator, Menu, Panel, + WorkSpaceTool, ) - # Example of wrapping every class 'as is' from bl_ui import properties_data_modifier @@ -97,7 +104,8 @@ class PovDataButtonsPanel(properties_data_mesh.MeshButtonsPanel): 'CONE', 'TORUS', 'BLOB', - 'ISOSURFACE', + 'ISOSURFACE_NODE', + 'ISOSURFACE_VIEW', 'SUPERELLIPSOID', 'SUPERTORUS', 'HEIGHT_FIELD', @@ -170,7 +178,6 @@ 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 @@ -180,15 +187,14 @@ class MODIFIERS_PT_POV_modifiers(ModifierButtonsPanel, Panel): # scene = context.scene layout = self.layout ob = context.object - mod = ob.modifiers col = layout.column() # Find Boolean Modifiers for displaying CSG option - onceCSG = 0 + once_csg = 0 for mod in ob.modifiers: - if onceCSG == 0 and mod: + if once_csg == 0 and mod: if mod.type == 'BOOLEAN': col.prop(ob.pov, "boolean_mod") - onceCSG = 1 + once_csg = 1 if ob.pov.boolean_mod == "POV": # split = layout.split() # better ? @@ -201,7 +207,6 @@ 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): @@ -531,11 +536,33 @@ class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel): col.prop(obj.pov, "st_max_gradient") +class OBJECT_PT_POV_obj_isosurface(PovDataButtonsPanel, Panel): + """Use this class to define pov generic isosurface primitive function user field.""" + + bl_label = "POV Isosurface" + 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 == 'ISOSURFACE_VIEW' and (engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + obj = context.object + + col = layout.column() + + if obj.pov.object_as == 'ISOSURFACE_VIEW': + col.prop(obj.pov, "isosurface_eq") + 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 @@ -607,9 +634,7 @@ def check_add_mesh_extra_objects(): This addon is currently used to generate the proxy for POV parametric surface which is almost the same principle as its Math xyz surface """ - if "add_mesh_extra_objects" in bpy.context.preferences.addons.keys(): - return True - return False + return "add_mesh_extra_objects" in bpy.context.preferences.addons.keys() def menu_func_add(self, context): @@ -651,7 +676,7 @@ class VIEW_MT_POV_Basic_Shapes(Menu): 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.addcone", text="Cone", icon="MESH_CONE") layout.operator("pov.addtorus", text="Torus", icon='MESH_TORUS') layout.separator() layout.operator("pov.addrainbow", text="Rainbow", icon="COLOR") @@ -690,6 +715,463 @@ class VIEW_MT_POV_Basic_Shapes(Menu): return layout.operator("pov.addparametric", text="Parametric", icon='SCRIPTPLUGINS') +# ------------ Tool bar button------------ # +icon_path = (os.path.join(os.path.dirname(__file__), "icons")) +class VIEW_WT_POV_plane_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addplane" + bl_label = "Add POV plane" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a plane of infinite dimension for POV" + ) + bl_icon = os.path.join(icon_path, "pov.add.infinite_plane") + bl_widget = None + bl_keymap = ( + ("pov.addplane", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_box_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addbox" + bl_label = "Add POV box" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV box solid primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.box") + bl_widget = None + bl_keymap = ( + ("pov.addbox", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_sphere_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addsphere" + bl_label = "Add POV sphere" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add an untesselated sphere for POV" + ) + bl_icon = os.path.join(icon_path, "pov.add.sphere") + bl_widget = None + bl_keymap = ( + ("pov.addsphere", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_cylinder_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addcylinder" + bl_label = "Add POV cylinder" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add an untesselated cylinder for POV" + ) + bl_icon = os.path.join(icon_path, "pov.add.cylinder") + bl_widget = None + bl_keymap = ( + ("pov.addcylinder", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_cone_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addcone" + bl_label = "Add POV cone" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add an untesselated cone for POV" + ) + bl_icon = os.path.join(icon_path, "pov.add.cone") + bl_widget = None + bl_keymap = ( + ("pov.addcone", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_torus_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addtorus" + bl_label = "Add POV torus" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add an untesselated torus for POV" + ) + bl_icon = os.path.join(icon_path, "pov.add.torus") + bl_widget = None + bl_keymap = ( + ("pov.addtorus", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_rainbow_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addrainbow" + bl_label = "Add POV rainbow" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV rainbow primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.rainbow") + bl_widget = None + bl_keymap = ( + ("pov.addrainbow", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_lathe_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addlathe" + bl_label = "Add POV lathe" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV lathe primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.lathe") + bl_widget = None + bl_keymap = ( + ("pov.addlathe", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_prism_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addprism" + bl_label = "Add POV prism" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV prism primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.prism") + bl_widget = None + bl_keymap = ( + ("pov.addprism", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_heightfield_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addheightfield" + bl_label = "Add POV heightfield" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV heightfield primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.heightfield") + bl_widget = None + bl_keymap = ( + ("pov.addheightfield", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_superellipsoid_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addsuperellipsoid" + bl_label = "Add POV superquadric ellipsoid" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV superquadric ellipsoid primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.superellipsoid") + bl_widget = None + bl_keymap = ( + ("pov.addsuperellipsoid", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_spheresweep_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addspheresweep" + bl_label = "Add POV spheresweep" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV spheresweep primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.spheresweep") + bl_widget = None + bl_keymap = ( + ("pov.addspheresweep", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_loft_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addloft" + bl_label = "Add POV loft macro" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV loft macro between editable spline cross sections" + ) + bl_icon = os.path.join(icon_path, "pov.add.loft") + bl_widget = None + bl_keymap = ( + ("pov.addloft", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_polytocircle_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addpolytocircle" + bl_label = "Add POV poly to circle macro" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV regular polygon to circle blending macro" + ) + bl_icon = os.path.join(icon_path, "pov.add.polytocircle") + bl_widget = None + bl_keymap = ( + ("pov.addpolytocircle", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_parametric_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addparametric" + bl_label = "Add POV parametric surface" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV parametric surface primitive shaped from three equations (for x, y, z directions)" + ) + bl_icon = os.path.join(icon_path, "pov.add.parametric") + bl_widget = None + bl_keymap = ( + ("pov.addparametric", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_isosurface_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addisosurface" + bl_label = "Add POV generic isosurface" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV generic shaped isosurface primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.isosurface") + bl_widget = None + bl_keymap = ( + ("pov.addisosurface", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_isosurfacebox_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addisosurfacebox" + bl_label = "Add POV isosurface box" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV box shaped isosurface primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.isosurfacebox") + bl_widget = None + bl_keymap = ( + ("pov.addisosurfacebox", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_isosurfacesphere_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addisosurfacesphere" + bl_label = "Add POV isosurface sphere" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV sphere shaped isosurface primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.isosurfacesphere") + bl_widget = None + bl_keymap = ( + ("pov.addisosurfacesphere", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_isosurfacesupertorus_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addsupertorus" + bl_label = "Add POV isosurface supertorus" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV torus shaped isosurface primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.isosurfacesupertorus") + bl_widget = None + bl_keymap = ( + ("pov.addsupertorus", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_blobsphere_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addblobsphere" + bl_label = "Add POV blob sphere" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV sphere shaped blob primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.blobsphere") + bl_widget = None + bl_keymap = ( + ("pov.addblobsphere", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_blobcapsule_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addblobcapsule" + bl_label = "Add POV blob capsule" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV capsule shaped blob primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.blobcapsule") + bl_widget = None + bl_keymap = ( + ("pov.addblobcapsule", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_blobplane_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addblobplane" + bl_label = "Add POV blob plane" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV plane shaped blob primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.blobplane") + bl_widget = None + bl_keymap = ( + ("pov.addblobplane", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_blobellipsoid_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addblobellipsoid" + bl_label = "Add POV blob ellipsoid" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV ellipsoid shaped blob primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.blobellipsoid") + bl_widget = None + bl_keymap = ( + ("pov.addblobellipsoid", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + + +class VIEW_WT_POV_blobcube_add(WorkSpaceTool): + bl_space_type='VIEW_3D' + bl_context_mode='OBJECT' + + # The prefix of the idname should be your add-on name. + bl_idname = "pov.addsblobcube" + bl_label = "Add POV blob cube" + bl_options = {'REGISTER', 'UNDO'} + bl_description = ( + "add a POV cube shaped blob primitive" + ) + bl_icon = os.path.join(icon_path, "pov.add.blobcube") + bl_widget = None + bl_keymap = ( + ("pov.addblobcube", {"type": 'LEFTMOUSE', "value": 'PRESS'}, + {"properties": None}), + ) + classes = ( # ObjectButtonsPanel, @@ -709,21 +1191,53 @@ classes = ( OBJECT_PT_POV_obj_superellipsoid, OBJECT_PT_POV_obj_torus, OBJECT_PT_POV_obj_supertorus, + OBJECT_PT_POV_obj_isosurface, OBJECT_PT_POV_obj_parametric, OBJECT_PT_povray_replacement_text, VIEW_MT_POV_primitives_add, VIEW_MT_POV_Basic_Shapes, ) - +tool_classes = ( + VIEW_WT_POV_plane_add, + VIEW_WT_POV_box_add, + VIEW_WT_POV_sphere_add, + VIEW_WT_POV_cylinder_add, + VIEW_WT_POV_cone_add, + VIEW_WT_POV_torus_add, + VIEW_WT_POV_prism_add, + VIEW_WT_POV_lathe_add, + VIEW_WT_POV_spheresweep_add, + VIEW_WT_POV_heightfield_add, + VIEW_WT_POV_superellipsoid_add, + VIEW_WT_POV_rainbow_add, + VIEW_WT_POV_loft_add, + VIEW_WT_POV_polytocircle_add, + VIEW_WT_POV_parametric_add, + VIEW_WT_POV_isosurface_add, + VIEW_WT_POV_isosurfacebox_add, + VIEW_WT_POV_isosurfacesphere_add, + VIEW_WT_POV_isosurfacesupertorus_add, + VIEW_WT_POV_blobsphere_add, + VIEW_WT_POV_blobcapsule_add, + VIEW_WT_POV_blobplane_add, + VIEW_WT_POV_blobellipsoid_add, + VIEW_WT_POV_blobcube_add, +) def register(): for cls in classes: register_class(cls) - bpy.types.VIEW3D_MT_add.prepend(menu_func_add) + # Register tools + last_tool = {"builtin.measure"} + for index, wtl in enumerate(tool_classes): + # Only separate first and 12th tools and hide subtools only in 8th (isosurfaces) + register_tool(wtl, after=last_tool, separator=index in [0,7,11,12,14,19], group=index == 15) + last_tool = {wtl.bl_idname} - # was used for parametric objects but made the other addon unreachable on - # unregister for other tools to use created a user action call instead + bpy.types.VIEW3D_MT_add.prepend(menu_func_add) + # Below 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) @@ -731,5 +1245,8 @@ def unregister(): # addon_utils.disable("add_mesh_extra_objects", default_set=False) bpy.types.VIEW3D_MT_add.remove(menu_func_add) + for wtl in reversed(tool_classes): + unregister_tool(wtl) + for cls in reversed(classes): unregister_class(cls) diff --git a/render_povray/object_mesh_topology.py b/render_povray/object_mesh_topology.py index cd98a6f4..e991070c 100755 --- a/render_povray/object_mesh_topology.py +++ b/render_povray/object_mesh_topology.py @@ -30,28 +30,48 @@ import bpy from . import texturing # for how textures influence shaders from .scenography import export_smoke - def matrix_as_pov_string(matrix): """Translate some transform 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 + return "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], + ) + + +def write_object_csg_inside_vector(ob, file): + """Write inside vector for use by pov CSG, only once per object using boolean""" + has_csg_inside_vector = False + for modif in ob.modifiers: + if ( + not has_csg_inside_vector + and modif.type == 'BOOLEAN' + and 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], + ) + ) + has_csg_inside_vector = True # objectNames = {} @@ -59,32 +79,32 @@ 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_interior, - 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, + preview_dir, + file, + scene, + sel, + csg, + string_strip_hyphen, + safety, + write_object_modifiers, + material_names_dictionary, + write_object_material_interior, + 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 @@ -93,72 +113,72 @@ def export_meshes( # # 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)) + # #'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)) + # 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)) + # #'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)) + # 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)) + # 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)) + # 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)) + # #'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)) + # 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)) + # 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 hasattr(obj, 'material_slots'): + # for ms in obj.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): + # def hasObjectMaterial(obj): # # Grab materials attached to object instances ... - # if hasattr(ob, 'material_slots'): - # for ms in ob.material_slots: + # if hasattr(obj, 'material_slots'): + # for ms in obj.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. @@ -173,12 +193,12 @@ def export_meshes( # ... can share a same instance in POV export. obmats2data = {} - def check_object_materials(ob, name, dataname): + def check_object_materials(obj, obj_name, dataname): """Compare other objects exported material slots to avoid rewriting duplicates""" - if hasattr(ob, 'material_slots'): + if hasattr(obj, 'material_slots'): has_local_mats = False key = [dataname] - for ms in ob.material_slots: + for ms in obj.material_slots: if ms.material is not None: key.append(ms.material.name) if ms.link == 'OBJECT' and not has_local_mats: @@ -193,7 +213,7 @@ def export_meshes( # 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 + obmats2data[key] = obj_name return obmats2data[key] return None @@ -242,10 +262,10 @@ def export_meshes( 'LATTICE', }: continue - fluid_flag = False + fluid_found = False for mod in ob.modifiers: if mod and hasattr(mod, 'fluid_type'): - fluid_flag = True + fluid_found = True if mod.fluid_type == 'DOMAIN': if mod.domain_settings.domain_type == 'GAS': export_smoke( @@ -255,30 +275,26 @@ def export_meshes( 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 not fluid_found: + # No fluid found if hasattr(ob, 'particle_systems'): - render_emitter = False - if ob.show_instancer_for_render: - render_emitter = True + # Importing function Export Hair + # here rather than at the top recommended for addons startup footprint + from .object_particles import export_hair for p_sys in ob.particle_systems: - for mod in [ + for particle_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) + (p_sys.settings.render_type == 'PATH') + and particle_mod.show_render + and (p_sys.name == particle_mod.particle_system.name) ): - export_hair(file, ob, mod, p_sys, global_matrix, write_matrix) - if not render_emitter: - continue # don't render mesh, skip to next object. + export_hair(file, ob, particle_mod, p_sys, global_matrix, write_matrix) + if not ob.show_instancer_for_render: + continue # don't render emitter mesh, skip to next object. # ------------------------------------------------ # Generating a name for object just like materials to be able to use it @@ -300,8 +316,8 @@ def export_meshes( # 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.data: # not an EMPTY type object + # obj.dupli_list_clear() #just don't store any reference to instance since 2.8 + elif ob.data: # not an EMPTY type object name_orig = "OB" + ob.name dataname_orig = "DATA" + ob.data.name elif ob.type == 'EMPTY': @@ -312,7 +328,7 @@ def export_meshes( 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: + # for slot in obj.material_slots: # if slot.material is not None and slot.link == 'OBJECT': # obmaterial = slot.material @@ -331,397 +347,25 @@ def export_meshes( print("Writing Down First Occurrence of " + name) - # ------------ Povray Primitives ------------ # + # ------------ Mesh 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'} + ob.pov.curveshape in {'lathe', 'sphere_sweep', 'loft'} ): continue # Don't render proxy mesh, skip to next object # pov_mat_name = "Default_texture" # Not used...remove? - 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) - 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_interior(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) - ) - 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_interior(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) - ) - 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_interior(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) - 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_interior(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) - 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_interior(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) - ) - 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_interior(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) - ) - 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_interior(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)) - 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_interior(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) - ) - 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_interior(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) - ) - 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_interior(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 - - # Implicit else-if (as not skipped by previous "continue") Keep this last. + # Implicit else-if (as not skipped by previous "continue") + # which itself has no "continue" (to combine custom pov code)?, so Keep this last. # For originals, but 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 instantiate the same for every - # empty in the final matrix writing, or even no marix and just a comment + # empty in the final matrix writing, or even no matrix and just a comment # with empty object transforms ? tab_write("\n//dummy sphere to represent Empty location\n") tab_write( @@ -730,7 +374,7 @@ def export_meshes( ) continue # Don't render empty object but this is later addition, watch it. - ob_eval = ob # not sure this is needed in case to_mesh_clear could damage ob ? + ob_eval = ob # not sure this is needed in case to_mesh_clear could damage obj ? try: me = ob_eval.to_mesh() @@ -886,7 +530,7 @@ def export_meshes( print('An exception occurred: {}'.format(e)) material = None if ( - material + material ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops] @@ -953,7 +597,7 @@ def export_meshes( cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops] if ( - not me_materials or me_materials[material_index] is None + not me_materials or me_materials[material_index] is None ): # No materials if linebreaksinlists: file.write(",\n") @@ -1085,21 +729,7 @@ def export_meshes( 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 + write_object_csg_inside_vector(ob, file) if me.materials: try: @@ -1110,7 +740,7 @@ def export_meshes( # POV object modifiers such as # hollow / sturm / double_illuminate etc. - write_object_modifiers(scene, ob, file) + write_object_modifiers(ob, file) # Importance for radiosity sampling added here: tab_write("radiosity { \n") @@ -1128,7 +758,7 @@ def export_meshes( for i, material in enumerate(me_materials): if ( - material and material.pov.material_use_nodes is False + material and material.pov.material_use_nodes is False ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # Multiply diffuse with SSS Color if material.pov_subsurface_scattering.use: @@ -1157,7 +787,7 @@ def export_meshes( vertCols[key] = [-1] idx = 0 - local_material_names = [] #XXX track and revert + local_material_names = [] # XXX track and revert material_finish = None for col, index in vertCols.items(): # if me_materials: @@ -1194,7 +824,7 @@ def export_meshes( else: file.write(tab_str + "%s" % (len(vertCols))) # vert count - # below "material" alias, added check ob.active_material + # below "material" alias, added check obj.active_material # to avoid variable referenced before assignment error try: material = ob.active_material @@ -1204,9 +834,9 @@ def export_meshes( # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if ( - material - and ob.active_material is not None - and not material.pov.material_use_nodes + material + and ob.active_material is not None + and not material.pov.material_use_nodes ): if material.pov.replacement_text != "": file.write("\n") @@ -1243,7 +873,7 @@ def export_meshes( cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops] if ( - not me_materials or me_materials[material_index] is None + not me_materials or me_materials[material_index] is None ): # No materials if linebreaksinlists: file.write(",\n") @@ -1377,22 +1007,7 @@ def export_meshes( 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 - + write_object_csg_inside_vector(ob, file) if me.materials: try: material = me.materials[0] # dodgy @@ -1402,7 +1017,7 @@ def export_meshes( # POV object modifiers such as # hollow / sturm / double_illuminate etc. - write_object_modifiers(scene, ob, file) + write_object_modifiers(ob, file) # Importance for radiosity sampling added here: tab_write("radiosity { \n") @@ -1412,15 +1027,426 @@ def export_meshes( tab_write("}\n") # End of mesh block ob_eval.to_mesh_clear() + continue + # ------------ Povray Primitives ------------ # + # Also implicit elif (continue) clauses and sorted after mesh + # as less often used. + if ob.pov.object_as == 'PLANE': + tab_write("#declare %s = plane{ <0,0,1>,0\n" % povdataname) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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 == 'SPHERE': + + tab_write( + "#declare %s = sphere { 0,%6f\n" % (povdataname, ob.pov.sphere_radius) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(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(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 = obj.pov.hf_hierarchy + tab_write('#declare %s = height_field { %s\n' % (povdataname, data)) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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 == 'TORUS': + tab_write( + "#declare %s = torus { %.4f,%.4f\n" + % (povdataname, ob.pov.torus_major_radius, ob.pov.torus_minor_radius) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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 == 'ISOSURFACE_NODE': + 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) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(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 == 'ISOSURFACE_VIEW': + simple_isosurface_function = ob.pov.isosurface_eq + if simple_isosurface_function: + tab_write("#declare %s = isosurface{ \n" % povdataname) + tab_write("function{ \n") + tab_write(simple_isosurface_function) + 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) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(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) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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) + ) + if ob.active_material: + # pov_mat_name = string_strip_hyphen(bpy.path.clean_name(obj.active_material.name)) + try: + material = ob.active_material + write_object_material_interior(material, ob, tab_write) + except IndexError: + print(me) + # tab_write("texture {%s}\n"%pov_mat_name) + write_object_modifiers(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 == '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 if csg: duplidata_ref = [] _dupnames_seen = dict() # avoid duplicate output during introspection for ob in sel: - # matrix = global_matrix @ ob.matrix_world + # matrix = global_matrix @ obj.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 + # obj.dupli_list_create(scene) #deprecated in 2.8 dup = "" if ob.is_modified(scene, 'RENDER'): # modified object always unique so using object name rather than data name @@ -1433,7 +1459,7 @@ def export_meshes( ) for eachduplicate in depsgraph.object_instances: if ( - eachduplicate.is_instance + eachduplicate.is_instance ): # Real dupli instance filtered because original included in list since 2.8 _dupname = eachduplicate.object.name _dupobj = bpy.data.objects[_dupname] @@ -1450,7 +1476,7 @@ def export_meshes( % (_dupname, _thing, getattr(_dupobj, _thing)) ) _dupnames_seen[_dupname] = 1 - print("''=> Unparseable objects so far: %s" % (_dupnames_seen)) + 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 @@ -1467,19 +1493,21 @@ def export_meshes( ) # 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 + 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))] + ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+obj.name))] dup += "}\n" - # ob.dupli_list_clear()# just do not store any reference to instance since 2.8 + # obj.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)) + if _dupnames_seen: + print("WARNING: Unparseable objects in current .blend file:\n''--> %s" % _dupnames_seen) + if duplidata_ref: + 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 diff --git a/render_povray/object_particles.py b/render_povray/object_particles.py index 4949af21..215a6f15 100755 --- a/render_povray/object_particles.py +++ b/render_povray/object_particles.py @@ -111,7 +111,7 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix): ) # +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)) + 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)) @@ -160,6 +160,8 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix): elif step == 0: hair_strand_diameter = strand_start else: + # still initialize variable + hair_strand_diameter = strand_start if strand_shape != 0.0: if strand_shape < 0.0: fac = pow(step, (1.0 + strand_shape)) diff --git a/render_povray/object_primitives.py b/render_povray/object_primitives.py index abbba148..d906f665 100755 --- a/render_povray/object_primitives.py +++ b/render_povray/object_primitives.py @@ -42,24 +42,15 @@ from mathutils import Vector, Matrix # import collections -def write_object_modifiers(scene, ob, File): +def write_object_modifiers(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 + # import .object_mesh_topology.write_object_csg_inside_vector + write_object_csg_inside_vector(ob, file) ''' if ob.pov.hollow: @@ -126,9 +117,9 @@ class POVRAY_OT_lathe_add(Operator): bl_idname = "pov.addlathe" bl_label = "Lathe" - bl_options = {'REGISTER', 'UNDO'} bl_description = "adds lathe" - + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # ayers=[False]*20 # layers[0]=True @@ -195,13 +186,13 @@ def pov_superellipsoid_define(context, op, ob): step += 1 angSegment += stepSegment x = r * (abs(cos(angRing)) ** n1) * (abs(cos(angSegment)) ** n2) - if (cos(angRing) < 0 and cos(angSegment) > 0) or ( - cos(angRing) > 0 and cos(angSegment) < 0 + if (cos(angRing) < 0 < cos(angSegment)) or ( + cos(angRing) > 0 > cos(angSegment) ): x = -x y = r * (abs(cos(angRing)) ** n1) * (abs(sin(angSegment)) ** n2) - if (cos(angRing) < 0 and sin(angSegment) > 0) or ( - cos(angRing) > 0 and sin(angSegment) < 0 + if (cos(angRing) < 0 < sin(angSegment)) or ( + cos(angRing) > 0 > sin(angSegment) ): y = -y z = r * (abs(sin(angRing)) ** n1) @@ -287,6 +278,7 @@ class POVRAY_OT_superellipsoid_add(Operator): # 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! + # XXX ARE the first two used for import ? could we hide or suppress them otherwise? 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) @@ -704,11 +696,12 @@ class POVRAY_OT_plane_add(Operator): bl_label = "Plane" bl_description = "Add Plane" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] # layers[0] = True - bpy.ops.mesh.primitive_plane_add(size=100000) + bpy.ops.mesh.primitive_plane_add(size=10000) ob = context.object ob.name = ob.data.name = 'PovInfinitePlane' bpy.ops.object.mode_set(mode="EDIT") @@ -732,6 +725,7 @@ class POVRAY_OT_box_add(Operator): bl_label = "Box" bl_description = "Add Box" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] @@ -797,6 +791,7 @@ class POVRAY_OT_cylinder_add(Operator): bl_label = "Cylinder" bl_description = "Add Cylinder" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} # Keep in sync within object_properties.py section Cylinder # as this allows interactive update @@ -821,13 +816,12 @@ class POVRAY_OT_cylinder_add(Operator): LOC = ob.pov.imported_cyl_loc if ob.pov.imported_cyl_loc_cap: LOC_CAP = ob.pov.imported_cyl_loc_cap + elif not props.imported_cyl_loc: + LOC_CAP = LOC = bpy.context.scene.cursor.location + LOC_CAP[2] += 2.0 else: - if not props.imported_cyl_loc: - LOC_CAP = LOC = bpy.context.scene.cursor.location - LOC_CAP[2] += 2.0 - else: - LOC = props.imported_cyl_loc - LOC_CAP = props.imported_cyl_loc_cap + LOC = props.imported_cyl_loc + 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", @@ -928,6 +922,7 @@ class POVRAY_OT_sphere_add(Operator): bl_label = "Sphere" bl_description = "Add Sphere Shape" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} # Keep in sync within object_properties.py section Sphere # as this allows interactive update @@ -945,16 +940,15 @@ class POVRAY_OT_sphere_add(Operator): if ob: if ob.pov.imported_loc: LOC = ob.pov.imported_loc - else: - if not props.imported_loc: - LOC = bpy.context.scene.cursor.location + elif not props.imported_loc: + LOC = bpy.context.scene.cursor.location - else: - LOC = props.imported_loc - self.report( - {'INFO'}, - "This native POV-Ray primitive " "won't have any vertex to show in edit mode", - ) + else: + LOC = props.imported_loc + self.report( + {'INFO'}, + "This native POV-Ray primitive " "won't have any vertex to show in edit mode", + ) pov_sphere_define(context, self, None, LOC) return {'FINISHED'} @@ -1067,7 +1061,7 @@ def pov_cone_define(context, op, ob): class POVRAY_OT_cone_add(Operator): """Add the representation of POV cone using pov_cone_define() function.""" - bl_idname = "pov.cone_add" + bl_idname = "pov.addcone" bl_label = "Cone" bl_description = "Add Cone" bl_options = {'REGISTER', 'UNDO'} @@ -1147,6 +1141,109 @@ class POVRAY_OT_cone_update(Operator): # ----------------------------------- ISOSURFACES ----------------------------------- # +def pov_isosurface_view_define(context, op, ob, loc): + """create the representation of POV isosurface using a Blender empty.""" + + if op: + eq = op.isosurface_eq + + loc = bpy.context.scene.cursor.location + + else: + assert ob + eq = ob.pov.isosurface_eq + + # keep object rotation and location for the add object operator + obrot = ob.rotation_euler + # obloc = ob.location + obscale = ob.scale + + #bpy.ops.object.empty_add(type='CUBE', location=loc, rotation=obrot) + bpy.ops.mesh.primitive_emptyvert_add() + + # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL') + bpy.ops.transform.resize(value=obscale) + # bpy.ops.transform.rotate(axis=obrot, proportional_size=1) + bpy.ops.object.mode_set(mode="OBJECT") + if not ob: + #bpy.ops.object.empty_add(type='CUBE', location=loc) + bpy.ops.mesh.primitive_emptyvert_add() + ob = context.object + ob.name = ob.data.name = "PovIsosurface" + ob.pov.object_as = "ISOSURFACE_VIEW" + ob.pov.isosurface_eq = eq + ob.pov.contained_by = 'box' + bpy.ops.object.mode_set(mode="OBJECT") + +class POVRAY_OT_isosurface_add(Operator): + """Add the representation of POV isosurface sphere by a Blender mesh icosphere. + + Flag its primitive type with a specific pov.object_as attribute and lock edit mode + to keep proxy consistency by hiding edit geometry.""" + + bl_idname = "pov.addisosurface" + bl_label = "Generic Isosurface" + bl_description = "Add Isosurface" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + # Keep in sync within object_properties.py section Sphere + # as this allows interactive update + isosurface_eq: StringProperty( + name="f(x,y,z)=", + description="Type the POV Isosurface function syntax for equation, " + "pattern,etc. ruling an implicit surface to be rendered", + default="sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 1.5", + ) + imported_loc: FloatVectorProperty( + name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0) + ) + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + props = self.properties + ob = context.object + if ob: + if ob.pov.imported_loc: + LOC = ob.pov.imported_loc + elif not props.imported_loc: + LOC = bpy.context.scene.cursor.location + else: + LOC = props.imported_loc + pov_isosurface_view_define(context, self, None, LOC) + self.report( + {'INFO'}, "This native POV-Ray primitive " "is only an abstract proxy in Blender" + ) + return {'FINISHED'} + + + +class POVRAY_OT_isosurface_update(Operator): + """Update the POV isosurface. + + Rerun pov_isosurface_view_define() function + with the new parameters""" + + bl_idname = "pov.isosurface_update" + bl_label = "Update" + bl_description = "Update Isosurface" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + ob = context.object + return ob and ob.data and ob.type == 'ISOSURFACE_VIEW' and engine in cls.COMPAT_ENGINES + + def execute(self, context): + + pov_isosurface_view_define(context, None, context.object, context.object.location) + + return {'FINISHED'} + + class POVRAY_OT_isosurface_box_add(Operator): """Add the representation of POV isosurface box using also just a Blender mesh cube. @@ -1157,6 +1254,7 @@ class POVRAY_OT_isosurface_box_add(Operator): bl_label = "Isosurface Box" bl_description = "Add Isosurface contained by Box" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] @@ -1169,7 +1267,7 @@ class POVRAY_OT_isosurface_box_add(Operator): ) bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") - ob.pov.object_as = "ISOSURFACE" + ob.pov.object_as = "ISOSURFACE_NODE" ob.pov.contained_by = 'box' ob.name = 'PovIsosurfaceBox' return {'FINISHED'} @@ -1185,6 +1283,7 @@ class POVRAY_OT_isosurface_sphere_add(Operator): bl_label = "Isosurface Sphere" bl_description = "Add Isosurface contained by Sphere" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] @@ -1198,7 +1297,7 @@ class POVRAY_OT_isosurface_sphere_add(Operator): bpy.ops.mesh.hide(unselected=False) bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.object.shade_smooth() - ob.pov.object_as = "ISOSURFACE" + ob.pov.object_as = "ISOSURFACE_NODE" ob.pov.contained_by = 'sphere' ob.name = 'PovIsosurfaceSphere' return {'FINISHED'} @@ -1214,6 +1313,7 @@ class POVRAY_OT_sphere_sweep_add(Operator): bl_label = "Sphere Sweep" bl_description = "Create Sphere Sweep along curve" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] @@ -1230,7 +1330,7 @@ class POVRAY_OT_sphere_sweep_add(Operator): return {'FINISHED'} -class POVRAY_OT_blob_add(Operator): +class POVRAY_OT_blobsphere_add(Operator): """Add the representation of POV blob using a Blender meta ball. No need to flag its primitive type as meta are exported to blobs @@ -1240,6 +1340,7 @@ class POVRAY_OT_blob_add(Operator): bl_label = "Blob Sphere" bl_description = "Add Blob Sphere" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): # layers = 20*[False] @@ -1250,6 +1351,89 @@ class POVRAY_OT_blob_add(Operator): return {'FINISHED'} +class POVRAY_OT_blobcapsule_add(Operator): + """Add the representation of POV blob using a Blender meta ball. + + No need to flag its primitive type as meta are exported to blobs + and leave access to edit mode to keep user editable thresholds.""" + + bl_idname = "pov.addblobcapsule" + bl_label = "Blob Capsule" + bl_description = "Add Blob Capsule" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.object.metaball_add(type='CAPSULE') + ob = context.object + ob.name = "PovBlob" + return {'FINISHED'} + + +class POVRAY_OT_blobplane_add(Operator): + """Add the representation of POV blob using a Blender meta ball. + + No need to flag its primitive type as meta are exported to blobs + and leave access to edit mode to keep user editable thresholds.""" + + bl_idname = "pov.addblobplane" + bl_label = "Blob Plane" + bl_description = "Add Blob Plane" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.object.metaball_add(type='PLANE') + ob = context.object + ob.name = "PovBlob" + return {'FINISHED'} + + +class POVRAY_OT_blobellipsoid_add(Operator): + """Add the representation of POV blob using a Blender meta ball. + + No need to flag its primitive type as meta are exported to blobs + and leave access to edit mode to keep user editable thresholds.""" + + bl_idname = "pov.addblobellipsoid" + bl_label = "Blob Ellipsoid" + bl_description = "Add Blob Ellipsoid" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.object.metaball_add(type='ELLIPSOID') + ob = context.object + ob.name = "PovBlob" + return {'FINISHED'} + + +class POVRAY_OT_blobcube_add(Operator): + """Add the representation of POV blob using a Blender meta ball. + + No need to flag its primitive type as meta are exported to blobs + and leave access to edit mode to keep user editable thresholds.""" + + bl_idname = "pov.addblobcube" + bl_label = "Blob Cube" + bl_description = "Add Blob Cube" + bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} + + def execute(self, context): + # layers = 20*[False] + # layers[0] = True + bpy.ops.object.metaball_add(type='CUBE') + ob = context.object + ob.name = "PovBlob" + return {'FINISHED'} + + class POVRAY_OT_rainbow_add(Operator): """Add the representation of POV rainbow using a Blender spot light. @@ -1264,6 +1448,7 @@ class POVRAY_OT_rainbow_add(Operator): bl_label = "Rainbow" bl_description = "Add Rainbow" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} def execute(self, context): cam = context.scene.camera @@ -1302,6 +1487,7 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper): bl_label = "Height Field" bl_description = "Add Height Field" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} # Keep in sync within object_properties.py section HeightFields # as this allows interactive update @@ -1431,6 +1617,7 @@ class POVRAY_OT_torus_add(Operator): bl_label = "Torus" bl_description = "Add Torus" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} # Keep in sync within object_properties.py section Torus # as this allows interactive update @@ -1487,6 +1674,7 @@ class POVRAY_OT_prism_add(Operator): bl_label = "Prism" bl_description = "Create Prism" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} 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) @@ -1617,6 +1805,7 @@ class POVRAY_OT_parametric_add(Operator): bl_label = "Parametric" bl_description = "Add Paramertic" bl_options = {'REGISTER', 'UNDO'} + COMPAT_ENGINES = {'POVRAY_RENDER'} # Keep in sync within object_properties.py section Parametric primitive # as this allows interactive update @@ -1637,11 +1826,15 @@ class POVRAY_OT_parametric_add(Operator): x_eq = props.x_eq y_eq = props.y_eq z_eq = props.z_eq - - pov_parametric_define(context, self, None) - self.report( - {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" - ) + try: + pov_parametric_define(context, self, None) + self.report( + {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode" + ) + except AttributeError: + self.report( + {'INFO'}, "Please enable Add Mesh: Extra Objects addon" + ) return {'FINISHED'} @@ -1748,10 +1941,16 @@ classes = ( POVRAY_OT_sphere_update, POVRAY_OT_cone_add, POVRAY_OT_cone_update, + POVRAY_OT_isosurface_add, + POVRAY_OT_isosurface_update, POVRAY_OT_isosurface_box_add, POVRAY_OT_isosurface_sphere_add, POVRAY_OT_sphere_sweep_add, - POVRAY_OT_blob_add, + POVRAY_OT_blobsphere_add, + POVRAY_OT_blobcapsule_add, + POVRAY_OT_blobplane_add, + POVRAY_OT_blobellipsoid_add, + POVRAY_OT_blobcube_add, POVRAY_OT_rainbow_add, POVRAY_OT_height_field_add, POVRAY_OT_torus_add, diff --git a/render_povray/object_properties.py b/render_povray/object_properties.py index f599da9b..752df203 100755 --- a/render_povray/object_properties.py +++ b/render_povray/object_properties.py @@ -283,7 +283,13 @@ class RenderPovSettingsObject(PropertyGroup): cone_base_z: FloatProperty() cone_cap_z: FloatProperty() - + # -------- Generic isosurface + isosurface_eq: StringProperty( + name="f (x,y,z)=", + description="Type the POV Isosurface function syntax for equation, " + "pattern,etc. ruling an implicit surface to be rendered", + default="sqrt(pow(x,2) + pow(y,2) + pow(z,2)) - 1.5", + ) # -------- Parametric def prop_update_parametric(self, context): diff --git a/render_povray/render.py b/render_povray/render.py index 25e0697c..929e6e01 100755 --- a/render_povray/render.py +++ b/render_povray/render.py @@ -106,7 +106,7 @@ def is_renderable(ob): return not ob.hide_render and ob not in csg_list -def renderable_objects(scene): +def renderable_objects(): """test for non hidden, non boolean operands objects to render""" return [ob for ob in bpy.data.objects if is_renderable(ob)] @@ -141,24 +141,15 @@ smoke_path = os.path.join(preview_dir, "smoke.df3") ''' -# def write_object_modifiers(scene, ob, File): +# def write_object_modifiers(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 +# import .object_mesh_topology.write_object_csg_inside_vector +# write_object_csg_inside_vector(ob, file) # ''' # if ob.pov.hollow: @@ -209,541 +200,670 @@ def write_pov(filename, scene=None, info_callback=None): import mathutils - # file = filename - file = open(filename, "w") + with open(filename, "w") as file: + # Only for testing + if not scene: + scene = bpy.data.scenes[0] + + render = scene.render + 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 + using_uberpov = feature_set == 'uberpov' + pov_binary = PovrayRender._locate_binary() - # Only for testing - if not scene: - scene = bpy.data.scenes[0] + if using_uberpov: + print("Unofficial UberPOV feature set chosen in preferences") + 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") + else: + print("The name of the binary suggests you are probably rendering with standard POV engine") - render = scene.render - 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 - using_uberpov = feature_set == 'uberpov' - pov_binary = PovrayRender._locate_binary() - - if using_uberpov: - print("Unofficial UberPOV feature set chosen in preferences") - 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") - else: - print("The name of the binary suggests you are probably rendering with standard POV engine") - - def set_tab(tabtype, spaces): - tab_str = "" - if tabtype == 'NONE': + def set_tab(tabtype, spaces): tab_str = "" - elif tabtype == 'TAB': - tab_str = "\t" - elif tabtype == 'SPACE': - tab_str = spaces * " " - return tab_str - - tab = set_tab(scene.pov.indentation_character, scene.pov.indentation_spaces) - if not scene.pov.tempfiles_enable: - - def tab_write(str_o): - """Indent POV syntax from brackets levels and write to exported file """ - global tab_level - brackets = str_o.count("{") - str_o.count("}") + str_o.count("[") - str_o.count("]") - if brackets < 0: - 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: - tab_level = tab_level + brackets - - else: - - def tab_write(str_o): - """write directly to exported file if user checked autonamed temp files (faster).""" - - file.write(str_o) - - def unique_name(name, name_seq): - """Increment any generated POV name that could get identical to avoid collisions""" - - if name not in name_seq: + if tabtype == 'NONE': + tab_str = "" + elif tabtype == 'TAB': + tab_str = "\t" + elif tabtype == 'SPACE': + tab_str = spaces * " " + return tab_str + + tab = set_tab(scene.pov.indentation_character, scene.pov.indentation_spaces) + if not scene.pov.tempfiles_enable: + + def tab_write(str_o): + """Indent POV syntax from brackets levels and write to exported file """ + global tab_level + brackets = str_o.count("{") - str_o.count("}") + str_o.count("[") - str_o.count("]") + if brackets < 0: + 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: + tab_level = tab_level + brackets + + else: + + def tab_write(str_o): + """write directly to exported file if user checked autonamed temp files (faster).""" + + file.write(str_o) + + def unique_name(name, name_seq): + """Increment any generated POV name that could get identical to avoid collisions""" + + if name not in name_seq: + name = string_strip_hyphen(name) + return name + + name_orig = name + i = 1 + while name in name_seq: + name = "%s_%.3d" % (name_orig, i) + i += 1 name = string_strip_hyphen(name) return name - name_orig = name - i = 1 - while name in name_seq: - name = "%s_%.3d" % (name_orig, i) - i += 1 - name = string_strip_hyphen(name) - return name - - def write_matrix(matrix): - """Translate some transform matrix from Blender UI - to POV syntax and write to exported file """ - tab_write( - "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], + def write_matrix(matrix): + """Translate some transform matrix from Blender UI + to POV syntax and write to exported file """ + tab_write( + "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], + ) ) - ) - material_names_dictionary = {} - DEF_MAT_NAME = "" # or "Default"? - - # ----------------------------------------------------------------------------- - - def export_meta(metas): - """write all POV blob primitives and Blender Metas to exported file """ - # TODO - blenders 'motherball' naming is not supported. - - if comments and len(metas) >= 1: - file.write("//--Blob objects--\n\n") - # Get groups of metaballs by blender name prefix. - meta_group = {} - meta_elems = {} - for ob in metas: - prefix = ob.name.split(".")[0] - if prefix not in meta_group: - meta_group[prefix] = ob # .data.threshold - elems = [ - (elem, ob) - for elem in ob.data.elements - if elem.type in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'} - ] - if prefix in meta_elems: - meta_elems[prefix].extend(elems) - else: - meta_elems[prefix] = elems - - # empty metaball - if len(elems) == 0: - 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, mob in meta_group.items(): - if len(meta_elems[mg]) != 0: - tab_write("blob{threshold %.4g // %s \n" % (mob.data.threshold, mg)) - for elems in meta_elems[mg]: - elem = elems[0] - loc = elem.co - stiffness = elem.stiffness - if elem.use_negative: - stiffness = -stiffness - if elem.type == 'BALL': - tab_write( - "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g " - % (loc.x, loc.y, loc.z, elem.radius, stiffness) - ) - write_matrix(global_matrix @ elems[1].matrix_world) - tab_write("}\n") - elif elem.type == 'ELLIPSOID': - tab_write( - "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g " - % ( - loc.x / elem.size_x, - loc.y / elem.size_y, - loc.z / elem.size_z, - elem.radius, - stiffness, + material_names_dictionary = {} + DEF_MAT_NAME = "" # or "Default"? + + # ----------------------------------------------------------------------------- + + def export_meta(metas): + """write all POV blob primitives and Blender Metas to exported file """ + # TODO - blenders 'motherball' naming is not supported. + + if comments and len(metas) >= 1: + file.write("//--Blob objects--\n\n") + # Get groups of metaballs by blender name prefix. + meta_group = {} + meta_elems = {} + for meta_ob in metas: + prefix = meta_ob.name.split(".")[0] + if prefix not in meta_group: + meta_group[prefix] = meta_ob # .data.threshold + elems = [ + (elem, meta_ob) + for elem in meta_ob.data.elements + if elem.type in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'} + ] + if prefix in meta_elems: + meta_elems[prefix].extend(elems) + else: + meta_elems[prefix] = elems + + # empty metaball + if len(elems) == 0: + 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" + % (meta_ob.location.x, meta_ob.location.y, meta_ob.location.z) + ) # meta_ob.name > povdataname) + # other metaballs + else: + for mg, mob in meta_group.items(): + if len(meta_elems[mg]) != 0: + tab_write("blob{threshold %.4g // %s \n" % (mob.data.threshold, mg)) + for elems in meta_elems[mg]: + elem = elems[0] + loc = elem.co + stiffness = elem.stiffness + if elem.use_negative: + stiffness = -stiffness + if elem.type == 'BALL': + tab_write( + "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g " + % (loc.x, loc.y, loc.z, elem.radius, stiffness) ) - ) - 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 == 'CAPSULE': - tab_write( - "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g " - % ( - (loc.x - elem.size_x), - (loc.y), - (loc.z), - (loc.x + elem.size_x), - (loc.y), - (loc.z), - elem.radius, - stiffness, + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + elif elem.type == 'ELLIPSOID': + tab_write( + "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g " + % ( + loc.x / elem.size_x, + loc.y / elem.size_y, + loc.z / elem.size_z, + elem.radius, + stiffness, + ) ) - ) - # 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': - 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, - stiffness / 4.0, - loc.x, - loc.y, - loc.z, - elem.size_x, - elem.size_y, - elem.size_z, + 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") - 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, - stiffness / 4.0, - loc.x, - loc.y, - loc.z, - elem.size_x, - elem.size_y, - elem.size_z, + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + elif elem.type == 'CAPSULE': + tab_write( + "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g " + % ( + (loc.x - elem.size_x), + loc.y, + loc.z, + (loc.x + elem.size_x), + loc.y, + loc.z, + elem.radius, + stiffness, + ) ) - ) - 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, - stiffness / 4.0, - loc.x, - loc.y, - loc.z, - elem.size_x, - elem.size_y, - elem.size_z, + # 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': + 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, + stiffness / 4.0, + loc.x, + loc.y, + loc.z, + elem.size_x, + elem.size_y, + elem.size_z, + ) ) - ) - write_matrix(global_matrix @ elems[1].matrix_world) - tab_write("}\n") - - elif elem.type == 'PLANE': - 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, - stiffness / 4.0, - loc.x, - loc.y, - loc.z, - elem.size_x, - elem.size_y, - elem.size_z, + 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, + stiffness / 4.0, + loc.x, + loc.y, + loc.z, + elem.size_x, + elem.size_y, + elem.size_z, + ) ) - ) - write_matrix(global_matrix @ elems[1].matrix_world) - tab_write("}\n") + 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, + stiffness / 4.0, + loc.x, + loc.y, + loc.z, + elem.size_x, + elem.size_y, + elem.size_z, + ) + ) + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + + elif elem.type == 'PLANE': + 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, + stiffness / 4.0, + loc.x, + loc.y, + loc.z, + elem.size_x, + elem.size_y, + elem.size_z, + ) + ) + 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, + stiffness / 4.0, + loc.x, + loc.y, + loc.z, + elem.size_x, + elem.size_y, + elem.size_z, + ) + ) + write_matrix(global_matrix @ elems[1].matrix_world) + tab_write("}\n") + + try: + one_material = elems[1].data.materials[ + 0 + ] # lame! - blender cant do enything else. + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) + one_material = None + if one_material: + diffuse_color = one_material.diffuse_color + trans = 1.0 - one_material.pov.alpha + if ( + one_material.use_transparency + and one_material.transparency_method == 'RAYTRACE' + ): + pov_filter = one_material.pov_raytrace_transparency.filter * ( + 1.0 - one_material.alpha + ) + trans = (1.0 - one_material.pov.alpha) - pov_filter + else: + pov_filter = 0.0 + material_finish = material_names_dictionary[one_material.name] tab_write( - "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n" + "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" % ( - elem.radius * 2.0, - stiffness / 4.0, - loc.x, - loc.y, - loc.z, - elem.size_x, - elem.size_y, - elem.size_z, + diffuse_color[0], + diffuse_color[1], + diffuse_color[2], + pov_filter, + trans, ) ) - 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 BaseException as e: - print(e.__doc__) - print('An exception occurred: {}'.format(e)) - material = None - if material: - diffuse_color = material.diffuse_color - trans = 1.0 - material.pov.alpha - if ( - material.use_transparency - and material.transparency_method == 'RAYTRACE' - ): - pov_filter = material.pov_raytrace_transparency.filter * ( - 1.0 - material.alpha - ) - trans = (1.0 - material.pov.alpha) - pov_filter + tab_write("finish{%s} " % safety(material_finish, ref_level_bound=2)) else: - 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], - pov_filter, - trans, + material_finish = DEF_MAT_NAME + trans = 0.0 + tab_write( + "pigment{srgbt<1,1,1,%.3g>} finish{%s} " + % (trans, safety(material_finish, ref_level_bound=2)) ) - ) - tab_write("finish{%s} " % safety(material_finish, ref_level_bound=2)) - else: - material_finish = DEF_MAT_NAME - trans = 0.0 - tab_write( - "pigment{srgbt<1,1,1,%.3g} finish{%s} " - % (trans, safety(material_finish, ref_level_bound=2)) - ) - - write_object_material_interior(material, mob, tab_write) - # write_object_material_interior(material, elems[1]) - tab_write("radiosity{importance %3g}\n" % mob.pov.importance_value) - tab_write("}\n\n") # End of Metaball block - - ''' - meta = ob.data - - # important because no elements will break parsing. - elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}] - - if elements: - tab_write("blob {\n") - tab_write("threshold %.4g\n" % meta.threshold) - importance = ob.pov.importance_value - try: - material = meta.materials[0] # lame! - blender cant do enything else. - except: - material = None - - for elem in elements: - loc = elem.co - - stiffness = elem.stiffness - if elem.use_negative: - stiffness = - stiffness - - if elem.type == 'BALL': - - 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... - # "pigment {Blue} }" - # except we'll write the color - - elif elem.type == 'ELLIPSOID': - # location is modified by scale - 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)) - 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': - pov_filter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha) - trans = (1.0 - material.pov.alpha) - pov_filter + write_object_material_interior(one_material, mob, tab_write) + # write_object_material_interior(one_material, elems[1]) + tab_write("radiosity{importance %3g}\n" % mob.pov.importance_value) + tab_write("}\n\n") # End of Metaball block + + ''' + meta = ob.data + + # important because no elements will break parsing. + elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}] + + if elements: + tab_write("blob {\n") + tab_write("threshold %.4g\n" % meta.threshold) + importance = ob.pov.importance_value + + try: + material = meta.materials[0] # lame! - blender cant do enything else. + except: + material = None + + for elem in elements: + loc = elem.co + + stiffness = elem.stiffness + if elem.use_negative: + stiffness = - stiffness + + if elem.type == 'BALL': + + 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... + # "pigment {Blue} }" + # except we'll write the color + + elif elem.type == 'ELLIPSOID': + # location is modified by scale + 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)) + 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': + pov_filter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha) + trans = (1.0 - material.pov.alpha) - pov_filter + else: + 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], + pov_filter, trans)) + tab_write("finish {%s}\n" % safety(material_finish, ref_level_bound=2)) + else: - 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], - pov_filter, trans)) - tab_write("finish {%s}\n" % safety(material_finish, ref_level_bound=2)) - - else: - tab_write("pigment {srgb 1} \n") - # Write the finish last. - tab_write("finish {%s}\n" % (safety(DEF_MAT_NAME, ref_level_bound=2))) - - write_object_material_interior(material, elems[1]) - - write_matrix(global_matrix @ ob.matrix_world) - # Importance for radiosity sampling added here - tab_write("radiosity { \n") - # importance > ob.pov.importance_value - tab_write("importance %3g \n" % importance) + tab_write("pigment {srgb 1} \n") + # Write the finish last. + tab_write("finish {%s}\n" % (safety(DEF_MAT_NAME, ref_level_bound=2))) + + write_object_material_interior(material, elems[1]) + + write_matrix(global_matrix @ ob.matrix_world) + # Importance for radiosity sampling added here + tab_write("radiosity { \n") + # importance > ob.pov.importance_value + tab_write("importance %3g \n" % importance) + tab_write("}\n") + + tab_write("}\n") # End of Metaball block + + if comments and len(metas) >= 1: + file.write("\n") + ''' + + def export_global_settings(scene): + """write all POV global settings to exported file """ + # Imperial units warning + if scene.unit_settings.system == "IMPERIAL": + print("Warning: Imperial units not supported") + + tab_write("global_settings {\n") + tab_write("assumed_gamma 1.0\n") + tab_write("max_trace_level %d\n" % scene.pov.max_trace_level) + + if scene.pov.global_settings_advanced: + 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(" 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: + 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 once_sss: + # In pov, the scale has reversed influence compared to blender. these number + # should correct that + 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 + + tab_write("subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10)) + once_sss = 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 once_photons and ( + material.pov.refraction_type == "2" or material.pov.photons_reflection + ): + 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) + ) + if scene.pov.photon_map_file_save_load in {'save'}: + ph_file_name = 'Photon_map_file.ph' + if scene.pov.photon_map_file != '': + 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): + 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'}: + 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 - tab_write("}\n") # End of Metaball block - - if comments and len(metas) >= 1: - file.write("\n") - ''' - - def export_global_settings(scene): - """write all POV global settings to exported file """ - tab_write("global_settings {\n") - tab_write("assumed_gamma 1.0\n") - tab_write("max_trace_level %d\n" % scene.pov.max_trace_level) - - if scene.pov.global_settings_advanced: - 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(" 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: - 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 once_sss: - # In pov, the scale has reversed influence compared to blender. these number - # should correct that - 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 + # sel = renderable_objects() #removed for booleans + if comments: + file.write( + "//----------------------------------------------\n" + "//--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( + "#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n" + ) + if comments: + file.write("\n//--Global settings--\n\n") + + export_global_settings(scene) + + if comments: + file.write("\n//--Custom Code--\n\n") + scripting.export_custom_code(file) + + if comments: + file.write("\n//--Patterns Definitions--\n\n") + local_pattern_names = [] + for texture in bpy.data.textures: # ok? + if texture.users > 0: + current_pat_name = string_strip_hyphen(bpy.path.clean_name(texture.name)) + # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above + 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" % current_pat_name) + file.write(shading.export_pattern(texture)) + file.write("\n") + if comments: + file.write("\n//--Background--\n\n") + + scenography.export_world(scene.world, scene, global_matrix, tab_write) + + if comments: + file.write("\n//--Cameras--\n\n") + + scenography.export_camera(scene, global_matrix, render, tab_write) + + if comments: + file.write("\n//--Lamps--\n\n") + + for ob in bpy.data.objects: + if ob.type == 'MESH': + for mod in ob.modifiers: + if mod.type == 'BOOLEAN' and mod.object not in csg_list: + csg_list.append(mod.object) + if csg_list: + csg = False + sel = no_renderable_objects() + # export non rendered boolean objects operands + object_mesh_topology.export_meshes( + preview_dir, + file, + scene, + sel, + csg, + string_strip_hyphen, + safety, + write_object_modifiers, + material_names_dictionary, + write_object_material_interior, + 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, + ) - # formerly sslt_samples were multiplied by 100 instead of 10 - sslt_samples = (11 - material.pov_subsurface_scattering.error_threshold) * 10 + csg = True + sel = renderable_objects() - tab_write("subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10)) - once_sss = 0 + 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 world and once_ambient: - tab_write("ambient_light rgb<%.3g, %.3g, %.3g>\n" % world.pov.ambient_color[:]) - once_ambient = 0 + if comments: + file.write("\n//--Rainbows--\n\n") + 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 scene.pov.photon_enable: - if once_photons and ( - material.pov.refraction_type == "2" or material.pov.photons_reflection - ): - 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) + if comments: + file.write("\n//--Special Curves--\n\n") + for c in sel: + if c.is_modified(scene, 'RENDER'): + continue # don't export as pov curves objects with modifiers, but as mesh + # 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(file, c, string_strip_hyphen, tab_write) + + if comments: + file.write("\n//--Material Definitions--\n\n") + # write a default pigment for objects with no material (comment out to show black) + file.write("#default{ pigment{ color srgb 0.8 }}\n") + # Convert all materials to strings we can access directly per vertex. + # exportMaterials() + shading.write_material( + using_uberpov, + DEF_MAT_NAME, + tab_write, + safety, + comments, + 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 + pov_mat_name = string_strip_hyphen(bpy.path.clean_name(material.name)) + if len(ntree.nodes) == 0: + file.write('#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color)) + else: + shading.write_nodes(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": + pov_mat_name = ( + string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + + "_%s" % pov_mat_name + ) + else: + file.write( + '#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color) + ) + else: + shading.write_material( + using_uberpov, + DEF_MAT_NAME, + tab_write, + safety, + comments, + unique_name, + material_names_dictionary, + material, ) - if scene.pov.photon_map_file_save_load in {'save'}: - ph_file_name = 'Photon_map_file.ph' - if scene.pov.photon_map_file != '': - 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): - 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'}: - 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 - - tab_write("}\n") - - # sel = renderable_objects(scene) #removed for booleans - if comments: - file.write( - "//----------------------------------------------\n" - "//--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( - "#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n" - ) - if comments: - file.write("\n//--Global settings--\n\n") - - export_global_settings(scene) - - if comments: - file.write("\n//--Custom Code--\n\n") - scripting.export_custom_code(file) - - if comments: - file.write("\n//--Patterns Definitions--\n\n") - local_pattern_names = [] - for texture in bpy.data.textures: # ok? - if texture.users > 0: - current_pat_name = string_strip_hyphen(bpy.path.clean_name(texture.name)) - # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above - 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" % current_pat_name) - file.write(shading.export_pattern(texture)) + # attributes are all the variables needed by the other python file... + if comments: file.write("\n") - if comments: - file.write("\n//--Background--\n\n") - - scenography.export_world(scene.world, scene, global_matrix, tab_write) - if comments: - file.write("\n//--Cameras--\n\n") + export_meta([m for m in sel if m.type == 'META']) - scenography.export_camera(scene, global_matrix, render, tab_write) + if comments: + file.write("//--Mesh objects--\n") - if comments: - file.write("\n//--Lamps--\n\n") - - for ob in bpy.data.objects: - if ob.type == 'MESH': - for mod in ob.modifiers: - if mod.type == 'BOOLEAN' and mod.object not in csg_list: - csg_list.append(mod.object) - if csg_list != []: - csg = False - sel = no_renderable_objects() - #export non rendered boolean objects operands + # tbefore = time.time() object_mesh_topology.export_meshes( preview_dir, file, @@ -772,150 +892,21 @@ def write_pov(filename, scene=None, info_callback=None): tab_write, info_callback, ) + # totime = time.time() - tbefore + # print("export_meshes took" + str(totime)) - csg = True - sel = renderable_objects(scene) - - 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") - 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: - file.write("\n//--Special Curves--\n\n") - for c in sel: - if c.is_modified(scene, 'RENDER'): - continue # don't export as pov curves objects with modifiers, but as mesh - # 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(file, c, string_strip_hyphen, global_matrix, tab_write) - - if comments: - file.write("\n//--Material Definitions--\n\n") - # write a default pigment for objects with no material (comment out to show black) - file.write("#default{ pigment{ color srgb 0.8 }}\n") - # Convert all materials to strings we can access directly per vertex. - # exportMaterials() - shading.write_material( - using_uberpov, - DEF_MAT_NAME, - tab_write, - safety, - comments, - 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 - pov_mat_name = string_strip_hyphen(bpy.path.clean_name(material.name)) - if len(ntree.nodes) == 0: - file.write('#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color)) - else: - 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": - pov_mat_name = ( - string_strip_hyphen( - bpy.path.clean_name(link.from_node.name) - ) - + "_%s" % pov_mat_name - ) - else: - file.write( - '#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color) - ) - else: - shading.write_material( - using_uberpov, - DEF_MAT_NAME, - tab_write, - safety, - comments, - unique_name, - material_names_dictionary, - material, - ) - # attributes are all the variables needed by the other python file... - if comments: - file.write("\n") - - export_meta([m for m in sel if m.type == 'META']) - - if comments: - file.write("//--Mesh objects--\n") - - # 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_interior, - 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: - # 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. - - # print("pov file closed %s" % file.closed) - file.close() - # print("pov file closed %s" % file.closed) - - -def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_image): + # What follow used to happen here: + # 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. + + if not file.closed: + file.close() + +def write_pov_ini(filename_ini, filename_log, filename_pov, filename_image): """Write ini file.""" feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray using_uberpov = feature_set == 'uberpov' @@ -926,67 +917,66 @@ def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_imag x = int(render.resolution_x * render.resolution_percentage * 0.01) y = int(render.resolution_y * render.resolution_percentage * 0.01) - file = open(filename_ini, "w") - 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: - file.write("All_File=1\n") - - file.write("Input_File_Name='%s'\n" % filename_pov) - file.write("Output_File_Name='%s'\n" % filename_image) - - file.write("Width=%d\n" % x) - file.write("Height=%d\n" % y) - - # Border render. - if render.use_border: - file.write("Start_Column=%4g\n" % render.border_min_x) - file.write("End_Column=%4g\n" % (render.border_max_x)) - - 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 - - # Activated (turn this back off when better live exchange is done between the two programs - # (see next comment) - file.write("Display=1\n") - file.write("Pause_When_Done=0\n") - # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the - # POV-Ray interactive preview like bishop 3D could solve the preview for all formats. - file.write("Output_File_Type=N\n") - # file.write("Output_File_Type=T\n") # TGA, best progressive loading - file.write("Output_Alpha=1\n") - - if scene.pov.antialias_enable: - # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray - # needs higher sampling. - # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5} - if using_uberpov: - method = {"0": 1, "1": 2, "2": 3} - else: - method = {"0": 1, "1": 2, "2": 2} - file.write("Antialias=on\n") - 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) - else: - 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") - file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount) - else: - file.write("Jitter=off\n") # prevent animation flicker + with open(filename_ini, "w") as file: + 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: + file.write("All_File=1\n") + + file.write("Input_File_Name='%s'\n" % filename_pov) + file.write("Output_File_Name='%s'\n" % filename_image) + + file.write("Width=%d\n" % x) + file.write("Height=%d\n" % y) + + # Border render. + if render.use_border: + file.write("Start_Column=%4g\n" % render.border_min_x) + file.write("End_Column=%4g\n" % render.border_max_x) + + 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 + + # Activated (turn this back off when better live exchange is done between the two programs + # (see next comment) + file.write("Display=1\n") + file.write("Pause_When_Done=0\n") + # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the + # POV-Ray interactive preview like bishop 3D could solve the preview for all formats. + file.write("Output_File_Type=N\n") + # file.write("Output_File_Type=T\n") # TGA, best progressive loading + file.write("Output_Alpha=1\n") + + if scene.pov.antialias_enable: + # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray + # needs higher sampling. + # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5} + if using_uberpov: + method = {"0": 1, "1": 2, "2": 3} + else: + method = {"0": 1, "1": 2, "2": 2} + file.write("Antialias=on\n") + 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) + else: + 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") + file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount) + else: + file.write("Jitter=off\n") # prevent animation flicker - else: - file.write("Antialias=off\n") - # print("ini file closed %s" % file.closed) - file.close() - # print("ini file closed %s" % file.closed) + else: + file.write("Antialias=off\n") + if not file.closed: + file.close() class PovrayRender(bpy.types.RenderEngine): @@ -1096,7 +1086,7 @@ class PovrayRender(bpy.types.RenderEngine): return False write_pov_ini( - scene, self._temp_file_ini, self._temp_file_log, self._temp_file_in, self._temp_file_out + self._temp_file_ini, self._temp_file_log, self._temp_file_in, self._temp_file_out ) print("***-STARTING-***") @@ -1222,12 +1212,13 @@ class PovrayRender(bpy.types.RenderEngine): ''' print(scene.pov.text_block) text = bpy.data.texts[scene.pov.text_block] - file = open("%s" % self._temp_file_in, "w") - # Why are the newlines needed? - file.write("\n") - file.write(text.as_string()) - file.write("\n") - file.close() + with open(self._temp_file_in, "w") as file: + # Why are the newlines needed? + file.write("\n") + file.write(text.as_string()) + file.write("\n") + if not file.closed: + file.close() # has to be called to update the frame on exporting animations scene.frame_set(scene.frame_current) @@ -1242,7 +1233,6 @@ class PovrayRender(bpy.types.RenderEngine): self.update_stats("", "POV-Ray 3.7: Exporting ini options from Blender") write_pov_ini( - scene, self._temp_file_ini, self._temp_file_log, self._temp_file_in, @@ -1585,23 +1575,22 @@ class PovrayRender(bpy.types.RenderEngine): scr = win.screen for area in scr.areas: if area.type == 'CONSOLE': - # pass # XXX temp override - # context override - # ctx = {'window': win, 'screen': scr, 'area':area}#bpy.context.copy() try: - ctx = {} - ctx['area'] = area - ctx['region'] = area.regions[-1] - ctx['space_data'] = area.spaces.active - ctx['screen'] = scr # C.screen - ctx['window'] = win + # context override + ctx = { + 'area': area, + 'screen': scr, + 'window': win + } # bpy.ops.console.banner(ctx, text = "Hello world") bpy.ops.console.clear_line(ctx) - stdmsg = msg.split('\n') # XXX todo , test and see segfault crash? - for i in stdmsg: - # Crashes if no Terminal displayed on Windows - bpy.ops.console.scrollback_append(ctx, text=i, type='INFO') + for i in msg.split('\n'): + bpy.ops.console.scrollback_append( + ctx, + text=i, + type='INFO' + ) # bpy.ops.console.insert(ctx, text=(i + "\n")) except BaseException as e: print(e.__doc__) @@ -1614,7 +1603,7 @@ class PovrayRender(bpy.types.RenderEngine): self._cleanup() sound_on = bpy.context.preferences.addons[__package__].preferences.use_sounds - finished_render_message = "\'Render completed\'" + finished_render_message = "\'Et VoilĂ !\'" if platform.startswith('win') and sound_on: # Could not find tts Windows command so playing beeps instead :-) @@ -1666,14 +1655,27 @@ class PovrayRender(bpy.types.RenderEngine): 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)) - + # but if the os TTS package isn't up to date it + # still does thus, the try except clause + try: + os.system("say %s &" % finished_render_message) + except BaseException as e: + print(e.__doc__) + print("your Mac may need an update, try to restart computer") + pass # 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, + # We don't want the espeak command to block Python, # so we add an ampersand after the message - os.system("echo %s | espeak &" % (finished_render_message)) + # but if the espeak TTS package isn't installed it + # still does thus, the try except clause + try: + os.system("echo %s | espeak &" % finished_render_message) + except BaseException as e: + print(e.__doc__) + pass + # --------------------------------------------------------------------------------- # @@ -1697,54 +1699,56 @@ class RenderPovTexturePreview(Operator): input_prev_file = os.path.join(preview_dir, "Preview.pov") output_prev_file = os.path.join(preview_dir, tex_prev_name) # ---------------------------------- ini ---------------------------------- # - 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() + with open(ini_prev_file, "w") as file_ini: + 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') + if not file_ini.closed: + file_ini.close() # ---------------------------------- pov ---------------------------------- # - 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" - ) - file_pov.write( - " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n" - ) - 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() + with open(input_prev_file, "w") as file_pov: + 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" + ) + file_pov.write( + " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n" + ) + 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") + if not file_pov.closed: + file_pov.close() # ------------------------------- end write ------------------------------- # pov_binary = PovrayRender._locate_binary() @@ -1799,7 +1803,7 @@ class RunPovTextRender(Operator): bpy.ops.render.render() - # empty text name property engain + # empty text name property again scene.pov.text_block = "" return {'FINISHED'} diff --git a/render_povray/render_gui.py b/render_povray/render_gui.py index e527511f..ce842f04 100755 --- a/render_povray/render_gui.py +++ b/render_povray/render_gui.py @@ -61,14 +61,14 @@ for member in dir(properties_view_layer): del properties_view_layer # Use some of the existing buttons. -from bl_ui import properties_render +# from bl_ui import properties_render # DEPRECATED#properties_render.RENDER_PT_render.COMPAT_ENGINES.add('POVRAY_RENDER') # DEPRECATED#properties_render.RENDER_PT_format.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 +# del properties_render def check_render_freestyle_svg(): @@ -241,6 +241,29 @@ class RENDER_PT_POV_photons(RenderButtonsPanel, Panel): # end main photons +def uberpov_only_qmc_til_pov38release(layout): + 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") + + +def no_qmc_fallbacks(row, scene, layout): + 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") + sub.enabled = bool(scene.pov.jitter_enable) + row = layout.row() + row.prop(scene.pov, "antialias_threshold", text="AA Threshold") + row.prop(scene.pov, "antialias_gamma", text="AA Gamma") + + class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel): """Use this class to define pov antialiasing buttons.""" @@ -258,27 +281,6 @@ class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel): else: self.layout.prop(scene.pov, "antialias_enable", text="", icon='ALIASED') - def uberpov_only_qmc_til_pov38release(self, layout): - 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") - - def no_qmc_fallbacks(self, row, scene, layout): - 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") - sub.enabled = bool(scene.pov.jitter_enable) - row = layout.row() - row.prop(scene.pov, "antialias_threshold", text="AA Threshold") - row.prop(scene.pov, "antialias_gamma", text="AA Gamma") - def draw(self, context): prefs = bpy.context.preferences.addons[__package__].preferences layout = self.layout @@ -290,14 +292,15 @@ class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel): row.prop(scene.pov, "antialias_method", text="") if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2': - self.uberpov_only_qmc_til_pov38release(layout) + uberpov_only_qmc_til_pov38release(layout) else: - self.no_qmc_fallbacks(row, scene, layout) + no_qmc_fallbacks(row, scene, layout) if prefs.branch_feature_set_povray == 'uberpov': row = layout.row() row.prop(scene.pov, "antialias_confidence", text="AA Confidence") row.enabled = scene.pov.antialias_method == '2' + class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel): """Use this class to define pov radiosity buttons.""" diff --git a/render_povray/render_properties.py b/render_povray/render_properties.py index 0fbb60da..3b9c81ef 100755 --- a/render_povray/render_properties.py +++ b/render_povray/render_properties.py @@ -21,7 +21,7 @@ import bpy from bpy.utils import register_class, unregister_class -from bpy.types import PropertyGroup +from bpy.types import PropertyGroup, Scene from bpy.props import ( BoolProperty, IntProperty, @@ -32,6 +32,7 @@ from bpy.props import ( PointerProperty, ) + # ---------------------------------------------------------------- # # Scene POV properties. # ---------------------------------------------------------------- # @@ -180,7 +181,7 @@ class RenderPovSettingsScene(PropertyGroup): step=0.00000001, min=0.000000001, max=1.0, - default=(1.0), + default=1.0, ) media_diffusion_color: FloatVectorProperty( @@ -203,7 +204,7 @@ class RenderPovSettingsScene(PropertyGroup): step=0.000001, min=0.000000001, max=1.0, - default=(0.00002), + default=0.00002, ) media_absorption_color: FloatVectorProperty( @@ -229,7 +230,7 @@ class RenderPovSettingsScene(PropertyGroup): step=0.01, min=-1.0, max=1.0, - default=(0.0), + default=0.0, options={"ANIMATABLE"}, ) @@ -680,10 +681,10 @@ classes = ( def register(): for cls in classes: register_class(cls) - bpy.types.Scene.pov = PointerProperty(type=RenderPovSettingsScene) + Scene.pov = PointerProperty(type=RenderPovSettingsScene) def unregister(): - del bpy.types.Scene.pov + del Scene.pov for cls in reversed(classes): unregister_class(cls) diff --git a/render_povray/scenography.py b/render_povray/scenography.py index 6f892b3e..ff08b017 100755 --- a/render_povray/scenography.py +++ b/render_povray/scenography.py @@ -34,7 +34,7 @@ from .object_primitives import write_object_modifiers # -------- find image texture # used for export_world -------- # -def image_format(imgF): +def image_format(img_f): """Identify input image filetypes to transmit to POV.""" # First use the below explicit extensions to identify image file prospects ext = { @@ -50,13 +50,13 @@ def image_format(imgF): 'TIF': "tiff", 'EXR': "exr", 'HDR': "hdr", - }.get(os.path.splitext(imgF)[-1].upper(), "") + }.get(os.path.splitext(img_f)[-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 + ext = what(img_f) # imghdr is a python lib to identify image file types return ext @@ -267,9 +267,8 @@ def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write): # 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: + # Get all lamps and keep their count in a global variable + for exported_lights_count, ob in enumerate(lamps, start=1): lamp = ob.data matrix = global_matrix @ ob.matrix_world @@ -374,16 +373,14 @@ def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write): 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.location.x, + -ob.location.y, + -ob.location.z, ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z, @@ -486,40 +483,39 @@ def export_world(world, scene, global_matrix, tab_write): % (image_format(textures_blend), textures_blend, img_map_bg(t_blend)) ) tab_write("}\n") - tab_write("%s\n" % (mapping_blend)) + 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("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_tex_count == 0 and 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 @@ -543,7 +539,7 @@ def export_world(world, scene, global_matrix, tab_write): 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) + % (*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? @@ -557,7 +553,7 @@ def export_world(world, scene, global_matrix, 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_scale, *(scene.pov.media_diffusion_color[:]), ) ) @@ -636,7 +632,7 @@ def export_rainbows(rainbows, file, scene, global_matrix, write_matrix, tab_writ tab_write("}\n") # tab_write("texture {%s}\n"%pov_mat_name) - write_object_modifiers(scene, ob, file) + write_object_modifiers(ob, file) # tab_write("rotate x*90\n") # matrix = global_matrix @ ob.matrix_world # write_matrix(matrix) @@ -684,10 +680,11 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ # 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]) + big_res = [ + mod_set.domain_resolution[0], + mod_set.domain_resolution[1], + mod_set.domain_resolution[2] + ] if mod_set.use_noise: big_res[0] = big_res[0] * (mod_set.noise_scale + 1) @@ -791,7 +788,7 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ 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(" 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") diff --git a/render_povray/scripting.py b/render_povray/scripting.py index caa79b8c..5f7a2b6e 100755 --- a/render_povray/scripting.py +++ b/render_povray/scripting.py @@ -212,7 +212,7 @@ class ImportPOV(bpy.types.Operator, ImportHelper): 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)) + bpy.ops.pov.addcone(base=r0, cap=r1, height=(y1 - y0)) ob = context.object ob.location = (x0, y0, z0) # ob.scale = (r,r,r) @@ -327,7 +327,9 @@ class ImportPOV(bpy.types.Operator, ImportHelper): except ValueError: pass - except: + except BaseException as e: + print(e.__doc__) + print('An exception occurred: {}'.format(e)) x = y = z = float(cache[2]) r = float(cache[3]) bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z)) diff --git a/render_povray/scripting_gui.py b/render_povray/scripting_gui.py index 0dcdc4f1..60774f29 100755 --- a/render_povray/scripting_gui.py +++ b/render_povray/scripting_gui.py @@ -112,12 +112,13 @@ class TEXT_OT_POV_insert(Operator): def execute(self, context): if self.filepath and isfile(self.filepath): - file = open(self.filepath, "r") - bpy.ops.text.insert(text=file.read()) + with open(self.filepath, "r") as file: + bpy.ops.text.insert(text=file.read()) - # places the cursor at the end without scrolling -.- - # context.space_data.text.write(file.read()) - file.close() + # places the cursor at the end without scrolling -.- + # context.space_data.text.write(file.read()) + if not file.closed: + file.close() return {'FINISHED'} @@ -137,10 +138,9 @@ class TEXT_MT_POV_insert(Menu): 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) + + # todo: structure submenus by dir + pov_insert_items_list = [root for root, dirs, files in os.walk(pov_documents)] print(pov_insert_items_list) self.path_menu( pov_insert_items_list, diff --git a/render_povray/scripting_properties.py b/render_povray/scripting_properties.py index d4529c18..f0e61244 100755 --- a/render_povray/scripting_properties.py +++ b/render_povray/scripting_properties.py @@ -38,7 +38,7 @@ class RenderPovSettingsText(PropertyGroup): 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", + default="3dview", ) diff --git a/render_povray/shading.py b/render_povray/shading.py index 985afd34..c0eb3925 100755 --- a/render_povray/shading.py +++ b/render_povray/shading.py @@ -221,7 +221,7 @@ def write_material( material.pov.specular_shader == "COOKTORR" or material.pov.specular_shader == "PHONG" ): - tab_write("phong %.3g\n" % (material.pov.specular_intensity)) + 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. @@ -259,7 +259,7 @@ def write_material( 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 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. @@ -293,13 +293,20 @@ def write_material( 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? - tab_write( - "specular %.3g\n" - % ( - (material.pov.specular_intensity * material.pov.specular_color.v) - * (255 * slot.specular_factor) + if material.pov_texture_slots: + max_spec_factor = ( + material.pov.specular_intensity + * material.pov.specular_color.v + * 255 + * slot.specular_factor ) - ) + else: + max_spec_factor = ( + material.pov.specular_intensity + * material.pov.specular_color.v + * 255 + ) + tab_write("specular %.3g\n" % max_spec_factor) tab_write("roughness %.3g\n" % (1 / material.pov.specular_hardness)) tab_write("diffuse %.3g %.3g\n" % (front_diffuse, back_diffuse)) @@ -328,7 +335,7 @@ def write_material( tab_write("reflection {\n") tab_write("rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:]) if material.pov.mirror_metallic: - tab_write("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: # tab_write("#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n") @@ -392,6 +399,7 @@ def write_material( if material: special_texture_found = False tmpidx = -1 + slot = None for t in material.pov_texture_slots: tmpidx += 1 # index = material.pov.active_texture_index @@ -414,6 +422,7 @@ def write_material( ) ): special_texture_found = True + continue # Some texture found if special_texture_found or colored_specular_found: @@ -1048,7 +1057,7 @@ def string_strip_hyphen(name): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -def write_nodes(scene, pov_mat_name, ntree, file): +def write_nodes(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 @@ -1164,7 +1173,10 @@ def write_nodes(scene, pov_mat_name, ntree, file): pass else: color = link.from_node.inputs["Color"].default_value[:] - file.write(" <%.4g,%.4g,%.4g>\n" % color) + file.write( + " <%.4g,%.4g,%.4g>\n" + % (color[0], color[1], color[2]) + ) if link.from_node.inputs["Exponent"].is_linked: pass @@ -1255,7 +1267,7 @@ def write_nodes(scene, pov_mat_name, ntree, file): if node.bl_idname == "PovrayColorImageNode" and node.outputs["Pigment"].is_linked: declare_nodes.append(node.name) if node.image == "": - file.write("#declare %s = pigment { color rgb 0.8}\n" % (pov_node_name)) + file.write("#declare %s = pigment { color rgb 0.8}\n" % pov_node_name) else: im = bpy.data.images[node.image] if im.filepath and path.exists(bpy.path.abspath(im.filepath)): # (os.path) @@ -1287,7 +1299,7 @@ def write_nodes(scene, pov_mat_name, ntree, file): ) file.write(" %s\n" % once) if node.map_type != "uv_mapping": - file.write(" map_type %s\n" % (node.map_type)) + file.write(" map_type %s\n" % node.map_type) file.write( " interpolate %s\n filter all %.4g\n transmit all %.4g\n" % ( @@ -1335,7 +1347,7 @@ def write_nodes(scene, pov_mat_name, ntree, file): ) file.write(" %s\n" % once) if node.map_type != "uv_mapping": - file.write(" map_type %s\n" % (node.map_type)) + file.write(" map_type %s\n" % node.map_type) file.write(" interpolate %s\n" % node.interpolate) file.write(" }\n") file.write(" %s\n" % transform) @@ -1369,7 +1381,7 @@ def write_nodes(scene, pov_mat_name, ntree, file): 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)) + file.write(" map_type %s\n" % node.map_type) bump_size = node.inputs["Normal"].default_value if node.inputs["Normal"].is_linked: pass diff --git a/render_povray/shading_gui.py b/render_povray/shading_gui.py index 5e563097..3bb0782c 100755 --- a/render_povray/shading_gui.py +++ b/render_povray/shading_gui.py @@ -56,6 +56,40 @@ class MaterialButtonsPanel: return mat and (rd.engine in cls.COMPAT_ENGINES) +class MATERIAL_PT_POV_shading(MaterialButtonsPanel, Panel): + bl_label = "Shading" + 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 # FORMERLY : #active_node_mat(context.material) + + if mat.pov.type in {'SURFACE', 'WIRE'}: + split = layout.split() + + col = split.column() + sub = col.column() + sub.active = not mat.pov.use_shadeless + sub.prop(mat.pov, "emit") + sub.prop(mat.pov, "ambient") + sub = col.column() + sub.prop(mat.pov, "translucency") + + col = split.column() + col.prop(mat.pov, "use_shadeless") + sub = col.column() + sub.active = not mat.pov.use_shadeless + sub.prop(mat.pov, "use_tangent_shading") + sub.prop(mat.pov, "use_cubic") + + class MATERIAL_MT_POV_sss_presets(Menu): """Use this class to define pov sss preset menu.""" @@ -115,7 +149,7 @@ class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel): mat = context.material # FORMERLY : #active_node_mat(context.material) sss = mat.pov_subsurface_scattering - layout.active = (sss.use) and (not mat.pov.use_shadeless) + 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) @@ -635,6 +669,7 @@ class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel): classes = ( + MATERIAL_PT_POV_shading, MATERIAL_PT_POV_sss, MATERIAL_MT_POV_sss_presets, MATERIAL_OT_POV_sss_add_preset, diff --git a/render_povray/shading_nodes.py b/render_povray/shading_nodes.py index eb7c2bd2..55d323e3 100755 --- a/render_povray/shading_nodes.py +++ b/render_povray/shading_nodes.py @@ -608,7 +608,7 @@ def menu_func_nodes(self, context): class ObjectNodeTree(bpy.types.NodeTree): - '''Povray Material Nodes''' + """Povray Material Nodes""" bl_idname = 'ObjectNodeTree' bl_label = 'Povray Object Nodes' @@ -637,7 +637,7 @@ class ObjectNodeTree(bpy.types.NodeTree): class PovrayOutputNode(Node, ObjectNodeTree): - '''Output''' + """Output""" bl_idname = 'PovrayOutputNode' bl_label = 'Output' @@ -663,7 +663,7 @@ class PovrayOutputNode(Node, ObjectNodeTree): # -------- material # ---------------------------------------------------------------- # class PovrayTextureNode(Node, ObjectNodeTree): - '''Texture''' + """Texture""" bl_idname = 'PovrayTextureNode' bl_label = 'Simple texture' @@ -685,7 +685,7 @@ class PovrayTextureNode(Node, ObjectNodeTree): class PovrayFinishNode(Node, ObjectNodeTree): - '''Finish''' + """Finish""" bl_idname = 'PovrayFinishNode' bl_label = 'Finish' @@ -713,7 +713,7 @@ class PovrayFinishNode(Node, ObjectNodeTree): class PovrayDiffuseNode(Node, ObjectNodeTree): - '''Diffuse''' + """Diffuse""" bl_idname = 'PovrayDiffuseNode' bl_label = 'Diffuse' @@ -735,7 +735,7 @@ class PovrayDiffuseNode(Node, ObjectNodeTree): class PovrayPhongNode(Node, ObjectNodeTree): - '''Phong''' + """Phong""" bl_idname = 'PovrayPhongNode' bl_label = 'Phong' @@ -757,7 +757,7 @@ class PovrayPhongNode(Node, ObjectNodeTree): class PovraySpecularNode(Node, ObjectNodeTree): - '''Specular''' + """Specular""" bl_idname = 'PovraySpecularNode' bl_label = 'Specular' @@ -779,7 +779,7 @@ class PovraySpecularNode(Node, ObjectNodeTree): class PovrayMirrorNode(Node, ObjectNodeTree): - '''Mirror''' + """Mirror""" bl_idname = 'PovrayMirrorNode' bl_label = 'Mirror' @@ -803,7 +803,7 @@ class PovrayMirrorNode(Node, ObjectNodeTree): class PovrayAmbientNode(Node, ObjectNodeTree): - '''Ambient''' + """Ambient""" bl_idname = 'PovrayAmbientNode' bl_label = 'Ambient' @@ -820,7 +820,7 @@ class PovrayAmbientNode(Node, ObjectNodeTree): class PovrayIridescenceNode(Node, ObjectNodeTree): - '''Iridescence''' + """Iridescence""" bl_idname = 'PovrayIridescenceNode' bl_label = 'Iridescence' @@ -841,7 +841,7 @@ class PovrayIridescenceNode(Node, ObjectNodeTree): class PovraySubsurfaceNode(Node, ObjectNodeTree): - '''Subsurface''' + """Subsurface""" bl_idname = 'PovraySubsurfaceNode' bl_label = 'Subsurface' @@ -871,7 +871,7 @@ class PovraySubsurfaceNode(Node, ObjectNodeTree): class PovrayMappingNode(Node, ObjectNodeTree): - '''Mapping''' + """Mapping""" bl_idname = 'PovrayMappingNode' bl_label = 'Mapping' @@ -939,7 +939,7 @@ class PovrayMappingNode(Node, ObjectNodeTree): class PovrayMultiplyNode(Node, ObjectNodeTree): - '''Multiply''' + """Multiply""" bl_idname = 'PovrayMultiplyNode' bl_label = 'Multiply' @@ -983,7 +983,7 @@ class PovrayMultiplyNode(Node, ObjectNodeTree): class PovrayTransformNode(Node, ObjectNodeTree): - '''Transform''' + """Transform""" bl_idname = 'PovrayTransformNode' bl_label = 'Transform' @@ -1011,7 +1011,7 @@ class PovrayTransformNode(Node, ObjectNodeTree): class PovrayValueNode(Node, ObjectNodeTree): - '''Value''' + """Value""" bl_idname = 'PovrayValueNode' bl_label = 'Value' @@ -1026,7 +1026,7 @@ class PovrayValueNode(Node, ObjectNodeTree): class PovrayModifierNode(Node, ObjectNodeTree): - '''Modifier''' + """Modifier""" bl_idname = 'PovrayModifierNode' bl_label = 'Modifier' @@ -1057,7 +1057,7 @@ class PovrayModifierNode(Node, ObjectNodeTree): class PovrayPigmentNode(Node, ObjectNodeTree): - '''Pigment''' + """Pigment""" bl_idname = 'PovrayPigmentNode' bl_label = 'Color' @@ -1076,7 +1076,7 @@ class PovrayPigmentNode(Node, ObjectNodeTree): class PovrayColorImageNode(Node, ObjectNodeTree): - '''ColorImage''' + """ColorImage""" bl_idname = 'PovrayColorImageNode' bl_label = 'Image map' @@ -1162,7 +1162,7 @@ class PovrayColorImageNode(Node, ObjectNodeTree): class PovrayBumpMapNode(Node, ObjectNodeTree): - '''BumpMap''' + """BumpMap""" bl_idname = 'PovrayBumpMapNode' bl_label = 'Bump map' @@ -1242,7 +1242,7 @@ class PovrayBumpMapNode(Node, ObjectNodeTree): class PovrayImagePatternNode(Node, ObjectNodeTree): - '''ImagePattern''' + """ImagePattern""" bl_idname = 'PovrayImagePatternNode' bl_label = 'Image pattern' @@ -1323,7 +1323,7 @@ class PovrayImagePatternNode(Node, ObjectNodeTree): class ShaderPatternNode(Node, ObjectNodeTree): - '''Pattern''' + """Pattern""" bl_idname = 'ShaderPatternNode' bl_label = 'Other patterns' @@ -1407,7 +1407,7 @@ class ShaderPatternNode(Node, ObjectNodeTree): class ShaderTextureMapNode(Node, ObjectNodeTree): - '''Texture Map''' + """Texture Map""" bl_idname = 'ShaderTextureMapNode' bl_label = 'Texture map' @@ -1459,7 +1459,7 @@ class ShaderTextureMapNode(Node, ObjectNodeTree): class ShaderNormalMapNode(Node, ObjectNodeTree): - '''Normal Map''' + """Normal Map""" bl_idname = 'ShaderNormalMapNode' bl_label = 'Normal map' @@ -1507,7 +1507,7 @@ class ShaderNormalMapNode(Node, ObjectNodeTree): class ShaderNormalMapEntryNode(Node, ObjectNodeTree): - '''Normal Map Entry''' + """Normal Map Entry""" bl_idname = 'ShaderNormalMapEntryNode' bl_label = 'Normal map entry' @@ -1521,7 +1521,7 @@ class ShaderNormalMapEntryNode(Node, ObjectNodeTree): class IsoPropsNode(Node, CompositorNodeTree): - '''ISO Props''' + """ISO Props""" bl_idname = 'IsoPropsNode' bl_label = 'Iso' @@ -1546,7 +1546,7 @@ class IsoPropsNode(Node, CompositorNodeTree): class PovrayFogNode(Node, CompositorNodeTree): - '''Fog settings''' + """Fog settings""" bl_idname = 'PovrayFogNode' bl_label = 'Fog' @@ -1579,7 +1579,7 @@ class PovrayFogNode(Node, CompositorNodeTree): class PovraySlopeNode(Node, TextureNodeTree): - '''Output''' + """Output""" bl_idname = 'PovraySlopeNode' bl_label = 'Slope Map' @@ -1614,7 +1614,7 @@ class PovraySlopeNode(Node, TextureNodeTree): # -------- Texture nodes # ---------------------------------------------------------------- # class TextureOutputNode(Node, TextureNodeTree): - '''Output''' + """Output""" bl_idname = 'TextureOutputNode' bl_label = 'Color Map' @@ -1645,7 +1645,7 @@ class NODE_OT_iso_add(Operator): def execute(self, context): ob = bpy.context.object - if bpy.context.scene.use_nodes == False: + if not bpy.context.scene.use_nodes: bpy.context.scene.use_nodes = True tree = bpy.context.scene.node_tree for node in tree.nodes: @@ -1666,7 +1666,7 @@ class NODE_OT_map_create(Operator): space = context.space_data tree = space.edit_tree for node in tree.nodes: - if node.select == True: + if node.select: x, y = node.location node.select = False tmap = tree.nodes.new('ShaderTextureMapNode') @@ -1832,7 +1832,7 @@ class PovrayPatternNode(Operator): tree = space.edit_tree for node in tree.nodes: node.select = False - if self.add == True: + if self.add: tmap = tree.nodes.new('ShaderNodeValToRGB') tmap.label = "Pattern" for inp in tmap.inputs: @@ -1878,7 +1878,7 @@ class PovrayPatternNode(Operator): class UpdatePreviewMaterial(Operator): - '''Operator update preview material''' + """Operator update preview material""" bl_idname = "node.updatepreview" bl_label = "Update preview" @@ -1904,7 +1904,7 @@ class UpdatePreviewMaterial(Operator): class UpdatePreviewKey(Operator): - '''Operator update preview keymap''' + """Operator update preview keymap""" bl_idname = "wm.updatepreviewkey" bl_label = "Activate RMB" diff --git a/render_povray/shading_properties.py b/render_povray/shading_properties.py index 3a3c944d..9add125e 100755 --- a/render_povray/shading_properties.py +++ b/render_povray/shading_properties.py @@ -36,9 +36,8 @@ 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 + # No active node material + return not mat.node_tree return True return False @@ -66,12 +65,13 @@ def pov_context_tex_datablock(context): 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 + return idblock + def active_texture_name_from_uilist(self, context): """Name created texture slots the same as created texture""" @@ -105,7 +105,6 @@ def active_texture_name_from_search(self, context): except BaseException as e: print(e.__doc__) print('An exception occurred: {}'.format(e)) - pass def brush_texture_update(self, context): @@ -220,7 +219,7 @@ class RenderPovSettingsMaterial(PropertyGroup): diffuse_color: FloatVectorProperty( name="Diffuse color", - description=("Diffuse color of the material"), + description="Diffuse color of the material", precision=4, step=0.01, min=0, # max=inf, soft_max=1, @@ -367,7 +366,7 @@ class RenderPovSettingsMaterial(PropertyGroup): mirror_color: FloatVectorProperty( name="Mirror color", - description=("Mirror color of the material"), + description="Mirror color of the material", precision=4, step=0.01, min=0, # max=inf, soft_max=1, @@ -392,7 +391,7 @@ class RenderPovSettingsMaterial(PropertyGroup): line_color: FloatVectorProperty( name="Line color", - description=("Line color used for Freestyle line rendering"), + description="Line color used for Freestyle line rendering", precision=4, step=0.01, min=0, # max=inf, soft_max=1, @@ -422,10 +421,11 @@ class RenderPovSettingsMaterial(PropertyGroup): specular_color: FloatVectorProperty( name="Specular color", - description=("Specular color of the material "), + description="Specular color of the material ", precision=4, step=0.01, - min=0, # max=inf, soft_max=1, + min=0.0, + soft_max=1.0, default=(1.0, 1.0, 1.0), options={"ANIMATABLE"}, subtype="COLOR", @@ -1196,7 +1196,7 @@ class MaterialRaytraceMirror(PropertyGroup): mirror_color: FloatVectorProperty( name="Mirror color", - description=("Mirror color of the material"), + description="Mirror color of the material", precision=4, step=0.01, default=(1.0, 1.0, 1.0), @@ -1240,7 +1240,7 @@ class MaterialSubsurfaceScattering(PropertyGroup): color: FloatVectorProperty( name="Scattering color", - description=("Scattering color"), + description="Scattering color", precision=4, step=0.01, default=(0.604, 0.604, 0.604), @@ -1289,7 +1289,7 @@ class MaterialSubsurfaceScattering(PropertyGroup): radius: FloatVectorProperty( name="RGB Radius", - description=("Mean red/green/blue scattering path length"), + description="Mean red/green/blue scattering path length", precision=4, step=0.01, min=0.001, diff --git a/render_povray/texturing.py b/render_povray/texturing.py index 225c5369..060316be 100755 --- a/render_povray/texturing.py +++ b/render_povray/texturing.py @@ -43,10 +43,7 @@ def write_texture_influence( ): """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 + trans = 1.0 - mater.pov.alpha if mater.pov.use_transparency else 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 @@ -79,9 +76,8 @@ def write_texture_influence( 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 + # PROCEDURAL TEXTURE image_filename = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name)) if image_filename: if t.use_map_color_diffuse: @@ -106,10 +102,9 @@ def write_texture_influence( # 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 + # NOT A PROCEDURAL TEXTURE # PACKED if tex.image.packed_file: orig_image_filename = tex.image.filepath_raw @@ -131,12 +126,14 @@ def write_texture_influence( 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) + if ( + image_filename + and 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: @@ -169,7 +166,7 @@ def write_texture_influence( 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) @@ -193,7 +190,7 @@ def write_texture_influence( c = 1 while c <= exported_lights_count: - tab_write("slope { lampTarget%s }\n" % (c)) + 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 @@ -292,18 +289,19 @@ def write_texture_influence( 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, + if texture_dif: + if 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) + elif 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_"): @@ -452,9 +450,9 @@ def write_texture_influence( and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov ): - tab_write("]}}\n") + tab_write(r"]}}"+"\n") else: - tab_write("]}\n") + tab_write(r"}"+"\n") if texture_spec != "": tab_write("]\n") # -------- Second index for mapping specular max value -------- # @@ -579,7 +577,7 @@ def write_texture_influence( ) # 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' + '{%s "%s" %s bump_size %.4g }%s\n' % ( image_format(texture_norm), texture_norm, @@ -594,7 +592,7 @@ def write_texture_influence( and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov ): - tab_write("}}\n") + tab_write("]\n}}\n") else: tab_write("}\n") elif colored_specular_found: @@ -771,7 +769,7 @@ def write_texture_influence( ) # 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' + '{%s "%s" %s bump_size %.4g }%s\n' % ( image_format(texture_norm), texture_norm, @@ -786,7 +784,7 @@ def write_texture_influence( and mater.pov_raytrace_mirror.gloss_factor < 1.0 and not using_uberpov ): - tab_write("}}\n") + tab_write("]}}\n") else: tab_write("}\n") if texture_spec != "" and mater.pov.replacement_text == "": @@ -853,7 +851,7 @@ def write_texture_influence( for t in mater.pov_texture_slots: if t and tex.pov.tex_pattern_type != "emulator": - procedural_flag = True + # PROCEDURAL TEXTURE image_filename = string_strip_hyphen(bpy.path.clean_name(tex.name)) if ( t @@ -865,30 +863,29 @@ def write_texture_influence( 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, - ) + if image_filename and 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 index 8d285383..2e317a6c 100755 --- a/render_povray/texturing_gui.py +++ b/render_povray/texturing_gui.py @@ -106,7 +106,7 @@ class WORLD_TEXTURE_SLOTS_UL_POV_layerlist(UIList): # 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') + layout.prop(slot, "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!). @@ -133,7 +133,7 @@ class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(UIList): # 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') + layout.prop(slot, "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!). @@ -203,7 +203,6 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel): @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 ( diff --git a/render_povray/texturing_properties.py b/render_povray/texturing_properties.py index ceb60264..c085cedf 100755 --- a/render_povray/texturing_properties.py +++ b/render_povray/texturing_properties.py @@ -203,7 +203,7 @@ class MaterialTextureSlot(PropertyGroup): offset: FloatVectorProperty( name="Offset", - description=("Fine tune of the texture mapping X, Y and Z locations "), + description="Fine tune of the texture mapping X, Y and Z locations ", precision=4, step=0.1, soft_min=-100.0, @@ -507,7 +507,7 @@ class WorldTextureSlot(PropertyGroup): offset: FloatVectorProperty( name="Offset", - description=("Fine tune of the texture mapping X, Y and Z locations "), + description="Fine tune of the texture mapping X, Y and Z locations ", precision=4, step=0.1, soft_min=-100.0, |