Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaurice Raybaud <mauriceraybaud@hotmail.fr>2022-04-25 15:38:30 +0300
committerMaurice Raybaud <mauriceraybaud@hotmail.fr>2022-04-25 15:38:30 +0300
commitd1b824f3c2a7a7b3e37e70f336e5a1580028c63e (patch)
tree5173c455757cbb873633adf47104b7a70975bd53
parentd8258116362889d6d5f7af4e544f7b9f1abef1e8 (diff)
POV: fix some nested code and further files structure cleanup
* FIX: wrongly nested pov braces made the default outpout file fail * FIX: use agnostic metallic property rather than create a duplicate * FIX: some 2.8 deprecated properties rewired in spec;diff; emit;ambient * FIX: clean up, hierarchize and redesign Global Settings ui panel * FIX: re-wire world background alpha to agnostic prop and redo its ui * FIX: wrong nested pov braces making the default outpout file fail * FIX: use agnostic metallic property rather than create a duplicate * FIX: reduced arguments numbers by imports and relocating variables * FIX: use more list comprehesions to reduce nested conditions levels * FIX: use more consistent class names but cleanup still not finished * FIX: use single quotes for enums preferably to distinguish strings * FIX: basic level of nodes based material (diffuse color) broken API * FIX: blurry reflection corner case caused output file to fail * FIX: added context managing ("with") syntaxes reducing crash cases ___________________________________________________________ * ADD: model_all.py file to extract mostly object level loop and utils * ADD: model_meta_topology.py file to extract metaballs export * ADD: object_primitives_topology.py to extract pov compound primitives * ADD: nodes_fn.py file to extract main node exporting function * ADD: nodes_gui.py file to extract node operators and menus * ADD: nodes_properties.py file to extract nodes sub parameters * ADD: particles_properties.py to extract particles and fx parameters * ADD: render_core.py to extract main RenderEngine inheriting class(es) * ADD: shading_ray_properties.py to extract pathtraced shader parameters * ADD: texturing_procedural.py to extract algorithmic texture influences ___________________________________________________________ * UPDATE: workspace tools icons and a couple of other icons choices * RENAME: pov.add.polygontocircle.dat macro workspace tool icon * RENAME: base_ui.py to ui_core.py * RENAME: shading_nodes.py to nodes.py * RENAME: df3_library.py to voxel_lib.py to make dot lookup inform more * RENAME: object_mesh_topology.py to model_poly_topology.py * RENAME: object_curve_topology.py to model_curve_topology.py * RENAME: object_gui.py to model_gui.py * RENAME: object_primitives.py to model_primitives.py * RENAME: object_properties.py to model_properties.py * RENAME: object_particles.py to particles.py
-rwxr-xr-xrender_povray/__init__.py399
-rwxr-xr-xrender_povray/base_ui.py305
-rw-r--r--render_povray/icons/pov.add.box.datbin4346 -> 4346 bytes
-rw-r--r--render_povray/icons/pov.add.cylinder.datbin5858 -> 5876 bytes
-rw-r--r--render_povray/icons/pov.add.infinite_plane.datbin8090 -> 9278 bytes
-rw-r--r--render_povray/icons/pov.add.loft.datbin17810 -> 16622 bytes
-rw-r--r--render_povray/icons/pov.add.polygontocircle.dat (renamed from render_povray/icons/pov.add.polytocircle.dat)bin5192 -> 6344 bytes
-rw-r--r--render_povray/icons/pov.add.prism.datbin4382 -> 4382 bytes
-rw-r--r--render_povray/icons/pov.add.rainbow.datbin9422 -> 9422 bytes
-rw-r--r--render_povray/icons/pov.add.sphere.datbin11402 -> 11348 bytes
-rw-r--r--render_povray/icons/pov.add.spheresweep.datbin9062 -> 9152 bytes
-rw-r--r--render_povray/model_all.py915
-rw-r--r--render_povray/model_curve_topology.py996
-rw-r--r--[-rwxr-xr-x]render_povray/model_gui.py (renamed from render_povray/object_gui.py)546
-rw-r--r--render_povray/model_meta_topology.py306
-rw-r--r--render_povray/model_poly_topology.py719
-rw-r--r--render_povray/model_primitives.py796
-rw-r--r--[-rwxr-xr-x]render_povray/model_primitives_topology.py (renamed from render_povray/object_primitives.py)1109
-rw-r--r--[-rwxr-xr-x]render_povray/model_properties.py (renamed from render_povray/object_properties.py)18
-rw-r--r--render_povray/nodes.py1056
-rw-r--r--render_povray/nodes_fn.py704
-rw-r--r--render_povray/nodes_gui.py278
-rw-r--r--render_povray/nodes_properties.py703
-rwxr-xr-xrender_povray/object_curve_topology.py971
-rwxr-xr-xrender_povray/object_mesh_topology.py1534
-rw-r--r--[-rwxr-xr-x]render_povray/particles.py (renamed from render_povray/object_particles.py)187
-rw-r--r--render_povray/particles_properties.py718
-rwxr-xr-xrender_povray/render.py1349
-rw-r--r--render_povray/render_core.py784
-rwxr-xr-xrender_povray/render_gui.py250
-rwxr-xr-xrender_povray/render_properties.py17
-rwxr-xr-xrender_povray/scenography.py688
-rwxr-xr-xrender_povray/scenography_gui.py112
-rwxr-xr-xrender_povray/scripting.py746
-rwxr-xr-xrender_povray/scripting_gui.py53
-rwxr-xr-xrender_povray/scripting_properties.py4
-rwxr-xr-xrender_povray/shading.py1663
-rwxr-xr-xrender_povray/shading_gui.py130
-rwxr-xr-xrender_povray/shading_nodes.py1992
-rwxr-xr-xrender_povray/shading_properties.py1336
-rw-r--r--render_povray/shading_ray_properties.py374
-rwxr-xr-xrender_povray/texturing.py623
-rwxr-xr-xrender_povray/texturing_gui.py282
-rw-r--r--render_povray/texturing_procedural.py694
-rwxr-xr-xrender_povray/texturing_properties.py91
-rw-r--r--render_povray/ui_core.py280
-rwxr-xr-xrender_povray/update_files.py32
-rw-r--r--[-rwxr-xr-x]render_povray/voxel_lib.py (renamed from render_povray/df3_library.py)0
48 files changed, 12066 insertions, 11694 deletions
diff --git a/render_povray/__init__.py b/render_povray/__init__.py
index 3d5e5315..fb7ccb28 100755
--- a/render_povray/__init__.py
+++ b/render_povray/__init__.py
@@ -4,75 +4,139 @@
"""Import, export and render to POV engines.
-These engines can be POV-Ray or Uberpov but others too, since POV is a
-Scene Description Language. The script has been split in as few files
-as possible :
+These engines can be POV-Ray, Uberpov, HgPovray but others too, since POV is a
+Scene Description Language. The script has been split in as few files as possible and
+metaphorically structured as a train with some boilerplate rendering locomotive,
+followed by its cars each representing a thematic in the 3D field:
+
+########################################### RENDERING ###########################################
__init__.py :
- Initialize properties
+ Provide script's metadata, Initialize addon preferences, (re)load package modules
+
+ui_core.py :
+ Set up workspaces and load other ui files for the user to set up all variables
+
+render_core.py :
+ Define the POV render engines declinations from generic Blender RenderEngine class
+
+render_properties.py :
+ Initialize properties for render parameters (Blender generic and POV native)
+
+render_gui.py :
+ Display properties from render_properties.py for user to change them
+
+render.py :
+ Translate render properties (Blender and POV native) to POV, ini file and bash
-base_ui.py :
- Provide property buttons for the user to set up the variables
+ __------------------Z__
+ _--¨¨] | __ __ _____________||
+ _-¨7____/ | | °|° | □□□ □□□ □□□ ||
+ (===========|=| | |=============||
+ `-(@)@)--------------------(@)(@)-'
+############################################# LAYOUT ##############################################
scenography_properties.py
- Initialize properties for translating Blender cam/light/environment parameters to pov
+ Initialize properties for passing layout (camera/light/environment) parameters to pov
scenography_gui.py :
- Display cam/light/environment properties from scenography_properties.py for user to change them
+ Display camera/light/environment properties from scenography_properties.py for user to change
scenography.py
- Translate cam/light/environment properties to corresponding pov features
+ Translate camera/light/environment properties to corresponding pov features
-object_properties.py :
- nitialize properties for translating Blender objects parameters to pov
+ __------------------Z__ ____________________
+ _--¨¨] | __ __ _____________|||____________________|
+ _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ |
+ (===========|=| | |=============|||====================|
+ `-(@)@)--------------------(@)(@)-^-(@)(@)--------(@)(@)-
+############################################### MODEL #############################################
-object_primitives.py :
- Display some POV native primitives in 3D view for input and output
+model_properties.py :
+ Initialize properties for translating Blender geometry objects parameters to pov
-object_mesh_topology.py :
- Translate to POV the meshes geometries
+model_gui.py :
+ Display properties from model_properties.py for the user to change them
-object_curve_topology.py :
+model_all.py :
+ Translate to POV the object level data
+
+model_poly_topology.py :
+ Translate to POV the mesh based geometries
+
+model_curve_topology.py :
Translate to POV the curve based geometries
-object_particles.py :
- Translate to POV the particle based geometries
+model_meta_topology.py :
+ Translate to POV the metaball based geometries
+
+model_primitives.py :
+ Display some simple POV native primitives in 3D view for input and output
+
+model_primitives_topology.py :
+ Display some POV native complex or compound primitives in 3D view for input and output
-object_gui.py :
- Display properties from object_properties.py for user to change them
+ __------------------Z__ ____________________ ____________________
+ _--¨¨] | __ __ _____________|||____________________|||____________________
+ _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□
+ (===========|=| | |=============|||====================|||====================
+ `-(@)@)--------------------(@)(@)-^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)
+############################################ SHADING #############################################
shading_properties.py :
Initialize properties for translating Blender materials parameters to pov
-shading_nodes.py :
- Translate node trees to the pov file
+shading_ray_properties.py :
+ Initialize properties for translating Blender ray paths relevant material parameters to pov
shading_gui.py :
- Display properties from shading_properties.py for user to change them
+ Display variables from shading_properties.py and shading_ray_properties.py for user to change
shading.py
Translate shading properties to declared textures at the top of a pov file
texturing_properties.py :
- Initialize properties for translating Blender materials /world... texture influences to pov
+ Initialize properties for translating Blender materials/world... texture influences to pov
texturing_gui.py :
- Display properties from texturing_properties.py for user to change them
+ Display properties from texturing_properties.py available for user to change
texturing.py :
- Translate blender texture influences into POV
+ Translate blender pixel based bitmap texture influences into POV
-render_properties.py :
- Initialize properties for render parameters (Blender and POV native)
+texturing_procedural.py :
+ Translate blender algorithmic procedural texture influences into POV
-render_gui.py :
- Display properties from render_properties.py for user to change them
+ __------------------Z__ ____________________ ____________________ ________________
+ _--¨¨] | __ __ _____________|||____________________|||____________________|||________________
+ _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□
+(===========|=| | |=============|||====================|||====================|||================
+ `-(@)@)--------------------(@)(@)-^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)-^-(@)(@)----------
+############################################ VFX/TECH #############################################
-render.py :
- Translate render properties (Blender and POV native) to POV, ini file and bash
+particles_properties.py :
+ Initialize all strands, fx, or particles based objects properties to be translated to POV
+
+particles.py :
+ Translate to POV the particle based geometries
+
+voxel_lib.py :
+ Render smoke to *.df3 files using an updated version of Mike Kost's df3.py legacy library
+
+nodes_properties.py :
+ Define all node items and their respective variables or sockets available to POV node trees
+
+nodes.py :
+ Translate node trees to the pov file
+
+nodes_fn.py :
+ Functions toolbox used by nodes.py to translate node trees to the pov file
+
+nodes_gui.py :
+ Operators and menus to interact with POV specific node trees
scripting_properties.py :
- Initialize properties for scene description language parameters (POV native)
+ Initialize properties for hand written scene description language fragments (POV native)
scripting_gui.py :
Display properties from scripting_properties.py for user to add his custom POV code
@@ -80,15 +144,13 @@ scripting_gui.py :
scripting.py :
Insert POV native scene description elements into blender scene or to exported POV file
-df3_library.py :
- Render smoke to *.df3 files
-
update_files.py :
Update new variables to values from older API. This file needs an update
+######################################## PRESETS/TEMPLATES ########################################
Along these essential files also coexist a few additional libraries to help make
-Blender stand up to other POV IDEs such as povwin or QTPOV
+Blender stand up to other POV enabled IDEs (povwin, POV for Mac, QTPOV, VSCode, Vim...)
presets :
Material (sss)
apple.py ; chicken.py ; cream.py ; Ketchup.py ; marble.py ;
@@ -135,7 +197,7 @@ Blender stand up to other POV IDEs such as povwin or QTPOV
bl_info = {
- 'name': "Persistence of Vision",
+ 'name': "POV@Ble",
'author': "Campbell Barton, "
"Maurice Raybaud, "
"Leonid Desyatkov, "
@@ -143,10 +205,10 @@ bl_info = {
"Constantin Rahn, "
"Silvio Falcinelli,"
"Paco García",
- "version": (0, 1, 2),
- 'blender': (2, 81, 0),
+ 'version': (0, 1, 3),
+ 'blender': (3, 2, 0),
'location': "Render Properties > Render Engine > Persistence of Vision",
- 'description': "Persistence of Vision integration for blender",
+ 'description': "Persistence of Vision addon for Blender",
'doc_url': "{BLENDER_MANUAL_URL}/addons/render/povray.html",
'category': "Render",
'warning': "Co-maintainers welcome",
@@ -161,25 +223,39 @@ bl_info = {
if "bpy" in locals():
import importlib
- importlib.reload(base_ui)
- importlib.reload(shading_nodes)
+ importlib.reload(ui_core)
+ importlib.reload(nodes_properties)
+ importlib.reload(nodes_gui)
+ importlib.reload(nodes_fn)
+ importlib.reload(nodes)
importlib.reload(scenography_properties)
+ importlib.reload(scenography_gui)
importlib.reload(scenography)
importlib.reload(render_properties)
importlib.reload(render_gui)
+ importlib.reload(render_core)
importlib.reload(render)
importlib.reload(shading_properties)
+ importlib.reload(shading_ray_properties)
importlib.reload(shading_gui)
importlib.reload(shading)
+ importlib.reload(texturing_procedural)
importlib.reload(texturing_properties)
importlib.reload(texturing_gui)
importlib.reload(texturing)
- importlib.reload(object_properties)
- importlib.reload(object_gui)
- importlib.reload(object_mesh_topology)
- importlib.reload(object_curve_topology)
- importlib.reload(object_primitives)
+ importlib.reload(model_properties)
+ importlib.reload(model_gui)
+ importlib.reload(model_all)
+ importlib.reload(model_poly_topology)
+ importlib.reload(model_meta_topology)
+ importlib.reload(model_curve_topology)
+ importlib.reload(model_primitives)
+ importlib.reload(model_primitives_topology)
+ importlib.reload(particles_properties)
+ importlib.reload(particles)
+ importlib.reload(scripting_properties)
importlib.reload(scripting_gui)
+ importlib.reload(scripting)
importlib.reload(update_files)
else:
@@ -189,15 +265,23 @@ else:
from bpy.props import StringProperty, BoolProperty, EnumProperty
from . import (
- base_ui,
+ ui_core,
render_properties,
scenography_properties,
+ nodes_properties,
+ nodes_gui,
+ nodes,
shading_properties,
+ shading_ray_properties,
texturing_properties,
- object_properties,
+ model_properties,
scripting_properties,
render,
- object_primitives, # for import and export of POV specific primitives
+ render_core,
+ model_primitives,
+ model_primitives_topology,
+ particles_properties,
+ particles,
)
# ---------------------------------------------------------------- #
@@ -211,85 +295,96 @@ class POV_OT_update_addon(bpy.types.Operator):
bl_idname = "pov.update_addon"
bl_label = "Update POV addon"
- def execute(self, context): # sourcery no-metrics
+ def execute(self, context):
import os
- import tempfile
import shutil
- import urllib.request
+ import tempfile
import urllib.error
+ import urllib.request
import zipfile
- def recursive_overwrite(src, dest, ignore=None):
- if os.path.isdir(src):
- if not os.path.isdir(dest):
- os.makedirs(dest)
- files = os.listdir(src)
- if ignore is not None:
- ignored = ignore(src, files)
- else:
- ignored = set()
- for f in files:
- if f not in ignored:
- recursive_overwrite(os.path.join(src, f), os.path.join(dest, f), ignore)
- else:
- shutil.copyfile(src, dest)
+ def recursive_overwrite(self, src, dest, ignore=None):
+ """Update the script automatically (along with other addons).
+
+ Arguments:
+ src -- path where to update from
+ dest -- storing temporary download here
+ Keyword Arguments:
+ ignore -- leave some directories alone (default: {None})
+
+ Returns:
+ finished flag for operator which is a set()
+ """
+ if os.path.isdir(src):
+ if not os.path.isdir(dest):
+ os.makedirs(dest)
+ files = os.listdir(src)
+ ignored = ignore(src, files) if ignore is not None else set()
+ unignored_files = (fle for fle in files if fle not in ignored)
+ for f in unignored_files:
+ source = os.path.join(src, f)
+ destination = os.path.join(dest, f)
+ recursive_overwrite(source, destination, ignore)
+ else:
+ shutil.copyfile(src, dest)
print("-" * 20)
print("Updating POV addon...")
with tempfile.TemporaryDirectory() as temp_dir_path:
- temp_zip_path = os.path.join(temp_dir_path, 'master.zip')
+ temp_zip_path = os.path.join(temp_dir_path, "master.zip")
# Download zip archive of latest addons master branch commit
# More work needed so we also get files from the shared addons presets /pov folder
# 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'
+ url = "https://github.com/blender/blender-addons/archive/refs/heads/master.zip"
try:
print("Downloading", url)
with urllib.request.urlopen(url, timeout=60) as url_handle, open(
- temp_zip_path, 'wb'
+ 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_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
- # becomes render_povray/shading_nodes.py
- target_path = os.path.join(
- temp_dir_path, os.path.join(*member.split('/')[1:-1])
- )
-
- filename = os.path.basename(member)
- # Skip directories
- if len(filename) == 0:
- continue
-
- # Create the target directory if necessary
- if not os.path.exists(target_path):
- os.makedirs(target_path)
-
- source = zip_archive.open(member)
- target = open(os.path.join(target_path, filename), "wb")
-
- with source, target:
- shutil.copyfileobj(source, target)
- print('copying', source, 'to', target)
-
- extracted_render_povray_path = os.path.join(temp_dir_path, 'render_povray')
+ pov_addon_pkg = (member for member in zip_archive.namelist() if
+ "blender-addons-master/render_povray" in member)
+ for member in pov_addon_pkg:
+ # Remove the first directory and the filename
+ # e.g. blender-addons-master/render_povray/nodes.py
+ # becomes render_povray/nodes.py
+ target_path = os.path.join(
+ temp_dir_path, os.path.join(*member.split("/")[1:-1])
+ )
+
+ filename = os.path.basename(member)
+ # Skip directories
+ if not filename:
+ continue
+
+ # Create the target directory if necessary
+ if not os.path.exists(target_path):
+ os.makedirs(target_path)
+
+ source = zip_archive.open(member)
+ target = open(os.path.join(target_path, filename), "wb")
+
+ with source, target:
+ shutil.copyfileobj(source, target)
+ print("copying", source, "to", target)
+
+ extracted_render_povray_path = os.path.join(temp_dir_path, "render_povray")
if not os.path.exists(extracted_render_povray_path):
- self.report({'ERROR'}, "Could not extract ZIP archive! Aborting.")
- return {'FINISHED'}
+ 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__))
+ render_povray_dir = os.path.abspath(os.path.dirname(__file__)) # Unnecessary abspath?
print("POV addon addon folder:", render_povray_dir)
# TODO: Create backup
@@ -298,17 +393,18 @@ class POV_OT_update_addon(bpy.types.Operator):
# (only directories and *.py files, user might have other stuff in there!)
print("Deleting old POV addon files")
# remove __init__.py
- os.remove(os.path.join(render_povray_dir, '__init__.py'))
+ os.remove(os.path.join(render_povray_dir, "__init__.py"))
# remove all folders
- DIRNAMES = 1
- for directory in next(os.walk(render_povray_dir))[DIRNAMES]:
+ dir_names = 1
+ for directory in next(os.walk(render_povray_dir))[dir_names]:
shutil.rmtree(os.path.join(render_povray_dir, directory))
print("Copying new POV addon files")
# copy new POV addon files
# copy __init__.py
shutil.copy2(
- os.path.join(extracted_render_povray_path, '__init__.py'), render_povray_dir
+ os.path.join(extracted_render_povray_path, "__init__.py"),
+ render_povray_dir,
)
# copy all folders
recursive_overwrite(extracted_render_povray_path, render_povray_dir)
@@ -316,8 +412,8 @@ class POV_OT_update_addon(bpy.types.Operator):
bpy.ops.preferences.addon_refresh()
print("POV addon update finished, restart Blender for the changes to take effect.")
print("-" * 20)
- self.report({'WARNING'}, "Restart Blender!")
- return {'FINISHED'}
+ self.report({"WARNING"}, "Restart Blender!")
+ return {"FINISHED"}
# ---------------------------------------------------------------- #
@@ -325,34 +421,35 @@ class POV_OT_update_addon(bpy.types.Operator):
# ---------------------------------------------------------------- #
-class PovrayPreferences(bpy.types.AddonPreferences):
- """Declare preference variables to set up POV binary"""
+class PovPreferences(bpy.types.AddonPreferences):
+ """Declare preference variables to set up POV binary."""
bl_idname = __name__
branch_feature_set_povray: EnumProperty(
- name='Feature Set',
- description='Choose between official (POV-Ray) or (UberPOV) '
- 'development branch features to write in the pov file',
+ 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",
+ default="",
)
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,
)
@@ -360,38 +457,40 @@ 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',
+ name="Cancel Render Sound",
+ description="Path to cancelled or render time error sound file",
subtype='FILE_PATH',
)
def draw(self, context):
layout = self.layout
- layout.prop(self, "branch_feature_set_povray")
- layout.prop(self, "filepath_povray")
- layout.prop(self, "docpath_povray")
- layout.prop(self, "filepath_complete_sound")
- layout.prop(self, "filepath_parse_error_sound")
- layout.prop(self, "filepath_cancel_sound")
- layout.prop(self, "use_sounds", icon="SOUND")
- layout.operator("pov.update_addon", icon='FILE_REFRESH')
+ layout.prop(self, 'branch_feature_set_povray')
+ layout.prop(self, 'filepath_povray')
+ layout.prop(self, 'docpath_povray')
+ layout.prop(self, 'filepath_complete_sound')
+ layout.prop(self, 'filepath_parse_error_sound')
+ layout.prop(self, 'filepath_cancel_sound')
+ layout.prop(self, 'use_sounds', icon='SOUND')
+ layout.operator('pov.update_addon', icon='FILE_REFRESH')
+ layout.operator("wm.url_open", text="Community",icon='EVENT_F').url = \
+ "https://www.facebook.com/povable"
classes = (
POV_OT_update_addon,
- PovrayPreferences,
+ PovPreferences,
)
@@ -402,21 +501,33 @@ def register():
render_properties.register()
scenography_properties.register()
shading_properties.register()
+ shading_ray_properties.register()
texturing_properties.register()
- object_properties.register()
+ model_properties.register()
+ particles_properties.register()
scripting_properties.register()
+ nodes_properties.register()
+ nodes_gui.register()
render.register()
- base_ui.register()
- object_primitives.register()
+ render_core.register()
+ ui_core.register()
+ model_primitives_topology.register()
+ model_primitives.register()
def unregister():
- object_primitives.unregister()
- base_ui.unregister()
+ model_primitives.unregister()
+ model_primitives_topology.unregister()
+ ui_core.unregister()
+ render_core.unregister()
render.unregister()
+ nodes_gui.unregister()
+ nodes_properties.unregister()
scripting_properties.unregister()
- object_properties.unregister()
+ particles_properties.unregister()
+ model_properties.unregister()
texturing_properties.unregister()
+ shading_ray_properties.unregister()
shading_properties.unregister()
scenography_properties.unregister()
render_properties.unregister()
@@ -425,7 +536,7 @@ def unregister():
unregister_class(cls)
-if __name__ == "__main__":
+if __name__ == '__main__':
register()
# ------------8<---------[ BREAKPOINT ]--------------8<----------- # Move this snippet around
diff --git a/render_povray/base_ui.py b/render_povray/base_ui.py
deleted file mode 100755
index 5ecf53b6..00000000
--- a/render_povray/base_ui.py
+++ /dev/null
@@ -1,305 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-
-"""User interface imports and preferences for the addon."""
-
-# 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 (
-# Operator,
-# Menu,
-# UIList,
-# Panel,
-# Brush,
-# Material,
-# Light,
-# World,
-# ParticleSettings,
-# FreestyleLineStyle,
-# )
-
-# from bl_operators.presets import AddPresetBase
-
-from . import (
- render_gui,
- scenography_gui,
- object_gui,
- shading_gui,
- texturing_gui,
- shading_nodes, # for POV specific nodes
- scripting_gui,
- update_files,
-)
-
-
-# ------------ POV-Centric WORKSPACE ------------ #
-@persistent
-def pov_centric_moray_like_workspace(dummy):
- """Set up a POV centric Workspace if addon was activated and saved as default renderer.
-
- This would bring a ’_RestrictData’ error because UI needs to be fully loaded before
- workspace changes so registering this function in bpy.app.handlers is needed.
- By default handlers are freed when loading new files, but here we want the handler
- to stay running across multiple files as part of this add-on. That is why the
- bpy.app.handlers.persistent decorator is used (@persistent) above.
- """
- # Scripting workspace may have been altered from factory though, so should
- # we put all within a Try... Except AttributeErrors ? Any better solution ?
- # Should it simply not run when opening existing file? be a preferences operator to create
- # Moray like workspace
-
- 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(
- "\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()
-
- # -----------------------------------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
- 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:
- return not mat.node_tree
- return True
- return False
-
-
-def simple_material(mat):
- """Test if a material is nodeless."""
- return (mat is not None) and (not mat.use_nodes)
-
-
-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':
- return idblock
-
- # idblock = bpy.context.active_object.active_material
- idblock = context.view_layer.objects.active.active_material
- if idblock and context.scene.texture_context == 'MATERIAL':
- return idblock
-
- idblock = context.scene.world
- if idblock and context.scene.texture_context == 'WORLD':
- return idblock
-
- idblock = context.light
- if idblock and context.scene.texture_context == 'LIGHT':
- return idblock
-
- if context.particle_system and context.scene.texture_context == 'PARTICLES':
- idblock = context.particle_system.settings
- return idblock
-
- idblock = context.line_style
- if idblock and context.scene.texture_context == 'LINESTYLE':
- return idblock
-
-
-# class TextureTypePanel(TextureButtonsPanel):
-
-# @classmethod
-# def poll(cls, context):
-# tex = context.texture
-# engine = context.scene.render.engine
-# return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES))
-
-
-def register():
- update_files.register()
- render_gui.register()
- scenography_gui.register()
- object_gui.register()
- shading_gui.register()
- texturing_gui.register()
- shading_nodes.register()
- scripting_gui.register()
-
- if pov_centric_moray_like_workspace not in bpy.app.handlers.load_post:
- bpy.app.handlers.load_post.append(pov_centric_moray_like_workspace)
-
-
-def unregister():
- if pov_centric_moray_like_workspace in bpy.app.handlers.load_post:
- bpy.app.handlers.load_post.remove(pov_centric_moray_like_workspace)
-
- scripting_gui.unregister()
- shading_nodes.unregister()
- texturing_gui.unregister()
- shading_gui.unregister()
- object_gui.unregister()
- scenography_gui.unregister()
- render_gui.unregister()
- update_files.unregister()
diff --git a/render_povray/icons/pov.add.box.dat b/render_povray/icons/pov.add.box.dat
index c6d27a5f..8df6b7a7 100644
--- a/render_povray/icons/pov.add.box.dat
+++ b/render_povray/icons/pov.add.box.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.cylinder.dat b/render_povray/icons/pov.add.cylinder.dat
index 9820412a..ae21a9ea 100644
--- a/render_povray/icons/pov.add.cylinder.dat
+++ b/render_povray/icons/pov.add.cylinder.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.infinite_plane.dat b/render_povray/icons/pov.add.infinite_plane.dat
index 932e0128..4de537bc 100644
--- a/render_povray/icons/pov.add.infinite_plane.dat
+++ b/render_povray/icons/pov.add.infinite_plane.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.loft.dat b/render_povray/icons/pov.add.loft.dat
index e340425a..cf73b495 100644
--- a/render_povray/icons/pov.add.loft.dat
+++ b/render_povray/icons/pov.add.loft.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.polytocircle.dat b/render_povray/icons/pov.add.polygontocircle.dat
index bc8df737..9b47d16c 100644
--- a/render_povray/icons/pov.add.polytocircle.dat
+++ b/render_povray/icons/pov.add.polygontocircle.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.prism.dat b/render_povray/icons/pov.add.prism.dat
index 0459bdd3..ce184e0c 100644
--- a/render_povray/icons/pov.add.prism.dat
+++ b/render_povray/icons/pov.add.prism.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.rainbow.dat b/render_povray/icons/pov.add.rainbow.dat
index fd72b434..c59f371a 100644
--- a/render_povray/icons/pov.add.rainbow.dat
+++ b/render_povray/icons/pov.add.rainbow.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.sphere.dat b/render_povray/icons/pov.add.sphere.dat
index b202e5ca..c820fe02 100644
--- a/render_povray/icons/pov.add.sphere.dat
+++ b/render_povray/icons/pov.add.sphere.dat
Binary files differ
diff --git a/render_povray/icons/pov.add.spheresweep.dat b/render_povray/icons/pov.add.spheresweep.dat
index 67551166..52a9f5c0 100644
--- a/render_povray/icons/pov.add.spheresweep.dat
+++ b/render_povray/icons/pov.add.spheresweep.dat
Binary files differ
diff --git a/render_povray/model_all.py b/render_povray/model_all.py
new file mode 100644
index 00000000..412894a4
--- /dev/null
+++ b/render_povray/model_all.py
@@ -0,0 +1,915 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Translate to POV the control point compound geometries.
+
+Such as polygon meshes or curve based shapes.
+"""
+
+# --------
+# -- Faster mesh export ...one day
+# import numpy as np
+# --------
+import bpy
+from . import texturing # for how textures influence shaders
+from . import model_poly_topology
+# from .texturing import local_material_names
+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"""
+ 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],
+ )
+ )
+
+# objectNames = {}
+DEF_OBJ_NAME = "Default"
+
+
+def objects_loop(
+ file,
+ scene,
+ sel,
+ csg,
+ material_names_dictionary,
+ unpacked_images,
+ tab_level,
+ tab_write,
+ info_callback,
+):
+ # global preview_dir
+ # global smoke_path
+ # global global_matrix
+ # global comments
+
+ # global tab
+ """write all meshes as POV mesh2{} syntax to exported file"""
+ # # some numpy functions to speed up mesh export NOT IN USE YET
+ # # Current 2.93 beta numpy linking has troubles so definitions commented off for now
+
+ # # TODO: also write a numpy function to read matrices at object level?
+ # # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
+ # def read_verts_co(self, mesh):
+ # #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # # using 'float32' vert coordinates for now until any issue is reported
+ # mverts_co = np.zeros((len(mesh.vertices) * 3), dtype=np.float32)
+ # mesh.vertices.foreach_get("co", mverts_co)
+ # return np.reshape(mverts_co, (len(mesh.vertices), 3))
+
+ # def read_verts_idx(self, mesh):
+ # mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
+ # mesh.vertices.foreach_get("index", mverts_idx)
+ # return np.reshape(mverts_idx, (len(mesh.vertices), 1))
+
+ # def read_verts_norms(self, mesh):
+ # #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # # using less accurate 'float16' normals for now until any issue is reported
+ # mverts_no = np.zeros((len(mesh.vertices) * 3), dtype=np.float16)
+ # mesh.vertices.foreach_get("normal", mverts_no)
+ # return np.reshape(mverts_no, (len(mesh.vertices), 3))
+
+ # def read_faces_idx(self, mesh):
+ # mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
+ # mesh.loop_triangles.foreach_get("index", mfaces_idx)
+ # return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))
+
+ # def read_faces_verts_indices(self, mesh):
+ # mfaces_verts_idx = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
+ # mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
+ # return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))
+
+ # # Why is below different from vertex indices?
+ # def read_faces_verts_loops(self, mesh):
+ # mfaces_verts_loops = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
+ # mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
+ # return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))
+
+ # def read_faces_norms(self, mesh):
+ # #'float64' would be a slower 64-bit floating-point number numpy datatype
+ # # using less accurate 'float16' normals for now until any issue is reported
+ # mfaces_no = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.float16)
+ # mesh.loop_triangles.foreach_get("normal", mfaces_no)
+ # return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))
+
+ # def read_faces_smooth(self, mesh):
+ # mfaces_smth = np.zeros((len(mesh.loop_triangles) * 1), dtype=np.bool)
+ # mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
+ # return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))
+
+ # def read_faces_material_indices(self, mesh):
+ # mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
+ # mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
+ # return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))
+
+ # obmatslist = []
+ # def hasUniqueMaterial():
+ # # Grab materials attached to object instances ...
+ # if hasattr(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(obj):
+ # # Grab materials attached to object instances ...
+ # 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.
+ # return True
+ # return False
+ # For objects using local material(s) only!
+ # This is a mapping between a tuple (dataname, material_names_dictionary, ...),
+ # and the POV dataname.
+ # As only objects using:
+ # * The same data.
+ # * EXACTLY the same materials, in EXACTLY the same sockets.
+ # ... can share a same instance in POV export.
+ from .render import (
+ string_strip_hyphen,
+ global_matrix,
+ tab,
+ comments,
+ )
+ from .render_core import (
+ preview_dir,
+ smoke_path,
+ )
+ from .model_primitives import write_object_modifiers
+ from .shading import write_object_material_interior
+ from .scenography import image_format, img_map, img_map_transforms
+
+ linebreaksinlists = scene.pov.list_lf_enable and not scene.pov.tempfiles_enable
+ obmats2data = {}
+
+
+ def check_object_materials(obj, obj_name, dataname):
+ """Compare other objects exported material slots to avoid rewriting duplicates"""
+ if hasattr(obj, "material_slots"):
+ has_local_mats = False
+ key = [dataname]
+ 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:
+ has_local_mats = True
+ else:
+ # Even if the slot is empty, it is important to grab it...
+ key.append("")
+ if has_local_mats:
+ # If this object uses local material(s), lets find if another object
+ # using the same data and exactly the same list of materials
+ # (in the same slots) has already been processed...
+ # Note that here also, we use object name as new, unique dataname for Pov.
+ key = tuple(key) # Lists are not hashable...
+ if key not in obmats2data:
+ obmats2data[key] = obj_name
+ return obmats2data[key]
+ return None
+
+ data_ref = {}
+
+ def store(scene, ob, name, dataname, matrix):
+ # The Object needs to be written at least once but if its data is
+ # already in data_ref this has already been done.
+ # This func returns the "povray" name of the data, or None
+ # if no writing is needed.
+ if ob.is_modified(scene, "RENDER"):
+ # Data modified.
+ # Create unique entry in data_ref by using object name
+ # (always unique in Blender) as data name.
+ data_ref[name] = [(name, matrix_as_pov_string(matrix))]
+ return name
+ # Here, we replace dataname by the value returned by check_object_materials, only if
+ # it is not evaluated to False (i.e. only if the object uses some local material(s)).
+ dataname = check_object_materials(ob, name, dataname) or dataname
+ if dataname in data_ref:
+ # Data already known, just add the object instance.
+ data_ref[dataname].append((name, matrix_as_pov_string(matrix)))
+ # No need to write data
+ return None
+ # Else (no return yet): Data not yet processed, create a new entry in data_ref.
+ data_ref[dataname] = [(name, matrix_as_pov_string(matrix))]
+ return dataname
+
+ ob_num = 0
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ for ob in sel:
+ # Using depsgraph
+ ob = bpy.data.objects[ob.name].evaluated_get(depsgraph)
+
+ # subtract original from the count of their instances as were not counted before 2.8
+ if (ob.is_instancer and ob.original != ob):
+ continue
+
+ ob_num += 1
+
+ # XXX I moved all those checks here, as there is no need to compute names
+ # for object we won't export here!
+ if ob.type in {
+ "LIGHT",
+ "CAMERA", # 'EMPTY', #empties can bear dupligroups
+ "META",
+ "ARMATURE",
+ "LATTICE",
+ }:
+ continue
+ fluid_found = False
+ for mod in ob.modifiers:
+ if mod and hasattr(mod, "fluid_type"):
+ fluid_found = True
+ if mod.fluid_type == "DOMAIN":
+ if mod.domain_settings.domain_type == "GAS":
+ export_smoke(file, ob.name, smoke_path, comments, global_matrix)
+ break # don't render domain mesh, skip to next object.
+ if mod.fluid_type == "FLOW": # The domain contains all the smoke. so that's it.
+ if mod.flow_settings.flow_type == "SMOKE": # Check how liquids behave
+ break # don't render smoke flow emitter mesh either, skip to next object.
+ if fluid_found:
+ return
+ # No fluid found
+ if hasattr(ob, "particle_systems"):
+ # Importing function Export Hair
+ # here rather than at the top recommended for addons startup footprint
+ from .particles import export_hair
+
+ for p_sys in ob.particle_systems:
+ 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 particle_mod.show_render
+ and (p_sys.name == particle_mod.particle_system.name)
+ ):
+ export_hair(file, ob, particle_mod, p_sys, global_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
+ # (baking for now or anything else).
+ # XXX I don't understand that if we are here, sel if a non-empty iterable,
+ # so this condition is always True, IMO -- mont29
+ # EMPTY type objects treated a little further below -- MR
+
+ # modified elif to if below as non EMPTY objects can also be instancers
+ if ob.is_instancer:
+ if ob.instance_type == "COLLECTION":
+ name_orig = "OB" + ob.name
+ dataname_orig = "DATA" + ob.instance_collection.name
+ else:
+ # hoping only dupligroups have several source datablocks
+ # ob_dupli_list_create(scene) #deprecated in 2.8
+ for eachduplicate in depsgraph.object_instances:
+ # Real dupli instance filtered because
+ # original included in list since 2.8
+ if eachduplicate.is_instance:
+ dataname_orig = "DATA" + eachduplicate.object.name
+ # 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":
+ name_orig = "OB" + ob.name
+ dataname_orig = "DATA" + ob.name
+ else:
+ name_orig = DEF_OBJ_NAME
+ dataname_orig = DEF_OBJ_NAME
+ name = string_strip_hyphen(bpy.path.clean_name(name_orig))
+ dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
+ # for slot in obj.material_slots:
+ # if slot.material is not None and slot.link == 'OBJECT':
+ # obmaterial = slot.material
+
+ # ------------------------------------------------
+
+ if info_callback:
+ info_callback("Object %2.d of %2.d (%s)" % (ob_num, len(sel), ob.name))
+
+ me = ob.data
+
+ matrix = global_matrix @ ob.matrix_world
+ povdataname = store(scene, ob, name, dataname, matrix)
+ if povdataname is None:
+ print("This is an instance of " + name)
+ continue
+
+ print("Writing Down First Occurrence of " + name)
+
+ # ------------ 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"}
+ ):
+ continue # Don't render proxy mesh, skip to next object
+ # pov_mat_name = "Default_texture" # Not used...remove?
+
+ # 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 matrix and just a comment
+ # with empty object transforms ?
+ tab_write(file, "\n//dummy sphere to represent Empty location\n")
+ tab_write(
+ file,
+ "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
+ % povdataname,
+ )
+ continue # Don't render empty object but this is later addition, watch it.
+ if mesh_eval_exported := model_poly_topology.export_mesh(file, ob, povdataname,
+ material_names_dictionary,
+ unpacked_images,
+ tab_level, tab_write, linebreaksinlists):
+ pass # XXX Caution pass may write both proxy mesh and primitive, so propagate a test
+ # for non mesh_eval_exported or switch back other primitives before meshes
+ else:
+ continue
+
+ # ------------ Povray Primitives ------------ #
+ # Also implicit elif (continue) clauses and sorted after mesh
+ # as less often used.
+ if ob.pov.object_as == "PLANE":
+ tab_write(file, "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ # tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "SPHERE":
+
+ tab_write(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ # tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "BOX":
+ tab_write(file, "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ # tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\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(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ # tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\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(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ # cylinders written at origin, translated below
+ write_object_modifiers(ob, file)
+ # tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\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(file, "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ tab_write(file, "rotate x*90\n")
+ tab_write(file, "translate <-0.5,0.5,0>\n")
+ tab_write(file, "scale <0,-1,0>\n")
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "TORUS":
+ tab_write(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "PARAMETRIC":
+ tab_write(file, "#declare %s = parametric {\n" % povdataname)
+ tab_write(file, "function { %s }\n" % ob.pov.x_eq)
+ tab_write(file, "function { %s }\n" % ob.pov.y_eq)
+ tab_write(file, "function { %s }\n" % ob.pov.z_eq)
+ tab_write(
+ file,
+ "<%.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(file, "max_gradient 0.001\n")
+ if ob.pov.contained_by == "sphere":
+ tab_write(file, "contained_by { sphere{0, 2} }\n")
+ else:
+ tab_write(file, "contained_by { box{-2, 2} }\n")
+ tab_write(file, "max_gradient %.6f\n" % ob.pov.max_gradient)
+ tab_write(file, "accuracy %.6f\n" % ob.pov.accuracy)
+ tab_write(file, "precompute 10 x,y,z\n")
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "ISOSURFACE_NODE":
+ tab_write(file, "#declare %s = isosurface{ \n" % povdataname)
+ tab_write(file, "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(
+ file,
+ "#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(file, "%s\n" % line.body)
+ else:
+ tab_write(file, "abs(x) - 2 + y")
+ tab_write(file, "}\n")
+ tab_write(file, "threshold %.6g\n" % ob.pov.threshold)
+ tab_write(file, "max_gradient %.6g\n" % ob.pov.max_gradient)
+ tab_write(file, "accuracy %.6g\n" % ob.pov.accuracy)
+ tab_write(file, "contained_by { ")
+ if ob.pov.contained_by == "sphere":
+ tab_write(file, "sphere {0,%.6g}}\n" % ob.pov.container_scale)
+ else:
+ tab_write(
+ file,
+ "box {-%.6g,%.6g}}\n"
+ % (ob.pov.container_scale, ob.pov.container_scale),
+ )
+ if ob.pov.all_intersections:
+ tab_write(file, "all_intersections\n")
+ else:
+ if ob.pov.max_trace > 1:
+ tab_write(file, "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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ tab_write(file, "scale %.6g\n" % (1 / ob.pov.container_scale))
+ tab_write(file, "}\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(file, "#declare %s = isosurface{ \n" % povdataname)
+ tab_write(file, "function{ \n")
+ tab_write(file, simple_isosurface_function)
+ tab_write(file, "}\n")
+ tab_write(file, "threshold %.6g\n" % ob.pov.threshold)
+ tab_write(file, "max_gradient %.6g\n" % ob.pov.max_gradient)
+ tab_write(file, "accuracy %.6g\n" % ob.pov.accuracy)
+ tab_write(file, "contained_by { ")
+ if ob.pov.contained_by == "sphere":
+ tab_write(file, "sphere {0,%.6g}}\n" % ob.pov.container_scale)
+ else:
+ tab_write(
+ file,
+ "box {-%.6g,%.6g}}\n"
+ % (ob.pov.container_scale, ob.pov.container_scale),
+ )
+ if ob.pov.all_intersections:
+ tab_write(file, "all_intersections\n")
+ else:
+ if ob.pov.max_trace > 1:
+ tab_write(file, "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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ tab_write(file, "scale %.6g\n" % (1 / ob.pov.container_scale))
+ tab_write(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+
+ if ob.pov.object_as == "SUPERELLIPSOID":
+ tab_write(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ tab_write(file, "}\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(
+ file,
+ "#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(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ write_object_modifiers(ob, file)
+ tab_write(file, "rotate x*90\n")
+ tab_write(file, "}\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(
+ file,
+ "#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(file, "}\n")
+ continue # Don't render proxy mesh, skip to next object
+ if csg:
+ # fluid_found early return no longer runs this
+ # todo maybe make a function to run in that other branch
+ duplidata_ref = []
+ _dupnames_seen = {} # avoid duplicate output during introspection
+ for ob in sel:
+ # matrix = global_matrix @ obj.matrix_world
+ if ob.is_instancer:
+ tab_write(file, "\n//--DupliObjects in %s--\n\n" % ob.name)
+ # 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
+ dup = "#declare OB%s = union{\n" % (
+ string_strip_hyphen(bpy.path.clean_name(ob.name))
+ )
+ else:
+ dup = "#declare DATA%s = union{\n" % (
+ string_strip_hyphen(bpy.path.clean_name(ob.name))
+ )
+
+ for eachduplicate in depsgraph.object_instances:
+ if (
+ eachduplicate.is_instance
+ ): # Real dupli instance filtered because original included in list since 2.8
+ _dupname = eachduplicate.object.name
+ _dupobj = bpy.data.objects[_dupname]
+ # BEGIN introspection for troubleshooting purposes
+ if "name" not in dir(_dupobj.data):
+ if _dupname not in _dupnames_seen:
+ print(
+ "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute"
+ % (_dupname, type(_dupobj.data))
+ )
+ for _thing in dir(_dupobj):
+ print(
+ "|| %s.%s = %s"
+ % (_dupname, _thing, getattr(_dupobj, _thing))
+ )
+ _dupnames_seen[_dupname] = 1
+ print("''=> Unparseable objects so far: %s" % _dupnames_seen)
+ else:
+ _dupnames_seen[_dupname] += 1
+ continue # don't try to parse data objects with no name attribute
+ # END introspection for troubleshooting purposes
+ duplidataname = "OB" + string_strip_hyphen(
+ bpy.path.clean_name(_dupobj.data.name)
+ )
+ dupmatrix = (
+ eachduplicate.matrix_world.copy()
+ ) # has to be copied to not store instance since 2.8
+ dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % (
+ string_strip_hyphen(bpy.path.clean_name(_dupobj.data.name)),
+ matrix_as_pov_string(ob.matrix_world.inverted() @ dupmatrix),
+ )
+ # add object to a list so that it is not rendered for some instance_types
+ if (
+ ob.instance_type != "COLLECTION"
+ and duplidataname not in duplidata_ref
+ ):
+ duplidata_ref.append(
+ duplidataname,
+ ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+obj.name))]
+ dup += "}\n"
+ # obj.dupli_list_clear()# just do not store any reference to instance since 2.8
+ tab_write(file, dup)
+ else:
+ continue
+ 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
+ tab_write(file, "\n//----Blender Object Name: %s----\n" %
+ ob_name.removeprefix("OB"))
+ if ob.pov.object_as == "":
+ tab_write(file, "object { \n")
+ tab_write(file, "%s\n" % data_name)
+ tab_write(file, "%s\n" % matrix_str)
+ tab_write(file, "}\n")
+ else:
+ no_boolean = True
+ for mod in ob.modifiers:
+ if mod.type == "BOOLEAN":
+ operation = None
+ no_boolean = False
+ if mod.operation == "INTERSECT":
+ operation = "intersection"
+ else:
+ operation = mod.operation.lower()
+ mod_ob_name = string_strip_hyphen(
+ bpy.path.clean_name(mod.object.name)
+ )
+ mod_matrix = global_matrix @ mod.object.matrix_world
+ mod_ob_matrix = matrix_as_pov_string(mod_matrix)
+ tab_write(file, "%s { \n" % operation)
+ tab_write(file, "object { \n")
+ tab_write(file, "%s\n" % data_name)
+ tab_write(file, "%s\n" % matrix_str)
+ tab_write(file, "}\n")
+ tab_write(file, "object { \n")
+ tab_write(file, "%s\n" % ("DATA" + mod_ob_name))
+ tab_write(file, "%s\n" % mod_ob_matrix)
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ break
+ if no_boolean:
+ tab_write(file, "object { \n")
+ tab_write(file, "%s\n" % data_name)
+ tab_write(file, "%s\n" % matrix_str)
+ tab_write(file, "}\n")
diff --git a/render_povray/model_curve_topology.py b/render_povray/model_curve_topology.py
new file mode 100644
index 00000000..121def67
--- /dev/null
+++ b/render_povray/model_curve_topology.py
@@ -0,0 +1,996 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Translate to POV the control point compounded geometries like polygon
+
+meshes or curve based shapes.
+"""
+
+import bpy
+
+
+# -------- LOFT, ETC.
+
+
+def export_curves(file, ob, 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
+ """
+ from .shading import write_object_material_interior
+ from .render import string_strip_hyphen
+
+ # name_orig = "OB" + ob.name # XXX Unused, check instantiation
+ dataname_orig = "DATA" + ob.data.name
+
+ # name = string_strip_hyphen(bpy.path.clean_name(name_orig)) # XXX Unused, check instantiation
+ dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
+
+ # matrix = global_matrix @ ob.matrix_world # XXX Unused, check instantiation
+ bezier_sweep = False
+ if ob.pov.curveshape == "sphere_sweep":
+ # TODO: Check radius ; shorten lines, may use tab_write() ? > fstrings since py 2.9
+ # inlined spheresweep macro, which itself calls Shapes.inc:
+ file.write(' #include "shapes.inc"\n')
+
+ file.write(
+ " #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n"
+ )
+ file.write(" //input adjusting and inspection\n")
+ file.write(" #if(_resolution <= 1)\n")
+ file.write(" #local res = 1;\n")
+ file.write(" #else\n")
+ file.write(" #local res = int(_resolution);\n")
+ file.write(" #end\n")
+ file.write(" #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n")
+ file.write(' #error ""\n')
+ file.write(
+ " #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n"
+ )
+ file.write(' #error ""\n')
+ file.write(
+ " #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n"
+ )
+ file.write(' #error ""\n')
+ file.write(" #else\n")
+ file.write(" #local n_of_seg = div(dimension_size(_points_array,1), 4);\n")
+ file.write(" #local ctrl_pts_array = array[n_of_seg]\n")
+ file.write(" #local ctrl_rs_array = array[n_of_seg]\n")
+ file.write(" #for(i, 0, n_of_seg-1)\n")
+ file.write(
+ " #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n"
+ )
+ file.write(
+ " #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n"
+ )
+ file.write(" #end\n")
+ file.write(" #end\n")
+
+ file.write(" //drawing\n")
+ file.write(" #local mockup1 =\n")
+ file.write(" #if(_merge_shape) merge{ #else union{ #end\n")
+ file.write(" #for(i, 0, n_of_seg-1)\n")
+ file.write(" #local has_head = true;\n")
+ file.write(" #if(i = 0)\n")
+ file.write(
+ " #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n"
+ )
+ file.write(" #local has_head = false;\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(
+ " #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n"
+ )
+ file.write(" #local has_head = false;\n")
+ file.write(" #end\n")
+ file.write(" #end\n")
+ file.write(" #if(has_head = true)\n")
+ file.write(" sphere{\n")
+ file.write(" ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n")
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" #local para_t = (1/2)/res;\n")
+ file.write(
+ " #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n"
+ )
+ file.write(
+ " #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n"
+ )
+ file.write(
+ " #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n"
+ )
+ file.write(" object{\n")
+ file.write(
+ " Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n"
+ )
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" sphere{\n")
+ file.write(" this_point, this_radius\n")
+ file.write(" }\n")
+ file.write(" #for(j, 1, res-1)\n")
+ file.write(" #local last_point = this_point;\n")
+ file.write(" #local last_radius = this_radius;\n")
+ file.write(" #local para_t = (1/2+j)/res;\n")
+ file.write(
+ " #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n"
+ )
+ file.write(
+ " #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n"
+ )
+ file.write(
+ " #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n"
+ )
+ file.write(" object{\n")
+ file.write(
+ " Connect_Spheres(last_point, last_radius, this_point, this_radius)\n"
+ )
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" sphere{\n")
+ file.write(" this_point, this_radius\n")
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" #local last_point = this_point;\n")
+ file.write(" #local last_radius = this_radius;\n")
+ file.write(" #local this_point = ctrl_pts_array[i][3];\n")
+ file.write(" #local this_radius = ctrl_rs_array[i][3];\n")
+ file.write(
+ " #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n"
+ )
+ file.write(" object{\n")
+ file.write(
+ " Connect_Spheres(last_point, last_radius, this_point, this_radius)\n"
+ )
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" sphere{\n")
+ file.write(" this_point, this_radius\n")
+ file.write(" }\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+ file.write(" mockup1\n")
+ file.write(" #end\n")
+
+ for spl in ob.data.splines:
+ if spl.type == "BEZIER":
+ bezier_sweep = True
+ if ob.pov.curveshape in ("loft", "birail"):
+ n = 0
+ for spline in ob.data.splines:
+ n += 1
+ tab_write(file, "#declare %s%s=spline {\n" % (dataname, n))
+ tab_write(file, "cubic_spline\n")
+ lp = len(spline.points)
+ delta = 1 / lp
+ d = -delta
+ point = spline.points[lp - 1]
+ x, y, z, w = point.co[:]
+ tab_write(file, "%.6f, <%.6f,%.6f,%.6f>\n" % (d, x, y, z))
+ d += delta
+ for point in spline.points:
+ x, y, z, w = point.co[:]
+ tab_write(file, "%.6f, <%.6f,%.6f,%.6f>\n" % (d, x, y, z))
+ d += delta
+ for i in range(2):
+ point = spline.points[i]
+ x, y, z, w = point.co[:]
+ tab_write(file, "%.6f, <%.6f,%.6f,%.6f>\n" % (d, x, y, z))
+ d += delta
+ tab_write(file, "}\n")
+ if ob.pov.curveshape == "loft":
+ n = len(ob.data.splines)
+ tab_write(file, "#declare %s = array[%s]{\n" % (dataname, (n + 3)))
+ tab_write(file, "spline{%s%s},\n" % (dataname, n))
+ for i in range(n):
+ tab_write(file, "spline{%s%s},\n" % (dataname, (i + 1)))
+ tab_write(file, "spline{%s1},\n" % dataname)
+ tab_write(file, "spline{%s2}\n" % dataname)
+ tab_write(file, "}\n")
+ # Use some of the Meshmaker.inc macro, here inlined
+ file.write("#macro CheckFileName(FileName)\n")
+ file.write(" #local Len=strlen(FileName);\n")
+ file.write(" #if(Len>0)\n")
+ file.write(" #if(file_exists(FileName))\n")
+ file.write(" #if(Len>=4)\n")
+ file.write(" #local Ext=strlwr(substr(FileName,Len-3,4))\n")
+ file.write(
+ ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
+ )
+ file.write(" #local Return=99;\n")
+ file.write(" #else\n")
+ file.write(" #local Return=0;\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(" #local Return=0;\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(" #if(Len>=4)\n")
+ file.write(" #local Ext=strlwr(substr(FileName,Len-3,4))\n")
+ file.write(
+ ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
+ )
+ file.write(' #if (strcmp(Ext,".obj")=0)\n')
+ file.write(" #local Return=2;\n")
+ file.write(" #end\n")
+ file.write(' #if (strcmp(Ext,".pcm")=0)\n')
+ file.write(" #local Return=3;\n")
+ file.write(" #end\n")
+ file.write(' #if (strcmp(Ext,".arr")=0)\n')
+ file.write(" #local Return=4;\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(" #local Return=1;\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(" #local Return=1;\n")
+ file.write(" #end\n")
+ file.write(" #end\n")
+ file.write(" #else\n")
+ file.write(" #local Return=1;\n")
+ file.write(" #end\n")
+ file.write(" (Return)\n")
+ file.write("#end\n")
+
+ file.write("#macro BuildSpline(Arr, SplType)\n")
+ file.write(" #local Ds=dimension_size(Arr,1);\n")
+ file.write(" #local Asc=asc(strupr(SplType));\n")
+ file.write(" #if(Asc!=67 & Asc!=76 & Asc!=81) \n")
+ file.write(" #local Asc=76;\n")
+ file.write(
+ ' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n'
+ )
+ file.write(" #end\n")
+ file.write(" spline {\n")
+ file.write(" #switch (Asc)\n")
+ file.write(" #case (67) //C cubic_spline\n")
+ file.write(" cubic_spline\n")
+ file.write(" #break\n")
+ file.write(" #case (76) //L linear_spline\n")
+ file.write(" linear_spline\n")
+ file.write(" #break\n")
+ file.write(" #case (78) //N linear_spline\n")
+ file.write(" natural_spline\n")
+ file.write(" #break\n")
+ file.write(" #case (81) //Q Quadratic_spline\n")
+ file.write(" quadratic_spline\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" #local Add=1/((Ds-2)-1);\n")
+ file.write(" #local J=0-Add;\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<Ds)\n")
+ file.write(" J\n")
+ file.write(" Arr[I]\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #local J=J+Add;\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+ file.write("#end\n")
+
+ file.write("#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n")
+ # suppressed some file checking from original macro because no more separate files
+ file.write(" #local Write=0;\n")
+ file.write(' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n')
+ file.write(" #local NumVertices=dimension_size(VecArr,1);\n")
+ file.write(" #switch (Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' " vertex_vectors {\\n",\n')
+ file.write(' " ", str(NumVertices,0,0),"\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' str(2*NumVertices,0,0),",\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" mesh2 {\n")
+ file.write(" vertex_vectors {\n")
+ file.write(" NumVertices\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<NumVertices)\n")
+ file.write(" VecArr[I]\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(MeshFile, VecArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(
+ ' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n'
+ )
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(MeshFile, VecArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #if(Write=1 | Write=4)\n")
+ file.write(" #if(mod(I,3)=0)\n")
+ file.write(' #write(MeshFile,"\\n ")\n')
+ file.write(" #end\n")
+ file.write(" #end \n")
+ file.write(" #end\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(' #write(MeshFile,"\\n }\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(' #write(MeshFile,"\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" // do nothing\n")
+ file.write(" #break\n")
+ file.write(" #case(4) \n")
+ file.write(' #write(MeshFile,"\\n}\\n")\n')
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+
+ file.write(' #debug concat(" - normal_vectors\\n") \n')
+ file.write(" #local NumVertices=dimension_size(NormArr,1);\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' " normal_vectors {\\n",\n')
+ file.write(' " ", str(NumVertices,0,0),"\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "# Normals: ",str(NumVertices,0,0),"\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" // do nothing\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(
+ ' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
+ )
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" normal_vectors {\n")
+ file.write(" NumVertices\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<NumVertices)\n")
+ file.write(" NormArr[I]\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(MeshFile NormArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(
+ ' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n'
+ )
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(MeshFile NormArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #if(Write=1 | Write=4) \n")
+ file.write(" #if(mod(I,3)=0)\n")
+ file.write(' #write(MeshFile,"\\n ")\n')
+ file.write(" #end\n")
+ file.write(" #end\n")
+ file.write(" #end\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(' #write(MeshFile,"\\n }\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(' #write(MeshFile,"\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" //do nothing\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(' #write(MeshFile,"\\n}\\n")\n')
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+
+ file.write(' #debug concat(" - uv_vectors\\n") \n')
+ file.write(" #local NumVertices=dimension_size(UVArr,1);\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile, \n")
+ file.write(' " uv_vectors {\\n",\n')
+ file.write(' " ", str(NumVertices,0,0),"\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" // do nothing, *.pcm does not support uv-vectors\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" uv_vectors {\n")
+ file.write(" NumVertices\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<NumVertices)\n")
+ file.write(" UVArr[I]\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(MeshFile UVArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" //do nothing\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(MeshFile UVArr[I])\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" #local I=I+1; \n")
+ file.write(" #if(Write=1 | Write=4)\n")
+ file.write(" #if(mod(I,3)=0)\n")
+ file.write(' #write(MeshFile,"\\n ")\n')
+ file.write(" #end \n")
+ file.write(" #end\n")
+ file.write(" #end \n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(' #write(MeshFile,"\\n }\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(' #write(MeshFile,"\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" //do nothing\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(' #write(MeshFile,"\\n}\\n")\n')
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+ file.write("\n")
+ file.write(' #debug concat(" - face_indices\\n") \n')
+ file.write(" #declare NumFaces=U*V*2;\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' " face_indices {\\n"\n')
+ file.write(' " ", str(NumFaces,0,0),"\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write (\n")
+ file.write(" MeshFile,\n")
+ file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #write (\n")
+ file.write(" MeshFile,\n")
+ file.write(' "0,",str(NumFaces,0,0),",\\n"\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n')
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" face_indices {\n")
+ file.write(" NumFaces\n")
+ file.write(" #local I=0;\n")
+ file.write(" #local H=0;\n")
+ file.write(" #local NumVertices=dimension_size(VecArr,1);\n")
+ file.write(" #while (I<V)\n")
+ file.write(" #local J=0;\n")
+ file.write(" #while (J<U)\n")
+ file.write(" #local Ind=(I*U)+I+J;\n")
+ file.write(" <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(" <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n")
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(
+ ' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n'
+ )
+ file.write(
+ ' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n'
+ )
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(
+ ' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
+ )
+ file.write(
+ ' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
+ )
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(" #write(\n")
+ file.write(" MeshFile,\n")
+ file.write(" <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n")
+ file.write(" )\n")
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" #local J=J+1;\n")
+ file.write(" #local H=H+1;\n")
+ file.write(" #if(Write=1 | Write=4)\n")
+ file.write(" #if(mod(H,3)=0)\n")
+ file.write(' #write(MeshFile,"\\n ")\n')
+ file.write(" #end \n")
+ file.write(" #end\n")
+ file.write(" #end\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+ file.write(" #switch(Write)\n")
+ file.write(" #case(1)\n")
+ file.write(' #write(MeshFile, "\\n }\\n}")\n')
+ file.write(" #fclose MeshFile\n")
+ file.write(' #debug concat(" Done writing\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(2)\n")
+ file.write(" #fclose MeshFile\n")
+ file.write(' #debug concat(" Done writing\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(3)\n")
+ file.write(" #fclose MeshFile\n")
+ file.write(' #debug concat(" Done writing\\n")\n')
+ file.write(" #break\n")
+ file.write(" #case(4)\n")
+ file.write(' #write(MeshFile, "\\n}\\n}")\n')
+ file.write(" #fclose MeshFile\n")
+ file.write(' #debug concat(" Done writing\\n")\n')
+ file.write(" #break\n")
+ file.write(" #end\n")
+ file.write(" }\n")
+ file.write("#end\n")
+
+ file.write("#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n")
+ file.write(" #declare Build=CheckFileName(FileName);\n")
+ file.write(" #if(Build=0)\n")
+ file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
+ file.write(" #include FileName\n")
+ file.write(" object{Surface}\n")
+ file.write(" #else\n")
+ file.write(" #local NumVertices=(SplRes+1)*(InterpRes+1);\n")
+ file.write(" #local NumFaces=SplRes*InterpRes*2;\n")
+ file.write(
+ ' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n'
+ )
+ file.write(" #local VecArr=array[NumVertices]\n")
+ file.write(" #local NormArr=array[NumVertices]\n")
+ file.write(" #local UVArr=array[NumVertices]\n")
+ file.write(" #local N=dimension_size(SplineArray,1);\n")
+ file.write(" #local TempSplArr0=array[N];\n")
+ file.write(" #local TempSplArr1=array[N];\n")
+ file.write(" #local TempSplArr2=array[N];\n")
+ file.write(" #local PosStep=1/SplRes;\n")
+ file.write(" #local InterpStep=1/InterpRes;\n")
+ file.write(" #local Count=0;\n")
+ file.write(" #local Pos=0;\n")
+ file.write(" #while(Pos<=1)\n")
+ file.write(" #local I=0;\n")
+ file.write(" #if (Pos=0)\n")
+ file.write(" #while (I<N)\n")
+ file.write(" #local Spl=spline{SplineArray[I]}\n")
+ file.write(" #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n")
+ file.write(" #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n")
+ file.write(" #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #end\n")
+ file.write(" #local S0=BuildSpline(TempSplArr0, Interp_type)\n")
+ file.write(" #local S1=BuildSpline(TempSplArr1, Interp_type)\n")
+ file.write(" #local S2=BuildSpline(TempSplArr2, Interp_type)\n")
+ file.write(" #else\n")
+ file.write(" #while (I<N)\n")
+ file.write(" #local Spl=spline{SplineArray[I]}\n")
+ file.write(" #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n")
+ file.write(" #local I=I+1;\n")
+ file.write(" #end\n")
+ file.write(" #local S1=BuildSpline(TempSplArr1, Interp_type)\n")
+ file.write(" #end\n")
+ file.write(" #local J=0;\n")
+ file.write(" #while (J<=1)\n")
+ file.write(" #local P0=<0,0,0>+S0(J);\n")
+ file.write(" #local P1=<0,0,0>+S1(J);\n")
+ file.write(" #local P2=<0,0,0>+S2(J);\n")
+ file.write(" #local P3=<0,0,0>+S0(J+InterpStep);\n")
+ file.write(" #local P4=<0,0,0>+S0(J-InterpStep);\n")
+ file.write(" #local B1=P4-P0;\n")
+ file.write(" #local B2=P2-P0;\n")
+ file.write(" #local B3=P3-P0;\n")
+ file.write(" #local B4=P1-P0;\n")
+ file.write(" #local N1=vcross(B1,B2);\n")
+ file.write(" #local N2=vcross(B2,B3);\n")
+ file.write(" #local N3=vcross(B3,B4);\n")
+ file.write(" #local N4=vcross(B4,B1);\n")
+ file.write(" #local Norm=vnormalize((N1+N2+N3+N4));\n")
+ file.write(" #local VecArr[Count]=P0;\n")
+ file.write(" #local NormArr[Count]=Norm;\n")
+ file.write(" #local UVArr[Count]=<J,Pos>;\n")
+ file.write(" #local J=J+InterpStep;\n")
+ file.write(" #local Count=Count+1;\n")
+ file.write(" #end\n")
+ file.write(" #local S2=spline{S0}\n")
+ file.write(" #local S0=spline{S1}\n")
+ file.write(
+ ' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n'
+ )
+ file.write(" #local Pos=Pos+PosStep;\n")
+ file.write(" #end\n")
+ file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n')
+ file.write(" #end\n")
+ file.write("#end\n\n")
+
+ file.write("#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n")
+ file.write(" #declare Build=CheckFileName(FileName);\n")
+ file.write(" #if(Build=0)\n")
+ file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
+ file.write(" #include FileName\n")
+ file.write(" object{Surface}\n")
+ file.write(" #else\n")
+ file.write(" #local NumVertices=(Iter_U+1)*(Iter_V+1);\n")
+ file.write(" #local NumFaces=Iter_U*Iter_V*2;\n")
+ file.write(
+ ' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n'
+ )
+ file.write(" #declare VecArr=array[NumVertices] \n")
+ file.write(" #declare NormArr=array[NumVertices] \n")
+ file.write(" #local UVArr=array[NumVertices] \n")
+ file.write(" #local Spl1_0=Spl1(0);\n")
+ file.write(" #local Spl2_0=Spl2(0);\n")
+ file.write(" #local Spl3_0=Spl3(0);\n")
+ file.write(" #local Spl4_0=Spl4(0);\n")
+ file.write(" #local UStep=1/Iter_U;\n")
+ file.write(" #local VStep=1/Iter_V;\n")
+ file.write(" #local Count=0;\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<=1)\n")
+ file.write(" #local Im=1-I;\n")
+ file.write(" #local J=0;\n")
+ file.write(" #while (J<=1)\n")
+ file.write(" #local Jm=1-J;\n")
+ file.write(
+ " #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n"
+ )
+ file.write(" #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n")
+ file.write(" LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n")
+ file.write(" #declare VecArr[Count]=P0;\n")
+ file.write(" #local UVArr[Count]=<J,I>;\n")
+ file.write(" #local J=J+UStep;\n")
+ file.write(" #local Count=Count+1;\n")
+ file.write(" #end\n")
+ file.write(" #debug concat(\n")
+ file.write(' "\r Done ", str(Count,0,0)," vertices : ",\n')
+ file.write(' str(100*Count/NumVertices,0,2)," %"\n')
+ file.write(" )\n")
+ file.write(" #local I=I+VStep;\n")
+ file.write(" #end\n")
+ file.write(' #debug "\r Normals "\n')
+ file.write(" #local Count=0;\n")
+ file.write(" #local I=0;\n")
+ file.write(" #while (I<=Iter_V)\n")
+ file.write(" #local J=0;\n")
+ file.write(" #while (J<=Iter_U)\n")
+ file.write(" #local Ind=(I*Iter_U)+I+J;\n")
+ file.write(" #local P0=VecArr[Ind];\n")
+ file.write(" #if(J=0)\n")
+ file.write(" #local P1=P0+(P0-VecArr[Ind+1]);\n")
+ file.write(" #else\n")
+ file.write(" #local P1=VecArr[Ind-1];\n")
+ file.write(" #end\n")
+ file.write(" #if (J=Iter_U)\n")
+ file.write(" #local P2=P0+(P0-VecArr[Ind-1]);\n")
+ file.write(" #else\n")
+ file.write(" #local P2=VecArr[Ind+1];\n")
+ file.write(" #end\n")
+ file.write(" #if (I=0)\n")
+ file.write(" #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n")
+ file.write(" #else\n")
+ file.write(" #local P3=VecArr[Ind-Iter_U-1];\n")
+ file.write(" #end\n")
+ file.write(" #if (I=Iter_V)\n")
+ file.write(" #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n")
+ file.write(" #else\n")
+ file.write(" #local P4=VecArr[Ind+Iter_U+1];\n")
+ file.write(" #end\n")
+ file.write(" #local B1=P4-P0;\n")
+ file.write(" #local B2=P2-P0;\n")
+ file.write(" #local B3=P3-P0;\n")
+ file.write(" #local B4=P1-P0;\n")
+ file.write(" #local N1=vcross(B1,B2);\n")
+ file.write(" #local N2=vcross(B2,B3);\n")
+ file.write(" #local N3=vcross(B3,B4);\n")
+ file.write(" #local N4=vcross(B4,B1);\n")
+ file.write(" #local Norm=vnormalize((N1+N2+N3+N4));\n")
+ file.write(" #declare NormArr[Count]=Norm;\n")
+ file.write(" #local J=J+1;\n")
+ file.write(" #local Count=Count+1;\n")
+ file.write(" #end\n")
+ file.write(
+ ' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n'
+ )
+ file.write(" #local I=I+1;\n")
+ file.write(" #end\n")
+ file.write(" BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n")
+ file.write(" #end\n")
+ file.write("#end\n\n")
+ # Empty curves
+ if len(ob.data.splines) == 0:
+ tab_write(file, "\n//dummy sphere to represent empty curve location\n")
+ tab_write(file, "#declare %s =\n" % dataname)
+ tab_write(
+ file,
+ "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} "
+ "no_image no_reflection no_radiosity "
+ "photons{pass_through collect off} hollow}\n\n"
+ % (ob.location.x, ob.location.y, ob.location.z),
+ ) # ob.name > povdataname)
+ # And non empty curves
+ else:
+ if not bezier_sweep:
+ tab_write(file, "#declare %s =\n" % dataname)
+ if ob.pov.curveshape == "sphere_sweep" and not bezier_sweep:
+ tab_write(file, "union {\n")
+ for spl in ob.data.splines:
+ if spl.type != "BEZIER":
+ spl_type = "linear"
+ if spl.type == "NURBS":
+ spl_type = "cubic"
+ points = spl.points
+ num_points = len(points)
+ if spl.use_cyclic_u:
+ num_points += 3
+
+ tab_write(file, "sphere_sweep { %s_spline %s,\n" % (spl_type, num_points))
+ if spl.use_cyclic_u:
+ pt1 = points[len(points) - 1]
+ wpt1 = pt1.co
+ tab_write(
+ file,
+ "<%.4g,%.4g,%.4g>,%.4g\n"
+ % (
+ wpt1[0],
+ wpt1[1],
+ wpt1[2],
+ pt1.radius * ob.data.bevel_depth,
+ ),
+ )
+ for pt in points:
+ wpt = pt.co
+ tab_write(
+ file,
+ "<%.4g,%.4g,%.4g>,%.4g\n"
+ % (wpt[0], wpt[1], wpt[2], pt.radius * ob.data.bevel_depth),
+ )
+ if spl.use_cyclic_u:
+ for i in range(0, 2):
+ end_pt = points[i]
+ wpt = end_pt.co
+ tab_write(
+ file,
+ "<%.4g,%.4g,%.4g>,%.4g\n"
+ % (
+ wpt[0],
+ wpt[1],
+ wpt[2],
+ end_pt.radius * ob.data.bevel_depth,
+ ),
+ )
+
+ tab_write(file, "}\n")
+ # below not used yet?
+ if ob.pov.curveshape == "sor":
+ for spl in ob.data.splines:
+ if spl.type in ("POLY", "NURBS"):
+ points = spl.points
+ num_points = len(points)
+ tab_write(file, "sor { %s,\n" % num_points)
+ for pt in points:
+ wpt = pt.co
+ tab_write(file, "<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
+ else:
+ tab_write(file, "box { 0,0\n")
+ if ob.pov.curveshape in ("lathe", "prism"):
+ spl = ob.data.splines[0]
+ if spl.type == "BEZIER":
+ points = spl.bezier_points
+ len_cur = len(points) - 1
+ len_pts = len_cur * 4
+ ifprism = ""
+ if ob.pov.curveshape == "prism":
+ height = ob.data.extrude
+ ifprism = "-%s, %s," % (height, height)
+ len_cur += 1
+ len_pts += 4
+ tab_write(
+ file, "%s { bezier_spline %s %s,\n" % (ob.pov.curveshape, ifprism, len_pts)
+ )
+ for i in range(0, len_cur):
+ p1 = points[i].co
+ pR = points[i].handle_right
+ end = i + 1
+ if i == len_cur - 1 and ob.pov.curveshape == "prism":
+ end = 0
+ pL = points[end].handle_left
+ p2 = points[end].co
+ line = "<%.4g,%.4g>" % (p1[0], p1[1])
+ line += "<%.4g,%.4g>" % (pR[0], pR[1])
+ line += "<%.4g,%.4g>" % (pL[0], pL[1])
+ line += "<%.4g,%.4g>" % (p2[0], p2[1])
+ tab_write(file, "%s\n" % line)
+ else:
+ points = spl.points
+ len_cur = len(points)
+ len_pts = len_cur
+ ifprism = ""
+ if ob.pov.curveshape == "prism":
+ height = ob.data.extrude
+ ifprism = "-%s, %s," % (height, height)
+ len_pts += 3
+ spl_type = "quadratic"
+ if spl.type == "POLY":
+ spl_type = "linear"
+ tab_write(
+ file,
+ "%s { %s_spline %s %s,\n" % (ob.pov.curveshape, spl_type, ifprism, len_pts),
+ )
+ if ob.pov.curveshape == "prism":
+ pt = points[len(points) - 1]
+ wpt = pt.co
+ tab_write(file, "<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
+ for pt in points:
+ wpt = pt.co
+ tab_write(file, "<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
+ if ob.pov.curveshape == "prism":
+ for i in range(2):
+ pt = points[i]
+ wpt = pt.co
+ tab_write(file, "<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
+ if bezier_sweep:
+ for p, spl in enumerate(ob.data.splines, start=1):
+ br = []
+ depth = ob.data.bevel_depth
+ points = spl.bezier_points
+ len_cur = len(points) - 1
+ num_points = len_cur * 4
+ if spl.use_cyclic_u:
+ len_cur += 1
+ num_points += 4
+ tab_write(file, "#declare %s_points_%s = array[%s]{\n" % (dataname, p, num_points))
+ for i in range(len_cur):
+ p1 = points[i].co
+ pR = points[i].handle_right
+ end = i + 1
+ if spl.use_cyclic_u and i == (len_cur - 1):
+ end = 0
+ pL = points[end].handle_left
+ p2 = points[end].co
+ r3 = points[end].radius * depth
+ r0 = points[i].radius * depth
+ r1 = 2 / 3 * r0 + 1 / 3 * r3
+ r2 = 1 / 3 * r0 + 2 / 3 * r3
+ br.append((r0, r1, r2, r3))
+ line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2])
+ line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2])
+ line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2])
+ line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2])
+ tab_write(file, "%s\n" % line)
+ tab_write(file, "}\n")
+ tab_write(file, "#declare %s_radii_%s = array[%s]{\n" % (dataname, p, len(br) * 4))
+ for rad_tuple in br:
+ tab_write(
+ file,
+ "%.4f,%.4f,%.4f,%.4f\n"
+ % (rad_tuple[0], rad_tuple[1], rad_tuple[2], rad_tuple[3]),
+ )
+ tab_write(file, "}\n")
+ if len(ob.data.splines) == 1:
+ p = 1
+ tab_write(file, "#declare %s = object{\n" % dataname)
+ tab_write(
+ file,
+ " Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n"
+ % (ob.data.resolution_u, dataname, p, dataname, p),
+ )
+ else:
+ tab_write(file, "#declare %s = union{\n" % dataname)
+ for p, spl in enumerate(ob.data.splines, start=1):
+ tab_write(
+ file,
+ " object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n"
+ % (ob.data.resolution_u, dataname, p, dataname, p),
+ )
+ # tab_write(file, '#include "bezier_spheresweep.inc"\n') #now inlined
+ # tab_write(file, '#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth))
+ if ob.pov.curveshape == "loft":
+ tab_write(
+ file, 'object {MSM(%s,%s,"c",%s,"")\n' % (dataname, ob.pov.res_u, ob.pov.res_v)
+ )
+ if ob.pov.curveshape == "birail":
+ splines = "%s1,%s2,%s3,%s4" % (dataname, dataname, dataname, dataname)
+ tab_write(
+ file, 'object {Coons(%s, %s, %s, "")\n' % (splines, ob.pov.res_u, ob.pov.res_v)
+ )
+ # pov_mat_name = "Default_texture" # XXX! Unused, check instantiation
+ 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(file, material, ob, tab_write)
+ except IndexError:
+ print(ob.data)
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
+ if ob.pov.curveshape == "prism":
+ tab_write(file, "rotate <90,0,0>\n")
+ tab_write(file, "scale y*-1\n")
+ tab_write(file, "}\n")
diff --git a/render_povray/object_gui.py b/render_povray/model_gui.py
index 2033a0d4..2ffebf20 100755..100644
--- a/render_povray/object_gui.py
+++ b/render_povray/model_gui.py
@@ -7,12 +7,7 @@ import bpy
import os
-from bpy.utils import (
- register_class,
- unregister_class,
- register_tool,
- unregister_tool
-)
+from bpy.utils import register_class, unregister_class, register_tool, unregister_tool
from bpy.types import (
# Operator,
Menu,
@@ -26,15 +21,15 @@ from bl_ui import properties_data_modifier
for member in dir(properties_data_modifier):
subclass = getattr(properties_data_modifier, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_data_modifier
from bl_ui import properties_data_mesh
# These panels are kept
-properties_data_mesh.DATA_PT_custom_props_mesh.COMPAT_ENGINES.add('POVRAY_RENDER')
-properties_data_mesh.DATA_PT_context_mesh.COMPAT_ENGINES.add('POVRAY_RENDER')
+properties_data_mesh.DATA_PT_custom_props_mesh.COMPAT_ENGINES.add("POVRAY_RENDER")
+properties_data_mesh.DATA_PT_context_mesh.COMPAT_ENGINES.add("POVRAY_RENDER")
# make some native panels contextual to some object variable
# by recreating custom panels inheriting their properties
@@ -47,10 +42,10 @@ class ModifierButtonsPanel:
"""Use this class to define buttons from the modifier tab of
properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "modifier"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -63,10 +58,10 @@ class ObjectButtonsPanel:
"""Use this class to define buttons from the object tab of
properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "object"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -79,22 +74,22 @@ class PovDataButtonsPanel(properties_data_mesh.MeshButtonsPanel):
"""Use this class to define buttons from the edit data tab of
properties window."""
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
POV_OBJECT_TYPES = {
- 'PLANE',
- 'BOX',
- 'SPHERE',
- 'CYLINDER',
- 'CONE',
- 'TORUS',
- 'BLOB',
- 'ISOSURFACE_NODE',
- 'ISOSURFACE_VIEW',
- 'SUPERELLIPSOID',
- 'SUPERTORUS',
- 'HEIGHT_FIELD',
- 'PARAMETRIC',
- 'POLYCIRCLE',
+ "PLANE",
+ "BOX",
+ "SPHERE",
+ "CYLINDER",
+ "CONE",
+ "TORUS",
+ "BLOB",
+ "ISOSURFACE_NODE",
+ "ISOSURFACE_VIEW",
+ "SUPERELLIPSOID",
+ "SUPERTORUS",
+ "HEIGHT_FIELD",
+ "PARAMETRIC",
+ "POLYCIRCLE",
}
@classmethod
@@ -176,7 +171,7 @@ class MODIFIERS_PT_POV_modifiers(ModifierButtonsPanel, Panel):
once_csg = 0
for mod in ob.modifiers:
if once_csg == 0 and mod:
- if mod.type == 'BOOLEAN':
+ if mod.type == "BOOLEAN":
col.prop(ob.pov, "boolean_mod")
once_csg = 1
@@ -220,7 +215,7 @@ class OBJECT_PT_POV_obj_parameters(ObjectButtonsPanel, Panel):
col.prop(obj.pov, "hollow")
col.prop(obj.pov, "double_illuminate")
- if obj.type == 'META' or obj.pov.curveshape == 'lathe':
+ if obj.type == "META" or obj.pov.curveshape == "lathe":
# if obj.pov.curveshape == 'sor'
col.prop(obj.pov, "sturm")
col.prop(obj.pov, "no_shadow")
@@ -242,14 +237,14 @@ class OBJECT_PT_POV_obj_sphere(PovDataButtonsPanel, Panel):
"""Use this class to define pov sphere primitive parameters buttons."""
bl_label = "POV Sphere"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'SPHERE' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "SPHERE" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -258,16 +253,16 @@ class OBJECT_PT_POV_obj_sphere(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'SPHERE':
+ if obj.pov.object_as == "SPHERE":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Sphere radius: " + str(obj.pov.sphere_radius))
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -282,14 +277,14 @@ class OBJECT_PT_POV_obj_cylinder(PovDataButtonsPanel, Panel):
"""Use this class to define pov cylinder primitive parameters buttons."""
bl_label = "POV Cylinder"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'CYLINDER' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "CYLINDER" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -298,21 +293,17 @@ class OBJECT_PT_POV_obj_cylinder(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'CYLINDER':
+ if obj.pov.object_as == "CYLINDER":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters",
- text="Exported parameters below",
- icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Cylinder radius: " + str(obj.pov.cylinder_radius))
col.label(text="Cylinder cap location: " + str(obj.pov.cylinder_location_cap))
else:
col.prop(
- obj.pov, "unlock_parameters",
- text="Edit exported parameters",
- icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -328,14 +319,14 @@ class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel):
"""Use this class to define pov cone primitive parameters buttons."""
bl_label = "POV Cone"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'CONE' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "CONE" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -344,10 +335,10 @@ class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'CONE':
+ if obj.pov.object_as == "CONE":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Cone base radius: " + str(obj.pov.cone_base_radius))
col.label(text="Cone cap radius: " + str(obj.pov.cone_cap_radius))
@@ -355,7 +346,7 @@ class OBJECT_PT_POV_obj_cone(PovDataButtonsPanel, Panel):
col.label(text="Cone height: " + str(obj.pov.cone_height))
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -373,14 +364,14 @@ class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel):
"""Use this class to define pov superellipsoid primitive parameters buttons."""
bl_label = "POV Superquadric ellipsoid"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'SUPERELLIPSOID' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "SUPERELLIPSOID" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -389,10 +380,10 @@ class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'SUPERELLIPSOID':
+ if obj.pov.object_as == "SUPERELLIPSOID":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Radial segmentation: " + str(obj.pov.se_u))
col.label(text="Lateral segmentation: " + str(obj.pov.se_v))
@@ -401,7 +392,7 @@ class OBJECT_PT_POV_obj_superellipsoid(PovDataButtonsPanel, Panel):
col.label(text="Fill up and down: " + str(obj.pov.se_edit))
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -420,14 +411,14 @@ class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel):
"""Use this class to define pov torus primitive parameters buttons."""
bl_label = "POV Torus"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'TORUS' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "TORUS" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -436,10 +427,10 @@ class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'TORUS':
+ if obj.pov.object_as == "TORUS":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Torus major radius: " + str(obj.pov.torus_major_radius))
col.label(text="Torus minor radius: " + str(obj.pov.torus_minor_radius))
@@ -447,7 +438,7 @@ class OBJECT_PT_POV_obj_torus(PovDataButtonsPanel, Panel):
col.label(text="Torus minor segments: " + str(obj.pov.torus_minor_segments))
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -465,14 +456,14 @@ class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel):
"""Use this class to define pov supertorus primitive parameters buttons."""
bl_label = "POV SuperTorus"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'SUPERTORUS' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "SUPERTORUS" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -481,10 +472,10 @@ class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'SUPERTORUS':
+ if obj.pov.object_as == "SUPERTORUS":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="SuperTorus major radius: " + str(obj.pov.st_major_radius))
col.label(text="SuperTorus minor radius: " + str(obj.pov.st_minor_radius))
@@ -500,7 +491,7 @@ class OBJECT_PT_POV_obj_supertorus(PovDataButtonsPanel, Panel):
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -524,14 +515,14 @@ 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'}
+ 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)
+ return obj and obj.pov.object_as == "ISOSURFACE_VIEW" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -540,8 +531,9 @@ class OBJECT_PT_POV_obj_isosurface(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'ISOSURFACE_VIEW':
- col.prop(obj.pov, "isosurface_eq")
+ 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."""
@@ -553,7 +545,7 @@ class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel):
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'PARAMETRIC' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "PARAMETRIC" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -562,10 +554,10 @@ class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'PARAMETRIC':
+ if obj.pov.object_as == "PARAMETRIC":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Minimum U: " + str(obj.pov.u_min))
col.label(text="Minimum V: " + str(obj.pov.v_min))
@@ -577,7 +569,7 @@ class OBJECT_PT_POV_obj_parametric(PovDataButtonsPanel, Panel):
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
@@ -597,7 +589,7 @@ class OBJECT_PT_povray_replacement_text(ObjectButtonsPanel, Panel):
"""Use this class to define pov object replacement field."""
bl_label = "Custom POV Code"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
@@ -624,7 +616,7 @@ def check_add_mesh_extra_objects():
def menu_func_add(self, context):
"""Append the POV primitives submenu to blender add objects menu"""
engine = context.scene.render.engine
- if engine == 'POVRAY_RENDER':
+ if engine == "POVRAY_RENDER":
self.layout.menu("VIEW_MT_POV_primitives_add", icon="PLUGIN")
@@ -633,16 +625,16 @@ class VIEW_MT_POV_primitives_add(Menu):
bl_idname = "VIEW_MT_POV_primitives_add"
bl_label = "Povray"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
- return engine == 'POVRAY_RENDER'
+ return engine == "POVRAY_RENDER"
def draw(self, context):
layout = self.layout
- layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator_context = "INVOKE_REGION_WIN"
layout.menu(VIEW_MT_POV_Basic_Shapes.bl_idname, text="Primitives", icon="GROUP")
layout.menu(VIEW_MT_POV_import.bl_idname, text="Import", icon="IMPORT")
@@ -655,22 +647,22 @@ class VIEW_MT_POV_Basic_Shapes(Menu):
def draw(self, context):
layout = self.layout
- layout.operator_context = 'INVOKE_REGION_WIN'
- layout.operator("pov.addplane", text="Infinite Plane", icon='MESH_PLANE')
- layout.operator("pov.addbox", text="Box", icon='MESH_CUBE')
- layout.operator("pov.addsphere", text="Sphere", icon='SHADING_RENDERED')
+ layout.operator_context = "INVOKE_REGION_WIN"
+ layout.operator("pov.addplane", text="Infinite Plane", icon="MESH_PLANE")
+ layout.operator("pov.addbox", text="Box", icon="MESH_CUBE")
+ layout.operator("pov.addsphere", text="Sphere", icon="SHADING_RENDERED")
layout.operator("pov.addcylinder", text="Cylinder", icon="MESH_CYLINDER")
layout.operator("pov.addcone", text="Cone", icon="MESH_CONE")
- layout.operator("pov.addtorus", text="Torus", icon='MESH_TORUS')
+ layout.operator("pov.addtorus", text="Torus", icon="MESH_TORUS")
layout.separator()
layout.operator("pov.addrainbow", text="Rainbow", icon="COLOR")
- layout.operator("pov.addlathe", text="Lathe", icon='MOD_SCREW')
- layout.operator("pov.addprism", text="Prism", icon='MOD_SOLIDIFY')
- layout.operator("pov.addsuperellipsoid", text="Superquadric Ellipsoid", icon='MOD_SUBSURF')
+ layout.operator("pov.addlathe", text="Lathe", icon="MOD_SCREW")
+ layout.operator("pov.addprism", text="Prism", icon="MOD_SOLIDIFY")
+ layout.operator("pov.addsuperellipsoid", text="Superquadric Ellipsoid", icon="MOD_SUBSURF")
layout.operator("pov.addheightfield", text="Height Field", icon="RNDCURVE")
- layout.operator("pov.addspheresweep", text="Sphere Sweep", icon='FORCE_CURVE')
+ layout.operator("pov.addspheresweep", text="Sphere Sweep", icon="FORCE_CURVE")
layout.separator()
- layout.operator("pov.addblobsphere", text="Blob Sphere", icon='META_DATA')
+ layout.operator("pov.addblobsphere", text="Blob Sphere", icon="META_DATA")
layout.separator()
layout.label(text="Isosurfaces")
layout.operator("pov.addisosurfacebox", text="Isosurface Box", icon="META_CUBE")
@@ -697,464 +689,377 @@ class VIEW_MT_POV_Basic_Shapes(Menu):
# layout.separator()
return
- layout.operator("pov.addparametric", text="Parametric", icon='SCRIPTPLUGINS')
+ layout.operator("pov.addparametric", text="Parametric", icon="SCRIPTPLUGINS")
+
# ------------ Tool bar button------------ #
-icon_path = (os.path.join(os.path.dirname(__file__), "icons"))
+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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ ("pov.addcylinder", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_cone_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ 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'
+ 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_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}),
- )
+ ("pov.addheightfield", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_superellipsoid_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addsuperellipsoid", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_spheresweep_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addspheresweep", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_loft_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ 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'
+ 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_idname = "pov.addpolygontocircle"
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_description = "add a POV regular polygon to circle blending macro"
+ bl_icon = os.path.join(icon_path, "pov.add.polygontocircle")
bl_widget = None
bl_keymap = (
- ("pov.addpolytocircle", {"type": 'LEFTMOUSE', "value": 'PRESS'},
- {"properties": None}),
- )
+ ("pov.addpolygontocircle", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_parametric_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addparametric", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_isosurface_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addisosurface", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_isosurfacebox_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addisosurfacebox", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_isosurfacesphere_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addisosurfacesphere", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_isosurfacesupertorus_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addsupertorus", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_blobsphere_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addblobsphere", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_blobcapsule_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addblobcapsule", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_blobplane_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addblobplane", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_blobellipsoid_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addblobellipsoid", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
class VIEW_WT_POV_blobcube_add(WorkSpaceTool):
- bl_space_type='VIEW_3D'
- bl_context_mode='OBJECT'
+ 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_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}),
- )
+ ("pov.addblobcube", {"type": "LEFTMOUSE", "value": "PRESS"}, {"properties": None}),
+ )
classes = (
@@ -1208,6 +1113,7 @@ tool_classes = (
VIEW_WT_POV_blobcube_add,
)
+
def register():
for cls in classes:
register_class(cls)
@@ -1216,7 +1122,9 @@ def register():
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)
+ register_tool(
+ wtl, after=last_tool, separator=index in {0, 7, 11, 12, 14, 19}, group=index == 15
+ )
last_tool = {wtl.bl_idname}
bpy.types.VIEW3D_MT_add.prepend(menu_func_add)
diff --git a/render_povray/model_meta_topology.py b/render_povray/model_meta_topology.py
new file mode 100644
index 00000000..6a22be12
--- /dev/null
+++ b/render_povray/model_meta_topology.py
@@ -0,0 +1,306 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Translate Blender meta balls to POV blobs."""
+
+import bpy
+from .shading import write_object_material_interior
+
+def export_meta(file, metas, tab_write, DEF_MAT_NAME):
+ """write all POV blob primitives and Blender Metas to exported file """
+ # TODO - blenders 'motherball' naming is not supported.
+
+ from .render import (
+ safety,
+ global_matrix,
+ write_matrix,
+ comments,
+ )
+ 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 not elems:
+ tab_write(file, "\n//dummy sphere to represent empty meta location\n")
+ tab_write(file,
+ "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)
+ return
+
+ # other metaballs
+
+ for mg, mob in meta_group.items():
+ if len(meta_elems[mg]) != 0:
+ tab_write(file, "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(file,
+ "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g "
+ % (loc.x, loc.y, loc.z, elem.radius, stiffness)
+ )
+ write_matrix(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+ elif elem.type == 'ELLIPSOID':
+ tab_write(file,
+ "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(file,
+ "scale <%.6g, %.6g, %.6g>"
+ % (elem.size_x, elem.size_y, elem.size_z)
+ )
+ write_matrix(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+ elif elem.type == 'CAPSULE':
+ tab_write(file,
+ "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,
+ )
+ )
+ # tab_write(file, "scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z))
+ write_matrix(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+
+ elif elem.type == 'CUBE':
+ tab_write(file,
+ "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(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+ tab_write(file,
+ "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(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+ tab_write(file,
+ "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(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+
+ elif elem.type == 'PLANE':
+ tab_write(file,
+ "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(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\n")
+ tab_write(file,
+ "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(file, global_matrix @ elems[1].matrix_world)
+ tab_write(file, "}\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.pov.use_transparency
+ and one_material.pov.transparency_method == 'RAYTRACE'
+ ):
+ pov_filter = one_material.pov_raytrace_transparency.filter * (
+ 1.0 - one_material.pov.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(file,
+ "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
+ % (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ pov_filter,
+ trans,
+ )
+ )
+ tab_write(file, "finish{%s} " % safety(material_finish, ref_level_bound=2))
+ else:
+ material_finish = DEF_MAT_NAME
+ trans = 0.0
+ tab_write(file,
+ "pigment{srgbt<1,1,1,%.3g>} finish{%s} "
+ % (trans, safety(material_finish, ref_level_bound=2))
+ )
+
+ write_object_material_interior(file, one_material, mob, tab_write)
+ # write_object_material_interior(file, one_material, elems[1])
+ tab_write(file, "radiosity{importance %3g}\n" % mob.pov.importance_value)
+ tab_write(file, "}\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(file, "blob {\n")
+ tab_write(file, "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(file, "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(file, "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(file, "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.pov.use_transparency and material.pov.transparency_method == 'RAYTRACE':
+ pov_filter = material.pov_raytrace_transparency.filter * (1.0 -
+ material.pov.alpha)
+ trans = (1.0 - material.pov.alpha) - pov_filter
+ else:
+ pov_filter = 0.0
+
+ material_finish = material_names_dictionary[material.name]
+
+ tab_write(file, "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" %
+ (diffuse_color[0], diffuse_color[1], diffuse_color[2],
+ pov_filter, trans))
+ tab_write(file, "finish {%s}\n" % safety(material_finish, ref_level_bound=2))
+
+ else:
+ tab_write(file, "pigment {srgb 1} \n")
+ # Write the finish last.
+ tab_write(file, "finish {%s}\n" % (safety(DEF_MAT_NAME, ref_level_bound=2)))
+
+ write_object_material_interior(file, material, elems[1], tab_write)
+
+ write_matrix(file, global_matrix @ ob.matrix_world)
+ # Importance for radiosity sampling added here
+ tab_write(file, "radiosity { \n")
+ # importance > ob.pov.importance_value
+ tab_write(file, "importance %3g \n" % importance)
+ tab_write(file, "}\n")
+
+ tab_write(file, "}\n") # End of Metaball block
+
+ if comments and len(metas) >= 1:
+ file.write("\n")
+'''
diff --git a/render_povray/model_poly_topology.py b/render_povray/model_poly_topology.py
new file mode 100644
index 00000000..f6d400a2
--- /dev/null
+++ b/render_povray/model_poly_topology.py
@@ -0,0 +1,719 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Translate to POV the control point compound geometries.
+
+Here polygon meshes as POV mesh2 objects.
+"""
+
+import bpy
+from . import texturing
+from .scenography import image_format, img_map, img_map_transforms
+from .shading import write_object_material_interior
+from .model_primitives import write_object_modifiers
+
+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
+
+def export_mesh(file,
+ ob,
+ povdataname,
+ material_names_dictionary,
+ unpacked_images,
+ tab_level,
+ tab_write,
+ linebreaksinlists):
+ from .render import (
+ string_strip_hyphen,
+ tab,
+ comments,
+ preview_dir,
+ )
+
+ ob_eval = ob # not sure this is needed in case to_mesh_clear could damage obj ?
+ importance = ob.pov.importance_value
+
+ try:
+ me = ob_eval.to_mesh()
+
+ # Here identify the exception for mesh object with no data: Runtime-Error ?
+ # So we can write something for the dataname or maybe treated "if not me" below
+ except BaseException as e:
+ print(e.__doc__)
+ print("An exception occurred: {}".format(e))
+ # also happens when curves cant be made into meshes because of no-data
+ return False # To continue object loop
+ if me:
+ me.calc_loop_triangles()
+ me_materials = me.materials
+ me_faces = me.loop_triangles[:]
+ # --- numpytest
+ # me_looptris = me.loops
+
+ # Below otypes = ['int32'] is a 32-bit signed integer number numpy datatype
+ # get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
+ # faces_verts_idx = get_v_index(me_looptris)
+
+ # if len(me_faces)==0:
+ # tab_write(file, "\n//dummy sphere to represent empty mesh location\n")
+ # tab_write(file, "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)
+
+ if not me or not me_faces:
+ tab_write(file, "\n//dummy sphere to represent empty mesh location\n")
+ tab_write(
+ file,
+ "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
+ % povdataname,
+ )
+ return False # To continue object loop
+
+ uv_layers = me.uv_layers
+ if len(uv_layers) > 0:
+ if me.uv_layers.active and uv_layers.active.data:
+ uv_layer = uv_layers.active.data
+ else:
+ uv_layer = None
+
+ try:
+ # vcol_layer = me.vertex_colors.active.data
+ vcol_layer = me.vertex_colors.active.data
+ except AttributeError:
+ vcol_layer = None
+
+ faces_verts = [f.vertices[:] for f in me_faces]
+ faces_normals = [f.normal[:] for f in me_faces]
+ verts_normals = [v.normal[:] for v in me.vertices]
+
+ # Use named declaration to allow reference e.g. for baking. MR
+ file.write("\n")
+ tab_write(file, "#declare %s =\n" % povdataname)
+ tab_write(file, "mesh2 {\n")
+ tab_write(file, "vertex_vectors {\n")
+ tab_write(file, "%d" % len(me.vertices)) # vert count
+
+ tab_str = tab * tab_level
+ for v in me.vertices:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ # tab_write(file, "<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ # Build unique Normal list
+ uniqueNormals = {}
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ # [-1] is a dummy index, use a list so we can modify in place
+ if f.use_smooth: # Use vertex normals
+ for v in fv:
+ key = verts_normals[v]
+ uniqueNormals[key] = [-1]
+ else: # Use face normal
+ key = faces_normals[fi]
+ uniqueNormals[key] = [-1]
+
+ tab_write(file, "normal_vectors {\n")
+ tab_write(file, "%d" % len(uniqueNormals)) # vert count
+ idx = 0
+ tab_str = tab * tab_level
+ for no, index in uniqueNormals.items():
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f, %.6f>" % no) # vert count
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f, %.6f>" % no) # vert count
+ index[0] = idx
+ idx += 1
+ file.write("\n")
+ tab_write(file, "}\n")
+ # Vertex colors
+ vertCols = {} # Use for material colors also.
+
+ if uv_layer:
+ # Generate unique UV's
+ uniqueUVs = {}
+ # n = 0
+ for f in me_faces: # me.faces in 2.7
+ uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
+
+ for uv in uvs:
+ uniqueUVs[uv[:]] = [-1]
+
+ tab_write(file, "uv_vectors {\n")
+ # print unique_uvs
+ tab_write(file, "%d" % len(uniqueUVs)) # vert count
+ idx = 0
+ tab_str = tab * tab_level
+ for uv, index in uniqueUVs.items():
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(tab_str + "<%.6f, %.6f>" % uv)
+ else:
+ file.write(", ")
+ file.write("<%.6f, %.6f>" % uv)
+ index[0] = idx
+ idx += 1
+ """
+ else:
+ # Just add 1 dummy vector, no real UV's
+ tab_write(file, '1') # vert count
+ file.write(',\n\t\t<0.0, 0.0>')
+ """
+ file.write("\n")
+ tab_write(file, "}\n")
+ if me.vertex_colors:
+ # Write down vertex colors as a texture for each vertex
+ tab_write(file, "texture_list {\n")
+ tab_write(
+ file, "%d\n" % (len(me_faces) * 3)
+ ) # assumes we have only triangles
+ VcolIdx = 0
+ if comments:
+ file.write(
+ "\n //Vertex colors: one simple pigment texture per vertex\n"
+ )
+ for fi, f in enumerate(me_faces):
+ # annoying, index may be invalid
+ material_index = f.material_index
+ try:
+ material = me_materials[material_index]
+ except BaseException as e:
+ print(e.__doc__)
+ print("An exception occurred: {}".format(e))
+ material = None
+ if (
+ material
+ # and material.pov.use_vertex_color_paint
+ ): # Or maybe Always use vertex color when there is some for now
+
+ cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
+
+ for col in cols:
+ key = (
+ col[0],
+ col[1],
+ col[2],
+ material_index,
+ ) # Material index!
+ VcolIdx += 1
+ vertCols[key] = [VcolIdx]
+ if linebreaksinlists:
+ tab_write(
+ file,
+ "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
+ % (col[0], col[1], col[2]),
+ )
+ else:
+ tab_write(
+ file,
+ "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
+ % (col[0], col[1], col[2]),
+ )
+ tab_str = tab * tab_level
+ else:
+ if material:
+ # Multiply diffuse with SSS Color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ material_index,
+ )
+ vertCols[key] = [-1]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ material_index,
+ )
+ vertCols[key] = [-1]
+
+ tab_write(file, "\n}\n")
+ # Face indices
+ tab_write(file, "\nface_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ material_index = f.material_index
+
+ if vcol_layer:
+ cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
+
+ if not me_materials or (
+ me_materials[material_index] is None
+ ): # No materials
+ if linebreaksinlists:
+ file.write(",\n")
+ # vert count
+ file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
+ else:
+ material = me_materials[material_index]
+ if me.vertex_colors: # and material.pov.use_vertex_color_paint:
+ # Color per vertex - vertex color
+
+ col1 = cols[0]
+ col2 = cols[1]
+ col3 = cols[2]
+
+ ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
+ ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
+ ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
+ else:
+ # Color per material - flat material color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ ci1 = ci2 = ci3 = vertCols[
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ f.material_index,
+ ][0]
+ # ci are zero based index so we'll subtract 1 from them
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>, %d,%d,%d"
+ % (
+ fv[0],
+ fv[1],
+ fv[2],
+ ci1 - 1,
+ ci2 - 1,
+ ci3 - 1,
+ )
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>, %d,%d,%d"
+ % (
+ fv[0],
+ fv[1],
+ fv[2],
+ ci1 - 1,
+ ci2 - 1,
+ ci3 - 1,
+ )
+ ) # vert count
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ # normal_indices indices
+ tab_write(file, "normal_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for fi, fv in enumerate(faces_verts):
+
+ if me_faces[fi].use_smooth:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ idx = uniqueNormals[faces_normals[fi]][0]
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str + "<%d,%d,%d>" % (idx, idx, idx)
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ if uv_layer:
+ tab_write(file, "uv_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for f in me_faces:
+ uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ # XXX BOOLEAN MODIFIER
+ write_object_csg_inside_vector(ob, file)
+
+ if me.materials:
+ try:
+ material = me.materials[0] # dodgy
+ write_object_material_interior(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+
+ # POV object modifiers such as
+ # hollow / sturm / double_illuminate etc.
+ write_object_modifiers(ob, file)
+
+ # Importance for radiosity sampling added here:
+ tab_write(file, "radiosity { \n")
+ tab_write(file, "importance %3g \n" % importance)
+ tab_write(file, "}\n")
+
+ tab_write(file, "}\n") # End of mesh block
+ else:
+ facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!!
+ if me_materials:
+ new_me_faces_mat_idx = (f for f in me_faces if f.material_index not in
+ facesMaterials)
+ for f in new_me_faces_mat_idx:
+ facesMaterials.append(f.material_index)
+ # No vertex colors, so write material colors as vertex colors
+
+ for i, material in enumerate(me_materials):
+ if (
+ material and material.pov.material_use_nodes is False
+ ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ # Multiply diffuse with SSS Color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ i,
+ ) # i == f.mat
+ vertCols[key] = [-1]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ key = (
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ i,
+ ) # i == f.mat
+ vertCols[key] = [-1]
+
+ idx = 0
+ texturing.local_material_names = []
+ for col, index in vertCols.items():
+ # if me_materials:
+ mater = me_materials[col[3]]
+ if me_materials is not None:
+ texturing.write_texture_influence(
+ file,
+ mater,
+ material_names_dictionary,
+ image_format,
+ img_map,
+ img_map_transforms,
+ tab_write,
+ comments,
+ col,
+ preview_dir,
+ unpacked_images,
+ )
+ # ------------------------------------------------
+ index[0] = idx
+ idx += 1
+
+ # Vert Colors
+ tab_write(file, "texture_list {\n")
+ # In case there's is no material slot, give at least one texture
+ # (an empty one so it uses pov default)
+ if len(vertCols) != 0:
+ tab_write(
+ file, "%s" % (len(vertCols))
+ ) # vert count
+ else:
+ tab_write(file, "1")
+ # below "material" alias, added check obj.active_material
+ # to avoid variable referenced before assignment error
+ try:
+ material = ob.active_material
+ except IndexError:
+ # when no material slot exists,
+ material = None
+
+ # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ if (
+ material
+ and ob.active_material is not None
+ and not material.pov.material_use_nodes
+ and not material.use_nodes
+ ):
+ if material.pov.replacement_text != "":
+ file.write("\n")
+ file.write(" texture{%s}\n" % material.pov.replacement_text)
+
+ else:
+ # Loop through declared materials list
+ # global local_material_names
+ for cMN in texturing.local_material_names:
+ if material != "Default":
+ file.write("\n texture{MAT_%s}\n" % cMN)
+ # use string_strip_hyphen(material_names_dictionary[material]))
+ # or Something like that to clean up the above?
+ elif material and material.pov.material_use_nodes:
+ for index in facesMaterials:
+ faceMaterial = string_strip_hyphen(
+ bpy.path.clean_name(me_materials[index].name)
+ )
+ file.write("\n texture{%s}\n" % faceMaterial)
+ # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ elif vertCols:
+ for cMN in vertCols: # or in texturing.local_material_names:
+ # if possible write only one, though
+ file.write(" texture{}\n")
+ else:
+ file.write(" texture{}\n")
+ tab_write(file, "}\n")
+
+ # Face indices
+ tab_write(file, "face_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+
+ for fi, f in enumerate(me_faces):
+ fv = faces_verts[fi]
+ material_index = f.material_index
+
+ if vcol_layer:
+ cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
+
+ if (
+ not me_materials or me_materials[material_index] is None
+ ): # No materials
+ if linebreaksinlists:
+ file.write(",\n")
+ # vert count
+ file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
+ else:
+ material = me_materials[material_index]
+ ci1 = ci2 = ci3 = f.material_index
+ if me.vertex_colors: # and material.pov.use_vertex_color_paint:
+ # Color per vertex - vertex color
+
+ col1 = cols[0]
+ col2 = cols[1]
+ col3 = cols[2]
+
+ ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
+ ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
+ ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
+ elif material.pov.material_use_nodes:
+ ci1 = ci2 = ci3 = 0
+ else:
+ # Color per material - flat material color
+ if material.pov_subsurface_scattering.use:
+ diffuse_color = [
+ i * j
+ for i, j in zip(
+ material.pov_subsurface_scattering.color[:],
+ material.diffuse_color[:],
+ )
+ ]
+ else:
+ diffuse_color = material.diffuse_color[:]
+ ci1 = ci2 = ci3 = vertCols[
+ diffuse_color[0],
+ diffuse_color[1],
+ diffuse_color[2],
+ f.material_index,
+ ][0]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>, %d,%d,%d"
+ % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
+ ) # vert count
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ # normal_indices indices
+ tab_write(file, "normal_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for fi, fv in enumerate(faces_verts):
+ if me_faces[fi].use_smooth:
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueNormals[verts_normals[fv[0]]][0],
+ uniqueNormals[verts_normals[fv[1]]][0],
+ uniqueNormals[verts_normals[fv[2]]][0],
+ )
+ ) # vert count
+ else:
+ idx = uniqueNormals[faces_normals[fi]][0]
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str + "<%d,%d,%d>" % (idx, idx, idx)
+ ) # vertcount
+ else:
+ file.write(", ")
+ file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ if uv_layer:
+ tab_write(file, "uv_indices {\n")
+ tab_write(file, "%d" % (len(me_faces))) # faces count
+ tab_str = tab * tab_level
+ for f in me_faces:
+ uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
+
+ if linebreaksinlists:
+ file.write(",\n")
+ file.write(
+ tab_str
+ + "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+ else:
+ file.write(", ")
+ file.write(
+ "<%d,%d,%d>"
+ % (
+ uniqueUVs[uvs[0]][0],
+ uniqueUVs[uvs[1]][0],
+ uniqueUVs[uvs[2]][0],
+ )
+ )
+
+ file.write("\n")
+ tab_write(file, "}\n")
+
+ # XXX BOOLEAN
+ write_object_csg_inside_vector(ob, file)
+ if me.materials:
+ try:
+ material = me.materials[0] # dodgy
+ write_object_material_interior(file, material, ob, tab_write)
+ except IndexError:
+ print(me)
+
+ # POV object modifiers such as
+ # hollow / sturm / double_illuminate etc.
+ write_object_modifiers(ob, file)
+
+ # Importance for radiosity sampling added here:
+ tab_write(file, "radiosity { \n")
+ tab_write(file, "importance %3g \n" % importance)
+ tab_write(file, "}\n")
+
+ tab_write(file, "}\n") # End of mesh block
+
+ ob_eval.to_mesh_clear()
+ return True
diff --git a/render_povray/model_primitives.py b/render_povray/model_primitives.py
new file mode 100644
index 00000000..16ca04e4
--- /dev/null
+++ b/render_povray/model_primitives.py
@@ -0,0 +1,796 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+""" Get POV-Ray specific objects In and Out of Blender """
+from math import pi, cos, sin
+import os.path
+import bpy
+from bpy_extras.object_utils import object_data_add
+from bpy_extras.io_utils import ImportHelper
+from bpy.utils import register_class, unregister_class
+from bpy.types import Operator
+
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ IntProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ EnumProperty,
+)
+
+from mathutils import Vector, Matrix
+
+
+# import collections
+
+
+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
+ # import .model_all.write_object_csg_inside_vector
+ write_object_csg_inside_vector(ob, file)
+ """
+
+ if ob.pov.hollow:
+ File.write("\thollow\n")
+ if ob.pov.double_illuminate:
+ File.write("\tdouble_illuminate\n")
+ if ob.pov.sturm:
+ File.write("\tsturm\n")
+ if ob.pov.no_shadow:
+ File.write("\tno_shadow\n")
+ if ob.pov.no_image:
+ File.write("\tno_image\n")
+ if ob.pov.no_reflection:
+ File.write("\tno_reflection\n")
+ if ob.pov.no_radiosity:
+ File.write("\tno_radiosity\n")
+ if ob.pov.inverse:
+ File.write("\tinverse\n")
+ if ob.pov.hierarchy:
+ File.write("\thierarchy\n")
+
+ # XXX, Commented definitions
+ """
+ if scene.pov.photon_enable:
+ File.write("photons {\n")
+ if ob.pov.target:
+ File.write("target %.4g\n"%ob.pov.target_value)
+ if ob.pov.refraction:
+ File.write("refraction on\n")
+ if ob.pov.reflection:
+ File.write("reflection on\n")
+ if ob.pov.pass_through:
+ File.write("pass_through\n")
+ File.write("}\n")
+ if ob.pov.object_ior > 1:
+ File.write("interior {\n")
+ File.write("ior %.4g\n"%ob.pov.object_ior)
+ if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
+ File.write("ior %.4g\n"%ob.pov.dispersion_value)
+ File.write("ior %s\n"%ob.pov.dispersion_samples)
+ if scene.pov.photon_enable == False:
+ File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
+ """
+
+
+def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
+ """Generate proxy mesh."""
+ if mesh is None:
+ mesh = bpy.data.meshes.new(name)
+ mesh.from_pydata(verts, edges, faces)
+ # Function Arguments change : now bpy.types.Mesh.update (calc_edges, calc_edges_loose,
+ # calc_loop_triangles), was (calc_edges, calc_tessface)
+
+
+ mesh.update()
+ mesh.validate(
+ verbose=False
+ ) # Set it to True to see debug messages (helps ensure you generate valid geometry).
+ if hide_geometry:
+ mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
+ mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
+ mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
+ return mesh
+
+
+class POV_OT_plane_add(Operator):
+ """Add the representation of POV infinite plane using just a very big Blender Plane.
+
+ 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.addplane"
+ 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=10000)
+ ob = context.object
+ ob.name = ob.data.name = "PovInfinitePlane"
+ bpy.ops.object.mode_set(mode="EDIT")
+ self.report(
+ {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
+ )
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "PLANE"
+ return {"FINISHED"}
+
+
+class POV_OT_box_add(Operator):
+ """Add the representation of POV box using a simple Blender mesh cube.
+
+ 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.addbox"
+ bl_label = "Box"
+ bl_description = "Add Box"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ def execute(self, context):
+ # layers = 20*[False]
+ # layers[0] = True
+ bpy.ops.mesh.primitive_cube_add()
+ ob = context.object
+ ob.name = ob.data.name = "PovBox"
+ bpy.ops.object.mode_set(mode="EDIT")
+ self.report(
+ {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
+ )
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ ob.pov.object_as = "BOX"
+ return {"FINISHED"}
+
+
+def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
+ """Pick POV cylinder properties either from creation operator, import, or data update"""
+ if op:
+ cy_rad = op.cy_rad
+ loc = bpy.context.scene.cursor.location
+ loc_cap[0] = loc[0]
+ loc_cap[1] = loc[1]
+ loc_cap[2] = loc[2] + 2
+ vec = Vector(loc_cap) - Vector(loc)
+ depth = vec.length
+ rot = Vector((0, 0, 1)).rotation_difference(vec) # Rotation from Z axis.
+ trans = rot @ Vector(
+ (0, 0, depth / 2)
+ ) # Such that origin is at center of the base of the cylinder.
+ roteuler = rot.to_euler()
+ if not ob:
+ bpy.ops.object.add(type="MESH", location=loc)
+ ob = context.object
+ ob.name = ob.data.name = "PovCylinder"
+ ob.pov.cylinder_radius = radius
+ ob.pov.cylinder_location_cap = vec
+ ob.data.use_auto_smooth = True
+ ob.pov.object_as = "CYLINDER"
+
+ else:
+ ob.location = loc
+
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.reveal()
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
+ bpy.ops.mesh.primitive_cylinder_add(
+ radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type="NGON"
+ ) # 'NOTHING'
+ bpy.ops.transform.translate(value=trans)
+
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ bpy.ops.object.shade_smooth()
+
+
+class POV_OT_cylinder_add(Operator):
+ """Add the representation of POV cylinder using pov_cylinder_define() function.
+
+ Use imported_cyl_loc when this operator is run by POV importer."""
+ bl_options = {'REGISTER', 'UNDO'}
+ bl_idname = "pov.addcylinder"
+ bl_label = "Cylinder"
+ bl_description = "Add Cylinder"
+
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ # Keep in sync within model_properties.py section Cylinder
+ # as this allows interactive update
+ cy_rad: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
+
+ imported_cyl_loc: FloatVectorProperty(
+ name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
+ )
+
+ imported_cyl_loc_cap: FloatVectorProperty(
+ name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
+ )
+
+ def execute(self, context):
+ props = self.properties
+ cy_rad = props.cy_rad
+ if ob := context.object:
+ if ob.pov.imported_cyl_loc:
+ 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:
+ 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",
+ )
+
+ pov_cylinder_define(context, self, None, self.cy_rad, LOC, LOC_CAP)
+
+ return {"FINISHED"}
+
+
+class POV_OT_cylinder_update(Operator):
+ """Update the POV cylinder.
+
+ Delete its previous proxy geometry and rerun pov_cylinder_define() function
+ with the new parameters"""
+
+ bl_idname = "pov.cylinder_update"
+ bl_label = "Update"
+ bl_description = "Update Cylinder"
+ 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 == "MESH"
+ and ob.pov.object_as == "CYLINDER"
+ and engine in cls.COMPAT_ENGINES
+ )
+
+ def execute(self, context):
+ ob = context.object
+ radius = ob.pov.cylinder_radius
+ loc = ob.location
+ loc_cap = loc + ob.pov.cylinder_location_cap
+
+ pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
+
+ return {"FINISHED"}
+
+
+# ----------------------------------- SPHERE---------------------------------- #
+def pov_sphere_define(context, op, ob, loc):
+ """create the representation of POV sphere using a Blender icosphere.
+
+ Its nice platonic solid curvature better represents pov rendertime
+ tessellation than a UV sphere"""
+
+ if op:
+ sphe_rad = op.sphe_rad
+ loc = bpy.context.scene.cursor.location
+ else:
+ assert ob
+ sphe_rad = ob.pov.sphere_radius
+
+ # keep object rotation and location for the add object operator
+ obrot = ob.rotation_euler
+ # obloc = ob.location
+ obscale = ob.scale
+
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.reveal()
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
+ bpy.ops.mesh.primitive_ico_sphere_add(
+ subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot
+ )
+ # 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.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
+
+ if not ob:
+ bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=sphe_rad, location=loc)
+ ob = context.object
+ ob.name = ob.data.name = "PovSphere"
+ ob.pov.sphere_radius = sphe_rad
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ ob.data.use_auto_smooth = True
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "SPHERE"
+
+
+class POV_OT_sphere_add(Operator):
+ """Add the representation of POV sphere using pov_sphere_define() function.
+
+ Use imported_loc when this operator is run by POV importer."""
+
+ bl_idname = "pov.addsphere"
+ bl_label = "Sphere"
+ bl_description = "Add Sphere Shape"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ # Keep in sync within model_properties.py section Sphere
+ # as this allows interactive update
+ sphe_rad: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
+
+ imported_loc: FloatVectorProperty(
+ name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
+ )
+
+ def execute(self, context):
+ props = self.properties
+ sphe_rad = props.sphe_rad
+ if ob := context.object:
+ 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
+ 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"}
+
+ # def execute(self,context):
+ # layers = 20*[False]
+ # layers[0] = True
+
+ # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
+ # ob = context.object
+ # bpy.ops.object.mode_set(mode="EDIT")
+ # self.report({'INFO'}, "This native POV-Ray primitive "
+ # "won't have any vertex to show in edit mode")
+ # bpy.ops.mesh.hide(unselected=False)
+ # bpy.ops.object.mode_set(mode="OBJECT")
+ # bpy.ops.object.shade_smooth()
+ # ob.pov.object_as = "SPHERE"
+ # ob.name = ob.data.name = 'PovSphere'
+ # return {'FINISHED'}
+
+
+class POV_OT_sphere_update(Operator):
+ """Update the POV sphere.
+
+ Delete its previous proxy geometry and rerun pov_sphere_define() function
+ with the new parameters"""
+
+ bl_idname = "pov.sphere_update"
+ bl_label = "Update"
+ bl_description = "Update Sphere"
+ 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 == "MESH"
+ and ob.pov.object_as == "SPHERE"
+ and engine in cls.COMPAT_ENGINES
+ )
+ def execute(self, context):
+
+ pov_sphere_define(context, None, context.object, context.object.location)
+
+ return {"FINISHED"}
+
+
+# ----------------------------------- CONE ---------------------------------- #
+def pov_cone_define(context, op, ob):
+ """Add the representation of POV cone using pov_define_mesh() function.
+
+ Blender cone does not offer the same features such as a second radius."""
+ verts = []
+ faces = []
+ if op:
+ mesh = None
+ base = op.base
+ cap = op.cap
+ seg = op.seg
+ height = op.height
+ else:
+ assert ob
+ mesh = ob.data
+ base = ob.pov.cone_base_radius
+ cap = ob.pov.cone_cap_radius
+ seg = ob.pov.cone_segments
+ height = ob.pov.cone_height
+
+ zc = height / 2
+ zb = -zc
+ angle = 2 * pi / seg
+ t = 0
+ for i in range(seg):
+ xb = base * cos(t)
+ yb = base * sin(t)
+ xc = cap * cos(t)
+ yc = cap * sin(t)
+ verts.extend([(xb, yb, zb), (xc, yc, zc)])
+ t += angle
+ for i in range(seg):
+ f = i * 2
+ if i == seg - 1:
+ faces.append([0, 1, f + 1, f])
+ else:
+ faces.append([f + 2, f + 3, f + 1, f])
+ if base != 0:
+ base_face = [i * 2 for i in range(seg - 1, -1, -1)]
+ faces.append(base_face)
+ if cap != 0:
+ cap_face = [i * 2 + 1 for i in range(seg)]
+ faces.append(cap_face)
+
+ mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
+ if not ob:
+ ob = object_data_add(context, mesh, operator=None)
+ ob.pov.cone_base_radius = base
+ ob.pov.cone_cap_radius = cap
+ ob.pov.cone_height = height
+ ob.pov.cone_base_z = zb
+ ob.pov.cone_cap_z = zc
+ ob.data.use_auto_smooth = True
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "CONE"
+
+class POV_OT_cone_add(Operator):
+ """Add the representation of POV cone using pov_cone_define() function."""
+
+ bl_idname = "pov.addcone"
+ bl_label = "Cone"
+ bl_description = "Add Cone"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ # Keep in sync within model_properties.py section Cone
+ # If someone knows how to define operators' props from a func, I'd be delighted to learn it!
+ base: FloatProperty(
+ name="Base radius",
+ description="The first radius of the cone",
+ default=1.0,
+ min=0.01,
+ max=100.0,
+ )
+ cap: FloatProperty(
+ name="Cap radius",
+ description="The second radius of the cone",
+ default=0.3,
+ min=0.0,
+ max=100.0,
+ )
+ seg: IntProperty(
+ name="Segments",
+ description="Radial segmentation of the proxy mesh",
+ default=16,
+ min=3,
+ max=265,
+ )
+ height: FloatProperty(
+ name="Height", description="Height of the cone", default=2.0, min=0.01, max=100.0
+ )
+
+ @classmethod
+ def poll(cls, context):
+ engine = context.scene.render.engine
+ return engine in cls.COMPAT_ENGINES
+
+ def execute(self, context):
+ pov_cone_define(context, self, None)
+
+ self.report(
+ {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
+ )
+ return {"FINISHED"}
+
+
+class POV_OT_cone_update(Operator):
+ """Update the POV cone.
+
+ Delete its previous proxy geometry and rerun pov_cone_define() function
+ with the new parameters"""
+
+ bl_idname = "pov.cone_update"
+ bl_label = "Update"
+ bl_description = "Update Cone"
+ 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 == "MESH"
+ and ob.pov.object_as == "CONE"
+ and engine in cls.COMPAT_ENGINES
+ )
+ def execute(self, context):
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.reveal()
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ pov_cone_define(context, None, context.object)
+
+ return {"FINISHED"}
+
+
+class POV_OT_rainbow_add(Operator):
+ """Add the representation of POV rainbow using a Blender spot light.
+
+ Rainbows indeed propagate along a visibility cone.
+ Flag its primitive type with a specific ob.pov.object_as attribute
+ and leave access to edit mode to keep user editable handles.
+ Add a constraint to orient it towards camera because POV Rainbows
+ are view dependant and having it always initially visible is less
+ confusing"""
+
+ bl_idname = "pov.addrainbow"
+ bl_label = "Rainbow"
+ bl_description = "Add Rainbow"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ def execute(self, context):
+ cam = context.scene.camera
+ bpy.ops.object.light_add(type="SPOT", radius=1)
+ ob = context.object
+ ob.data.show_cone = False
+ ob.data.spot_blend = 0.5
+ # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
+ ob.data.shadow_buffer_clip_start = 4 * cam.location.length
+ ob.data.distance = cam.location.length
+ ob.data.energy = 0
+ ob.name = ob.data.name = "PovRainbow"
+ ob.pov.object_as = "RAINBOW"
+
+ # obj = context.object
+ bpy.ops.object.constraint_add(type="DAMPED_TRACK")
+
+ ob.constraints["Damped Track"].target = cam
+ ob.constraints["Damped Track"].track_axis = "TRACK_NEGATIVE_Z"
+ ob.location = -cam.location
+
+ # refocus on the actual rainbow
+ bpy.context.view_layer.objects.active = ob
+ ob.select_set(True)
+
+ return {"FINISHED"}
+
+
+# ----------------------------------- TORUS ----------------------------------- #
+def pov_torus_define(context, op, ob):
+ """Add the representation of POV torus using just a Blender torus.
+
+ Picking properties either from creation operator, import, or data update.
+ But flag its primitive type with a specific pov.object_as attribute and lock edit mode
+ to keep proxy consistency by hiding edit geometry."""
+
+ if op:
+ mas = op.mas
+ mis = op.mis
+ mar = op.mar
+ mir = op.mir
+ else:
+ assert ob
+ mas = ob.pov.torus_major_segments
+ mis = ob.pov.torus_minor_segments
+ mar = ob.pov.torus_major_radius
+ mir = ob.pov.torus_minor_radius
+
+ # keep object rotation and location for the add object operator
+ obrot = ob.rotation_euler
+ obloc = ob.location
+
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.reveal()
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
+ bpy.ops.mesh.primitive_torus_add(
+ rotation=obrot,
+ location=obloc,
+ major_segments=mas,
+ minor_segments=mis,
+ major_radius=mar,
+ minor_radius=mir,
+ )
+
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ if not ob:
+ bpy.ops.mesh.primitive_torus_add(
+ major_segments=mas, minor_segments=mis, major_radius=mar, minor_radius=mir
+ )
+ ob = context.object
+ ob.name = ob.data.name = "PovTorus"
+ ob.pov.torus_major_segments = mas
+ ob.pov.torus_minor_segments = mis
+ ob.pov.torus_major_radius = mar
+ ob.pov.torus_minor_radius = mir
+ bpy.ops.object.mode_set(mode="EDIT")
+ bpy.ops.mesh.hide(unselected=False)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ ob.data.use_auto_smooth = True
+ ob.data.auto_smooth_angle = 0.6
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "TORUS"
+
+class POV_OT_torus_add(Operator):
+ """Add the representation of POV torus using using pov_torus_define() function."""
+
+ bl_idname = "pov.addtorus"
+ bl_label = "Torus"
+ bl_description = "Add Torus"
+ bl_options = {'REGISTER', 'UNDO'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ # Keep in sync within model_properties.py section Torus
+ # as this allows interactive update
+ mas: IntProperty(name="Major Segments", description="", default=48, min=3, max=720)
+ mis: IntProperty(name="Minor Segments", description="", default=12, min=3, max=720)
+ mar: FloatProperty(name="Major Radius", description="", default=1.0)
+ mir: FloatProperty(name="Minor Radius", description="", default=0.25)
+
+ def execute(self, context):
+ props = self.properties
+ mar = props.mar
+ mir = props.mir
+ mas = props.mas
+ mis = props.mis
+ pov_torus_define(context, self, None)
+ self.report(
+ {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
+ )
+ return {"FINISHED"}
+
+
+class POV_OT_torus_update(Operator):
+ """Update the POV torus.
+
+ Delete its previous proxy geometry and rerun pov_torus_define() function
+ with the new parameters"""
+
+ bl_idname = "pov.torus_update"
+ bl_label = "Update"
+ bl_description = "Update Torus"
+ 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 == "MESH"
+ and ob.pov.object_as == "TORUS"
+ and engine in cls.COMPAT_ENGINES
+ )
+
+ def execute(self, context):
+
+ pov_torus_define(context, None, context.object)
+
+ return {"FINISHED"}
+
+
+# -----------------------------------------------------------------------------
+
+
+class POV_OT_prism_add(Operator):
+ """Add the representation of POV prism using using an extruded curve."""
+
+ bl_idname = "pov.addprism"
+ 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)
+
+ def execute(self, context):
+
+ props = self.properties
+ loft_data = bpy.data.curves.new("Prism", type="CURVE")
+ loft_data.dimensions = "2D"
+ loft_data.resolution_u = 2
+ # loft_data.show_normal_face = False
+ loft_data.extrude = 2
+ n = props.prism_n
+ r = props.prism_r
+ coords = []
+ z = 0
+ angle = 0
+ for p in range(n):
+ x = r * cos(angle)
+ y = r * sin(angle)
+ coords.append((x, y, z))
+ angle += pi * 2 / n
+ poly = loft_data.splines.new("POLY")
+ poly.points.add(len(coords) - 1)
+ for i, coord in enumerate(coords):
+ x, y, z = coord
+ poly.points[i].co = (x, y, z, 1)
+ poly.use_cyclic_u = True
+
+ ob = bpy.data.objects.new("Prism_shape", loft_data)
+ scn = bpy.context.scene
+ scn.collection.objects.link(ob)
+ context.view_layer.objects.active = ob
+ ob.select_set(True)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ bpy.ops.object.shade_flat()
+ ob.data.fill_mode = 'BOTH'
+ ob.pov.curveshape = "prism"
+ ob.name = ob.data.name = "Prism"
+ return {"FINISHED"}
+
+
+classes = (
+ POV_OT_plane_add,
+ POV_OT_box_add,
+ POV_OT_cylinder_add,
+ POV_OT_cylinder_update,
+ POV_OT_sphere_add,
+ POV_OT_sphere_update,
+ POV_OT_cone_add,
+ POV_OT_cone_update,
+ POV_OT_rainbow_add,
+ POV_OT_torus_add,
+ POV_OT_torus_update,
+ POV_OT_prism_add,
+)
+
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/render_povray/object_primitives.py b/render_povray/model_primitives_topology.py
index d017c4d0..6204ee04 100755..100644
--- a/render_povray/object_primitives.py
+++ b/render_povray/model_primitives_topology.py
@@ -3,6 +3,7 @@
# <pep8 compliant>
""" Get POV-Ray specific objects In and Out of Blender """
+
from math import pi, cos, sin
import os.path
import bpy
@@ -22,88 +23,17 @@ from bpy.props import (
from mathutils import Vector, Matrix
+from . import model_primitives
-# import collections
-
-
-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
- # import .object_mesh_topology.write_object_csg_inside_vector
- write_object_csg_inside_vector(ob, file)
- '''
-
- if ob.pov.hollow:
- File.write("\thollow\n")
- if ob.pov.double_illuminate:
- File.write("\tdouble_illuminate\n")
- if ob.pov.sturm:
- File.write("\tsturm\n")
- if ob.pov.no_shadow:
- File.write("\tno_shadow\n")
- if ob.pov.no_image:
- File.write("\tno_image\n")
- if ob.pov.no_reflection:
- File.write("\tno_reflection\n")
- if ob.pov.no_radiosity:
- File.write("\tno_radiosity\n")
- if ob.pov.inverse:
- File.write("\tinverse\n")
- if ob.pov.hierarchy:
- File.write("\thierarchy\n")
-
- # XXX, Commented definitions
- '''
- if scene.pov.photon_enable:
- File.write("photons {\n")
- if ob.pov.target:
- File.write("target %.4g\n"%ob.pov.target_value)
- if ob.pov.refraction:
- File.write("refraction on\n")
- if ob.pov.reflection:
- File.write("reflection on\n")
- if ob.pov.pass_through:
- File.write("pass_through\n")
- File.write("}\n")
- if ob.pov.object_ior > 1:
- File.write("interior {\n")
- File.write("ior %.4g\n"%ob.pov.object_ior)
- if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
- File.write("ior %.4g\n"%ob.pov.dispersion_value)
- File.write("ior %s\n"%ob.pov.dispersion_samples)
- if scene.pov.photon_enable == False:
- File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
- '''
-
-
-def pov_define_mesh(mesh, verts, edges, faces, name, hide_geometry=True):
- """Generate proxy mesh."""
- if mesh is None:
- mesh = bpy.data.meshes.new(name)
- mesh.from_pydata(verts, edges, faces)
- mesh.update()
- mesh.validate(
- verbose=False
- ) # Set it to True to see debug messages (helps ensure you generate valid geometry).
- if hide_geometry:
- mesh.vertices.foreach_set("hide", [True] * len(mesh.vertices))
- mesh.edges.foreach_set("hide", [True] * len(mesh.edges))
- mesh.polygons.foreach_set("hide", [True] * len(mesh.polygons))
- return mesh
-
-
-class POVRAY_OT_lathe_add(Operator):
+class POV_OT_lathe_add(Operator):
"""Add the representation of POV lathe using a screw modifier."""
bl_idname = "pov.addlathe"
bl_label = "Lathe"
bl_description = "adds lathe"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
def execute(self, context):
# ayers=[False]*20
# layers[0]=True
@@ -115,18 +45,18 @@ class POVRAY_OT_lathe_add(Operator):
ob = context.view_layer.objects.active
ob_data = ob.data
ob.name = ob_data.name = "PovLathe"
- ob_data.dimensions = '2D'
- ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, 'Z'))
- ob.pov.object_as = 'LATHE'
+ ob_data.dimensions = "2D"
+ ob_data.transform(Matrix.Rotation(-pi / 2.0, 4, "Z"))
+ ob.pov.object_as = "LATHE"
self.report(
- {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
+ {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
)
ob.pov.curveshape = "lathe"
- bpy.ops.object.modifier_add(type='SCREW')
+ bpy.ops.object.modifier_add(type="SCREW")
mod = ob.modifiers[-1]
- mod.axis = 'Y'
+ mod.axis = "Y"
mod.show_render = False
- return {'FINISHED'}
+ return {"FINISHED"}
def pov_superellipsoid_define(context, op, ob):
@@ -170,14 +100,10 @@ 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 < cos(angSegment)) or (
- cos(angRing) > 0 > cos(angSegment)
- ):
+ 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 < sin(angSegment)) or (
- cos(angRing) > 0 > sin(angSegment)
- ):
+ if (cos(angRing) < 0 < sin(angSegment)) or (cos(angRing) > 0 > sin(angSegment)):
y = -y
z = r * (abs(sin(angRing)) ** n1)
if sin(angRing) < 0:
@@ -186,9 +112,8 @@ def pov_superellipsoid_define(context, op, ob):
y = round(y, 4)
z = round(z, 4)
verts.append((x, y, z))
- if edit == 'TRIANGLES':
- verts.append((0, 0, 1))
- verts.append((0, 0, -1))
+ if edit == "TRIANGLES":
+ verts.extend([(0, 0, 1),(0, 0, -1)])
faces = []
@@ -200,7 +125,7 @@ def pov_superellipsoid_define(context, op, ob):
if p == v - 1:
face = (m + p, m, v + m, v + m + p)
faces.append(face)
- if edit == 'TRIANGLES':
+ if edit == "TRIANGLES":
indexUp = len(verts) - 2
indexDown = len(verts) - 1
indexStartDown = len(verts) - 2 - v
@@ -218,24 +143,21 @@ def pov_superellipsoid_define(context, op, ob):
if i == v - 1:
face = (indexUp, i + indexStartDown, indexStartDown)
faces.append(face)
- if edit == 'NGONS':
- face = []
- for i in range(0, v):
- face.append(i)
+ if edit == "NGONS":
+ face = list(range(v))
faces.append(face)
face = []
indexUp = len(verts) - 1
for i in range(0, v):
face.append(indexUp - i)
faces.append(face)
- mesh = pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
+ mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "SuperEllipsoid")
if not ob:
ob = object_data_add(context, mesh, operator=None)
# engine = context.scene.render.engine what for?
ob = context.object
ob.name = ob.data.name = "PovSuperellipsoid"
- ob.pov.object_as = 'SUPERELLIPSOID'
ob.pov.se_param1 = n2
ob.pov.se_param2 = n1
@@ -248,18 +170,20 @@ def pov_superellipsoid_define(context, op, ob):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
+ ob.data.auto_smooth_angle = 1.3
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "SUPERELLIPSOID"
-
-class POVRAY_OT_superellipsoid_add(Operator):
+class POV_OT_superellipsoid_add(Operator):
"""Add the representation of POV superellipsoid using the pov_superellipsoid_define()."""
bl_idname = "pov.addsuperellipsoid"
bl_label = "Add SuperEllipsoid"
bl_description = "Create a SuperEllipsoid"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- # Keep in sync within object_properties.py section Superellipsoid
+ # Keep in sync within model_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?
@@ -291,7 +215,7 @@ class POVRAY_OT_superellipsoid_add(Operator):
items=[("NOTHING", "Nothing", ""), ("NGONS", "N-Gons", ""), ("TRIANGLES", "Triangles", "")],
name="Fill up and down",
description="",
- default='TRIANGLES',
+ default="TRIANGLES",
)
@classmethod
@@ -303,13 +227,13 @@ class POVRAY_OT_superellipsoid_add(Operator):
pov_superellipsoid_define(context, self, None)
self.report(
- {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
+ {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
)
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_superellipsoid_update(Operator):
+class POV_OT_superellipsoid_update(Operator):
"""Update the superellipsoid.
Delete its previous proxy geometry and rerun pov_superellipsoid_define() function
@@ -319,24 +243,24 @@ class POVRAY_OT_superellipsoid_update(Operator):
bl_label = "Update"
bl_description = "Update Superellipsoid"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ 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 == 'MESH' and engine in cls.COMPAT_ENGINES
+ return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
def execute(self, context):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
bpy.ops.object.mode_set(mode="OBJECT")
pov_superellipsoid_define(context, None, context.object)
- return {'FINISHED'}
+ return {"FINISHED"}
def create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False):
@@ -358,36 +282,30 @@ def create_faces(vert_idx_1, vert_idx_2, closed=False, flipped=False):
face = [vert_idx_1[0], vert_idx_2[0], vert_idx_2[total - 1]]
if not fan:
face.append(vert_idx_1[total - 1])
- faces.append(face)
-
else:
face = [vert_idx_2[0], vert_idx_1[0]]
if not fan:
face.append(vert_idx_1[total - 1])
face.append(vert_idx_2[total - 1])
- faces.append(face)
+ faces.append(face)
+
for num in range(total - 1):
if flipped:
if fan:
face = [vert_idx_2[num], vert_idx_1[0], vert_idx_2[num + 1]]
else:
face = [vert_idx_2[num], vert_idx_1[num], vert_idx_1[num + 1], vert_idx_2[num + 1]]
- faces.append(face)
+ elif fan:
+ face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
else:
- if fan:
- face = [vert_idx_1[0], vert_idx_2[num], vert_idx_2[num + 1]]
- else:
- face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
- faces.append(face)
-
+ face = [vert_idx_1[num], vert_idx_2[num], vert_idx_2[num + 1], vert_idx_1[num + 1]]
+ faces.append(face)
return faces
def power(a, b):
"""Workaround to negative a, where the math.pow() method would return a ValueError."""
- if a < 0:
- return -((-a) ** b)
- return a ** b
+ return -((-a) ** b) if a < 0 else a**b
def supertoroid(R, r, u, v, n1, n2):
@@ -446,10 +364,10 @@ def pov_supertorus_define(context, op, ob):
if rad2 > rad1:
rad1 = rad2
verts, faces = supertoroid(rad1, rad2, st_u, st_v, st_n1, st_n2)
- mesh = pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
+ mesh = model_primitives.pov_define_mesh(mesh, verts, [], faces, "PovSuperTorus", True)
if not ob:
ob = object_data_add(context, mesh, operator=None)
- ob.pov.object_as = 'SUPERTORUS'
+ ob.pov.object_as = "SUPERTORUS"
ob.pov.st_major_radius = st_R
ob.pov.st_minor_radius = st_r
ob.pov.st_u = st_u
@@ -460,14 +378,14 @@ def pov_supertorus_define(context, op, ob):
ob.pov.st_edit = st_edit
-class POVRAY_OT_supertorus_add(Operator):
+class POV_OT_supertorus_add(Operator):
"""Add the representation of POV supertorus using the pov_supertorus_define() function."""
bl_idname = "pov.addsupertorus"
bl_label = "Add Supertorus"
bl_description = "Create a SuperTorus"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
st_R: FloatProperty(
name="big radius",
@@ -502,7 +420,7 @@ class POVRAY_OT_supertorus_add(Operator):
st_ie: BoolProperty(
name="Use Int.+Ext. radii", description="Use internal and external radii", default=False
)
- st_edit: BoolProperty(name="", description="", default=False, options={'HIDDEN'})
+ st_edit: BoolProperty(name="", description="", default=False, options={"HIDDEN"})
@classmethod
def poll(cls, context):
@@ -513,12 +431,12 @@ class POVRAY_OT_supertorus_add(Operator):
pov_supertorus_define(context, self, None)
self.report(
- {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
+ {"INFO"}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
)
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_supertorus_update(Operator):
+class POV_OT_supertorus_update(Operator):
"""Update the supertorus.
Delete its previous proxy geometry and rerun pov_supetorus_define() function
@@ -528,35 +446,35 @@ class POVRAY_OT_supertorus_update(Operator):
bl_label = "Update"
bl_description = "Update SuperTorus"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ 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 == 'MESH' and engine in cls.COMPAT_ENGINES
+ return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
def execute(self, context):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
bpy.ops.object.mode_set(mode="OBJECT")
pov_supertorus_define(context, None, context.object)
- return {'FINISHED'}
+ return {"FINISHED"}
# -----------------------------------------------------------------------------
-class POVRAY_OT_loft_add(Operator):
+class POV_OT_loft_add(Operator):
"""Create the representation of POV loft using Blender curves."""
bl_idname = "pov.addloft"
bl_label = "Add Loft Data"
bl_description = "Create a Curve data for Meshmaker"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
loft_n: IntProperty(
name="Segments", description="Vertical segments", default=16, min=3, max=720
@@ -584,8 +502,8 @@ class POVRAY_OT_loft_add(Operator):
def execute(self, context):
props = self.properties
- loft_data = bpy.data.curves.new('Loft', type='CURVE')
- loft_data.dimensions = '3D'
+ loft_data = bpy.data.curves.new("Loft", type="CURVE")
+ loft_data.dimensions = "3D"
loft_data.resolution_u = 2
# loft_data.show_normal_face = False # deprecated in 2.8
n = props.loft_n
@@ -607,7 +525,7 @@ class POVRAY_OT_loft_add(Operator):
coords.append((x, y, z))
angle += pi * 2 / n
r0 += distB
- nurbs = loft_data.splines.new('NURBS')
+ nurbs = loft_data.splines.new("NURBS")
nurbs.points.add(len(coords) - 1)
for c, coord in enumerate(coords):
x, y, z = coord
@@ -622,7 +540,7 @@ class POVRAY_OT_loft_add(Operator):
y = r * sin(angle)
coords.append((x, y, z))
angle += pi * 2 / n
- nurbs = loft_data.splines.new('NURBS')
+ nurbs = loft_data.splines.new("NURBS")
nurbs.points.add(len(coords) - 1)
for c, coord in enumerate(coords):
x, y, z = coord
@@ -637,7 +555,7 @@ class POVRAY_OT_loft_add(Operator):
y = r * sin(angle)
coords.append((x, y, z))
angle += pi * 2 / n
- nurbs = loft_data.splines.new('NURBS')
+ nurbs = loft_data.splines.new("NURBS")
nurbs.points.add(len(coords) - 1)
for c, coord in enumerate(coords):
x, y, z = coord
@@ -655,471 +573,19 @@ class POVRAY_OT_loft_add(Operator):
coords.append((x, y, z))
angle += pi * 2 / n
r -= distB
- nurbs = loft_data.splines.new('NURBS')
+ nurbs = loft_data.splines.new("NURBS")
nurbs.points.add(len(coords) - 1)
for c, coord in enumerate(coords):
x, y, z = coord
nurbs.points[c].co = (x, y, z, 1)
nurbs.use_cyclic_u = True
- ob = bpy.data.objects.new('Loft_shape', loft_data)
+ ob = bpy.data.objects.new("Loft_shape", loft_data)
scn = bpy.context.scene
scn.collection.objects.link(ob)
context.view_layer.objects.active = ob
ob.select_set(True)
ob.pov.curveshape = "loft"
- return {'FINISHED'}
-
-
-class POVRAY_OT_plane_add(Operator):
- """Add the representation of POV infinite plane using just a very big Blender Plane.
-
- 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.addplane"
- 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=10000)
- ob = context.object
- ob.name = ob.data.name = 'PovInfinitePlane'
- bpy.ops.object.mode_set(mode="EDIT")
- self.report(
- {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
- )
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
- bpy.ops.object.shade_smooth()
- ob.pov.object_as = "PLANE"
- return {'FINISHED'}
-
-
-class POVRAY_OT_box_add(Operator):
- """Add the representation of POV box using a simple Blender mesh cube.
-
- 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.addbox"
- bl_label = "Box"
- bl_description = "Add Box"
- bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
-
- def execute(self, context):
- # layers = 20*[False]
- # layers[0] = True
- bpy.ops.mesh.primitive_cube_add()
- ob = context.object
- ob.name = ob.data.name = 'PovBox'
- bpy.ops.object.mode_set(mode="EDIT")
- self.report(
- {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
- )
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
- ob.pov.object_as = "BOX"
- return {'FINISHED'}
-
-
-def pov_cylinder_define(context, op, ob, radius, loc, loc_cap):
- """Pick POV cylinder properties either from creation operator, import, or data update """
- if op:
- R = op.R
- loc = bpy.context.scene.cursor.location
- loc_cap[0] = loc[0]
- loc_cap[1] = loc[1]
- loc_cap[2] = loc[2] + 2
- vec = Vector(loc_cap) - Vector(loc)
- depth = vec.length
- rot = Vector((0, 0, 1)).rotation_difference(vec) # Rotation from Z axis.
- trans = rot @ Vector(
- (0, 0, depth / 2)
- ) # Such that origin is at center of the base of the cylinder.
- roteuler = rot.to_euler()
- if not ob:
- bpy.ops.object.add(type='MESH', location=loc)
- ob = context.object
- ob.name = ob.data.name = "PovCylinder"
- ob.pov.cylinder_radius = radius
- ob.pov.cylinder_location_cap = vec
- ob.pov.object_as = "CYLINDER"
- else:
- ob.location = loc
-
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
- bpy.ops.mesh.primitive_cylinder_add(
- radius=radius, depth=depth, location=loc, rotation=roteuler, end_fill_type='NGON'
- ) # 'NOTHING'
- bpy.ops.transform.translate(value=trans)
-
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
- bpy.ops.object.shade_smooth()
-
-
-class POVRAY_OT_cylinder_add(Operator):
- """Add the representation of POV cylinder using pov_cylinder_define() function.
-
- Use imported_cyl_loc when this operator is run by POV importer."""
-
- bl_idname = "pov.addcylinder"
- 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
- R: FloatProperty(name="Cylinder radius", min=0.00, max=10.0, default=1.0)
-
- imported_cyl_loc: FloatVectorProperty(
- name="Imported Pov base location", precision=6, default=(0.0, 0.0, 0.0)
- )
-
- imported_cyl_loc_cap: FloatVectorProperty(
- name="Imported Pov cap location", precision=6, default=(0.0, 0.0, 2.0)
- )
-
- def execute(self, context):
- props = self.properties
- R = props.R
- ob = context.object
- # layers = 20*[False]
- # layers[0] = True
- if ob:
- if ob.pov.imported_cyl_loc:
- 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:
- 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",
- )
-
- pov_cylinder_define(context, self, None, self.R, LOC, LOC_CAP)
-
- return {'FINISHED'}
-
-
-class POVRAY_OT_cylinder_update(Operator):
- """Update the POV cylinder.
-
- Delete its previous proxy geometry and rerun pov_cylinder_define() function
- with the new parameters"""
-
- bl_idname = "pov.cylinder_update"
- bl_label = "Update"
- bl_description = "Update Cylinder"
- 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 == 'MESH'
- and ob.pov.object_as == "CYLINDER"
- and engine in cls.COMPAT_ENGINES
- )
-
- def execute(self, context):
- ob = context.object
- radius = ob.pov.cylinder_radius
- loc = ob.location
- loc_cap = loc + ob.pov.cylinder_location_cap
-
- pov_cylinder_define(context, None, ob, radius, loc, loc_cap)
-
- return {'FINISHED'}
-
-
-# ----------------------------------- SPHERE---------------------------------- #
-def pov_sphere_define(context, op, ob, loc):
- """create the representation of POV sphere using a Blender icosphere.
-
- Its nice platonic solid curvature better represents pov rendertime
- tessellation than a UV sphere"""
-
- if op:
- R = op.R
- loc = bpy.context.scene.cursor.location
- else:
- assert ob
- R = ob.pov.sphere_radius
-
- # keep object rotation and location for the add object operator
- obrot = ob.rotation_euler
- # obloc = ob.location
- obscale = ob.scale
-
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
- bpy.ops.mesh.primitive_ico_sphere_add(
- subdivisions=4, radius=ob.pov.sphere_radius, location=loc, rotation=obrot
- )
- # 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.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
- bpy.ops.object.shade_smooth()
- # bpy.ops.transform.rotate(axis=obrot,orient_type='GLOBAL')
-
- if not ob:
- bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=R, location=loc)
- ob = context.object
- ob.name = ob.data.name = "PovSphere"
- ob.pov.object_as = "SPHERE"
- ob.pov.sphere_radius = R
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
-
-
-class POVRAY_OT_sphere_add(Operator):
- """Add the representation of POV sphere using pov_sphere_define() function.
-
- Use imported_loc when this operator is run by POV importer."""
-
- bl_idname = "pov.addsphere"
- 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
- R: FloatProperty(name="Sphere radius", min=0.00, max=10.0, default=0.5)
-
- imported_loc: FloatVectorProperty(
- name="Imported Pov location", precision=6, default=(0.0, 0.0, 0.0)
- )
-
- def execute(self, context):
- props = self.properties
- R = props.R
- 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
- 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'}
-
- # def execute(self,context):
- # layers = 20*[False]
- # layers[0] = True
-
- # bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=4, radius=ob.pov.sphere_radius)
- # ob = context.object
- # bpy.ops.object.mode_set(mode="EDIT")
- # self.report({'INFO'}, "This native POV-Ray primitive "
- # "won't have any vertex to show in edit mode")
- # bpy.ops.mesh.hide(unselected=False)
- # bpy.ops.object.mode_set(mode="OBJECT")
- # bpy.ops.object.shade_smooth()
- # ob.pov.object_as = "SPHERE"
- # ob.name = ob.data.name = 'PovSphere'
- # return {'FINISHED'}
-
-
-class POVRAY_OT_sphere_update(Operator):
- """Update the POV sphere.
-
- Delete its previous proxy geometry and rerun pov_sphere_define() function
- with the new parameters"""
-
- bl_idname = "pov.sphere_update"
- bl_label = "Update"
- bl_description = "Update Sphere"
- 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 == 'MESH' and engine in cls.COMPAT_ENGINES
-
- def execute(self, context):
-
- pov_sphere_define(context, None, context.object, context.object.location)
-
- return {'FINISHED'}
-
-
-# ----------------------------------- CONE ---------------------------------- #
-def pov_cone_define(context, op, ob):
- """Add the representation of POV cone using pov_define_mesh() function.
-
- Blender cone does not offer the same features such as a second radius."""
- verts = []
- faces = []
- if op:
- mesh = None
- base = op.base
- cap = op.cap
- seg = op.seg
- height = op.height
- else:
- assert ob
- mesh = ob.data
- base = ob.pov.cone_base_radius
- cap = ob.pov.cone_cap_radius
- seg = ob.pov.cone_segments
- height = ob.pov.cone_height
-
- zc = height / 2
- zb = -zc
- angle = 2 * pi / seg
- t = 0
- for i in range(seg):
- xb = base * cos(t)
- yb = base * sin(t)
- xc = cap * cos(t)
- yc = cap * sin(t)
- verts.append((xb, yb, zb))
- verts.append((xc, yc, zc))
- t += angle
- for i in range(seg):
- f = i * 2
- if i == seg - 1:
- faces.append([0, 1, f + 1, f])
- else:
- faces.append([f + 2, f + 3, f + 1, f])
- if base != 0:
- base_face = []
- for i in range(seg - 1, -1, -1):
- p = i * 2
- base_face.append(p)
- faces.append(base_face)
- if cap != 0:
- cap_face = []
- for i in range(seg):
- p = i * 2 + 1
- cap_face.append(p)
- faces.append(cap_face)
-
- mesh = pov_define_mesh(mesh, verts, [], faces, "PovCone", True)
- if not ob:
- ob = object_data_add(context, mesh, operator=None)
- ob.pov.object_as = "CONE"
- ob.pov.cone_base_radius = base
- ob.pov.cone_cap_radius = cap
- ob.pov.cone_height = height
- ob.pov.cone_base_z = zb
- ob.pov.cone_cap_z = zc
-
-
-class POVRAY_OT_cone_add(Operator):
- """Add the representation of POV cone using pov_cone_define() function."""
-
- bl_idname = "pov.addcone"
- bl_label = "Cone"
- bl_description = "Add Cone"
- bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
-
- # Keep in sync within object_properties.py section Cone
- # If someone knows how to define operators' props from a func, I'd be delighted to learn it!
- base: FloatProperty(
- name="Base radius",
- description="The first radius of the cone",
- default=1.0,
- min=0.01,
- max=100.0,
- )
- cap: FloatProperty(
- name="Cap radius",
- description="The second radius of the cone",
- default=0.3,
- min=0.0,
- max=100.0,
- )
- seg: IntProperty(
- name="Segments",
- description="Radial segmentation of the proxy mesh",
- default=16,
- min=3,
- max=265,
- )
- height: FloatProperty(
- name="Height", description="Height of the cone", default=2.0, min=0.01, max=100.0
- )
-
- @classmethod
- def poll(cls, context):
- engine = context.scene.render.engine
- return engine in cls.COMPAT_ENGINES
-
- def execute(self, context):
- pov_cone_define(context, self, None)
-
- self.report(
- {'INFO'}, "This native POV-Ray primitive" "won't have any vertex to show in edit mode"
- )
- return {'FINISHED'}
-
-
-class POVRAY_OT_cone_update(Operator):
- """Update the POV cone.
-
- Delete its previous proxy geometry and rerun pov_cone_define() function
- with the new parameters"""
-
- bl_idname = "pov.cone_update"
- bl_label = "Update"
- bl_description = "Update Cone"
- 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 == 'MESH' and engine in cls.COMPAT_ENGINES
-
- def execute(self, context):
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
- bpy.ops.object.mode_set(mode="OBJECT")
-
- pov_cone_define(context, None, context.object)
-
- return {'FINISHED'}
+ return {"FINISHED"}
# ----------------------------------- ISOSURFACES ----------------------------------- #
@@ -1142,7 +608,7 @@ def pov_isosurface_view_define(context, op, ob, loc):
# obloc = ob.location
obscale = ob.scale
- #bpy.ops.object.empty_add(type='CUBE', location=loc, rotation=obrot)
+ # 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')
@@ -1150,16 +616,17 @@ def pov_isosurface_view_define(context, op, ob, loc):
# 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.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'
+ ob.pov.contained_by = "box"
bpy.ops.object.mode_set(mode="OBJECT")
-class POVRAY_OT_isosurface_add(Operator):
+
+class POV_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
@@ -1169,9 +636,9 @@ class POVRAY_OT_isosurface_add(Operator):
bl_label = "Generic Isosurface"
bl_description = "Add Isosurface"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- # Keep in sync within object_properties.py section Sphere
+ # Keep in sync within model_properties.py section Sphere
# as this allows interactive update
isosurface_eq: StringProperty(
name="f(x,y,z)=",
@@ -1187,23 +654,24 @@ class POVRAY_OT_isosurface_add(Operator):
# layers = 20*[False]
# layers[0] = True
props = self.properties
- ob = context.object
- if ob:
+ if ob := context.object:
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'}
-
+ try:
+ pov_isosurface_view_define(context, self, None, LOC)
+ self.report(
+ {"INFO"}, "This native POV-Ray primitive " "is only an abstract proxy in Blender"
+ )
+ except AttributeError:
+ self.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
+ return {"FINISHED"}
-class POVRAY_OT_isosurface_update(Operator):
+class POV_OT_isosurface_update(Operator):
"""Update the POV isosurface.
Rerun pov_isosurface_view_define() function
@@ -1213,22 +681,22 @@ class POVRAY_OT_isosurface_update(Operator):
bl_label = "Update"
bl_description = "Update Isosurface"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ 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
+ 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'}
+ return {"FINISHED"}
-class POVRAY_OT_isosurface_box_add(Operator):
+class POV_OT_isosurface_box_add(Operator):
"""Add the representation of POV isosurface box using also just a Blender mesh cube.
Flag its primitive type with a specific pov.object_as attribute and lock edit mode
@@ -1238,7 +706,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'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
@@ -1247,17 +715,17 @@ class POVRAY_OT_isosurface_box_add(Operator):
ob = context.object
bpy.ops.object.mode_set(mode="EDIT")
self.report(
- {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
+ {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
)
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
ob.pov.object_as = "ISOSURFACE_NODE"
- ob.pov.contained_by = 'box'
- ob.name = 'PovIsosurfaceBox'
- return {'FINISHED'}
+ ob.pov.contained_by = "box"
+ ob.name = "PovIsosurfaceBox"
+ return {"FINISHED"}
-class POVRAY_OT_isosurface_sphere_add(Operator):
+class POV_OT_isosurface_sphere_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
@@ -1267,7 +735,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'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
@@ -1276,18 +744,18 @@ class POVRAY_OT_isosurface_sphere_add(Operator):
ob = context.object
bpy.ops.object.mode_set(mode="EDIT")
self.report(
- {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
+ {"INFO"}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
)
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.shade_smooth()
ob.pov.object_as = "ISOSURFACE_NODE"
- ob.pov.contained_by = 'sphere'
- ob.name = 'PovIsosurfaceSphere'
- return {'FINISHED'}
+ ob.pov.contained_by = "sphere"
+ ob.name = "PovIsosurfaceSphere"
+ return {"FINISHED"}
-class POVRAY_OT_sphere_sweep_add(Operator):
+class POV_OT_sphere_sweep_add(Operator):
"""Add the representation of POV sphere_sweep using a Blender NURBS curve.
Flag its primitive type with a specific ob.pov.curveshape attribute and
@@ -1297,7 +765,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'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
@@ -1308,13 +776,13 @@ class POVRAY_OT_sphere_sweep_add(Operator):
ob.pov.curveshape = "sphere_sweep"
ob.data.bevel_depth = 0.02
ob.data.bevel_resolution = 4
- ob.data.fill_mode = 'FULL'
+ ob.data.fill_mode = "FULL"
# ob.data.splines[0].order_u = 4
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_blobsphere_add(Operator):
+class POV_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
@@ -1324,18 +792,18 @@ class POVRAY_OT_blobsphere_add(Operator):
bl_label = "Blob Sphere"
bl_description = "Add Blob Sphere"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
# layers[0] = True
- bpy.ops.object.metaball_add(type='BALL')
+ bpy.ops.object.metaball_add(type="BALL")
ob = context.object
ob.name = "PovBlob"
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_blobcapsule_add(Operator):
+class POV_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
@@ -1345,18 +813,18 @@ class POVRAY_OT_blobcapsule_add(Operator):
bl_label = "Blob Capsule"
bl_description = "Add Blob Capsule"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
# layers[0] = True
- bpy.ops.object.metaball_add(type='CAPSULE')
+ bpy.ops.object.metaball_add(type="CAPSULE")
ob = context.object
ob.name = "PovBlob"
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_blobplane_add(Operator):
+class POV_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
@@ -1366,18 +834,18 @@ class POVRAY_OT_blobplane_add(Operator):
bl_label = "Blob Plane"
bl_description = "Add Blob Plane"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
# layers[0] = True
- bpy.ops.object.metaball_add(type='PLANE')
+ bpy.ops.object.metaball_add(type="PLANE")
ob = context.object
ob.name = "PovBlob"
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_blobellipsoid_add(Operator):
+class POV_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
@@ -1386,18 +854,18 @@ class POVRAY_OT_blobellipsoid_add(Operator):
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')
+ bpy.ops.object.metaball_add(type="ELLIPSOID")
ob = context.object
ob.name = "PovBlob"
- return {'FINISHED'}
+ return {"FINISHED"}
-class POVRAY_OT_blobcube_add(Operator):
+class POV_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
@@ -1407,61 +875,18 @@ class POVRAY_OT_blobcube_add(Operator):
bl_label = "Blob Cube"
bl_description = "Add Blob Cube"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
# layers = 20*[False]
# layers[0] = True
- bpy.ops.object.metaball_add(type='CUBE')
+ 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.
-
- Rainbows indeed propagate along a visibility cone.
- Flag its primitive type with a specific ob.pov.object_as attribute
- and leave access to edit mode to keep user editable handles.
- Add a constraint to orient it towards camera because POV Rainbows
- are view dependant and having it always initially visible is less
- confusing """
-
- bl_idname = "pov.addrainbow"
- bl_label = "Rainbow"
- bl_description = "Add Rainbow"
- bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
-
- def execute(self, context):
- cam = context.scene.camera
- bpy.ops.object.light_add(type='SPOT', radius=1)
- ob = context.object
- ob.data.show_cone = False
- ob.data.spot_blend = 0.5
- # ob.data.shadow_buffer_clip_end = 0 # deprecated in 2.8
- ob.data.shadow_buffer_clip_start = 4 * cam.location.length
- ob.data.distance = cam.location.length
- ob.data.energy = 0
- ob.name = ob.data.name = "PovRainbow"
- ob.pov.object_as = "RAINBOW"
-
- # obj = context.object
- bpy.ops.object.constraint_add(type='DAMPED_TRACK')
-
- ob.constraints["Damped Track"].target = cam
- ob.constraints["Damped Track"].track_axis = 'TRACK_NEGATIVE_Z'
- ob.location = -cam.location
-
- # refocus on the actual rainbow
- bpy.context.view_layer.objects.active = ob
- ob.select_set(True)
+ return {"FINISHED"}
- return {'FINISHED'}
-
-class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
+class POV_OT_height_field_add(bpy.types.Operator, ImportHelper):
"""Add the representation of POV height_field using a displaced grid.
texture slot fix and displace modifier will be needed because noise
@@ -1471,9 +896,9 @@ 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'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- # Keep in sync within object_properties.py section HeightFields
+ # Keep in sync within model_properties.py section HeightFields
# as this allows interactive update
# filename_ext = ".png"
@@ -1503,9 +928,9 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
img = bpy.data.images.load(impath)
im_name = img.name
im_name, file_extension = os.path.splitext(im_name)
- hf_tex = bpy.data.textures.new('%s_hf_image' % im_name, type='IMAGE')
+ hf_tex = bpy.data.textures.new("%s_hf_image" % im_name, type="IMAGE")
hf_tex.image = img
- mat = bpy.data.materials.new('Tex_%s_hf' % im_name)
+ mat = bpy.data.materials.new("Tex_%s_hf" % im_name)
hf_slot = mat.pov_texture_slots.add()
hf_slot.texture = hf_tex.name
# layers = 20*[False]
@@ -1517,7 +942,7 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
h = int(h / res)
bpy.ops.mesh.primitive_grid_add(x_subdivisions=w, y_subdivisions=h, size=0.5)
ob = context.object
- ob.name = ob.data.name = '%s' % im_name
+ ob.name = ob.data.name = "%s" % im_name
ob.data.materials.append(mat)
bpy.ops.object.mode_set(mode="EDIT")
# bpy.ops.mesh.noise(factor=1) # TODO replace by displace modifier, noise deprecated in 2.8
@@ -1531,171 +956,11 @@ class POVRAY_OT_height_field_add(bpy.types.Operator, ImportHelper):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
- ob.pov.object_as = 'HEIGHT_FIELD'
+ ob.pov.object_as = "HEIGHT_FIELD"
# POV-Ray will soon use only forwards slashes on every OS and already can
- forward_impath = impath.replace(os.sep, '/')
+ forward_impath = impath.replace(os.sep, "/")
ob.pov.hf_filename = forward_impath
- return {'FINISHED'}
-
-
-# ----------------------------------- TORUS ----------------------------------- #
-def pov_torus_define(context, op, ob):
- """Add the representation of POV torus using just a Blender torus.
-
- Picking properties either from creation operator, import, or data update.
- But flag its primitive type with a specific pov.object_as attribute and lock edit mode
- to keep proxy consistency by hiding edit geometry."""
-
- if op:
- mas = op.mas
- mis = op.mis
- mar = op.mar
- mir = op.mir
- else:
- assert ob
- mas = ob.pov.torus_major_segments
- mis = ob.pov.torus_minor_segments
- mar = ob.pov.torus_major_radius
- mir = ob.pov.torus_minor_radius
-
- # keep object rotation and location for the add object operator
- obrot = ob.rotation_euler
- obloc = ob.location
-
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
- bpy.ops.mesh.primitive_torus_add(
- rotation=obrot,
- location=obloc,
- major_segments=mas,
- minor_segments=mis,
- major_radius=mar,
- minor_radius=mir,
- )
-
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
-
- if not ob:
- bpy.ops.mesh.primitive_torus_add(
- major_segments=mas, minor_segments=mis, major_radius=mar, minor_radius=mir
- )
- ob = context.object
- ob.name = ob.data.name = "PovTorus"
- ob.pov.object_as = "TORUS"
- ob.pov.torus_major_segments = mas
- ob.pov.torus_minor_segments = mis
- ob.pov.torus_major_radius = mar
- ob.pov.torus_minor_radius = mir
- bpy.ops.object.mode_set(mode="EDIT")
- bpy.ops.mesh.hide(unselected=False)
- bpy.ops.object.mode_set(mode="OBJECT")
-
-
-class POVRAY_OT_torus_add(Operator):
- """Add the representation of POV torus using using pov_torus_define() function."""
-
- bl_idname = "pov.addtorus"
- 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
- mas: IntProperty(name="Major Segments", description="", default=48, min=3, max=720)
- mis: IntProperty(name="Minor Segments", description="", default=12, min=3, max=720)
- mar: FloatProperty(name="Major Radius", description="", default=1.0)
- mir: FloatProperty(name="Minor Radius", description="", default=0.25)
-
- def execute(self, context):
- props = self.properties
- mar = props.mar
- mir = props.mir
- mas = props.mas
- mis = props.mis
- pov_torus_define(context, self, None)
- self.report(
- {'INFO'}, "This native POV-Ray primitive " "won't have any vertex to show in edit mode"
- )
- return {'FINISHED'}
-
-
-class POVRAY_OT_torus_update(Operator):
- """Update the POV torus.
-
- Delete its previous proxy geometry and rerun pov_torus_define() function
- with the new parameters"""
-
- bl_idname = "pov.torus_update"
- bl_label = "Update"
- bl_description = "Update Torus"
- 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 == 'MESH' and engine in cls.COMPAT_ENGINES
-
- def execute(self, context):
-
- pov_torus_define(context, None, context.object)
-
- return {'FINISHED'}
-
-
-# -----------------------------------------------------------------------------
-
-
-class POVRAY_OT_prism_add(Operator):
- """Add the representation of POV prism using using an extruded curve."""
-
- bl_idname = "pov.addprism"
- 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)
-
- def execute(self, context):
-
- props = self.properties
- loft_data = bpy.data.curves.new('Prism', type='CURVE')
- loft_data.dimensions = '2D'
- loft_data.resolution_u = 2
- # loft_data.show_normal_face = False
- loft_data.extrude = 2
- n = props.prism_n
- r = props.prism_r
- coords = []
- z = 0
- angle = 0
- for p in range(n):
- x = r * cos(angle)
- y = r * sin(angle)
- coords.append((x, y, z))
- angle += pi * 2 / n
- poly = loft_data.splines.new('POLY')
- poly.points.add(len(coords) - 1)
- for i, coord in enumerate(coords):
- x, y, z = coord
- poly.points[i].co = (x, y, z, 1)
- poly.use_cyclic_u = True
-
- ob = bpy.data.objects.new('Prism_shape', loft_data)
- scn = bpy.context.scene
- scn.collection.objects.link(ob)
- context.view_layer.objects.active = ob
- ob.select_set(True)
- ob.pov.curveshape = "prism"
- ob.name = ob.data.name = "Prism"
- return {'FINISHED'}
+ return {"FINISHED"}
# ----------------------------------- PARAMETRIC ----------------------------------- #
@@ -1733,8 +998,8 @@ def pov_parametric_define(context, op, ob):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.reveal()
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.mesh.delete(type='VERT')
+ bpy.ops.mesh.select_all(action="SELECT")
+ bpy.ops.mesh.delete(type="VERT")
bpy.ops.mesh.primitive_xyz_function_surface(
x_eq=x_eq,
y_eq=y_eq,
@@ -1744,7 +1009,7 @@ def pov_parametric_define(context, op, ob):
range_v_min=v_min,
range_v_max=v_max,
)
- bpy.ops.mesh.select_all(action='SELECT')
+ bpy.ops.mesh.select_all(action="SELECT")
# extra work:
bpy.ops.transform.translate(value=(obloc - curloc), proportional_size=1)
# XXX TODO : https://devtalk.blender.org/t/bpy-ops-transform-rotate-option-axis/6235/7
@@ -1767,7 +1032,6 @@ def pov_parametric_define(context, op, ob):
)
ob = context.object
ob.name = ob.data.name = "PovParametric"
- ob.pov.object_as = "PARAMETRIC"
ob.pov.u_min = u_min
ob.pov.u_max = u_max
@@ -1780,18 +1044,20 @@ def pov_parametric_define(context, op, ob):
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
+ ob.data.auto_smooth_angle = 0.6
+ bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "PARAMETRIC"
-
-class POVRAY_OT_parametric_add(Operator):
+class POV_OT_parametric_add(Operator):
"""Add the representation of POV parametric surfaces using pov_parametric_define() function."""
bl_idname = "pov.addparametric"
bl_label = "Parametric"
bl_description = "Add Paramertic"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- # Keep in sync within object_properties.py section Parametric primitive
+ # Keep in sync within model_properties.py section Parametric primitive
# as this allows interactive update
u_min: FloatProperty(name="U Min", description="", default=0.0)
v_min: FloatProperty(name="V Min", description="", default=0.0)
@@ -1813,16 +1079,15 @@ class POVRAY_OT_parametric_add(Operator):
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"
+ {"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'}
+ self.report({"INFO"}, "Please enable Add Mesh: Extra Objects addon")
+ return {"FINISHED"}
-class POVRAY_OT_parametric_update(Operator):
+class POV_OT_parametric_update(Operator):
"""Update the representation of POV parametric surfaces.
Delete its previous proxy geometry and rerun pov_parametric_define() function
@@ -1832,34 +1097,34 @@ class POVRAY_OT_parametric_update(Operator):
bl_label = "Update"
bl_description = "Update parametric object"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ 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 == 'MESH' and engine in cls.COMPAT_ENGINES
+ return ob and ob.data and ob.type == "MESH" and engine in cls.COMPAT_ENGINES
def execute(self, context):
pov_parametric_define(context, None, context.object)
- return {'FINISHED'}
+ return {"FINISHED"}
# -----------------------------------------------------------------------------
-class POVRAY_OT_shape_polygon_to_circle_add(Operator):
+class POV_OT_polygon_to_circle_add(Operator):
"""Add the proxy mesh for POV Polygon to circle lofting macro"""
bl_idname = "pov.addpolygontocircle"
bl_label = "Polygon To Circle Blending"
bl_description = "Add Polygon To Circle Blending Surface"
bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- # Keep in sync within object_properties.py section PolygonToCircle properties
+ # Keep in sync within model_properties.py section PolygonToCircle properties
# as this allows interactive update
polytocircle_resolution: IntProperty(
name="Resolution", description="", default=3, min=0, max=256
@@ -1877,72 +1142,62 @@ class POVRAY_OT_shape_polygon_to_circle_add(Operator):
# layers = 20*[False]
# layers[0] = True
bpy.ops.mesh.primitive_circle_add(
- vertices=ngon, radius=ngonR, fill_type='NGON', enter_editmode=True
+ vertices=ngon, radius=ngonR, fill_type="NGON", enter_editmode=True
)
bpy.ops.transform.translate(value=(0, 0, 1))
bpy.ops.mesh.subdivide(number_cuts=resolution)
numCircleVerts = ngon + (ngon * resolution)
- bpy.ops.mesh.select_all(action='DESELECT')
+ bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.mesh.primitive_circle_add(
- vertices=numCircleVerts, radius=circleR, fill_type='NGON', enter_editmode=True
+ vertices=numCircleVerts, radius=circleR, fill_type="NGON", enter_editmode=True
)
bpy.ops.transform.translate(value=(0, 0, -1))
- bpy.ops.mesh.select_all(action='SELECT')
+ bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.bridge_edge_loops()
if ngon < 5:
- bpy.ops.mesh.select_all(action='DESELECT')
+ bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.mesh.primitive_circle_add(
- vertices=ngon, radius=ngonR, fill_type='TRIFAN', enter_editmode=True
+ vertices=ngon, radius=ngonR, fill_type="TRIFAN", enter_editmode=True
)
bpy.ops.transform.translate(value=(0, 0, 1))
- bpy.ops.mesh.select_all(action='SELECT')
+ bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.remove_doubles()
- bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.mode_set(mode="OBJECT")
ob = context.object
ob.name = "Polygon_To_Circle"
- ob.pov.object_as = 'POLYCIRCLE'
ob.pov.ngon = ngon
ob.pov.ngonR = ngonR
ob.pov.circleR = circleR
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.hide(unselected=False)
bpy.ops.object.mode_set(mode="OBJECT")
- return {'FINISHED'}
+ #ob.data.auto_smooth_angle = 0.1
+ #bpy.ops.object.shade_smooth()
+ ob.pov.object_as = "POLYCIRCLE"
+ return {"FINISHED"}
classes = (
- POVRAY_OT_lathe_add,
- POVRAY_OT_superellipsoid_add,
- POVRAY_OT_superellipsoid_update,
- POVRAY_OT_supertorus_add,
- POVRAY_OT_supertorus_update,
- POVRAY_OT_loft_add,
- POVRAY_OT_plane_add,
- POVRAY_OT_box_add,
- POVRAY_OT_cylinder_add,
- POVRAY_OT_cylinder_update,
- POVRAY_OT_sphere_add,
- 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_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,
- POVRAY_OT_torus_update,
- POVRAY_OT_prism_add,
- POVRAY_OT_parametric_add,
- POVRAY_OT_parametric_update,
- POVRAY_OT_shape_polygon_to_circle_add,
+ POV_OT_lathe_add,
+ POV_OT_superellipsoid_add,
+ POV_OT_superellipsoid_update,
+ POV_OT_supertorus_add,
+ POV_OT_supertorus_update,
+ POV_OT_loft_add,
+ POV_OT_isosurface_add,
+ POV_OT_isosurface_update,
+ POV_OT_isosurface_box_add,
+ POV_OT_isosurface_sphere_add,
+ POV_OT_sphere_sweep_add,
+ POV_OT_blobsphere_add,
+ POV_OT_blobcapsule_add,
+ POV_OT_blobplane_add,
+ POV_OT_blobellipsoid_add,
+ POV_OT_blobcube_add,
+ POV_OT_height_field_add,
+ POV_OT_parametric_add,
+ POV_OT_parametric_update,
+ POV_OT_polygon_to_circle_add,
)
diff --git a/render_povray/object_properties.py b/render_povray/model_properties.py
index ed02f1d2..4a472b9b 100755..100644
--- a/render_povray/object_properties.py
+++ b/render_povray/model_properties.py
@@ -215,7 +215,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV sphere primitive parameters at creation and anytime they change in UI."""
- bpy.ops.pov.sphere_update()
+ if bpy.ops.pov.sphere_update.poll():
+ bpy.ops.pov.sphere_update()
sphere_radius: FloatProperty(
name="Sphere radius", min=0.00, max=10.0, default=0.5, update=prop_update_sphere
@@ -226,7 +227,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV cone primitive parameters at creation and anytime they change in UI."""
- bpy.ops.pov.cone_update()
+ if bpy.ops.pov.cone_update.poll():
+ bpy.ops.pov.cone_update()
cone_base_radius: FloatProperty(
name="Base radius",
@@ -279,7 +281,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV parametric surface primitive settings at creation and on any UI change."""
- bpy.ops.pov.parametric_update()
+ if bpy.ops.pov.parametric_update.poll():
+ bpy.ops.pov.parametric_update()
u_min: FloatProperty(name="U Min", description="", default=0.0, update=prop_update_parametric)
@@ -307,7 +310,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV torus primitive parameters at creation and anytime they change in UI."""
- bpy.ops.pov.torus_update()
+ if bpy.ops.pov.torus_update.poll():
+ bpy.ops.pov.torus_update()
torus_major_segments: IntProperty(
name="Segments",
@@ -385,7 +389,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV superellipsoid primitive settings at creation and on any UI change."""
- bpy.ops.pov.superellipsoid_update()
+ if bpy.ops.pov.superellipsoid_update.poll():
+ bpy.ops.pov.superellipsoid_update()
se_param1: FloatProperty(name="Parameter 1", description="", min=0.00, max=10.0, default=0.04)
@@ -455,7 +460,8 @@ class RenderPovSettingsObject(PropertyGroup):
"""Update POV supertorus primitive parameters not only at creation and on any UI change."""
- bpy.ops.pov.supertorus_update()
+ if bpy.ops.pov.supertorus_update.poll():
+ bpy.ops.pov.supertorus_update()
st_major_radius: FloatProperty(
name="Major radius",
diff --git a/render_povray/nodes.py b/render_povray/nodes.py
new file mode 100644
index 00000000..d7662d14
--- /dev/null
+++ b/render_povray/nodes.py
@@ -0,0 +1,1056 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+""""Nodes based User interface for shaders exported to POV textures."""
+import bpy
+
+from bpy.utils import register_class, unregister_class
+from bpy.types import Node, CompositorNodeTree, TextureNodeTree
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ IntProperty,
+ FloatProperty,
+ EnumProperty,
+)
+from . import nodes_properties
+
+
+# -------- Output
+class PovrayOutputNode(Node, nodes_properties.ObjectNodeTree):
+ """Output"""
+
+ bl_idname = "PovrayOutputNode"
+ bl_label = "Output"
+ bl_icon = "SHADING_TEXTURE"
+
+ def init(self, context):
+
+ self.inputs.new("PovraySocketTexture", "Texture")
+
+ def draw_buttons(self, context, layout):
+
+ ob = context.object
+ layout.prop(ob.pov, "object_ior", slider=True)
+
+ def draw_buttons_ext(self, context, layout):
+
+ ob = context.object
+ layout.prop(ob.pov, "object_ior", slider=True)
+
+ def draw_label(self):
+ return "Output"
+
+
+# -------- Material
+class PovrayTextureNode(Node, nodes_properties.ObjectNodeTree):
+ """Texture"""
+
+ bl_idname = "PovrayTextureNode"
+ bl_label = "Simple texture"
+ bl_icon = "SHADING_TEXTURE"
+
+ def init(self, context):
+
+ color = self.inputs.new("PovraySocketColor", "Pigment")
+ color.default_value = (1, 1, 1)
+ normal = self.inputs.new("NodeSocketFloat", "Normal")
+ normal.hide_value = True
+ finish = self.inputs.new("NodeSocketVector", "Finish")
+ finish.hide_value = True
+
+ self.outputs.new("PovraySocketTexture", "Texture")
+
+ def draw_label(self):
+ return "Simple texture"
+
+
+class PovrayFinishNode(Node, nodes_properties.ObjectNodeTree):
+ """Finish"""
+
+ bl_idname = "PovrayFinishNode"
+ bl_label = "Finish"
+ bl_icon = "SHADING_TEXTURE"
+
+ def init(self, context):
+
+ self.inputs.new("PovraySocketFloat_0_1", "Emission")
+ ambient = self.inputs.new("NodeSocketVector", "Ambient")
+ ambient.hide_value = True
+ diffuse = self.inputs.new("NodeSocketVector", "Diffuse")
+ diffuse.hide_value = True
+ specular = self.inputs.new("NodeSocketVector", "Highlight")
+ specular.hide_value = True
+ mirror = self.inputs.new("NodeSocketVector", "Mirror")
+ mirror.hide_value = True
+ iridescence = self.inputs.new("NodeSocketVector", "Iridescence")
+ iridescence.hide_value = True
+ subsurface = self.inputs.new("NodeSocketVector", "Translucency")
+ subsurface.hide_value = True
+ self.outputs.new("NodeSocketVector", "Finish")
+
+ def draw_label(self):
+ return "Finish"
+
+
+class PovrayDiffuseNode(Node, nodes_properties.ObjectNodeTree):
+ """Diffuse"""
+
+ bl_idname = "PovrayDiffuseNode"
+ bl_label = "Diffuse"
+ bl_icon = "MATSPHERE"
+
+ def init(self, context):
+
+ intensity = self.inputs.new("PovraySocketFloat_0_1", "Intensity")
+ intensity.default_value = 0.8
+ albedo = self.inputs.new("NodeSocketBool", "Albedo")
+ albedo.default_value = False
+ brilliance = self.inputs.new("PovraySocketFloat_0_10", "Brilliance")
+ brilliance.default_value = 1.8
+ self.inputs.new("PovraySocketFloat_0_1", "Crand")
+ self.outputs.new("NodeSocketVector", "Diffuse")
+
+ def draw_label(self):
+ return "Diffuse"
+
+
+class PovrayPhongNode(Node, nodes_properties.ObjectNodeTree):
+ """Phong"""
+
+ bl_idname = "PovrayPhongNode"
+ bl_label = "Phong"
+ bl_icon = "MESH_UVSPHERE"
+
+ def init(self, context):
+
+ albedo = self.inputs.new("NodeSocketBool", "Albedo")
+ intensity = self.inputs.new("PovraySocketFloat_0_1", "Intensity")
+ intensity.default_value = 0.8
+ phong_size = self.inputs.new("PovraySocketInt_0_256", "Size")
+ phong_size.default_value = 60
+ metallic = self.inputs.new("PovraySocketFloat_0_1", "Metallic")
+
+ self.outputs.new("NodeSocketVector", "Phong")
+
+ def draw_label(self):
+ return "Phong"
+
+
+class PovraySpecularNode(Node, nodes_properties.ObjectNodeTree):
+ """Specular"""
+
+ bl_idname = "PovraySpecularNode"
+ bl_label = "Specular"
+ bl_icon = "MESH_UVSPHERE"
+
+ def init(self, context):
+
+ albedo = self.inputs.new("NodeSocketBool", "Albedo")
+ intensity = self.inputs.new("PovraySocketFloat_0_1", "Intensity")
+ intensity.default_value = 0.8
+ roughness = self.inputs.new("PovraySocketFloat_0_1", "Roughness")
+ roughness.default_value = 0.02
+ metallic = self.inputs.new("PovraySocketFloat_0_1", "Metallic")
+
+ self.outputs.new("NodeSocketVector", "Specular")
+
+ def draw_label(self):
+ return "Specular"
+
+
+class PovrayMirrorNode(Node, nodes_properties.ObjectNodeTree):
+ """Mirror"""
+
+ bl_idname = "PovrayMirrorNode"
+ bl_label = "Mirror"
+ bl_icon = "SHADING_TEXTURE"
+
+ def init(self, context):
+
+ color = self.inputs.new("PovraySocketColor", "Color")
+ color.default_value = (1, 1, 1)
+ metallic = self.inputs.new("PovraySocketFloat_0_1", "Metallic")
+ metallic.default_value = 1.0
+ exponent = self.inputs.new("PovraySocketFloat_0_1", "Exponent")
+ exponent.default_value = 1.0
+ self.inputs.new("PovraySocketFloat_0_1", "Falloff")
+ self.inputs.new("NodeSocketBool", "Fresnel")
+ self.inputs.new("NodeSocketBool", "Conserve energy")
+ self.outputs.new("NodeSocketVector", "Mirror")
+
+ def draw_label(self):
+ return "Mirror"
+
+
+class PovrayAmbientNode(Node, nodes_properties.ObjectNodeTree):
+ """Ambient"""
+
+ bl_idname = "PovrayAmbientNode"
+ bl_label = "Ambient"
+ bl_icon = "SHADING_SOLID"
+
+ def init(self, context):
+
+ self.inputs.new("PovraySocketColor", "Ambient")
+
+ self.outputs.new("NodeSocketVector", "Ambient")
+
+ def draw_label(self):
+ return "Ambient"
+
+
+class PovrayIridescenceNode(Node, nodes_properties.ObjectNodeTree):
+ """Iridescence"""
+
+ bl_idname = "PovrayIridescenceNode"
+ bl_label = "Iridescence"
+ bl_icon = "MESH_UVSPHERE"
+
+ def init(self, context):
+
+ amount = self.inputs.new("NodeSocketFloat", "Amount")
+ amount.default_value = 0.25
+ thickness = self.inputs.new("NodeSocketFloat", "Thickness")
+ thickness.default_value = 1
+ self.inputs.new("NodeSocketFloat", "Turbulence")
+
+ self.outputs.new("NodeSocketVector", "Iridescence")
+
+ def draw_label(self):
+ return "Iridescence"
+
+
+class PovraySubsurfaceNode(Node, nodes_properties.ObjectNodeTree):
+ """Subsurface"""
+
+ bl_idname = "PovraySubsurfaceNode"
+ bl_label = "Subsurface"
+ bl_icon = "MESH_UVSPHERE"
+
+ def init(self, context):
+
+ translucency = self.inputs.new("NodeSocketColor", "Translucency")
+ translucency.default_value = (0, 0, 0, 1)
+ energy = self.inputs.new("PovraySocketInt_0_256", "Energy")
+ energy.default_value = 20
+ self.outputs.new("NodeSocketVector", "Translucency")
+
+ def draw_buttons(self, context, layout):
+ scene = context.scene
+ layout.prop(scene.pov, "sslt_enable", text="SSLT")
+
+ def draw_buttons_ext(self, context, layout):
+ scene = context.scene
+ layout.prop(scene.pov, "sslt_enable", text="SSLT")
+
+ def draw_label(self):
+ return "Subsurface"
+
+
+# ---------------------------------------------------------------- #
+
+
+class PovrayMappingNode(Node, nodes_properties.ObjectNodeTree):
+ """Mapping"""
+
+ bl_idname = "PovrayMappingNode"
+ bl_label = "Mapping"
+ bl_icon = "NODE_TEXTURE"
+
+ warp_type: EnumProperty(
+ name="Warp Types",
+ description="Select the type of warp",
+ items=(
+ ("cubic", "Cubic", ""),
+ ("cylindrical", "Cylindrical", ""),
+ ("planar", "Planar", ""),
+ ("spherical", "Spherical", ""),
+ ("toroidal", "Toroidal", ""),
+ ("uv_mapping", "UV", ""),
+ ("NONE", "None", "No indentation"),
+ ),
+ default="NONE",
+ )
+
+ warp_orientation: EnumProperty(
+ name="Warp Orientation",
+ description="Select the orientation of warp",
+ items=(("x", "X", ""), ("y", "Y", ""), ("z", "Z", "")),
+ default="y",
+ )
+
+ warp_dist_exp: FloatProperty(
+ name="Distance exponent", description="Distance exponent", min=0.0, max=100.0, default=1.0
+ )
+
+ warp_tor_major_radius: FloatProperty(
+ name="Major radius",
+ description="Torus is distance from major radius",
+ min=0.0,
+ max=5.0,
+ default=1.0,
+ )
+
+ def init(self, context):
+ self.outputs.new("NodeSocketVector", "Mapping")
+
+ def draw_buttons(self, context, layout):
+
+ column = layout.column()
+ column.prop(self, "warp_type", text="Warp type")
+ if self.warp_type in {"toroidal", "spherical", "cylindrical", "planar"}:
+ column.prop(self, "warp_orientation", text="Orientation")
+ column.prop(self, "warp_dist_exp", text="Exponent")
+ if self.warp_type == "toroidal":
+ column.prop(self, "warp_tor_major_radius", text="Major R")
+
+ def draw_buttons_ext(self, context, layout):
+
+ column = layout.column()
+ column.prop(self, "warp_type", text="Warp type")
+ if self.warp_type in {"toroidal", "spherical", "cylindrical", "planar"}:
+ column.prop(self, "warp_orientation", text="Orientation")
+ column.prop(self, "warp_dist_exp", text="Exponent")
+ if self.warp_type == "toroidal":
+ column.prop(self, "warp_tor_major_radius", text="Major R")
+
+ def draw_label(self):
+ return "Mapping"
+
+
+class PovrayMultiplyNode(Node, nodes_properties.ObjectNodeTree):
+ """Multiply"""
+
+ bl_idname = "PovrayMultiplyNode"
+ bl_label = "Multiply"
+ bl_icon = "SHADING_SOLID"
+
+ amount_x: FloatProperty(
+ name="X", description="Number of repeats", min=1.0, max=10000.0, default=1.0
+ )
+
+ amount_y: FloatProperty(
+ name="Y", description="Number of repeats", min=1.0, max=10000.0, default=1.0
+ )
+
+ amount_z: FloatProperty(
+ name="Z", description="Number of repeats", min=1.0, max=10000.0, default=1.0
+ )
+
+ def init(self, context):
+ self.outputs.new("NodeSocketVector", "Amount")
+
+ def draw_buttons(self, context, layout):
+
+ column = layout.column()
+ column.label(text="Amount")
+ row = column.row(align=True)
+ row.prop(self, "amount_x")
+ row.prop(self, "amount_y")
+ row.prop(self, "amount_z")
+
+ def draw_buttons_ext(self, context, layout):
+
+ column = layout.column()
+ column.label(text="Amount")
+ row = column.row(align=True)
+ row.prop(self, "amount_x")
+ row.prop(self, "amount_y")
+ row.prop(self, "amount_z")
+
+ def draw_label(self):
+ return "Multiply"
+
+
+class PovrayTransformNode(Node, nodes_properties.ObjectNodeTree):
+ """Transform"""
+
+ bl_idname = "PovrayTransformNode"
+ bl_label = "Transform"
+ bl_icon = "NODE_TEXTURE"
+
+ def init(self, context):
+
+ self.inputs.new("PovraySocketFloatUnlimited", "Translate x")
+ self.inputs.new("PovraySocketFloatUnlimited", "Translate y")
+ self.inputs.new("PovraySocketFloatUnlimited", "Translate z")
+ self.inputs.new("PovraySocketFloatUnlimited", "Rotate x")
+ self.inputs.new("PovraySocketFloatUnlimited", "Rotate y")
+ self.inputs.new("PovraySocketFloatUnlimited", "Rotate z")
+ sX = self.inputs.new("PovraySocketFloatUnlimited", "Scale x")
+ sX.default_value = 1.0
+ sY = self.inputs.new("PovraySocketFloatUnlimited", "Scale y")
+ sY.default_value = 1.0
+ sZ = self.inputs.new("PovraySocketFloatUnlimited", "Scale z")
+ sZ.default_value = 1.0
+
+ self.outputs.new("NodeSocketVector", "Transform")
+
+ def draw_label(self):
+ return "Transform"
+
+
+class PovrayValueNode(Node, nodes_properties.ObjectNodeTree):
+ """Value"""
+
+ bl_idname = "PovrayValueNode"
+ bl_label = "Value"
+ bl_icon = "SHADING_SOLID"
+
+ def init(self, context):
+
+ self.outputs.new("PovraySocketUniversal", "Value")
+
+ def draw_label(self):
+ return "Value"
+
+
+class PovrayModifierNode(Node, nodes_properties.ObjectNodeTree):
+ """Modifier"""
+
+ bl_idname = "PovrayModifierNode"
+ bl_label = "Modifier"
+ bl_icon = "NODE_TEXTURE"
+
+ def init(self, context):
+
+ turb_x = self.inputs.new("PovraySocketFloat_0_10", "Turb X")
+ turb_x.default_value = 0.1
+ turb_y = self.inputs.new("PovraySocketFloat_0_10", "Turb Y")
+ turb_y.default_value = 0.1
+ turb_z = self.inputs.new("PovraySocketFloat_0_10", "Turb Z")
+ turb_z.default_value = 0.1
+ octaves = self.inputs.new("PovraySocketInt_1_9", "Octaves")
+ octaves.default_value = 1
+ lambat = self.inputs.new("PovraySocketFloat_0_10", "Lambda")
+ lambat.default_value = 2.0
+ omega = self.inputs.new("PovraySocketFloat_0_10", "Omega")
+ omega.default_value = 0.5
+ freq = self.inputs.new("PovraySocketFloat_0_10", "Frequency")
+ freq.default_value = 2.0
+ self.inputs.new("PovraySocketFloat_0_10", "Phase")
+
+ self.outputs.new("NodeSocketVector", "Modifier")
+
+ def draw_label(self):
+ return "Modifier"
+
+
+class PovrayPigmentNode(Node, nodes_properties.ObjectNodeTree):
+ """Pigment"""
+
+ bl_idname = "PovrayPigmentNode"
+ bl_label = "Color"
+ bl_icon = "SHADING_SOLID"
+
+ def init(self, context):
+
+ color = self.inputs.new("PovraySocketColor", "Color")
+ color.default_value = (1, 1, 1)
+ pov_filter = self.inputs.new("PovraySocketFloat_0_1", "Filter")
+ transmit = self.inputs.new("PovraySocketFloat_0_1", "Transmit")
+ self.outputs.new("NodeSocketColor", "Pigment")
+
+ def draw_label(self):
+ return "Color"
+
+
+class PovrayColorImageNode(Node, nodes_properties.ObjectNodeTree):
+ """ColorImage"""
+
+ bl_idname = "PovrayColorImageNode"
+ bl_label = "Image map"
+
+ map_type: bpy.props.EnumProperty(
+ name="Map type",
+ description="",
+ items=(
+ ("uv_mapping", "UV", ""),
+ ("0", "Planar", "Default planar mapping"),
+ ("1", "Spherical", "Spherical mapping"),
+ ("2", "Cylindrical", "Cylindrical mapping"),
+ ("5", "Torroidal", "Torus or donut shaped mapping"),
+ ),
+ default="0",
+ )
+ image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
+ interpolate: EnumProperty(
+ name="Interpolate",
+ description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
+ items=(
+ ("2", "Bilinear", "Gives bilinear interpolation"),
+ ("4", "Normalized", "Gives normalized distance"),
+ ),
+ default="2",
+ )
+ premultiplied: BoolProperty(default=False)
+ once: BoolProperty(description="Not to repeat", default=False)
+
+ def init(self, context):
+
+ gamma = self.inputs.new("PovraySocketFloat_000001_10", "Gamma")
+ gamma.default_value = 2.0
+ transmit = self.inputs.new("PovraySocketFloat_0_1", "Transmit")
+ pov_filter = self.inputs.new("PovraySocketFloat_0_1", "Filter")
+ mapping = self.inputs.new("NodeSocketVector", "Mapping")
+ mapping.hide_value = True
+ transform = self.inputs.new("NodeSocketVector", "Transform")
+ transform.hide_value = True
+ modifier = self.inputs.new("NodeSocketVector", "Modifier")
+ modifier.hide_value = True
+
+ self.outputs.new("NodeSocketColor", "Pigment")
+
+ def draw_buttons(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ row = column.row()
+ row.prop(self, "premultiplied", text="Premul")
+ row.prop(self, "once", text="Once")
+
+ def draw_buttons_ext(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ row = column.row()
+ row.prop(self, "premultiplied", text="Premul")
+ row.prop(self, "once", text="Once")
+
+ def draw_label(self):
+ return "Image map"
+
+
+class PovrayBumpMapNode(Node, nodes_properties.ObjectNodeTree):
+ """BumpMap"""
+
+ bl_idname = "PovrayBumpMapNode"
+ bl_label = "Bump map"
+ bl_icon = "TEXTURE"
+
+ map_type: bpy.props.EnumProperty(
+ name="Map type",
+ description="",
+ items=(
+ ("uv_mapping", "UV", ""),
+ ("0", "Planar", "Default planar mapping"),
+ ("1", "Spherical", "Spherical mapping"),
+ ("2", "Cylindrical", "Cylindrical mapping"),
+ ("5", "Torroidal", "Torus or donut shaped mapping"),
+ ),
+ default="0",
+ )
+ image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
+ interpolate: EnumProperty(
+ name="Interpolate",
+ description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
+ items=(
+ ("2", "Bilinear", "Gives bilinear interpolation"),
+ ("4", "Normalized", "Gives normalized distance"),
+ ),
+ default="2",
+ )
+ once: BoolProperty(description="Not to repeat", default=False)
+
+ def init(self, context):
+
+ self.inputs.new("PovraySocketFloat_0_10", "Normal")
+ mapping = self.inputs.new("NodeSocketVector", "Mapping")
+ mapping.hide_value = True
+ transform = self.inputs.new("NodeSocketVector", "Transform")
+ transform.hide_value = True
+ modifier = self.inputs.new("NodeSocketVector", "Modifier")
+ modifier.hide_value = True
+
+ normal = self.outputs.new("NodeSocketFloat", "Normal")
+ normal.hide_value = True
+
+ def draw_buttons(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ column.prop(self, "once", text="Once")
+
+ def draw_buttons_ext(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ column.prop(self, "once", text="Once")
+
+ def draw_label(self):
+ return "Bump Map"
+
+
+class PovrayImagePatternNode(Node, nodes_properties.ObjectNodeTree):
+ """ImagePattern"""
+
+ bl_idname = "PovrayImagePatternNode"
+ bl_label = "Image pattern"
+ bl_icon = "NODE_TEXTURE"
+
+ map_type: bpy.props.EnumProperty(
+ name="Map type",
+ description="",
+ items=(
+ ("uv_mapping", "UV", ""),
+ ("0", "Planar", "Default planar mapping"),
+ ("1", "Spherical", "Spherical mapping"),
+ ("2", "Cylindrical", "Cylindrical mapping"),
+ ("5", "Torroidal", "Torus or donut shaped mapping"),
+ ),
+ default="0",
+ )
+ image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
+ interpolate: EnumProperty(
+ name="Interpolate",
+ description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
+ items=(
+ ("2", "Bilinear", "Gives bilinear interpolation"),
+ ("4", "Normalized", "Gives normalized distance"),
+ ),
+ default="2",
+ )
+ premultiplied: BoolProperty(default=False)
+ once: BoolProperty(description="Not to repeat", default=False)
+ use_alpha: BoolProperty(default=True)
+
+ def init(self, context):
+
+ gamma = self.inputs.new("PovraySocketFloat_000001_10", "Gamma")
+ gamma.default_value = 2.0
+
+ self.outputs.new("PovraySocketPattern", "Pattern")
+
+ def draw_buttons(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ row = column.row()
+ row.prop(self, "premultiplied", text="Premul")
+ row.prop(self, "once", text="Once")
+ column.prop(self, "use_alpha", text="Use alpha")
+
+ def draw_buttons_ext(self, context, layout):
+
+ column = layout.column()
+ im = None
+ for image in bpy.data.images:
+ if image.name == self.image:
+ im = image
+ split = column.split(factor=0.8, align=True)
+ split.prop_search(self, "image", context.blend_data, "images", text="")
+ split.operator("pov.imageopen", text="", icon="FILEBROWSER")
+ if im is not None:
+ column.prop(im, "source", text="")
+ column.prop(self, "map_type", text="")
+ column.prop(self, "interpolate", text="")
+ row = column.row()
+ row.prop(self, "premultiplied", text="Premul")
+ row.prop(self, "once", text="Once")
+
+ def draw_label(self):
+ return "Image pattern"
+
+
+class ShaderPatternNode(Node, nodes_properties.ObjectNodeTree):
+ """Pattern"""
+
+ bl_idname = "ShaderPatternNode"
+ bl_label = "Other patterns"
+
+ pattern: EnumProperty(
+ name="Pattern",
+ description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling",
+ items=(
+ ("agate", "Agate", ""),
+ ("crackle", "Crackle", ""),
+ ("gradient", "Gradient", ""),
+ ("pavement", "Pavement", ""),
+ ("spiral1", "Spiral 1", ""),
+ ("spiral2", "Spiral 2", ""),
+ ("tiling", "Tiling", ""),
+ ),
+ default="agate",
+ )
+
+ agate_turb: FloatProperty(
+ name="Agate turb", description="Agate turbulence", min=0.0, max=100.0, default=0.5
+ )
+
+ crackle_form_x: FloatProperty(
+ name="X", description="Form vector X", min=-150.0, max=150.0, default=-1
+ )
+
+ crackle_form_y: FloatProperty(
+ name="Y", description="Form vector Y", min=-150.0, max=150.0, default=1
+ )
+
+ crackle_form_z: FloatProperty(
+ name="Z", description="Form vector Z", min=-150.0, max=150.0, default=0
+ )
+
+ crackle_metric: FloatProperty(
+ name="Metric", description="Crackle metric", min=0.0, max=150.0, default=1
+ )
+
+ crackle_solid: BoolProperty(name="Solid", description="Crackle solid", default=False)
+
+ spiral_arms: FloatProperty(name="Number", description="", min=0.0, max=256.0, default=2.0)
+
+ tiling_number: IntProperty(name="Number", description="", min=1, max=27, default=1)
+
+ gradient_orient: EnumProperty(
+ name="Orient",
+ description="",
+ items=(("x", "X", ""), ("y", "Y", ""), ("z", "Z", "")),
+ default="x",
+ )
+
+ def init(self, context):
+
+ pat = self.outputs.new("PovraySocketPattern", "Pattern")
+
+ def draw_buttons(self, context, layout):
+
+ layout.prop(self, "pattern", text="")
+ if self.pattern == "agate":
+ layout.prop(self, "agate_turb")
+ if self.pattern == "crackle":
+ layout.prop(self, "crackle_metric")
+ layout.prop(self, "crackle_solid")
+ layout.label(text="Form:")
+ layout.prop(self, "crackle_form_x")
+ layout.prop(self, "crackle_form_y")
+ layout.prop(self, "crackle_form_z")
+ if self.pattern in {"spiral1", "spiral2"}:
+ layout.prop(self, "spiral_arms")
+ if self.pattern in {"tiling"}:
+ layout.prop(self, "tiling_number")
+ if self.pattern in {"gradient"}:
+ layout.prop(self, "gradient_orient")
+
+ def draw_buttons_ext(self, context, layout):
+ pass
+
+ def draw_label(self):
+ return "Other patterns"
+
+
+class ShaderTextureMapNode(Node, nodes_properties.ObjectNodeTree):
+ """Texture Map"""
+
+ bl_idname = "ShaderTextureMapNode"
+ bl_label = "Texture map"
+
+ brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
+
+ brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
+
+ brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
+
+ brick_mortar: FloatProperty(
+ name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
+ )
+
+ def init(self, context):
+ mat = bpy.context.object.active_material
+ self.inputs.new("PovraySocketPattern", "")
+ color = self.inputs.new("NodeSocketColor", "Color ramp")
+ color.hide_value = True
+ for i in range(4):
+ transform = self.inputs.new("PovraySocketTransform", "Transform")
+ transform.hide_value = True
+ number = mat.pov.inputs_number
+ for i in range(number):
+ self.inputs.new("PovraySocketTexture", "%s" % i)
+
+ self.outputs.new("PovraySocketTexture", "Texture")
+
+ def draw_buttons(self, context, layout):
+
+ if self.inputs[0].default_value == "brick":
+ layout.prop(self, "brick_mortar")
+ layout.label(text="Brick size:")
+ layout.prop(self, "brick_size_x")
+ layout.prop(self, "brick_size_y")
+ layout.prop(self, "brick_size_z")
+
+ def draw_buttons_ext(self, context, layout):
+
+ if self.inputs[0].default_value == "brick":
+ layout.prop(self, "brick_mortar")
+ layout.label(text="Brick size:")
+ layout.prop(self, "brick_size_x")
+ layout.prop(self, "brick_size_y")
+ layout.prop(self, "brick_size_z")
+
+ def draw_label(self):
+ return "Texture map"
+
+
+class ShaderNormalMapNode(Node, nodes_properties.ObjectNodeTree):
+ """Normal Map"""
+
+ bl_idname = "ShaderNormalMapNode"
+ bl_label = "Normal map"
+
+ brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
+
+ brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
+
+ brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
+
+ brick_mortar: FloatProperty(
+ name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
+ )
+
+ def init(self, context):
+ self.inputs.new("PovraySocketPattern", "")
+ normal = self.inputs.new("PovraySocketFloat_10", "Normal")
+ slope = self.inputs.new("PovraySocketMap", "Slope map")
+ for i in range(4):
+ transform = self.inputs.new("PovraySocketTransform", "Transform")
+ transform.hide_value = True
+ self.outputs.new("PovraySocketNormal", "Normal")
+
+ def draw_buttons(self, context, layout):
+ # for i, inp in enumerate(self.inputs):
+
+ if self.inputs[0].default_value == "brick":
+ layout.prop(self, "brick_mortar")
+ layout.label(text="Brick size:")
+ layout.prop(self, "brick_size_x")
+ layout.prop(self, "brick_size_y")
+ layout.prop(self, "brick_size_z")
+
+ def draw_buttons_ext(self, context, layout):
+
+ if self.inputs[0].default_value == "brick":
+ layout.prop(self, "brick_mortar")
+ layout.label(text="Brick size:")
+ layout.prop(self, "brick_size_x")
+ layout.prop(self, "brick_size_y")
+ layout.prop(self, "brick_size_z")
+
+ def draw_label(self):
+ return "Normal map"
+
+
+class ShaderNormalMapEntryNode(Node, nodes_properties.ObjectNodeTree):
+ """Normal Map Entry"""
+
+ bl_idname = "ShaderNormalMapEntryNode"
+ bl_label = "Normal map entry"
+
+ def init(self, context):
+ self.inputs.new("PovraySocketFloat_0_1", "Stop")
+ self.inputs.new("PovraySocketFloat_0_1", "Gray")
+
+ def draw_label(self):
+ return "Normal map entry"
+
+
+class IsoPropsNode(Node, CompositorNodeTree):
+ """ISO Props"""
+
+ bl_idname = "IsoPropsNode"
+ bl_label = "Iso"
+ node_label: StringProperty(maxlen=1024)
+
+ def init(self, context):
+ ob = bpy.context.object
+ self.node_label = ob.name
+ text_name = ob.pov.function_text
+ if text_name:
+ text = bpy.data.texts[text_name]
+ for line in text.lines:
+ split = line.body.split()
+ if split[0] == "#declare":
+ socket = self.inputs.new("NodeSocketFloat", "%s" % split[1])
+ value = split[3].split(";")
+ value = value[0]
+ socket.default_value = float(value)
+
+ def draw_label(self):
+ return self.node_label
+
+
+class PovrayFogNode(Node, CompositorNodeTree):
+ """Fog settings"""
+
+ bl_idname = "PovrayFogNode"
+ bl_label = "Fog"
+
+ def init(self, context):
+ color = self.inputs.new("NodeSocketColor", "Color")
+ color.default_value = (0.7, 0.7, 0.7, 0.25)
+ self.inputs.new("PovraySocketFloat_0_1", "Filter")
+ distance = self.inputs.new("NodeSocketInt", "Distance")
+ distance.default_value = 150
+ self.inputs.new("NodeSocketBool", "Ground")
+ fog_offset = self.inputs.new("NodeSocketFloat", "Offset")
+ fog_alt = self.inputs.new("NodeSocketFloat", "Altitude")
+ turb = self.inputs.new("NodeSocketVector", "Turbulence")
+ turb_depth = self.inputs.new("PovraySocketFloat_0_10", "Depth")
+ turb_depth.default_value = 0.5
+ octaves = self.inputs.new("PovraySocketInt_1_9", "Octaves")
+ octaves.default_value = 5
+ lambdat = self.inputs.new("PovraySocketFloat_0_10", "Lambda")
+ lambdat.default_value = 1.25
+ omega = self.inputs.new("PovraySocketFloat_0_10", "Omega")
+ omega.default_value = 0.35
+ translate = self.inputs.new("NodeSocketVector", "Translate")
+ rotate = self.inputs.new("NodeSocketVector", "Rotate")
+ scale = self.inputs.new("NodeSocketVector", "Scale")
+ scale.default_value = (1, 1, 1)
+
+ def draw_label(self):
+ return "Fog"
+
+
+class PovraySlopeNode(Node, TextureNodeTree):
+ """Output"""
+
+ bl_idname = "PovraySlopeNode"
+ bl_label = "Slope Map"
+
+ def init(self, context):
+ self.use_custom_color = True
+ self.color = (0, 0.2, 0)
+ slope = self.inputs.new("PovraySocketSlope", "0")
+ slope = self.inputs.new("PovraySocketSlope", "1")
+ slopemap = self.outputs.new("PovraySocketMap", "Slope map")
+ slopemap.hide_value = True
+
+ def draw_buttons(self, context, layout):
+
+ layout.operator("pov.nodeinputadd")
+ row = layout.row()
+ row.label(text="Value")
+ row.label(text="Height")
+ row.label(text="Slope")
+
+ def draw_buttons_ext(self, context, layout):
+
+ layout.operator("pov.nodeinputadd")
+ row = layout.row()
+ row.label(text="Value")
+ row.label(text="Height")
+ row.label(text="Slope")
+
+ def draw_label(self):
+ return "Slope Map"
+
+
+# -------- Texture nodes
+class TextureOutputNode(Node, TextureNodeTree):
+ """Output"""
+
+ bl_idname = "TextureOutputNode"
+ bl_label = "Color Map"
+
+ def init(self, context):
+ tex = bpy.context.object.active_material.active_texture
+ num_sockets = int(tex.pov.density_lines / 32)
+ for i in range(num_sockets):
+ color = self.inputs.new("NodeSocketColor", "%s" % i)
+ color.hide_value = True
+
+ def draw_buttons(self, context, layout):
+
+ layout.label(text="Color Ramps:")
+
+ def draw_label(self):
+ return "Color Map"
+
+
+classes = (
+ PovrayOutputNode,
+ PovrayTextureNode,
+ PovrayFinishNode,
+ PovrayDiffuseNode,
+ PovrayPhongNode,
+ PovraySpecularNode,
+ PovrayMirrorNode,
+ PovrayAmbientNode,
+ PovrayIridescenceNode,
+ PovraySubsurfaceNode,
+ PovrayMappingNode,
+ PovrayMultiplyNode,
+ PovrayTransformNode,
+ PovrayValueNode,
+ PovrayModifierNode,
+ PovrayPigmentNode,
+ PovrayColorImageNode,
+ PovrayBumpMapNode,
+ PovrayImagePatternNode,
+ ShaderPatternNode,
+ ShaderTextureMapNode,
+ ShaderNormalMapNode,
+ ShaderNormalMapEntryNode,
+ IsoPropsNode,
+ PovrayFogNode,
+ PovraySlopeNode,
+ TextureOutputNode,
+)
+
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/render_povray/nodes_fn.py b/render_povray/nodes_fn.py
new file mode 100644
index 00000000..ef12032f
--- /dev/null
+++ b/render_povray/nodes_fn.py
@@ -0,0 +1,704 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Translate complex shaders to exported POV textures."""
+
+import bpy
+
+# WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+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
+ from os import path
+ from .render import string_strip_hyphen
+
+ declare_nodes = []
+ scene = bpy.context.scene
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayFinishNode" and node.outputs["Finish"].is_linked:
+ file.write("#declare %s = finish {\n" % pov_node_name)
+ emission = node.inputs["Emission"].default_value
+ if node.inputs["Emission"].is_linked:
+ pass
+ file.write(" emission %.4g\n" % emission)
+ for link in ntree.links:
+ if link.to_node == node:
+
+ if link.from_node.bl_idname == "PovrayDiffuseNode":
+ intensity = 0
+ albedo = ""
+ brilliance = 0
+ crand = 0
+ if link.from_node.inputs["Intensity"].is_linked:
+ pass
+ else:
+ intensity = link.from_node.inputs["Intensity"].default_value
+ if link.from_node.inputs["Albedo"].is_linked:
+ pass
+ else:
+ if link.from_node.inputs["Albedo"].default_value:
+ albedo = "albedo"
+ file.write(" diffuse %s %.4g\n" % (albedo, intensity))
+ if link.from_node.inputs["Brilliance"].is_linked:
+ pass
+ else:
+ brilliance = link.from_node.inputs["Brilliance"].default_value
+ file.write(" brilliance %.4g\n" % brilliance)
+ if link.from_node.inputs["Crand"].is_linked:
+ pass
+ else:
+ crand = link.from_node.inputs["Crand"].default_value
+ if crand > 0:
+ file.write(" crand %.4g\n" % crand)
+
+ if link.from_node.bl_idname == "PovraySubsurfaceNode":
+ if scene.povray.sslt_enable:
+ energy = 0
+ r = g = b = 0
+ if link.from_node.inputs["Translucency"].is_linked:
+ pass
+ else:
+ r, g, b, a = link.from_node.inputs["Translucency"].default_value[:]
+ if link.from_node.inputs["Energy"].is_linked:
+ pass
+ else:
+ energy = link.from_node.inputs["Energy"].default_value
+ file.write(
+ " subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n"
+ % (r, g, b, energy)
+ )
+
+ if link.from_node.bl_idname in {"PovraySpecularNode", "PovrayPhongNode"}:
+ intensity = 0
+ albedo = ""
+ roughness = 0
+ metallic = 0
+ phong_size = 0
+ highlight = "specular"
+ if link.from_node.inputs["Intensity"].is_linked:
+ pass
+ else:
+ intensity = link.from_node.inputs["Intensity"].default_value
+
+ if link.from_node.inputs["Albedo"].is_linked:
+ pass
+ else:
+ if link.from_node.inputs["Albedo"].default_value:
+ albedo = "albedo"
+ if link.from_node.bl_idname in {"PovrayPhongNode"}:
+ highlight = "phong"
+ file.write(" %s %s %.4g\n" % (highlight, albedo, intensity))
+
+ if link.from_node.bl_idname in {"PovraySpecularNode"}:
+ if link.from_node.inputs["Roughness"].is_linked:
+ pass
+ else:
+ roughness = link.from_node.inputs["Roughness"].default_value
+ file.write(" roughness %.6g\n" % roughness)
+
+ if link.from_node.bl_idname in {"PovrayPhongNode"}:
+ if link.from_node.inputs["Size"].is_linked:
+ pass
+ else:
+ phong_size = link.from_node.inputs["Size"].default_value
+ file.write(" phong_size %s\n" % phong_size)
+
+ if link.from_node.inputs["Metallic"].is_linked:
+ pass
+ else:
+ metallic = link.from_node.inputs["Metallic"].default_value
+ file.write(" metallic %.4g\n" % metallic)
+
+ if link.from_node.bl_idname in {"PovrayMirrorNode"}:
+ file.write(" reflection {\n")
+ color = None
+ exponent = 0
+ metallic = 0
+ falloff = 0
+ fresnel = ""
+ conserve = ""
+ if link.from_node.inputs["Color"].is_linked:
+ pass
+ else:
+ color = link.from_node.inputs["Color"].default_value[:]
+ file.write(" <%.4g,%.4g,%.4g>\n" % (color[0], color[1], color[2]))
+
+ if link.from_node.inputs["Exponent"].is_linked:
+ pass
+ else:
+ exponent = link.from_node.inputs["Exponent"].default_value
+ file.write(" exponent %.4g\n" % exponent)
+
+ if link.from_node.inputs["Falloff"].is_linked:
+ pass
+ else:
+ falloff = link.from_node.inputs["Falloff"].default_value
+ file.write(" falloff %.4g\n" % falloff)
+
+ if link.from_node.inputs["Metallic"].is_linked:
+ pass
+ else:
+ metallic = link.from_node.inputs["Metallic"].default_value
+ file.write(" metallic %.4g" % metallic)
+
+ if link.from_node.inputs["Fresnel"].is_linked:
+ pass
+ else:
+ if link.from_node.inputs["Fresnel"].default_value:
+ fresnel = "fresnel"
+
+ if link.from_node.inputs["Conserve energy"].is_linked:
+ pass
+ else:
+ if link.from_node.inputs["Conserve energy"].default_value:
+ conserve = "conserve_energy"
+
+ file.write(" %s}\n %s\n" % (fresnel, conserve))
+
+ if link.from_node.bl_idname == "PovrayAmbientNode":
+ ambient = (0, 0, 0)
+ if link.from_node.inputs["Ambient"].is_linked:
+ pass
+ else:
+ ambient = link.from_node.inputs["Ambient"].default_value[:]
+ file.write(" ambient <%.4g,%.4g,%.4g>\n" % ambient)
+
+ if link.from_node.bl_idname in {"PovrayIridescenceNode"}:
+ file.write(" irid {\n")
+ amount = 0
+ thickness = 0
+ turbulence = 0
+ if link.from_node.inputs["Amount"].is_linked:
+ pass
+ else:
+ amount = link.from_node.inputs["Amount"].default_value
+ file.write(" %.4g\n" % amount)
+
+ if link.from_node.inputs["Thickness"].is_linked:
+ pass
+ else:
+ exponent = link.from_node.inputs["Thickness"].default_value
+ file.write(" thickness %.4g\n" % thickness)
+
+ if link.from_node.inputs["Turbulence"].is_linked:
+ pass
+ else:
+ falloff = link.from_node.inputs["Turbulence"].default_value
+ file.write(" turbulence %.4g}\n" % turbulence)
+
+ file.write("}\n")
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayTransformNode" and node.outputs["Transform"].is_linked:
+ tx = node.inputs["Translate x"].default_value
+ ty = node.inputs["Translate y"].default_value
+ tz = node.inputs["Translate z"].default_value
+ rx = node.inputs["Rotate x"].default_value
+ ry = node.inputs["Rotate y"].default_value
+ rz = node.inputs["Rotate z"].default_value
+ sx = node.inputs["Scale x"].default_value
+ sy = node.inputs["Scale y"].default_value
+ sz = node.inputs["Scale z"].default_value
+ file.write(
+ "#declare %s = transform {\n"
+ " translate<%.4g,%.4g,%.4g>\n"
+ " rotate<%.4g,%.4g,%.4g>\n"
+ " scale<%.4g,%.4g,%.4g>}\n" % (pov_node_name, tx, ty, tz, rx, ry, rz, sx, sy, sz)
+ )
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayColorImageNode" and node.outputs["Pigment"].is_linked:
+ declare_nodes.append(node.name)
+ if node.image == "":
+ 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)
+ transform = ""
+ for link in ntree.links:
+ if (
+ link.from_node.bl_idname == "PovrayTransformNode"
+ and link.to_node == node
+ ):
+ pov_trans_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ transform = "transform {%s}" % pov_trans_name
+ uv = ""
+ if node.map_type == "uv_mapping":
+ uv = "uv_mapping"
+ filepath = bpy.path.abspath(im.filepath)
+ file.write("#declare %s = pigment {%s image_map {\n" % (pov_node_name, uv))
+ premul = "off"
+ if node.premultiplied:
+ premul = "on"
+ once = ""
+ if node.once:
+ once = "once"
+ file.write(
+ ' "%s"\n gamma %.6g\n premultiplied %s\n'
+ % (filepath, node.inputs["Gamma"].default_value, premul)
+ )
+ file.write(" %s\n" % once)
+ if node.map_type != "uv_mapping":
+ file.write(" map_type %s\n" % node.map_type)
+ file.write(
+ " interpolate %s\n filter all %.4g\n transmit all %.4g\n"
+ % (
+ node.interpolate,
+ node.inputs["Filter"].default_value,
+ node.inputs["Transmit"].default_value,
+ )
+ )
+ file.write(" }\n")
+ file.write(" %s\n" % transform)
+ file.write(" }\n")
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayImagePatternNode" and node.outputs["Pattern"].is_linked:
+ declare_nodes.append(node.name)
+ if node.image != "":
+ im = bpy.data.images[node.image]
+ if im.filepath and path.exists(bpy.path.abspath(im.filepath)):
+ transform = ""
+ for link in ntree.links:
+ if (
+ link.from_node.bl_idname == "PovrayTransformNode"
+ and link.to_node == node
+ ):
+ pov_trans_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ transform = "transform {%s}" % pov_trans_name
+ uv = ""
+ if node.map_type == "uv_mapping":
+ uv = "uv_mapping"
+ filepath = bpy.path.abspath(im.filepath)
+ file.write("#macro %s() %s image_pattern {\n" % (pov_node_name, uv))
+ premul = "off"
+ if node.premultiplied:
+ premul = "on"
+ once = ""
+ if node.once:
+ once = "once"
+ file.write(
+ ' "%s"\n gamma %.6g\n premultiplied %s\n'
+ % (filepath, node.inputs["Gamma"].default_value, premul)
+ )
+ file.write(" %s\n" % once)
+ if node.map_type != "uv_mapping":
+ file.write(" map_type %s\n" % node.map_type)
+ file.write(" interpolate %s\n" % node.interpolate)
+ file.write(" }\n")
+ file.write(" %s\n" % transform)
+ file.write("#end\n")
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayBumpMapNode" and node.outputs["Normal"].is_linked:
+ if node.image != "":
+ im = bpy.data.images[node.image]
+ if im.filepath and path.exists(bpy.path.abspath(im.filepath)):
+ transform = ""
+ for link in ntree.links:
+ if (
+ link.from_node.bl_idname == "PovrayTransformNode"
+ and link.to_node == node
+ ):
+ pov_trans_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ transform = "transform {%s}" % pov_trans_name
+ uv = ""
+ if node.map_type == "uv_mapping":
+ uv = "uv_mapping"
+ filepath = bpy.path.abspath(im.filepath)
+ file.write("#declare %s = normal {%s bump_map {\n" % (pov_node_name, uv))
+ once = ""
+ if node.once:
+ once = "once"
+ file.write(' "%s"\n' % filepath)
+ file.write(" %s\n" % once)
+ if node.map_type != "uv_mapping":
+ file.write(" map_type %s\n" % node.map_type)
+ bump_size = node.inputs["Normal"].default_value
+ if node.inputs["Normal"].is_linked:
+ pass
+ file.write(
+ " interpolate %s\n bump_size %.4g\n" % (node.interpolate, bump_size)
+ )
+ file.write(" }\n")
+ file.write(" %s\n" % transform)
+ file.write(" }\n")
+ declare_nodes.append(node.name)
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayPigmentNode" and node.outputs["Pigment"].is_linked:
+ declare_nodes.append(node.name)
+ r, g, b = node.inputs["Color"].default_value[:]
+ f = node.inputs["Filter"].default_value
+ t = node.inputs["Transmit"].default_value
+ if node.inputs["Color"].is_linked:
+ pass
+ file.write(
+ "#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n"
+ % (pov_node_name, r, g, b, f, t)
+ )
+
+ for node in ntree.nodes:
+ pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
+ if node.bl_idname == "PovrayTextureNode" and node.outputs["Texture"].is_linked:
+ declare_nodes.append(node.name)
+ r, g, b = node.inputs["Pigment"].default_value[:]
+ pov_col_name = "color rgb <%.4g,%.4g,%.4g>" % (r, g, b)
+ if node.inputs["Pigment"].is_linked:
+ for link in ntree.links:
+ if link.to_node == node and link.to_socket.name == "Pigment":
+ pov_col_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ file.write("#declare %s = texture{\n pigment{%s}\n" % (pov_node_name, pov_col_name))
+ if node.inputs["Normal"].is_linked:
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.to_socket.name == "Normal"
+ and link.from_node.name in declare_nodes
+ ):
+ pov_nor_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ file.write(" normal{%s}\n" % pov_nor_name)
+ if node.inputs["Finish"].is_linked:
+ for link in ntree.links:
+ if link.to_node == node and link.to_socket.name == "Finish":
+ pov_fin_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
+ + "_%s" % pov_mat_name
+ )
+ file.write(" finish{%s}\n" % pov_fin_name)
+ file.write("}\n")
+ declare_nodes.append(node.name)
+
+ for i in range(0, len(ntree.nodes)):
+ for node in ntree.nodes:
+ if node.bl_idname in {"ShaderNodeGroup", "ShaderTextureMapNode"}:
+ for output in node.outputs:
+ if (
+ output.name == "Texture"
+ and output.is_linked
+ and (node.name not in declare_nodes)
+ ):
+ declare = True
+ for link in ntree.links:
+ if link.to_node == node and link.to_socket.name not in {
+ "",
+ "Color ramp",
+ "Mapping",
+ "Transform",
+ "Modifier",
+ }:
+ if link.from_node.name not in declare_nodes:
+ declare = False
+ if declare:
+ pov_node_name = (
+ string_strip_hyphen(bpy.path.clean_name(node.name))
+ + "_%s" % pov_mat_name
+ )
+ uv = ""
+ warp = ""
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "PovrayMappingNode"
+ and link.from_node.warp_type != "NONE"
+ ):
+ w_type = link.from_node.warp_type
+ if w_type == "uv_mapping":
+ uv = "uv_mapping"
+ else:
+ tor = ""
+ if w_type == "toroidal":
+ tor = (
+ "major_radius %.4g"
+ % link.from_node.warp_tor_major_radius
+ )
+ orient = link.from_node.warp_orientation
+ exp = link.from_node.warp_dist_exp
+ warp = "warp{%s orientation %s dist_exp %.4g %s}" % (
+ w_type,
+ orient,
+ exp,
+ tor,
+ )
+ if link.from_node.warp_type == "planar":
+ warp = "warp{%s %s %.4g}" % (w_type, orient, exp)
+ if link.from_node.warp_type == "cubic":
+ warp = "warp{%s}" % w_type
+ file.write("#declare %s = texture {%s\n" % (pov_node_name, uv))
+ pattern = node.inputs[0].default_value
+ advanced = ""
+ if node.inputs[0].is_linked:
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "ShaderPatternNode"
+ ):
+ # ------------ advanced ------------------------- #
+ lfn = link.from_node
+ pattern = lfn.pattern
+ if pattern == "agate":
+ advanced = "agate_turb %.4g" % lfn.agate_turb
+ if pattern == "crackle":
+ advanced = "form <%.4g,%.4g,%.4g>" % (
+ lfn.crackle_form_x,
+ lfn.crackle_form_y,
+ lfn.crackle_form_z,
+ )
+ advanced += " metric %.4g" % lfn.crackle_metric
+ if lfn.crackle_solid:
+ advanced += " solid"
+ if pattern in {"spiral1", "spiral2"}:
+ advanced = "%.4g" % lfn.spiral_arms
+ if pattern in {"tiling"}:
+ advanced = "%.4g" % lfn.tiling_number
+ if pattern in {"gradient"}:
+ advanced = "%s" % lfn.gradient_orient
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "PovrayImagePatternNode"
+ ):
+ pov_macro_name = (
+ string_strip_hyphen(
+ bpy.path.clean_name(link.from_node.name)
+ )
+ + "_%s" % pov_mat_name
+ )
+ pattern = "%s()" % pov_macro_name
+ file.write(" %s %s %s\n" % (pattern, advanced, warp))
+
+ repeat = ""
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "PovrayMultiplyNode"
+ ):
+ if link.from_node.amount_x > 1:
+ repeat += "warp{repeat %.4g * x}" % link.from_node.amount_x
+ if link.from_node.amount_y > 1:
+ repeat += " warp{repeat %.4g * y}" % link.from_node.amount_y
+ if link.from_node.amount_z > 1:
+ repeat += " warp{repeat %.4g * z}" % link.from_node.amount_z
+
+ transform = ""
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "PovrayTransformNode"
+ ):
+ pov_trans_name = (
+ string_strip_hyphen(
+ bpy.path.clean_name(link.from_node.name)
+ )
+ + "_%s" % pov_mat_name
+ )
+ transform = "transform {%s}" % pov_trans_name
+ x = 0
+ y = 0
+ z = 0
+ d = 0
+ e = 0
+ f = 0
+ g = 0
+ h = 0
+ modifier = False
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "PovrayModifierNode"
+ ):
+ modifier = True
+ if link.from_node.inputs["Turb X"].is_linked:
+ pass
+ else:
+ x = link.from_node.inputs["Turb X"].default_value
+
+ if link.from_node.inputs["Turb Y"].is_linked:
+ pass
+ else:
+ y = link.from_node.inputs["Turb Y"].default_value
+
+ if link.from_node.inputs["Turb Z"].is_linked:
+ pass
+ else:
+ z = link.from_node.inputs["Turb Z"].default_value
+
+ if link.from_node.inputs["Octaves"].is_linked:
+ pass
+ else:
+ d = link.from_node.inputs["Octaves"].default_value
+
+ if link.from_node.inputs["Lambda"].is_linked:
+ pass
+ else:
+ e = link.from_node.inputs["Lambda"].default_value
+
+ if link.from_node.inputs["Omega"].is_linked:
+ pass
+ else:
+ f = link.from_node.inputs["Omega"].default_value
+
+ if link.from_node.inputs["Frequency"].is_linked:
+ pass
+ else:
+ g = link.from_node.inputs["Frequency"].default_value
+
+ if link.from_node.inputs["Phase"].is_linked:
+ pass
+ else:
+ h = link.from_node.inputs["Phase"].default_value
+
+ turb = "turbulence <%.4g,%.4g,%.4g>" % (x, y, z)
+ octv = "octaves %s" % d
+ lmbd = "lambda %.4g" % e
+ omg = "omega %.4g" % f
+ freq = "frequency %.4g" % g
+ pha = "phase %.4g" % h
+
+ file.write("\n")
+ if pattern not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ file.write(" texture_map {\n")
+ if node.inputs["Color ramp"].is_linked:
+ for link in ntree.links:
+ if (
+ link.to_node == node
+ and link.from_node.bl_idname == "ShaderNodeValToRGB"
+ ):
+ els = link.from_node.color_ramp.elements
+ n = -1
+ for el in els:
+ n += 1
+ pov_in_mat_name = string_strip_hyphen(
+ bpy.path.clean_name(link.from_node.name)
+ ) + "_%s_%s" % (n, pov_mat_name)
+ default = True
+ for ilink in ntree.links:
+ if (
+ ilink.to_node == node
+ and ilink.to_socket.name == str(n)
+ ):
+ default = False
+ pov_in_mat_name = (
+ string_strip_hyphen(
+ bpy.path.clean_name(
+ ilink.from_node.name
+ )
+ )
+ + "_%s" % pov_mat_name
+ )
+ if default:
+ r, g, b, a = el.color[:]
+ file.write(
+ " #declare %s = texture{"
+ "pigment{"
+ "color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n"
+ % (pov_in_mat_name, r, g, b, 1 - a)
+ )
+ file.write(
+ " [%s %s]\n" % (el.position, pov_in_mat_name)
+ )
+ else:
+ els = [[0, 0, 0, 0], [1, 1, 1, 1]]
+ for t in range(0, 2):
+ pov_in_mat_name = string_strip_hyphen(
+ bpy.path.clean_name(link.from_node.name)
+ ) + "_%s_%s" % (t, pov_mat_name)
+ default = True
+ for ilink in ntree.links:
+ if ilink.to_node == node and ilink.to_socket.name == str(t):
+ default = False
+ pov_in_mat_name = (
+ string_strip_hyphen(
+ bpy.path.clean_name(ilink.from_node.name)
+ )
+ + "_%s" % pov_mat_name
+ )
+ if default:
+ r, g, b = els[t][1], els[t][2], els[t][3]
+ if pattern not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ file.write(
+ " #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n"
+ % (pov_in_mat_name, r, g, b)
+ )
+ else:
+ file.write(
+ " texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n"
+ % (r, g, b)
+ )
+ if pattern not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ file.write(" [%s %s]\n" % (els[t][0], pov_in_mat_name))
+ else:
+ if not default:
+ file.write(" texture{%s}\n" % pov_in_mat_name)
+ if pattern not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ file.write("}\n")
+ if pattern == "brick":
+ file.write(
+ "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n"
+ % (
+ node.brick_size_x,
+ node.brick_size_y,
+ node.brick_size_z,
+ node.brick_mortar,
+ )
+ )
+ file.write(" %s %s" % (repeat, transform))
+ if modifier:
+ file.write(
+ " %s %s %s %s %s %s" % (turb, octv, lmbd, omg, freq, pha)
+ )
+ file.write("}\n")
+ declare_nodes.append(node.name)
+
+ for link in ntree.links:
+ if link.to_node.bl_idname == "PovrayOutputNode" and link.from_node.name in declare_nodes:
+ pov_mat_node_name = (
+ string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + "_%s" % pov_mat_name
+ )
+ file.write("#declare %s = %s\n" % (pov_mat_name, pov_mat_node_name))
diff --git a/render_povray/nodes_gui.py b/render_povray/nodes_gui.py
new file mode 100644
index 00000000..64fcc130
--- /dev/null
+++ b/render_povray/nodes_gui.py
@@ -0,0 +1,278 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+""""Nodes based User interface for shaders exported to POV textures."""
+import bpy
+
+from bpy.utils import register_class, unregister_class
+from bpy.types import Menu, Operator
+from bpy.props import (
+ StringProperty,
+)
+
+# def find_node_input(node, name):
+# for input in node.inputs:
+# if input.name == name:
+# return input
+
+# def panel_node_draw(layout, id_data, output_type, input_name):
+# if not id_data.use_nodes:
+# #layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
+# #layout.operator("pov.use_shading_nodes", icon='NODETREE')
+# layout.operator("WM_OT_context_toggle", icon='NODETREE').data_path = \
+# "material.pov.material_use_nodes"
+# return False
+
+# ntree = id_data.node_tree
+
+# node = find_node(id_data, output_type)
+# if not node:
+# layout.label(text="No output node")
+# else:
+# input = find_node_input(node, input_name)
+# layout.template_node_view(ntree, node, input)
+
+# return True
+
+
+class NODE_MT_POV_map_create(Menu):
+ """Create maps"""
+
+ bl_idname = "POVRAY_MT_node_map_create"
+ bl_label = "Create map"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator("node.map_create")
+
+
+def menu_func_nodes(self, context):
+ ob = context.object
+ if hasattr(ob, "active_material"):
+ mat = context.object.active_material
+ if mat and context.space_data.tree_type == "ObjectNodeTree":
+ self.layout.prop(mat.pov, "material_use_nodes")
+ self.layout.menu(NODE_MT_POV_map_create.bl_idname)
+ self.layout.operator("wm.updatepreviewkey")
+ if hasattr(mat, "active_texture") and context.scene.render.engine == "POVRAY_RENDER":
+ tex = mat.active_texture
+ if tex and context.space_data.tree_type == "TextureNodeTree":
+ self.layout.prop(tex.pov, "texture_use_nodes")
+
+
+# ------------------------------------------------------------------------------ #
+# --------------------------------- Operators ---------------------------------- #
+# ------------------------------------------------------------------------------ #
+
+
+class NODE_OT_iso_add(Operator):
+ bl_idname = "pov.nodeisoadd"
+ bl_label = "Create iso props"
+
+ def execute(self, context):
+ ob = bpy.context.object
+ if not bpy.context.scene.use_nodes:
+ bpy.context.scene.use_nodes = True
+ tree = bpy.context.scene.node_tree
+ for node in tree.nodes:
+ if node.bl_idname == "IsoPropsNode" and node.label == ob.name:
+ tree.nodes.remove(node)
+ isonode = tree.nodes.new("IsoPropsNode")
+ isonode.location = (0, 0)
+ isonode.label = ob.name
+ return {"FINISHED"}
+
+
+class NODE_OT_map_create(Operator):
+ bl_idname = "node.map_create"
+ bl_label = "Create map"
+
+ def execute(self, context):
+ x = y = 0
+ space = context.space_data
+ tree = space.edit_tree
+ for node in tree.nodes:
+ if node.select:
+ x, y = node.location
+ node.select = False
+ tmap = tree.nodes.new("ShaderTextureMapNode")
+ tmap.location = (x - 200, y)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
+
+ def draw(self, context):
+ layout = self.layout
+ mat = context.object.active_material
+ layout.prop(mat.pov, "inputs_number")
+
+
+class NODE_OT_povray_node_texture_map_add(Operator):
+ bl_idname = "pov.nodetexmapadd"
+ bl_label = "Texture map"
+
+ def execute(self, context):
+ tree = bpy.context.object.active_material.node_tree
+ tmap = tree.nodes.active
+ mtl = context.object.active_material
+ mtl.node_tree.nodes.active = tmap
+ el = tmap.color_ramp.elements.new(0.5)
+ for el in tmap.color_ramp.elements:
+ el.color = (0, 0, 0, 1)
+ for inp in tmap.inputs:
+ tmap.inputs.remove(inp)
+ for outp in tmap.outputs:
+ tmap.outputs.remove(outp)
+ pattern = tmap.inputs.new("NodeSocketVector", "Pattern")
+ pattern.hide_value = True
+ for i in range(3):
+ tmap.inputs.new("NodeSocketColor", "Shader")
+ tmap.outputs.new("NodeSocketShader", "BSDF")
+ tmap.label = "Texture Map"
+ return {"FINISHED"}
+
+
+class NODE_OT_povray_node_output_add(Operator):
+ bl_idname = "pov.nodeoutputadd"
+ bl_label = "Output"
+
+ def execute(self, context):
+ tree = bpy.context.object.active_material.node_tree
+ tmap = tree.nodes.new("ShaderNodeOutputMaterial")
+ mtl = context.object.active_material
+ mtl.node_tree.nodes.active = tmap
+ for inp in tmap.inputs:
+ tmap.inputs.remove(inp)
+ tmap.inputs.new("NodeSocketShader", "Surface")
+ tmap.label = "Output"
+ return {"FINISHED"}
+
+
+class NODE_OT_povray_node_layered_add(Operator):
+ bl_idname = "pov.nodelayeredadd"
+ bl_label = "Layered material"
+
+ def execute(self, context):
+ tree = bpy.context.object.active_material.node_tree
+ tmap = tree.nodes.new("ShaderNodeAddShader")
+ mtl = context.object.active_material
+ mtl.node_tree.nodes.active = tmap
+ tmap.label = "Layered material"
+ return {"FINISHED"}
+
+
+class NODE_OT_povray_input_add(Operator):
+ bl_idname = "pov.nodeinputadd"
+ bl_label = "Add entry"
+
+ def execute(self, context):
+ mtl = context.object.active_material
+ node = mtl.node_tree.nodes.active
+ if node.type == "VALTORGB":
+ number = 1
+ for inp in node.inputs:
+ if inp.type == "SHADER":
+ number += 1
+ node.inputs.new("NodeSocketShader", "%s" % number)
+ els = node.color_ramp.elements
+ pos1 = els[len(els) - 1].position
+ pos2 = els[len(els) - 2].position
+ pos = (pos1 - pos2) / 2 + pos2
+ el = els.new(pos)
+
+ if node.bl_idname == "PovraySlopeNode":
+ number = len(node.inputs)
+ node.inputs.new("PovraySocketSlope", "%s" % number)
+
+ return {"FINISHED"}
+
+
+class NODE_OT_povray_input_remove(Operator):
+ bl_idname = "pov.nodeinputremove"
+ bl_label = "Remove input"
+
+ def execute(self, context):
+ mtl = context.object.active_material
+ node = mtl.node_tree.nodes.active
+ if node.type in {"VALTORGB", "ADD_SHADER"}:
+ number = len(node.inputs) - 1
+ if number > 5:
+ inp = node.inputs[number]
+ node.inputs.remove(inp)
+ if node.type == "VALTORGB":
+ els = node.color_ramp.elements
+ number = len(els) - 2
+ el = els[number]
+ els.remove(el)
+ return {"FINISHED"}
+
+
+class NODE_OT_povray_image_open(Operator):
+ bl_idname = "pov.imageopen"
+ bl_label = "Open"
+
+ filepath: StringProperty(
+ name="File Path", description="Open image", maxlen=1024, subtype="FILE_PATH"
+ )
+
+ def invoke(self, context, event):
+ context.window_manager.fileselect_add(self)
+ return {"RUNNING_MODAL"}
+
+ def execute(self, context):
+ im = bpy.data.images.load(self.filepath)
+ mtl = context.object.active_material
+ node = mtl.node_tree.nodes.active
+ node.image = im.name
+ return {"FINISHED"}
+
+
+# class TEXTURE_OT_povray_open_image(Operator):
+# bl_idname = "pov.openimage"
+# bl_label = "Open Image"
+
+# filepath = StringProperty(
+# name="File Path",
+# description="Open image",
+# maxlen=1024,
+# subtype='FILE_PATH',
+# )
+
+# def invoke(self, context, event):
+# context.window_manager.fileselect_add(self)
+# return {'RUNNING_MODAL'}
+
+# def execute(self, context):
+# im=bpy.data.images.load(self.filepath)
+# tex = context.texture
+# tex.pov.image = im.name
+# view_layer = context.view_layer
+# view_layer.update()
+# return {'FINISHED'}
+
+
+classes = (
+ NODE_MT_POV_map_create,
+ NODE_OT_iso_add,
+ NODE_OT_map_create,
+ NODE_OT_povray_node_texture_map_add,
+ NODE_OT_povray_node_output_add,
+ NODE_OT_povray_node_layered_add,
+ NODE_OT_povray_input_add,
+ NODE_OT_povray_input_remove,
+ NODE_OT_povray_image_open,
+)
+
+
+def register():
+ bpy.types.NODE_HT_header.append(menu_func_nodes)
+ for cls in classes:
+ register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
+ bpy.types.NODE_HT_header.remove(menu_func_nodes)
diff --git a/render_povray/nodes_properties.py b/render_povray/nodes_properties.py
new file mode 100644
index 00000000..8fcc8a3f
--- /dev/null
+++ b/render_povray/nodes_properties.py
@@ -0,0 +1,703 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+""""Nodes based User interface for shaders exported to POV textures."""
+import bpy
+
+from bpy.utils import register_class, unregister_class
+from bpy.types import NodeSocket, Operator
+from bpy.props import (
+ StringProperty,
+ FloatVectorProperty,
+)
+import nodeitems_utils
+from nodeitems_utils import NodeCategory, NodeItem
+
+# ---------------------------------------------------------------- #
+# Pov Nodes init
+# ---------------------------------------------------------------- #
+# -------- Parent node class
+class ObjectNodeTree(bpy.types.NodeTree):
+ """Povray Material Nodes"""
+
+ bl_idname = "ObjectNodeTree"
+ bl_label = "Povray Object Nodes"
+ bl_icon = "PLUGIN"
+
+ @classmethod
+ def poll(cls, context):
+ return context.scene.render.engine == "POVRAY_RENDER"
+
+ @classmethod
+ def get_from_context(cls, context):
+ ob = context.active_object
+ if ob and ob.type != 'LIGHT':
+ ma = ob.active_material
+ if ma is not None:
+ nt_name = ma.node_tree
+ if nt_name != "":
+ return nt_name, ma, ma
+ return (None, None, None)
+
+ def update(self):
+ self.refresh = True
+
+
+# -------- Sockets classes
+class PovraySocketUniversal(NodeSocket):
+ bl_idname = "PovraySocketUniversal"
+ bl_label = "Povray Socket"
+ value_unlimited: bpy.props.FloatProperty(default=0.0)
+ value_0_1: bpy.props.FloatProperty(min=0.0, max=1.0, default=0.0)
+ value_0_10: bpy.props.FloatProperty(min=0.0, max=10.0, default=0.0)
+ value_000001_10: bpy.props.FloatProperty(min=0.000001, max=10.0, default=0.0)
+ value_1_9: bpy.props.IntProperty(min=1, max=9, default=1)
+ value_0_255: bpy.props.IntProperty(min=0, max=255, default=0)
+ percent: bpy.props.FloatProperty(min=0.0, max=100.0, default=0.0)
+
+ def draw(self, context, layout, node, text):
+ space = context.space_data
+ tree = space.edit_tree
+ links = tree.links
+ if self.is_linked:
+ value = []
+ for link in links:
+ # inps = link.to_node.inputs
+ linked_inps = (
+ inp for inp in link.to_node.inputs if link.from_node == node and inp.is_linked
+ )
+ for inp in linked_inps:
+ if inp.bl_idname == "PovraySocketFloat_0_1":
+ if (prop := "value_0_1") not in value:
+ value.append(prop)
+ if inp.bl_idname == "PovraySocketFloat_000001_10":
+ if (prop := "value_000001_10") not in value:
+ value.append(prop)
+ if inp.bl_idname == "PovraySocketFloat_0_10":
+ if (prop := "value_0_10") not in value:
+ value.append(prop)
+ if inp.bl_idname == "PovraySocketInt_1_9":
+ if (prop := "value_1_9") not in value:
+ value.append(prop)
+ if inp.bl_idname == "PovraySocketInt_0_255":
+ if (prop := "value_0_255") not in value:
+ value.append(prop)
+ if inp.bl_idname == "PovraySocketFloatUnlimited":
+ if (prop := "value_unlimited") not in value:
+ value.append(prop)
+ if len(value) == 1:
+ layout.prop(self, "%s" % value[0], text=text)
+ else:
+ layout.prop(self, "percent", text="Percent")
+ else:
+ layout.prop(self, "percent", text=text)
+
+ def draw_color(self, context, node):
+ return (1, 0, 0, 1)
+
+
+class PovraySocketFloat_0_1(NodeSocket):
+ bl_idname = "PovraySocketFloat_0_1"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(
+ description="Input node Value_0_1", min=0, max=1, default=0
+ )
+
+ def draw(self, context, layout, node, text):
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (0.5, 0.7, 0.7, 1)
+
+
+class PovraySocketFloat_0_10(NodeSocket):
+ bl_idname = "PovraySocketFloat_0_10"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(
+ description="Input node Value_0_10", min=0, max=10, default=0
+ )
+
+ def draw(self, context, layout, node, text):
+ if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked:
+ layout.label(text="")
+ self.hide_value = True
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (0.65, 0.65, 0.65, 1)
+
+
+class PovraySocketFloat_10(NodeSocket):
+ bl_idname = "PovraySocketFloat_10"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(
+ description="Input node Value_10", min=-10, max=10, default=0
+ )
+
+ def draw(self, context, layout, node, text):
+ if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked:
+ layout.label(text="")
+ self.hide_value = True
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (0.65, 0.65, 0.65, 1)
+
+
+class PovraySocketFloatPositive(NodeSocket):
+ bl_idname = "PovraySocketFloatPositive"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(
+ description="Input Node Value Positive", min=0.0, default=0
+ )
+
+ def draw(self, context, layout, node, text):
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (0.045, 0.005, 0.136, 1)
+
+
+class PovraySocketFloat_000001_10(NodeSocket):
+ bl_idname = "PovraySocketFloat_000001_10"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(min=0.000001, max=10, default=0.000001)
+
+ def draw(self, context, layout, node, text):
+ if self.is_output or self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (1, 0, 0, 1)
+
+
+class PovraySocketFloatUnlimited(NodeSocket):
+ bl_idname = "PovraySocketFloatUnlimited"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(default=0.0)
+
+ def draw(self, context, layout, node, text):
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text, slider=True)
+
+ def draw_color(self, context, node):
+ return (0.7, 0.7, 1, 1)
+
+
+class PovraySocketInt_1_9(NodeSocket):
+ bl_idname = "PovraySocketInt_1_9"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.IntProperty(
+ description="Input node Value_1_9", min=1, max=9, default=6
+ )
+
+ def draw(self, context, layout, node, text):
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text)
+
+ def draw_color(self, context, node):
+ return (1, 0.7, 0.7, 1)
+
+
+class PovraySocketInt_0_256(NodeSocket):
+ bl_idname = "PovraySocketInt_0_256"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.IntProperty(min=0, max=255, default=0)
+
+ def draw(self, context, layout, node, text):
+ if self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text)
+
+ def draw_color(self, context, node):
+ return (0.5, 0.5, 0.5, 1)
+
+
+class PovraySocketPattern(NodeSocket):
+ bl_idname = "PovraySocketPattern"
+ bl_label = "Povray Socket"
+
+ default_value: bpy.props.EnumProperty(
+ name="Pattern",
+ description="Select the pattern",
+ items=(
+ ("boxed", "Boxed", ""),
+ ("brick", "Brick", ""),
+ ("cells", "Cells", ""),
+ ("checker", "Checker", ""),
+ ("granite", "Granite", ""),
+ ("leopard", "Leopard", ""),
+ ("marble", "Marble", ""),
+ ("onion", "Onion", ""),
+ ("planar", "Planar", ""),
+ ("quilted", "Quilted", ""),
+ ("ripples", "Ripples", ""),
+ ("radial", "Radial", ""),
+ ("spherical", "Spherical", ""),
+ ("spotted", "Spotted", ""),
+ ("waves", "Waves", ""),
+ ("wood", "Wood", ""),
+ ("wrinkles", "Wrinkles", ""),
+ ),
+ default="granite",
+ )
+
+ def draw(self, context, layout, node, text):
+ if self.is_output or self.is_linked:
+ layout.label(text="Pattern")
+ else:
+ layout.prop(self, "default_value", text=text)
+
+ def draw_color(self, context, node):
+ return (1, 1, 1, 1)
+
+
+class PovraySocketColor(NodeSocket):
+ bl_idname = "PovraySocketColor"
+ bl_label = "Povray Socket"
+
+ default_value: FloatVectorProperty(
+ precision=4,
+ step=0.01,
+ min=0,
+ soft_max=1,
+ default=(0.0, 0.0, 0.0),
+ options={"ANIMATABLE"},
+ subtype="COLOR",
+ )
+
+ def draw(self, context, layout, node, text):
+ if self.is_output or self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text)
+
+ def draw_color(self, context, node):
+ return (1, 1, 0, 1)
+
+
+class PovraySocketColorRGBFT(NodeSocket):
+ bl_idname = "PovraySocketColorRGBFT"
+ bl_label = "Povray Socket"
+
+ default_value: FloatVectorProperty(
+ precision=4,
+ step=0.01,
+ min=0,
+ soft_max=1,
+ default=(0.0, 0.0, 0.0),
+ options={"ANIMATABLE"},
+ subtype="COLOR",
+ )
+ f: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0)
+ t: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0)
+
+ def draw(self, context, layout, node, text):
+ if self.is_output or self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text=text)
+
+ def draw_color(self, context, node):
+ return (1, 1, 0, 1)
+
+
+class PovraySocketTexture(NodeSocket):
+ bl_idname = "PovraySocketTexture"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.IntProperty()
+
+ def draw(self, context, layout, node, text):
+ layout.label(text=text)
+
+ def draw_color(self, context, node):
+ return (0, 1, 0, 1)
+
+
+class PovraySocketTransform(NodeSocket):
+ bl_idname = "PovraySocketTransform"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.IntProperty(min=0, max=255, default=0)
+
+ def draw(self, context, layout, node, text):
+ layout.label(text=text)
+
+ def draw_color(self, context, node):
+ return (99 / 255, 99 / 255, 199 / 255, 1)
+
+
+class PovraySocketNormal(NodeSocket):
+ bl_idname = "PovraySocketNormal"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.IntProperty(min=0, max=255, default=0)
+
+ def draw(self, context, layout, node, text):
+ layout.label(text=text)
+
+ def draw_color(self, context, node):
+ return (0.65, 0.65, 0.65, 1)
+
+
+class PovraySocketSlope(NodeSocket):
+ bl_idname = "PovraySocketSlope"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.FloatProperty(min=0.0, max=1.0)
+ height: bpy.props.FloatProperty(min=0.0, max=10.0)
+ slope: bpy.props.FloatProperty(min=-10.0, max=10.0)
+
+ def draw(self, context, layout, node, text):
+ if self.is_output or self.is_linked:
+ layout.label(text=text)
+ else:
+ layout.prop(self, "default_value", text="")
+ layout.prop(self, "height", text="")
+ layout.prop(self, "slope", text="")
+
+ def draw_color(self, context, node):
+ return (0, 0, 0, 1)
+
+
+class PovraySocketMap(NodeSocket):
+ bl_idname = "PovraySocketMap"
+ bl_label = "Povray Socket"
+ default_value: bpy.props.StringProperty()
+
+ def draw(self, context, layout, node, text):
+ layout.label(text=text)
+
+ def draw_color(self, context, node):
+ return (0.2, 0, 0.2, 1)
+
+
+class PovrayPatternNode(Operator):
+ bl_idname = "pov.patternnode"
+ bl_label = "Pattern"
+
+ add = True
+
+ def execute(self, context):
+ space = context.space_data
+ tree = space.edit_tree
+ for node in tree.nodes:
+ node.select = False
+ if self.add:
+ tmap = tree.nodes.new("ShaderNodeValToRGB")
+ tmap.label = "Pattern"
+ for inp in tmap.inputs:
+ tmap.inputs.remove(inp)
+ for outp in tmap.outputs:
+ tmap.outputs.remove(outp)
+ pattern = tmap.inputs.new("PovraySocketPattern", "Pattern")
+ pattern.hide_value = True
+ mapping = tmap.inputs.new("NodeSocketVector", "Mapping")
+ mapping.hide_value = True
+ transform = tmap.inputs.new("NodeSocketVector", "Transform")
+ transform.hide_value = True
+ modifier = tmap.inputs.new("NodeSocketVector", "Modifier")
+ modifier.hide_value = True
+ for i in range(0, 2):
+ tmap.inputs.new("NodeSocketShader", "%s" % (i + 1))
+ tmap.outputs.new("NodeSocketShader", "Material")
+ tmap.outputs.new("NodeSocketColor", "Color")
+ tree.nodes.active = tmap
+ self.add = False
+ aNode = tree.nodes.active
+ aNode.select = True
+ v2d = context.region.view2d
+ x, y = v2d.region_to_view(self.x, self.y)
+ aNode.location = (x, y)
+
+ def modal(self, context, event):
+ if event.type == "MOUSEMOVE":
+ self.x = event.mouse_region_x
+ self.y = event.mouse_region_y
+ self.execute(context)
+ return {"RUNNING_MODAL"}
+ if event.type == "LEFTMOUSE":
+ return {"FINISHED"}
+ if event.type in ("RIGHTMOUSE", "ESC"):
+ return {"CANCELLED"}
+
+ return {"RUNNING_MODAL"}
+
+ def invoke(self, context, event):
+ context.window_manager.modal_handler_add(self)
+ return {"RUNNING_MODAL"}
+
+
+class UpdatePreviewMaterial(Operator):
+ """Operator update preview material"""
+
+ bl_idname = "node.updatepreview"
+ bl_label = "Update preview"
+
+ def execute(self, context):
+ scene = context.view_layer
+ ob = context.object
+ for obj in scene.objects:
+ if obj != ob:
+ scene.objects.active = ob
+ break
+ scene.objects.active = ob
+
+ def modal(self, context, event):
+ if event.type == "RIGHTMOUSE":
+ self.execute(context)
+ return {"FINISHED"}
+ return {"PASS_THROUGH"}
+
+ def invoke(self, context, event):
+ context.window_manager.modal_handler_add(self)
+ return {"RUNNING_MODAL"}
+
+
+class UpdatePreviewKey(Operator):
+ """Operator update preview keymap"""
+
+ bl_idname = "wm.updatepreviewkey"
+ bl_label = "Activate RMB"
+
+ @classmethod
+ def poll(cls, context):
+ conf = context.window_manager.keyconfigs.active
+ mapstr = "Node Editor"
+ map = conf.keymaps[mapstr]
+ try:
+ map.keymap_items["node.updatepreview"]
+ return False
+ except BaseException as e:
+ print(e.__doc__)
+ print("An exception occurred: {}".format(e))
+ return True
+
+ def execute(self, context):
+ conf = context.window_manager.keyconfigs.active
+ mapstr = "Node Editor"
+ map = conf.keymaps[mapstr]
+ map.keymap_items.new("node.updatepreview", type="RIGHTMOUSE", value="PRESS")
+ return {"FINISHED"}
+
+
+class PovrayShaderNodeCategory(NodeCategory):
+ @classmethod
+ def poll(cls, context):
+ return context.space_data.tree_type == "ObjectNodeTree"
+
+
+class PovrayTextureNodeCategory(NodeCategory):
+ @classmethod
+ def poll(cls, context):
+ return context.space_data.tree_type == "TextureNodeTree"
+
+
+class PovraySceneNodeCategory(NodeCategory):
+ @classmethod
+ def poll(cls, context):
+ return context.space_data.tree_type == "CompositorNodeTree"
+
+
+node_categories = [
+ PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items=[NodeItem("PovrayOutputNode")]),
+ PovrayShaderNodeCategory("SIMPLE", "Simple texture", items=[NodeItem("PovrayTextureNode")]),
+ PovrayShaderNodeCategory(
+ "MAPS",
+ "Maps",
+ items=[
+ NodeItem("PovrayBumpMapNode"),
+ NodeItem("PovrayColorImageNode"),
+ NodeItem("ShaderNormalMapNode"),
+ NodeItem("PovraySlopeNode"),
+ NodeItem("ShaderTextureMapNode"),
+ NodeItem("ShaderNodeValToRGB"),
+ ],
+ ),
+ PovrayShaderNodeCategory(
+ "OTHER",
+ "Other patterns",
+ items=[NodeItem("PovrayImagePatternNode"), NodeItem("ShaderPatternNode")],
+ ),
+ PovrayShaderNodeCategory("COLOR", "Color", items=[NodeItem("PovrayPigmentNode")]),
+ PovrayShaderNodeCategory(
+ "TRANSFORM",
+ "Transform",
+ items=[
+ NodeItem("PovrayMappingNode"),
+ NodeItem("PovrayMultiplyNode"),
+ NodeItem("PovrayModifierNode"),
+ NodeItem("PovrayTransformNode"),
+ NodeItem("PovrayValueNode"),
+ ],
+ ),
+ PovrayShaderNodeCategory(
+ "FINISH",
+ "Finish",
+ items=[
+ NodeItem("PovrayFinishNode"),
+ NodeItem("PovrayDiffuseNode"),
+ NodeItem("PovraySpecularNode"),
+ NodeItem("PovrayPhongNode"),
+ NodeItem("PovrayAmbientNode"),
+ NodeItem("PovrayMirrorNode"),
+ NodeItem("PovrayIridescenceNode"),
+ NodeItem("PovraySubsurfaceNode"),
+ ],
+ ),
+ PovrayShaderNodeCategory(
+ "CYCLES",
+ "Cycles",
+ items=[
+ NodeItem("ShaderNodeAddShader"),
+ NodeItem("ShaderNodeAmbientOcclusion"),
+ NodeItem("ShaderNodeAttribute"),
+ NodeItem("ShaderNodeBackground"),
+ NodeItem("ShaderNodeBlackbody"),
+ NodeItem("ShaderNodeBrightContrast"),
+ NodeItem("ShaderNodeBsdfAnisotropic"),
+ NodeItem("ShaderNodeBsdfDiffuse"),
+ NodeItem("ShaderNodeBsdfGlass"),
+ NodeItem("ShaderNodeBsdfGlossy"),
+ NodeItem("ShaderNodeBsdfHair"),
+ NodeItem("ShaderNodeBsdfRefraction"),
+ NodeItem("ShaderNodeBsdfToon"),
+ NodeItem("ShaderNodeBsdfTranslucent"),
+ NodeItem("ShaderNodeBsdfTransparent"),
+ NodeItem("ShaderNodeBsdfVelvet"),
+ NodeItem("ShaderNodeBump"),
+ NodeItem("ShaderNodeCameraData"),
+ NodeItem("ShaderNodeCombineHSV"),
+ NodeItem("ShaderNodeCombineRGB"),
+ NodeItem("ShaderNodeCombineXYZ"),
+ NodeItem("ShaderNodeEmission"),
+ NodeItem("ShaderNodeExtendedMaterial"),
+ NodeItem("ShaderNodeFresnel"),
+ NodeItem("ShaderNodeGamma"),
+ NodeItem("ShaderNodeGeometry"),
+ NodeItem("ShaderNodeGroup"),
+ NodeItem("ShaderNodeHairInfo"),
+ NodeItem("ShaderNodeHoldout"),
+ NodeItem("ShaderNodeHueSaturation"),
+ NodeItem("ShaderNodeInvert"),
+ NodeItem("ShaderNodeLampData"),
+ NodeItem("ShaderNodeLayerWeight"),
+ NodeItem("ShaderNodeLightFalloff"),
+ NodeItem("ShaderNodeLightPath"),
+ NodeItem("ShaderNodeMapping"),
+ NodeItem("ShaderNodeMaterial"),
+ NodeItem("ShaderNodeMath"),
+ NodeItem("ShaderNodeMixRGB"),
+ NodeItem("ShaderNodeMixShader"),
+ NodeItem("ShaderNodeNewGeometry"),
+ NodeItem("ShaderNodeNormal"),
+ NodeItem("ShaderNodeNormalMap"),
+ NodeItem("ShaderNodeObjectInfo"),
+ NodeItem("ShaderNodeOutput"),
+ NodeItem("ShaderNodeOutputLamp"),
+ NodeItem("ShaderNodeOutputLineStyle"),
+ NodeItem("ShaderNodeOutputMaterial"),
+ NodeItem("ShaderNodeOutputWorld"),
+ NodeItem("ShaderNodeParticleInfo"),
+ NodeItem("ShaderNodeRGB"),
+ NodeItem("ShaderNodeRGBCurve"),
+ NodeItem("ShaderNodeRGBToBW"),
+ NodeItem("ShaderNodeScript"),
+ NodeItem("ShaderNodeSeparateHSV"),
+ NodeItem("ShaderNodeSeparateRGB"),
+ NodeItem("ShaderNodeSeparateXYZ"),
+ NodeItem("ShaderNodeSqueeze"),
+ NodeItem("ShaderNodeSubsurfaceScattering"),
+ NodeItem("ShaderNodeTangent"),
+ NodeItem("ShaderNodeTexBrick"),
+ NodeItem("ShaderNodeTexChecker"),
+ NodeItem("ShaderNodeTexCoord"),
+ NodeItem("ShaderNodeTexEnvironment"),
+ NodeItem("ShaderNodeTexGradient"),
+ NodeItem("ShaderNodeTexImage"),
+ NodeItem("ShaderNodeTexMagic"),
+ NodeItem("ShaderNodeTexMusgrave"),
+ NodeItem("ShaderNodeTexNoise"),
+ NodeItem("ShaderNodeTexPointDensity"),
+ NodeItem("ShaderNodeTexSky"),
+ NodeItem("ShaderNodeTexVoronoi"),
+ NodeItem("ShaderNodeTexWave"),
+ NodeItem("ShaderNodeTexture"),
+ NodeItem("ShaderNodeUVAlongStroke"),
+ NodeItem("ShaderNodeUVMap"),
+ NodeItem("ShaderNodeValToRGB"),
+ NodeItem("ShaderNodeValue"),
+ NodeItem("ShaderNodeVectorCurve"),
+ NodeItem("ShaderNodeVectorMath"),
+ NodeItem("ShaderNodeVectorTransform"),
+ NodeItem("ShaderNodeVolumeAbsorption"),
+ NodeItem("ShaderNodeVolumeScatter"),
+ NodeItem("ShaderNodeWavelength"),
+ NodeItem("ShaderNodeWireframe"),
+ ],
+ ),
+ PovrayTextureNodeCategory(
+ "TEXTUREOUTPUT",
+ "Output",
+ items=[NodeItem("TextureNodeValToRGB"), NodeItem("TextureOutputNode")],
+ ),
+ PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items=[NodeItem("IsoPropsNode")]),
+ PovraySceneNodeCategory("FOG", "Fog", items=[NodeItem("PovrayFogNode")]),
+]
+# -------- end nodes init
+
+
+classes = (
+ ObjectNodeTree,
+ PovraySocketUniversal,
+ PovraySocketFloat_0_1,
+ PovraySocketFloat_0_10,
+ PovraySocketFloat_10,
+ PovraySocketFloatPositive,
+ PovraySocketFloat_000001_10,
+ PovraySocketFloatUnlimited,
+ PovraySocketInt_1_9,
+ PovraySocketInt_0_256,
+ PovraySocketPattern,
+ PovraySocketColor,
+ PovraySocketColorRGBFT,
+ PovraySocketTexture,
+ PovraySocketTransform,
+ PovraySocketNormal,
+ PovraySocketSlope,
+ PovraySocketMap,
+ # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
+ # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
+ # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
+ PovrayPatternNode,
+ UpdatePreviewMaterial,
+ UpdatePreviewKey,
+)
+
+
+def register():
+ nodeitems_utils.register_node_categories("POVRAYNODES", node_categories)
+ for cls in classes:
+ register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
+ nodeitems_utils.unregister_node_categories("POVRAYNODES")
diff --git a/render_povray/object_curve_topology.py b/render_povray/object_curve_topology.py
deleted file mode 100755
index 2be6c3de..00000000
--- a/render_povray/object_curve_topology.py
+++ /dev/null
@@ -1,971 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-
-"""Translate to POV the control point compounded geometries like polygon
-
-meshes or curve based shapes.
-"""
-
-import bpy
-
-from .shading import write_object_material_interior
-
-# -------- LOFT, ETC.
-
-
-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
-
- # name = string_strip_hyphen(bpy.path.clean_name(name_orig)) # XXX Unused, check instantiation
- dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
-
- # matrix = global_matrix @ ob.matrix_world # XXX Unused, check instantiation
- bezier_sweep = False
- if ob.pov.curveshape == 'sphere_sweep':
- # TODO: Check radius ; shorten lines, may use tab_write() ? > fstrings since py 2.9
- # inlined spheresweep macro, which itself calls Shapes.inc:
- file.write(' #include "shapes.inc"\n')
-
- file.write(
- ' #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n'
- )
- file.write(' //input adjusting and inspection\n')
- file.write(' #if(_resolution <= 1)\n')
- file.write(' #local res = 1;\n')
- file.write(' #else\n')
- file.write(' #local res = int(_resolution);\n')
- file.write(' #end\n')
- file.write(' #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n')
- file.write(' #error ""\n')
- file.write(
- ' #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n'
- )
- file.write(' #error ""\n')
- file.write(
- ' #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n'
- )
- file.write(' #error ""\n')
- file.write(' #else\n')
- file.write(' #local n_of_seg = div(dimension_size(_points_array,1), 4);\n')
- file.write(' #local ctrl_pts_array = array[n_of_seg]\n')
- file.write(' #local ctrl_rs_array = array[n_of_seg]\n')
- file.write(' #for(i, 0, n_of_seg-1)\n')
- file.write(
- ' #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n'
- )
- file.write(
- ' #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n'
- )
- file.write(' #end\n')
- file.write(' #end\n')
-
- file.write(' //drawing\n')
- file.write(' #local mockup1 =\n')
- file.write(' #if(_merge_shape) merge{ #else union{ #end\n')
- file.write(' #for(i, 0, n_of_seg-1)\n')
- file.write(' #local has_head = true;\n')
- file.write(' #if(i = 0)\n')
- file.write(
- ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n'
- )
- file.write(' #local has_head = false;\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(
- ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n'
- )
- file.write(' #local has_head = false;\n')
- file.write(' #end\n')
- file.write(' #end\n')
- file.write(' #if(has_head = true)\n')
- file.write(' sphere{\n')
- file.write(' ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n')
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' #local para_t = (1/2)/res;\n')
- file.write(
- ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
- )
- file.write(
- ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
- )
- file.write(
- ' #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n'
- )
- file.write(' object{\n')
- file.write(
- ' Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n'
- )
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' sphere{\n')
- file.write(' this_point, this_radius\n')
- file.write(' }\n')
- file.write(' #for(j, 1, res-1)\n')
- file.write(' #local last_point = this_point;\n')
- file.write(' #local last_radius = this_radius;\n')
- file.write(' #local para_t = (1/2+j)/res;\n')
- file.write(
- ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
- )
- file.write(
- ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
- )
- file.write(
- ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
- )
- file.write(' object{\n')
- file.write(
- ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
- )
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' sphere{\n')
- file.write(' this_point, this_radius\n')
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' #local last_point = this_point;\n')
- file.write(' #local last_radius = this_radius;\n')
- file.write(' #local this_point = ctrl_pts_array[i][3];\n')
- file.write(' #local this_radius = ctrl_rs_array[i][3];\n')
- file.write(
- ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
- )
- file.write(' object{\n')
- file.write(
- ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
- )
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' sphere{\n')
- file.write(' this_point, this_radius\n')
- file.write(' }\n')
- file.write(' #end\n')
- file.write(' }\n')
- file.write(' mockup1\n')
- file.write(' #end\n')
-
- for spl in ob.data.splines:
- if spl.type == "BEZIER":
- bezier_sweep = True
- if ob.pov.curveshape in {'loft', 'birail'}:
- n = 0
- for spline in ob.data.splines:
- n += 1
- tab_write('#declare %s%s=spline {\n' % (dataname, n))
- tab_write('cubic_spline\n')
- lp = len(spline.points)
- delta = 1 / lp
- d = -delta
- point = spline.points[lp - 1]
- x, y, z, w = point.co[:]
- tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
- d += delta
- for point in spline.points:
- x, y, z, w = point.co[:]
- tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
- d += delta
- for i in range(2):
- point = spline.points[i]
- x, y, z, w = point.co[:]
- tab_write('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
- d += delta
- tab_write('}\n')
- if ob.pov.curveshape in {'loft'}:
- n = len(ob.data.splines)
- tab_write('#declare %s = array[%s]{\n' % (dataname, (n + 3)))
- tab_write('spline{%s%s},\n' % (dataname, n))
- for i in range(n):
- tab_write('spline{%s%s},\n' % (dataname, (i + 1)))
- tab_write('spline{%s1},\n' % dataname)
- tab_write('spline{%s2}\n' % dataname)
- tab_write('}\n')
- # Use some of the Meshmaker.inc macro, here inlined
- file.write('#macro CheckFileName(FileName)\n')
- file.write(' #local Len=strlen(FileName);\n')
- file.write(' #if(Len>0)\n')
- file.write(' #if(file_exists(FileName))\n')
- file.write(' #if(Len>=4)\n')
- file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n')
- file.write(
- ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
- )
- file.write(' #local Return=99;\n')
- file.write(' #else\n')
- file.write(' #local Return=0;\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(' #local Return=0;\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(' #if(Len>=4)\n')
- file.write(' #local Ext=strlwr(substr(FileName,Len-3,4))\n')
- file.write(
- ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
- )
- file.write(' #if (strcmp(Ext,".obj")=0)\n')
- file.write(' #local Return=2;\n')
- file.write(' #end\n')
- file.write(' #if (strcmp(Ext,".pcm")=0)\n')
- file.write(' #local Return=3;\n')
- file.write(' #end\n')
- file.write(' #if (strcmp(Ext,".arr")=0)\n')
- file.write(' #local Return=4;\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(' #local Return=1;\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(' #local Return=1;\n')
- file.write(' #end\n')
- file.write(' #end\n')
- file.write(' #else\n')
- file.write(' #local Return=1;\n')
- file.write(' #end\n')
- file.write(' (Return)\n')
- file.write('#end\n')
-
- file.write('#macro BuildSpline(Arr, SplType)\n')
- file.write(' #local Ds=dimension_size(Arr,1);\n')
- file.write(' #local Asc=asc(strupr(SplType));\n')
- file.write(' #if(Asc!=67 & Asc!=76 & Asc!=81) \n')
- file.write(' #local Asc=76;\n')
- file.write(
- ' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n'
- )
- file.write(' #end\n')
- file.write(' spline {\n')
- file.write(' #switch (Asc)\n')
- file.write(' #case (67) //C cubic_spline\n')
- file.write(' cubic_spline\n')
- file.write(' #break\n')
- file.write(' #case (76) //L linear_spline\n')
- file.write(' linear_spline\n')
- file.write(' #break\n')
- file.write(' #case (78) //N linear_spline\n')
- file.write(' natural_spline\n')
- file.write(' #break\n')
- file.write(' #case (81) //Q Quadratic_spline\n')
- file.write(' quadratic_spline\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' #local Add=1/((Ds-2)-1);\n')
- file.write(' #local J=0-Add;\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<Ds)\n')
- file.write(' J\n')
- file.write(' Arr[I]\n')
- file.write(' #local I=I+1;\n')
- file.write(' #local J=J+Add;\n')
- file.write(' #end\n')
- file.write(' }\n')
- file.write('#end\n')
-
- file.write('#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n')
- # suppressed some file checking from original macro because no more separate files
- file.write(' #local Write=0;\n')
- file.write(' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n')
- file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
- file.write(' #switch (Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' " vertex_vectors {\\n",\n')
- file.write(' " ", str(NumVertices,0,0),"\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' str(2*NumVertices,0,0),",\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' mesh2 {\n')
- file.write(' vertex_vectors {\n')
- file.write(' NumVertices\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<NumVertices)\n')
- file.write(' VecArr[I]\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile, VecArr[I])\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(
- ' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n'
- )
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile, VecArr[I])\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' #local I=I+1;\n')
- file.write(' #if(Write=1 | Write=4)\n')
- file.write(' #if(mod(I,3)=0)\n')
- file.write(' #write(MeshFile,"\\n ")\n')
- file.write(' #end\n')
- file.write(' #end \n')
- file.write(' #end\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile,"\\n }\\n")\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(MeshFile,"\\n")\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' // do nothing\n')
- file.write(' #break\n')
- file.write(' #case(4) \n')
- file.write(' #write(MeshFile,"\\n}\\n")\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' }\n')
-
- file.write(' #debug concat(" - normal_vectors\\n") \n')
- file.write(' #local NumVertices=dimension_size(NormArr,1);\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' " normal_vectors {\\n",\n')
- file.write(' " ", str(NumVertices,0,0),"\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "# Normals: ",str(NumVertices,0,0),"\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' // do nothing\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(
- ' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
- )
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' normal_vectors {\n')
- file.write(' NumVertices\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<NumVertices)\n')
- file.write(' NormArr[I]\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile NormArr[I])\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(
- ' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n'
- )
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile NormArr[I])\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' #local I=I+1;\n')
- file.write(' #if(Write=1 | Write=4) \n')
- file.write(' #if(mod(I,3)=0)\n')
- file.write(' #write(MeshFile,"\\n ")\n')
- file.write(' #end\n')
- file.write(' #end\n')
- file.write(' #end\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile,"\\n }\\n")\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(MeshFile,"\\n")\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' //do nothing\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile,"\\n}\\n")\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' }\n')
-
- file.write(' #debug concat(" - uv_vectors\\n") \n')
- file.write(' #local NumVertices=dimension_size(UVArr,1);\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(\n')
- file.write(' MeshFile, \n')
- file.write(' " uv_vectors {\\n",\n')
- file.write(' " ", str(NumVertices,0,0),"\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' // do nothing, *.pcm does not support uv-vectors\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' uv_vectors {\n')
- file.write(' NumVertices\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<NumVertices)\n')
- file.write(' UVArr[I]\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile UVArr[I])\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' //do nothing\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile UVArr[I])\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' #local I=I+1; \n')
- file.write(' #if(Write=1 | Write=4)\n')
- file.write(' #if(mod(I,3)=0)\n')
- file.write(' #write(MeshFile,"\\n ")\n')
- file.write(' #end \n')
- file.write(' #end\n')
- file.write(' #end \n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile,"\\n }\\n")\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(MeshFile,"\\n")\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' //do nothing\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile,"\\n}\\n")\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' }\n')
- file.write('\n')
- file.write(' #debug concat(" - face_indices\\n") \n')
- file.write(' #declare NumFaces=U*V*2;\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' " face_indices {\\n"\n')
- file.write(' " ", str(NumFaces,0,0),"\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write (\n')
- file.write(' MeshFile,\n')
- file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #write (\n')
- file.write(' MeshFile,\n')
- file.write(' "0,",str(NumFaces,0,0),",\\n"\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' face_indices {\n')
- file.write(' NumFaces\n')
- file.write(' #local I=0;\n')
- file.write(' #local H=0;\n')
- file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
- file.write(' #while (I<V)\n')
- file.write(' #local J=0;\n')
- file.write(' #while (J<U)\n')
- file.write(' #local Ind=(I*U)+I+J;\n')
- file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(
- ' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n'
- )
- file.write(
- ' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n'
- )
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(
- ' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
- )
- file.write(
- ' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
- )
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(\n')
- file.write(' MeshFile,\n')
- file.write(' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n')
- file.write(' )\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' #local J=J+1;\n')
- file.write(' #local H=H+1;\n')
- file.write(' #if(Write=1 | Write=4)\n')
- file.write(' #if(mod(H,3)=0)\n')
- file.write(' #write(MeshFile,"\\n ")\n')
- file.write(' #end \n')
- file.write(' #end\n')
- file.write(' #end\n')
- file.write(' #local I=I+1;\n')
- file.write(' #end\n')
- file.write(' }\n')
- file.write(' #switch(Write)\n')
- file.write(' #case(1)\n')
- file.write(' #write(MeshFile, "\\n }\\n}")\n')
- file.write(' #fclose MeshFile\n')
- file.write(' #debug concat(" Done writing\\n")\n')
- file.write(' #break\n')
- file.write(' #case(2)\n')
- file.write(' #fclose MeshFile\n')
- file.write(' #debug concat(" Done writing\\n")\n')
- file.write(' #break\n')
- file.write(' #case(3)\n')
- file.write(' #fclose MeshFile\n')
- file.write(' #debug concat(" Done writing\\n")\n')
- file.write(' #break\n')
- file.write(' #case(4)\n')
- file.write(' #write(MeshFile, "\\n}\\n}")\n')
- file.write(' #fclose MeshFile\n')
- file.write(' #debug concat(" Done writing\\n")\n')
- file.write(' #break\n')
- file.write(' #end\n')
- file.write(' }\n')
- file.write('#end\n')
-
- file.write('#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n')
- file.write(' #declare Build=CheckFileName(FileName);\n')
- file.write(' #if(Build=0)\n')
- file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
- file.write(' #include FileName\n')
- file.write(' object{Surface}\n')
- file.write(' #else\n')
- file.write(' #local NumVertices=(SplRes+1)*(InterpRes+1);\n')
- file.write(' #local NumFaces=SplRes*InterpRes*2;\n')
- file.write(
- ' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n'
- )
- file.write(' #local VecArr=array[NumVertices]\n')
- file.write(' #local NormArr=array[NumVertices]\n')
- file.write(' #local UVArr=array[NumVertices]\n')
- file.write(' #local N=dimension_size(SplineArray,1);\n')
- file.write(' #local TempSplArr0=array[N];\n')
- file.write(' #local TempSplArr1=array[N];\n')
- file.write(' #local TempSplArr2=array[N];\n')
- file.write(' #local PosStep=1/SplRes;\n')
- file.write(' #local InterpStep=1/InterpRes;\n')
- file.write(' #local Count=0;\n')
- file.write(' #local Pos=0;\n')
- file.write(' #while(Pos<=1)\n')
- file.write(' #local I=0;\n')
- file.write(' #if (Pos=0)\n')
- file.write(' #while (I<N)\n')
- file.write(' #local Spl=spline{SplineArray[I]}\n')
- file.write(' #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n')
- file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n')
- file.write(' #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n')
- file.write(' #local I=I+1;\n')
- file.write(' #end\n')
- file.write(' #local S0=BuildSpline(TempSplArr0, Interp_type)\n')
- file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n')
- file.write(' #local S2=BuildSpline(TempSplArr2, Interp_type)\n')
- file.write(' #else\n')
- file.write(' #while (I<N)\n')
- file.write(' #local Spl=spline{SplineArray[I]}\n')
- file.write(' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n')
- file.write(' #local I=I+1;\n')
- file.write(' #end\n')
- file.write(' #local S1=BuildSpline(TempSplArr1, Interp_type)\n')
- file.write(' #end\n')
- file.write(' #local J=0;\n')
- file.write(' #while (J<=1)\n')
- file.write(' #local P0=<0,0,0>+S0(J);\n')
- file.write(' #local P1=<0,0,0>+S1(J);\n')
- file.write(' #local P2=<0,0,0>+S2(J);\n')
- file.write(' #local P3=<0,0,0>+S0(J+InterpStep);\n')
- file.write(' #local P4=<0,0,0>+S0(J-InterpStep);\n')
- file.write(' #local B1=P4-P0;\n')
- file.write(' #local B2=P2-P0;\n')
- file.write(' #local B3=P3-P0;\n')
- file.write(' #local B4=P1-P0;\n')
- file.write(' #local N1=vcross(B1,B2);\n')
- file.write(' #local N2=vcross(B2,B3);\n')
- file.write(' #local N3=vcross(B3,B4);\n')
- file.write(' #local N4=vcross(B4,B1);\n')
- file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n')
- file.write(' #local VecArr[Count]=P0;\n')
- file.write(' #local NormArr[Count]=Norm;\n')
- file.write(' #local UVArr[Count]=<J,Pos>;\n')
- file.write(' #local J=J+InterpStep;\n')
- file.write(' #local Count=Count+1;\n')
- file.write(' #end\n')
- file.write(' #local S2=spline{S0}\n')
- file.write(' #local S0=spline{S1}\n')
- file.write(
- ' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n'
- )
- file.write(' #local Pos=Pos+PosStep;\n')
- file.write(' #end\n')
- file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n')
- file.write(' #end\n')
- file.write('#end\n\n')
-
- file.write('#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n')
- file.write(' #declare Build=CheckFileName(FileName);\n')
- file.write(' #if(Build=0)\n')
- file.write(' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n')
- file.write(' #include FileName\n')
- file.write(' object{Surface}\n')
- file.write(' #else\n')
- file.write(' #local NumVertices=(Iter_U+1)*(Iter_V+1);\n')
- file.write(' #local NumFaces=Iter_U*Iter_V*2;\n')
- file.write(
- ' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n'
- )
- file.write(' #declare VecArr=array[NumVertices] \n')
- file.write(' #declare NormArr=array[NumVertices] \n')
- file.write(' #local UVArr=array[NumVertices] \n')
- file.write(' #local Spl1_0=Spl1(0);\n')
- file.write(' #local Spl2_0=Spl2(0);\n')
- file.write(' #local Spl3_0=Spl3(0);\n')
- file.write(' #local Spl4_0=Spl4(0);\n')
- file.write(' #local UStep=1/Iter_U;\n')
- file.write(' #local VStep=1/Iter_V;\n')
- file.write(' #local Count=0;\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<=1)\n')
- file.write(' #local Im=1-I;\n')
- file.write(' #local J=0;\n')
- file.write(' #while (J<=1)\n')
- file.write(' #local Jm=1-J;\n')
- file.write(
- ' #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n'
- )
- file.write(' #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n')
- file.write(' LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n')
- file.write(' #declare VecArr[Count]=P0;\n')
- file.write(' #local UVArr[Count]=<J,I>;\n')
- file.write(' #local J=J+UStep;\n')
- file.write(' #local Count=Count+1;\n')
- file.write(' #end\n')
- file.write(' #debug concat(\n')
- file.write(' "\r Done ", str(Count,0,0)," vertices : ",\n')
- file.write(' str(100*Count/NumVertices,0,2)," %"\n')
- file.write(' )\n')
- file.write(' #local I=I+VStep;\n')
- file.write(' #end\n')
- file.write(' #debug "\r Normals "\n')
- file.write(' #local Count=0;\n')
- file.write(' #local I=0;\n')
- file.write(' #while (I<=Iter_V)\n')
- file.write(' #local J=0;\n')
- file.write(' #while (J<=Iter_U)\n')
- file.write(' #local Ind=(I*Iter_U)+I+J;\n')
- file.write(' #local P0=VecArr[Ind];\n')
- file.write(' #if(J=0)\n')
- file.write(' #local P1=P0+(P0-VecArr[Ind+1]);\n')
- file.write(' #else\n')
- file.write(' #local P1=VecArr[Ind-1];\n')
- file.write(' #end\n')
- file.write(' #if (J=Iter_U)\n')
- file.write(' #local P2=P0+(P0-VecArr[Ind-1]);\n')
- file.write(' #else\n')
- file.write(' #local P2=VecArr[Ind+1];\n')
- file.write(' #end\n')
- file.write(' #if (I=0)\n')
- file.write(' #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n')
- file.write(' #else\n')
- file.write(' #local P3=VecArr[Ind-Iter_U-1];\n')
- file.write(' #end\n')
- file.write(' #if (I=Iter_V)\n')
- file.write(' #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n')
- file.write(' #else\n')
- file.write(' #local P4=VecArr[Ind+Iter_U+1];\n')
- file.write(' #end\n')
- file.write(' #local B1=P4-P0;\n')
- file.write(' #local B2=P2-P0;\n')
- file.write(' #local B3=P3-P0;\n')
- file.write(' #local B4=P1-P0;\n')
- file.write(' #local N1=vcross(B1,B2);\n')
- file.write(' #local N2=vcross(B2,B3);\n')
- file.write(' #local N3=vcross(B3,B4);\n')
- file.write(' #local N4=vcross(B4,B1);\n')
- file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n')
- file.write(' #declare NormArr[Count]=Norm;\n')
- file.write(' #local J=J+1;\n')
- file.write(' #local Count=Count+1;\n')
- file.write(' #end\n')
- file.write(
- ' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n'
- )
- file.write(' #local I=I+1;\n')
- file.write(' #end\n')
- file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n')
- file.write(' #end\n')
- file.write('#end\n\n')
- # Empty curves
- if len(ob.data.splines) == 0:
- tab_write("\n//dummy sphere to represent empty curve location\n")
- tab_write("#declare %s =\n" % dataname)
- tab_write(
- "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} "
- "no_image no_reflection no_radiosity "
- "photons{pass_through collect off} hollow}\n\n"
- % (ob.location.x, ob.location.y, ob.location.z)
- ) # ob.name > povdataname)
- # And non empty curves
- else:
- if not bezier_sweep:
- tab_write("#declare %s =\n" % dataname)
- if ob.pov.curveshape == 'sphere_sweep' and not bezier_sweep:
- tab_write("union {\n")
- for spl in ob.data.splines:
- if spl.type != "BEZIER":
- spl_type = "linear"
- if spl.type == "NURBS":
- spl_type = "cubic"
- points = spl.points
- num_points = len(points)
- if spl.use_cyclic_u:
- num_points += 3
-
- tab_write("sphere_sweep { %s_spline %s,\n" % (spl_type, num_points))
- if spl.use_cyclic_u:
- pt1 = points[len(points) - 1]
- wpt1 = pt1.co
- tab_write(
- "<%.4g,%.4g,%.4g>,%.4g\n"
- % (wpt1[0], wpt1[1], wpt1[2], pt1.radius * ob.data.bevel_depth)
- )
- for pt in points:
- wpt = pt.co
- tab_write(
- "<%.4g,%.4g,%.4g>,%.4g\n"
- % (wpt[0], wpt[1], wpt[2], pt.radius * ob.data.bevel_depth)
- )
- if spl.use_cyclic_u:
- for i in range(0, 2):
- end_pt = points[i]
- wpt = end_pt.co
- tab_write(
- "<%.4g,%.4g,%.4g>,%.4g\n"
- % (wpt[0], wpt[1], wpt[2], end_pt.radius * ob.data.bevel_depth)
- )
-
- tab_write("}\n")
- # below not used yet?
- if ob.pov.curveshape == 'sor':
- for spl in ob.data.splines:
- if spl.type in {'POLY', 'NURBS'}:
- points = spl.points
- num_points = len(points)
- tab_write("sor { %s,\n" % num_points)
- for pt in points:
- wpt = pt.co
- tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
- else:
- tab_write("box { 0,0\n")
- if ob.pov.curveshape in {'lathe', 'prism'}:
- spl = ob.data.splines[0]
- if spl.type == "BEZIER":
- points = spl.bezier_points
- len_cur = len(points) - 1
- len_pts = len_cur * 4
- ifprism = ''
- if ob.pov.curveshape in {'prism'}:
- height = ob.data.extrude
- ifprism = '-%s, %s,' % (height, height)
- len_cur += 1
- len_pts += 4
- tab_write("%s { bezier_spline %s %s,\n" % (ob.pov.curveshape, ifprism, len_pts))
- for i in range(0, len_cur):
- p1 = points[i].co
- pR = points[i].handle_right
- end = i + 1
- if i == len_cur - 1 and ob.pov.curveshape in {'prism'}:
- end = 0
- pL = points[end].handle_left
- p2 = points[end].co
- line = "<%.4g,%.4g>" % (p1[0], p1[1])
- line += "<%.4g,%.4g>" % (pR[0], pR[1])
- line += "<%.4g,%.4g>" % (pL[0], pL[1])
- line += "<%.4g,%.4g>" % (p2[0], p2[1])
- tab_write("%s\n" % line)
- else:
- points = spl.points
- len_cur = len(points)
- len_pts = len_cur
- ifprism = ''
- if ob.pov.curveshape in {'prism'}:
- height = ob.data.extrude
- ifprism = '-%s, %s,' % (height, height)
- len_pts += 3
- spl_type = 'quadratic'
- if spl.type == 'POLY':
- spl_type = 'linear'
- tab_write(
- "%s { %s_spline %s %s,\n" % (ob.pov.curveshape, spl_type, ifprism, len_pts)
- )
- if ob.pov.curveshape in {'prism'}:
- pt = points[len(points) - 1]
- wpt = pt.co
- tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
- for pt in points:
- wpt = pt.co
- tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
- if ob.pov.curveshape in {'prism'}:
- for i in range(2):
- pt = points[i]
- wpt = pt.co
- tab_write("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
- if bezier_sweep:
- for p in range(len(ob.data.splines)):
- br = []
- depth = ob.data.bevel_depth
- spl = ob.data.splines[p]
- points = spl.bezier_points
- len_cur = len(points) - 1
- num_points = len_cur * 4
- if spl.use_cyclic_u:
- len_cur += 1
- num_points += 4
- tab_write("#declare %s_points_%s = array[%s]{\n" % (dataname, p, num_points))
- for i in range(len_cur):
- p1 = points[i].co
- pR = points[i].handle_right
- end = i + 1
- if spl.use_cyclic_u and i == (len_cur - 1):
- end = 0
- pL = points[end].handle_left
- p2 = points[end].co
- r3 = points[end].radius * depth
- r0 = points[i].radius * depth
- r1 = 2 / 3 * r0 + 1 / 3 * r3
- r2 = 1 / 3 * r0 + 2 / 3 * r3
- br.append((r0, r1, r2, r3))
- line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2])
- line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2])
- line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2])
- line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2])
- tab_write("%s\n" % line)
- tab_write("}\n")
- tab_write("#declare %s_radii_%s = array[%s]{\n" % (dataname, p, len(br) * 4))
- for rad_tuple in br:
- tab_write(
- '%.4f,%.4f,%.4f,%.4f\n'
- % (rad_tuple[0], rad_tuple[1], rad_tuple[2], rad_tuple[3])
- )
- tab_write("}\n")
- if len(ob.data.splines) == 1:
- p = 1
- tab_write('#declare %s = object{\n' % dataname)
- tab_write(
- ' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n'
- % (ob.data.resolution_u, dataname, p, dataname, p)
- )
- else:
- tab_write('#declare %s = union{\n' % dataname)
- for p in range(len(ob.data.splines)):
- tab_write(
- ' object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n'
- % (ob.data.resolution_u, dataname, p, dataname, p)
- )
- # tab_write('#include "bezier_spheresweep.inc"\n') #now inlined
- # tab_write('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth))
- if ob.pov.curveshape in {'loft'}:
- tab_write('object {MSM(%s,%s,"c",%s,"")\n' % (dataname, ob.pov.res_u, ob.pov.res_v))
- if ob.pov.curveshape in {'birail'}:
- splines = '%s1,%s2,%s3,%s4' % (dataname, dataname, dataname, dataname)
- tab_write('object {Coons(%s, %s, %s, "")\n' % (splines, ob.pov.res_u, ob.pov.res_v))
- # pov_mat_name = "Default_texture" # XXX! Unused, check instantiation
- 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(ob.data)
- # tab_write("texture {%s}\n"%pov_mat_name)
- if ob.pov.curveshape in {'prism'}:
- tab_write("rotate <90,0,0>\n")
- tab_write("scale y*-1\n")
- tab_write("}\n")
diff --git a/render_povray/object_mesh_topology.py b/render_povray/object_mesh_topology.py
deleted file mode 100755
index cad028a1..00000000
--- a/render_povray/object_mesh_topology.py
+++ /dev/null
@@ -1,1534 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-
-"""Translate to POV the control point compounded geometries like polygon
-
-meshes or curve based shapes."""
-
-# --------
-# -- Faster mesh export ...one day
-# import numpy as np
-# --------
-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 """
- 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 = {}
-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,
-):
- """write all meshes as POV mesh2{} syntax to exported file """
- # # some numpy functions to speed up mesh export NOT IN USE YET
- # # Current 2.93 beta numpy linking has troubles so definitions commented off for now
-
- # # TODO: also write a numpy function to read matrices at object level?
- # # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
- # def read_verts_co(self, mesh):
- # #'float64' would be a slower 64-bit floating-point number numpy datatype
- # # using 'float32' vert coordinates for now until any issue is reported
- # mverts_co = np.zeros((len(mesh.vertices) * 3), dtype=np.float32)
- # mesh.vertices.foreach_get("co", mverts_co)
- # return np.reshape(mverts_co, (len(mesh.vertices), 3))
-
- # def read_verts_idx(self, mesh):
- # mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
- # mesh.vertices.foreach_get("index", mverts_idx)
- # return np.reshape(mverts_idx, (len(mesh.vertices), 1))
-
- # def read_verts_norms(self, mesh):
- # #'float64' would be a slower 64-bit floating-point number numpy datatype
- # # using less accurate 'float16' normals for now until any issue is reported
- # mverts_no = np.zeros((len(mesh.vertices) * 3), dtype=np.float16)
- # mesh.vertices.foreach_get("normal", mverts_no)
- # return np.reshape(mverts_no, (len(mesh.vertices), 3))
-
- # def read_faces_idx(self, mesh):
- # mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
- # mesh.loop_triangles.foreach_get("index", mfaces_idx)
- # return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))
-
- # def read_faces_verts_indices(self, mesh):
- # mfaces_verts_idx = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
- # mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
- # return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))
-
- # # Why is below different from vertex indices?
- # def read_faces_verts_loops(self, mesh):
- # mfaces_verts_loops = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
- # mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
- # return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))
-
- # def read_faces_norms(self, mesh):
- # #'float64' would be a slower 64-bit floating-point number numpy datatype
- # # using less accurate 'float16' normals for now until any issue is reported
- # mfaces_no = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.float16)
- # mesh.loop_triangles.foreach_get("normal", mfaces_no)
- # return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))
-
- # def read_faces_smooth(self, mesh):
- # mfaces_smth = np.zeros((len(mesh.loop_triangles) * 1), dtype=np.bool)
- # mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
- # return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))
-
- # def read_faces_material_indices(self, mesh):
- # mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
- # mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
- # return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))
-
- # obmatslist = []
- # def hasUniqueMaterial():
- # # Grab materials attached to object instances ...
- # if hasattr(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(obj):
- # # Grab materials attached to object instances ...
- # 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.
- # return True
- # return False
- # For objects using local material(s) only!
- # This is a mapping between a tuple (dataname, material_names_dictionary, ...),
- # and the POV dataname.
- # As only objects using:
- # * The same data.
- # * EXACTLY the same materials, in EXACTLY the same sockets.
- # ... can share a same instance in POV export.
- obmats2data = {}
-
- def check_object_materials(obj, obj_name, dataname):
- """Compare other objects exported material slots to avoid rewriting duplicates"""
- if hasattr(obj, 'material_slots'):
- has_local_mats = False
- key = [dataname]
- 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:
- has_local_mats = True
- else:
- # Even if the slot is empty, it is important to grab it...
- key.append("")
- if has_local_mats:
- # If this object uses local material(s), lets find if another object
- # using the same data and exactly the same list of materials
- # (in the same slots) has already been processed...
- # Note that here also, we use object name as new, unique dataname for Pov.
- key = tuple(key) # Lists are not hashable...
- if key not in obmats2data:
- obmats2data[key] = obj_name
- return obmats2data[key]
- return None
-
- data_ref = {}
-
- def store(scene, ob, name, dataname, matrix):
- # The Object needs to be written at least once but if its data is
- # already in data_ref this has already been done.
- # This func returns the "povray" name of the data, or None
- # if no writing is needed.
- if ob.is_modified(scene, 'RENDER'):
- # Data modified.
- # Create unique entry in data_ref by using object name
- # (always unique in Blender) as data name.
- data_ref[name] = [(name, matrix_as_pov_string(matrix))]
- return name
- # Here, we replace dataname by the value returned by check_object_materials, only if
- # it is not evaluated to False (i.e. only if the object uses some local material(s)).
- dataname = check_object_materials(ob, name, dataname) or dataname
- if dataname in data_ref:
- # Data already known, just add the object instance.
- data_ref[dataname].append((name, matrix_as_pov_string(matrix)))
- # No need to write data
- return None
- # Else (no return yet): Data not yet processed, create a new entry in data_ref.
- data_ref[dataname] = [(name, matrix_as_pov_string(matrix))]
- return dataname
-
- ob_num = 0
- depsgraph = bpy.context.evaluated_depsgraph_get()
- for ob in sel:
- # Using depsgraph
- ob = bpy.data.objects[ob.name].evaluated_get(depsgraph)
-
- # subtract original from the count of their instances as were not counted before 2.8
- if not (ob.is_instancer and ob.original != ob):
- ob_num += 1
-
- # XXX I moved all those checks here, as there is no need to compute names
- # for object we won't export here!
- if ob.type in {
- 'LIGHT',
- 'CAMERA', # 'EMPTY', #empties can bear dupligroups
- 'META',
- 'ARMATURE',
- 'LATTICE',
- }:
- continue
- fluid_found = False
- for mod in ob.modifiers:
- if mod and hasattr(mod, 'fluid_type'):
- fluid_found = True
- if mod.fluid_type == 'DOMAIN':
- if mod.domain_settings.domain_type == 'GAS':
- export_smoke(
- file, ob.name, smoke_path, comments, global_matrix, write_matrix
- )
- break # don't render domain mesh, skip to next object.
- if mod.fluid_type == 'FLOW': # The domain contains all the smoke. so that's it.
- if mod.flow_settings.flow_type == 'SMOKE': # Check how liquids behave
- break # don't render smoke flow emitter mesh either, skip to next object.
- if not fluid_found:
- # No fluid found
- if hasattr(ob, 'particle_systems'):
- # 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 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 particle_mod.show_render
- and (p_sys.name == particle_mod.particle_system.name)
- ):
- 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
- # (baking for now or anything else).
- # XXX I don't understand that if we are here, sel if a non-empty iterable,
- # so this condition is always True, IMO -- mont29
- # EMPTY type objects treated a little further below -- MR
-
- # modified elif to if below as non EMPTY objects can also be instancers
- if ob.is_instancer:
- if ob.instance_type == 'COLLECTION':
- name_orig = "OB" + ob.name
- dataname_orig = "DATA" + ob.instance_collection.name
- else:
- # hoping only dupligroups have several source datablocks
- # ob_dupli_list_create(scene) #deprecated in 2.8
- for eachduplicate in depsgraph.object_instances:
- # Real dupli instance filtered because
- # original included in list since 2.8
- if eachduplicate.is_instance:
- dataname_orig = "DATA" + eachduplicate.object.name
- # 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':
- name_orig = "OB" + ob.name
- dataname_orig = "DATA" + ob.name
- else:
- name_orig = DEF_OBJ_NAME
- dataname_orig = DEF_OBJ_NAME
- name = string_strip_hyphen(bpy.path.clean_name(name_orig))
- dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
- # for slot in obj.material_slots:
- # if slot.material is not None and slot.link == 'OBJECT':
- # obmaterial = slot.material
-
- # ------------------------------------------------
-
- if info_callback:
- info_callback("Object %2.d of %2.d (%s)" % (ob_num, len(sel), ob.name))
-
- me = ob.data
-
- matrix = global_matrix @ ob.matrix_world
- povdataname = store(scene, ob, name, dataname, matrix)
- if povdataname is None:
- print("This is an instance of " + name)
- continue
-
- print("Writing Down First Occurrence of " + name)
-
- # ------------ 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'}
- ):
- continue # Don't render proxy mesh, skip to next object
- # pov_mat_name = "Default_texture" # Not used...remove?
-
- # 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 matrix and just a comment
- # with empty object transforms ?
- tab_write("\n//dummy sphere to represent Empty location\n")
- tab_write(
- "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
- % povdataname
- )
- continue # Don't render empty object but this is later addition, watch it.
-
- ob_eval = ob # not sure this is needed in case to_mesh_clear could damage obj ?
- try:
- me = ob_eval.to_mesh()
-
- # Here identify the exception for mesh object with no data: Runtime-Error ?
- # So we can write something for the dataname or maybe treated "if not me" below
- except BaseException as e:
- print(e.__doc__)
- print('An exception occurred: {}'.format(e))
- # also happens when curves cant be made into meshes because of no-data
- continue
-
- importance = ob.pov.importance_value
- if me:
- me.calc_loop_triangles()
- me_materials = me.materials
- me_faces = me.loop_triangles[:]
- # --- numpytest
- # me_looptris = me.loops
-
- # Below otypes = ['int32'] is a 32-bit signed integer number numpy datatype
- # get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
- # faces_verts_idx = get_v_index(me_looptris)
-
- # if len(me_faces)==0:
- # tab_write("\n//dummy sphere to represent empty mesh location\n")
- # tab_write("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)
-
- if not me or not me_faces:
- tab_write("\n//dummy sphere to represent empty mesh location\n")
- tab_write(
- "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
- % povdataname
- )
- continue
-
- uv_layers = me.uv_layers
- if len(uv_layers) > 0:
- if me.uv_layers.active and uv_layers.active.data:
- uv_layer = uv_layers.active.data
- else:
- uv_layer = None
-
- try:
- # vcol_layer = me.vertex_colors.active.data
- vcol_layer = me.vertex_colors.active.data
- except AttributeError:
- vcol_layer = None
-
- faces_verts = [f.vertices[:] for f in me_faces]
- faces_normals = [f.normal[:] for f in me_faces]
- verts_normals = [v.normal[:] for v in me.vertices]
-
- # Use named declaration to allow reference e.g. for baking. MR
- file.write("\n")
- tab_write("#declare %s =\n" % povdataname)
- tab_write("mesh2 {\n")
- tab_write("vertex_vectors {\n")
- tab_write("%d" % len(me.vertices)) # vert count
-
- tab_str = tab * tab_level
- for v in me.vertices:
- if linebreaksinlists:
- file.write(",\n")
- file.write(tab_str + "<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
- else:
- file.write(", ")
- file.write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
- # tab_write("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
- file.write("\n")
- tab_write("}\n")
-
- # Build unique Normal list
- uniqueNormals = {}
- for fi, f in enumerate(me_faces):
- fv = faces_verts[fi]
- # [-1] is a dummy index, use a list so we can modify in place
- if f.use_smooth: # Use vertex normals
- for v in fv:
- key = verts_normals[v]
- uniqueNormals[key] = [-1]
- else: # Use face normal
- key = faces_normals[fi]
- uniqueNormals[key] = [-1]
-
- tab_write("normal_vectors {\n")
- tab_write("%d" % len(uniqueNormals)) # vert count
- idx = 0
- tab_str = tab * tab_level
- for no, index in uniqueNormals.items():
- if linebreaksinlists:
- file.write(",\n")
- file.write(tab_str + "<%.6f, %.6f, %.6f>" % no) # vert count
- else:
- file.write(", ")
- file.write("<%.6f, %.6f, %.6f>" % no) # vert count
- index[0] = idx
- idx += 1
- file.write("\n")
- tab_write("}\n")
-
- # Vertex colors
- vertCols = {} # Use for material colors also.
-
- if uv_layer:
- # Generate unique UV's
- uniqueUVs = {}
- # n = 0
- for f in me_faces: # me.faces in 2.7
- uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
-
- for uv in uvs:
- uniqueUVs[uv[:]] = [-1]
-
- tab_write("uv_vectors {\n")
- # print unique_uvs
- tab_write("%d" % len(uniqueUVs)) # vert count
- idx = 0
- tab_str = tab * tab_level
- for uv, index in uniqueUVs.items():
- if linebreaksinlists:
- file.write(",\n")
- file.write(tab_str + "<%.6f, %.6f>" % uv)
- else:
- file.write(", ")
- file.write("<%.6f, %.6f>" % uv)
- index[0] = idx
- idx += 1
- '''
- else:
- # Just add 1 dummy vector, no real UV's
- tab_write('1') # vert count
- file.write(',\n\t\t<0.0, 0.0>')
- '''
- file.write("\n")
- tab_write("}\n")
-
- if me.vertex_colors:
- # Write down vertex colors as a texture for each vertex
- tab_write("texture_list {\n")
- tab_write("%d\n" % (len(me_faces) * 3)) # assumes we have only triangles
- VcolIdx = 0
- if comments:
- file.write(
- "\n //Vertex colors: one simple pigment texture per vertex\n"
- )
- for fi, f in enumerate(me_faces):
- # annoying, index may be invalid
- material_index = f.material_index
- try:
- material = me_materials[material_index]
- except BaseException as e:
- print(e.__doc__)
- print('An exception occurred: {}'.format(e))
- material = None
- if (
- material
- ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now
-
- cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
-
- for col in cols:
- key = (
- col[0],
- col[1],
- col[2],
- material_index,
- ) # Material index!
- VcolIdx += 1
- vertCols[key] = [VcolIdx]
- if linebreaksinlists:
- tab_write(
- "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
- % (col[0], col[1], col[2])
- )
- else:
- tab_write(
- "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
- % (col[0], col[1], col[2])
- )
- tab_str = tab * tab_level
- else:
- if material:
- # Multiply diffuse with SSS Color
- if material.pov_subsurface_scattering.use:
- diffuse_color = [
- i * j
- for i, j in zip(
- material.pov_subsurface_scattering.color[:],
- material.diffuse_color[:],
- )
- ]
- key = (
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- material_index,
- )
- vertCols[key] = [-1]
- else:
- diffuse_color = material.diffuse_color[:]
- key = (
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- material_index,
- )
- vertCols[key] = [-1]
-
- tab_write("\n}\n")
- # Face indices
- tab_write("\nface_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
-
- for fi, f in enumerate(me_faces):
- fv = faces_verts[fi]
- material_index = f.material_index
-
- if vcol_layer:
- cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
-
- if (
- not me_materials or me_materials[material_index] is None
- ): # No materials
- if linebreaksinlists:
- file.write(",\n")
- # vert count
- file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
- else:
- file.write(", ")
- file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
- else:
- material = me_materials[material_index]
- if me.vertex_colors: # and material.use_vertex_color_paint:
- # Color per vertex - vertex color
-
- col1 = cols[0]
- col2 = cols[1]
- col3 = cols[2]
-
- ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
- ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
- ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
- else:
- # Color per material - flat material color
- if material.pov_subsurface_scattering.use:
- diffuse_color = [
- i * j
- for i, j in zip(
- material.pov_subsurface_scattering.color[:],
- material.diffuse_color[:],
- )
- ]
- else:
- diffuse_color = material.diffuse_color[:]
- ci1 = ci2 = ci3 = vertCols[
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- f.material_index,
- ][0]
- # ci are zero based index so we'll subtract 1 from them
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>, %d,%d,%d"
- % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
- ) # vert count
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>, %d,%d,%d"
- % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
- ) # vert count
-
- file.write("\n")
- tab_write("}\n")
-
- # normal_indices indices
- tab_write("normal_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
- for fi, fv in enumerate(faces_verts):
-
- if me_faces[fi].use_smooth:
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>"
- % (
- uniqueNormals[verts_normals[fv[0]]][0],
- uniqueNormals[verts_normals[fv[1]]][0],
- uniqueNormals[verts_normals[fv[2]]][0],
- )
- ) # vert count
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>"
- % (
- uniqueNormals[verts_normals[fv[0]]][0],
- uniqueNormals[verts_normals[fv[1]]][0],
- uniqueNormals[verts_normals[fv[2]]][0],
- )
- ) # vert count
- else:
- idx = uniqueNormals[faces_normals[fi]][0]
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str + "<%d,%d,%d>" % (idx, idx, idx)
- ) # vert count
- else:
- file.write(", ")
- file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
-
- file.write("\n")
- tab_write("}\n")
-
- if uv_layer:
- tab_write("uv_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
- for f in me_faces:
- uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
-
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>"
- % (
- uniqueUVs[uvs[0]][0],
- uniqueUVs[uvs[1]][0],
- uniqueUVs[uvs[2]][0],
- )
- )
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>"
- % (
- uniqueUVs[uvs[0]][0],
- uniqueUVs[uvs[1]][0],
- uniqueUVs[uvs[2]][0],
- )
- )
-
- file.write("\n")
- tab_write("}\n")
-
- # XXX BOOLEAN
- write_object_csg_inside_vector(ob, file)
-
- if me.materials:
- try:
- material = me.materials[0] # dodgy
- write_object_material_interior(material, ob, tab_write)
- except IndexError:
- print(me)
-
- # POV object modifiers such as
- # hollow / sturm / double_illuminate etc.
- write_object_modifiers(ob, file)
-
- # Importance for radiosity sampling added here:
- tab_write("radiosity { \n")
- tab_write("importance %3g \n" % importance)
- tab_write("}\n")
-
- tab_write("}\n") # End of mesh block
- else:
- facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!!
- if me_materials:
- for f in me_faces:
- if f.material_index not in facesMaterials:
- facesMaterials.append(f.material_index)
- # No vertex colors, so write material colors as vertex colors
- for i, material in enumerate(me_materials):
-
- if (
- material and material.pov.material_use_nodes is False
- ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # Multiply diffuse with SSS Color
- if material.pov_subsurface_scattering.use:
- diffuse_color = [
- i * j
- for i, j in zip(
- material.pov_subsurface_scattering.color[:],
- material.diffuse_color[:],
- )
- ]
- key = (
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- i,
- ) # i == f.mat
- vertCols[key] = [-1]
- else:
- diffuse_color = material.diffuse_color[:]
- key = (
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- i,
- ) # i == f.mat
- vertCols[key] = [-1]
-
- idx = 0
- local_material_names = [] # XXX track and revert
- material_finish = None
- for col, index in vertCols.items():
- # if me_materials:
- mater = me_materials[col[3]]
- if me_materials is not None:
- texturing.write_texture_influence(
- using_uberpov,
- mater,
- material_names_dictionary,
- local_material_names,
- path_image,
- exported_lights_count,
- image_format,
- img_map,
- img_map_transforms,
- tab_write,
- comments,
- string_strip_hyphen,
- safety,
- col,
- preview_dir,
- unpacked_images,
- )
- # ------------------------------------------------
- index[0] = idx
- idx += 1
-
- # Vert Colors
- tab_write("texture_list {\n")
- # In case there's is no material slot, give at least one texture
- # (an empty one so it uses pov default)
- if len(vertCols) == 0:
- file.write(tab_str + "1")
- else:
- file.write(tab_str + "%s" % (len(vertCols))) # vert count
-
- # below "material" alias, added check obj.active_material
- # to avoid variable referenced before assignment error
- try:
- material = ob.active_material
- except IndexError:
- # when no material slot exists,
- material = None
-
- # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- if (
- material
- and ob.active_material is not None
- and not material.pov.material_use_nodes
- ):
- if material.pov.replacement_text != "":
- file.write("\n")
- file.write(" texture{%s}\n" % material.pov.replacement_text)
-
- else:
- # Loop through declared materials list
- for cMN in local_material_names:
- if material != "Default":
- file.write("\n texture{MAT_%s}\n" % cMN)
- # use string_strip_hyphen(material_names_dictionary[material]))
- # or Something like that to clean up the above?
- elif material and material.pov.material_use_nodes:
- for index in facesMaterials:
- faceMaterial = string_strip_hyphen(
- bpy.path.clean_name(me_materials[index].name)
- )
- file.write("\n texture{%s}\n" % faceMaterial)
- # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- else:
- file.write(" texture{}\n")
- tab_write("}\n")
-
- # Face indices
- tab_write("face_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
-
- for fi, f in enumerate(me_faces):
- fv = faces_verts[fi]
- material_index = f.material_index
-
- if vcol_layer:
- cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
-
- if (
- not me_materials or me_materials[material_index] is None
- ): # No materials
- if linebreaksinlists:
- file.write(",\n")
- # vert count
- file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
- else:
- file.write(", ")
- file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2])) # vert count
- else:
- material = me_materials[material_index]
- ci1 = ci2 = ci3 = f.material_index
- if me.vertex_colors: # and material.use_vertex_color_paint:
- # Color per vertex - vertex color
-
- col1 = cols[0]
- col2 = cols[1]
- col3 = cols[2]
-
- ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
- ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
- ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
- elif material.pov.material_use_nodes:
- ci1 = ci2 = ci3 = 0
- else:
- # Color per material - flat material color
- if material.pov_subsurface_scattering.use:
- diffuse_color = [
- i * j
- for i, j in zip(
- material.pov_subsurface_scattering.color[:],
- material.diffuse_color[:],
- )
- ]
- else:
- diffuse_color = material.diffuse_color[:]
- ci1 = ci2 = ci3 = vertCols[
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- f.material_index,
- ][0]
-
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>, %d,%d,%d"
- % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
- ) # vert count
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>, %d,%d,%d"
- % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
- ) # vert count
-
- file.write("\n")
- tab_write("}\n")
-
- # normal_indices indices
- tab_write("normal_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
- for fi, fv in enumerate(faces_verts):
- if me_faces[fi].use_smooth:
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>"
- % (
- uniqueNormals[verts_normals[fv[0]]][0],
- uniqueNormals[verts_normals[fv[1]]][0],
- uniqueNormals[verts_normals[fv[2]]][0],
- )
- ) # vert count
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>"
- % (
- uniqueNormals[verts_normals[fv[0]]][0],
- uniqueNormals[verts_normals[fv[1]]][0],
- uniqueNormals[verts_normals[fv[2]]][0],
- )
- ) # vert count
- else:
- idx = uniqueNormals[faces_normals[fi]][0]
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str + "<%d,%d,%d>" % (idx, idx, idx)
- ) # vertcount
- else:
- file.write(", ")
- file.write("<%d,%d,%d>" % (idx, idx, idx)) # vert count
-
- file.write("\n")
- tab_write("}\n")
-
- if uv_layer:
- tab_write("uv_indices {\n")
- tab_write("%d" % (len(me_faces))) # faces count
- tab_str = tab * tab_level
- for f in me_faces:
- uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
-
- if linebreaksinlists:
- file.write(",\n")
- file.write(
- tab_str
- + "<%d,%d,%d>"
- % (
- uniqueUVs[uvs[0]][0],
- uniqueUVs[uvs[1]][0],
- uniqueUVs[uvs[2]][0],
- )
- )
- else:
- file.write(", ")
- file.write(
- "<%d,%d,%d>"
- % (
- uniqueUVs[uvs[0]][0],
- uniqueUVs[uvs[1]][0],
- uniqueUVs[uvs[2]][0],
- )
- )
-
- file.write("\n")
- tab_write("}\n")
-
- # XXX BOOLEAN
- write_object_csg_inside_vector(ob, file)
- if me.materials:
- try:
- material = me.materials[0] # dodgy
- write_object_material_interior(material, ob, tab_write)
- except IndexError:
- print(me)
-
- # POV object modifiers such as
- # hollow / sturm / double_illuminate etc.
- write_object_modifiers(ob, file)
-
- # Importance for radiosity sampling added here:
- tab_write("radiosity { \n")
- tab_write("importance %3g \n" % importance)
- tab_write("}\n")
-
- tab_write("}\n") # End of mesh block
-
- ob_eval.to_mesh_clear()
- 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 @ obj.matrix_world
- if ob.is_instancer:
- tab_write("\n//--DupliObjects in %s--\n\n" % ob.name)
- # 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
- dup = "#declare OB%s = union{\n" % (
- string_strip_hyphen(bpy.path.clean_name(ob.name))
- )
- else:
- dup = "#declare DATA%s = union{\n" % (
- string_strip_hyphen(bpy.path.clean_name(ob.name))
- )
- for eachduplicate in depsgraph.object_instances:
- if (
- eachduplicate.is_instance
- ): # Real dupli instance filtered because original included in list since 2.8
- _dupname = eachduplicate.object.name
- _dupobj = bpy.data.objects[_dupname]
- # BEGIN introspection for troubleshooting purposes
- if "name" not in dir(_dupobj.data):
- if _dupname not in _dupnames_seen:
- print(
- "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute"
- % (_dupname, type(_dupobj.data))
- )
- for _thing in dir(_dupobj):
- print(
- "|| %s.%s = %s"
- % (_dupname, _thing, getattr(_dupobj, _thing))
- )
- _dupnames_seen[_dupname] = 1
- print("''=> Unparseable objects so far: %s" % _dupnames_seen)
- else:
- _dupnames_seen[_dupname] += 1
- continue # don't try to parse data objects with no name attribute
- # END introspection for troubleshooting purposes
- duplidataname = "OB" + string_strip_hyphen(
- bpy.path.clean_name(_dupobj.data.name)
- )
- dupmatrix = (
- eachduplicate.matrix_world.copy()
- ) # has to be copied to not store instance since 2.8
- dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % (
- string_strip_hyphen(bpy.path.clean_name(_dupobj.data.name)),
- matrix_as_pov_string(ob.matrix_world.inverted() @ dupmatrix),
- )
- # add object to a list so that it is not rendered for some instance_types
- if (
- ob.instance_type not in {'COLLECTION'}
- and duplidataname not in duplidata_ref
- ):
- duplidata_ref.append(
- duplidataname
- ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+obj.name))]
- dup += "}\n"
- # obj.dupli_list_clear()# just do not store any reference to instance since 2.8
- tab_write(dup)
- else:
- continue
- 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
- tab_write("\n//----Blender Object Name:%s----\n" % ob_name)
- if ob.pov.object_as == '':
- tab_write("object { \n")
- tab_write("%s\n" % data_name)
- tab_write("%s\n" % matrix_str)
- tab_write("}\n")
- else:
- no_boolean = True
- for mod in ob.modifiers:
- if mod.type == 'BOOLEAN':
- operation = None
- no_boolean = False
- if mod.operation == 'INTERSECT':
- operation = 'intersection'
- else:
- operation = mod.operation.lower()
- mod_ob_name = string_strip_hyphen(
- bpy.path.clean_name(mod.object.name)
- )
- mod_matrix = global_matrix @ mod.object.matrix_world
- mod_ob_matrix = matrix_as_pov_string(mod_matrix)
- tab_write("%s { \n" % operation)
- tab_write("object { \n")
- tab_write("%s\n" % data_name)
- tab_write("%s\n" % matrix_str)
- tab_write("}\n")
- tab_write("object { \n")
- tab_write("%s\n" % ('DATA' + mod_ob_name))
- tab_write("%s\n" % mod_ob_matrix)
- tab_write("}\n")
- tab_write("}\n")
- break
- if no_boolean:
- tab_write("object { \n")
- tab_write("%s\n" % data_name)
- tab_write("%s\n" % matrix_str)
- tab_write("}\n")
diff --git a/render_povray/object_particles.py b/render_povray/particles.py
index 66477485..4ca09471 100755..100644
--- a/render_povray/object_particles.py
+++ b/render_povray/particles.py
@@ -4,12 +4,14 @@
"""Get some Blender particle objects translated to POV."""
import bpy
+
import random
def pixel_relative_guess(ob):
"""Convert some object x dimension to a rough pixel relative order of magnitude"""
from bpy_extras import object_utils
+
scene = bpy.context.scene
cam = scene.camera
render = scene.render
@@ -27,23 +29,25 @@ def pixel_relative_guess(ob):
return apparent_size / pixel_pitch_x
-def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
+def export_hair(file, ob, mod, p_sys, global_matrix):
"""Get Blender path particles (hair strands) objects translated to POV sphere_sweep unions."""
# tstart = time.time()
+ from .render import write_matrix
+
textured_hair = 0
- if ob.material_slots[p_sys.settings.material - 1].material and ob.active_material is not None:
- pmaterial = ob.material_slots[p_sys.settings.material - 1].material
+ depsgraph = bpy.context.evaluated_depsgraph_get()
+ p_sys_settings = p_sys.settings.evaluated_get(depsgraph)
+ if ob.material_slots[p_sys_settings.material - 1].material and ob.active_material is not None:
+ pmaterial = ob.material_slots[p_sys_settings.material - 1].material
# XXX Todo: replace by pov_(Particles?)_texture_slot
for th in pmaterial.pov_texture_slots:
povtex = th.texture # slot.name
tex = bpy.data.textures[povtex]
if (
- th
+ tex
and th.use
- and (
- (tex.type == 'IMAGE' and tex.image) or tex.type != 'IMAGE'
- )
+ and ((tex.type == "IMAGE" and tex.image) or tex.type != "IMAGE")
and th.use_map_color_diffuse
):
textured_hair = 1
@@ -74,12 +78,12 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
# In the viewport it will be at viewport resolution.
# So there is no need fo render engines to use this function anymore,
# it's automatic now.
- steps = p_sys.settings.display_step
- steps = 2 ** steps # or + 1 # Formerly : len(particle.hair_keys)
+ steps = p_sys_settings.display_step
+ steps = 2**steps # or + 1 # Formerly : len(particle.hair_keys)
- total_number_of_strands = p_sys.settings.count + p_sys.settings.rendered_child_count
+ total_number_of_strands = p_sys_settings.count * p_sys_settings.rendered_child_count
# hairCounter = 0
- file.write('#declare HairArray = array[%i] {\n' % total_number_of_strands)
+ file.write("#declare HairArray = array[%i] {\n" % total_number_of_strands)
for pindex in range(total_number_of_strands):
# if particle.is_exist and particle.is_visible:
@@ -87,32 +91,33 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
# controlPointCounter = 0
# Each hair is represented as a separate sphere_sweep in POV-Ray.
- file.write('sphere_sweep{')
- if p_sys.settings.use_hair_bspline:
- file.write('b_spline ')
+ file.write("sphere_sweep{")
+ if p_sys_settings.use_hair_bspline:
+ file.write("b_spline ")
file.write(
- '%i,\n' % (steps + 2)
+ "%i,\n" % (steps + 2)
) # +2 because the first point needs tripling to be more than a handle in POV
else:
- file.write('linear_spline ')
- file.write('%i,\n' % steps)
+ file.write("linear_spline ")
+ file.write("%i,\n" % steps)
# changing world coordinates to object local coordinates by
# multiplying with inverted matrix
init_coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=0))
+ init_coord = (init_coord[0], init_coord[1], init_coord[2])
if (
- ob.material_slots[p_sys.settings.material - 1].material
+ ob.material_slots[p_sys_settings.material - 1].material
and ob.active_material is not None
):
- pmaterial = ob.material_slots[p_sys.settings.material - 1].material
+ pmaterial = ob.material_slots[p_sys_settings.material - 1].material
for th in pmaterial.pov_texture_slots:
- if th and th.use and th.use_map_color_diffuse:
- povtex = th.texture # slot.name
- tex = bpy.data.textures[povtex]
+ povtex = th.texture # slot.name
+ tex = bpy.data.textures[povtex]
+ if tex and th.use and th.use_map_color_diffuse:
# treat POV textures as bitmaps
if (
- tex.type == 'IMAGE'
+ tex.type == "IMAGE"
and tex.image
- and th.texture_coords == 'UV'
+ and th.texture_coords == "UV"
and ob.data.uv_textures is not None
):
# or (
@@ -135,36 +140,36 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
init_color = (r, g, b, a)
else:
# only overwrite variable for each competing texture for now
- init_color = tex.evaluate((init_coord[0], init_coord[1], init_coord[2]))
+ init_color = tex.evaluate(init_coord)
for step in range(steps):
coord = ob.matrix_world.inverted() @ (p_sys.co_hair(ob, particle_no=pindex, step=step))
# for controlPoint in particle.hair_keys:
- if p_sys.settings.clump_factor != 0:
- hair_strand_diameter = p_sys.settings.clump_factor / 200.0 * random.uniform(0.5, 1)
+ if p_sys_settings.clump_factor:
+ hair_strand_diameter = p_sys_settings.clump_factor / 200.0 * random.uniform(0.5, 1)
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))
- else:
- fac = pow(step, (1.0 / (1.0 - strand_shape)))
- else:
+ if strand_shape == 0.0:
fac = step
- hair_strand_diameter += fac * (strand_end - strand_start) / (
- p_sys.settings.display_step + 1
+ elif strand_shape < 0:
+ fac = pow(step, (1.0 + strand_shape))
+ else:
+ fac = pow(step, (1.0 / (1.0 - strand_shape)))
+ hair_strand_diameter += (
+ fac * (strand_end - strand_start) / (p_sys_settings.display_step + 1)
) # XXX +1 or -1 or nothing ?
- if step == 0 and p_sys.settings.use_hair_bspline:
+ abs_hair_strand_diameter = abs(hair_strand_diameter)
+ if step == 0 and p_sys_settings.use_hair_bspline:
# Write three times the first point to compensate pov Bezier handling
file.write(
- '<%.6g,%.6g,%.6g>,%.7g,\n'
- % (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
+ "<%.6g,%.6g,%.6g>,%.7g,\n"
+ % (coord[0], coord[1], coord[2], abs_hair_strand_diameter)
)
file.write(
- '<%.6g,%.6g,%.6g>,%.7g,\n'
- % (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
+ "<%.6g,%.6g,%.6g>,%.7g,\n"
+ % (coord[0], coord[1], coord[2], abs_hair_strand_diameter)
)
# Useless because particle location is the tip, not the root:
# file.write(
@@ -173,7 +178,7 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
# particle.location[0],
# particle.location[1],
# particle.location[2],
- # abs(hair_strand_diameter)
+ # abs_hair_strand_diameter
# )
# )
# file.write(',\n')
@@ -183,7 +188,7 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
# Each control point is written out, along with the radius of the
# hair at that point.
file.write(
- '<%.6g,%.6g,%.6g>,%.7g' % (coord[0], coord[1], coord[2], abs(hair_strand_diameter))
+ "<%.6g,%.6g,%.6g>,%.7g" % (coord[0], coord[1], coord[2], abs_hair_strand_diameter)
)
# All coordinates except the last need a following comma.
@@ -193,32 +198,32 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
# Write pigment and alpha (between Pov and Blender,
# alpha 0 and 1 are reversed)
file.write(
- '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n'
+ "\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n"
% (init_color[0], init_color[1], init_color[2], 1.0 - init_color[3])
)
# End the sphere_sweep declaration for this hair
- file.write('}\n')
+ file.write("}\n")
else:
- file.write(',\n')
+ file.write(",\n")
# All but the final sphere_sweep (each array element) needs a terminating comma.
if pindex != total_number_of_strands:
- file.write(',\n')
+ file.write(",\n")
else:
- file.write('\n')
+ file.write("\n")
# End the array declaration.
- file.write('}\n')
- file.write('\n')
+ file.write("}\n")
+ file.write("\n")
if not textured_hair:
# Pick up the hair material diffuse color and create a default POV-Ray hair texture.
- file.write('#ifndef (HairTexture)\n')
- file.write(' #declare HairTexture = texture {\n')
+ file.write("#ifndef (HairTexture)\n")
+ file.write(" #declare HairTexture = texture {\n")
file.write(
- ' pigment {srgbt <%s,%s,%s,%s>}\n'
+ " pigment {srgbt <%s,%s,%s,%s>}\n"
% (
pmaterial.diffuse_color[0],
pmaterial.diffuse_color[1],
@@ -226,56 +231,48 @@ def export_hair(file, ob, mod, p_sys, global_matrix, write_matrix):
(pmaterial.strand.width_fade + 0.05),
)
)
- file.write(' }\n')
- file.write('#end\n')
- file.write('\n')
+ file.write(" }\n")
+ file.write("#end\n")
+ file.write("\n")
# Dynamically create a union of the hairstrands (or a subset of them).
# By default use every hairstrand, commented line is for hand tweaking test renders.
- file.write('//Increasing HairStep divides the amount of hair for test renders.\n')
- file.write('#ifndef(HairStep) #declare HairStep = 1; #end\n')
- file.write('union{\n')
- file.write(' #local I = 0;\n')
- file.write(' #while (I < %i)\n' % total_number_of_strands)
- file.write(' object {HairArray[I]')
+ file.write("//Increasing HairStep divides the amount of hair for test renders.\n")
+ file.write("#ifndef(HairStep) #declare HairStep = 1; #end\n")
+ file.write("union{\n")
+ file.write(" #local I = 0;\n")
+ file.write(" #while (I < %i)\n" % total_number_of_strands)
+ file.write(" object {HairArray[I]")
if textured_hair:
- file.write('\n')
+ file.write("\n")
else:
- file.write(' texture{HairTexture}\n')
+ file.write(" texture{HairTexture}\n")
# Translucency of the hair:
- file.write(' hollow\n')
- file.write(' double_illuminate\n')
- file.write(' interior {\n')
- file.write(' ior 1.45\n')
- file.write(' media {\n')
- file.write(' scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n')
- file.write(' absorption 10/<0.83, 0.75, 0.15>\n')
- file.write(' samples 1\n')
- file.write(' method 2\n')
- file.write(' density {cylindrical\n')
- file.write(' color_map {\n')
- file.write(' [0.0 rgb <0.83, 0.45, 0.35>]\n')
- file.write(' [0.5 rgb <0.8, 0.8, 0.4>]\n')
- file.write(' [1.0 rgb <1,1,1>]\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
- file.write(' }\n')
+ file.write(" hollow\n")
+ file.write(" double_illuminate\n")
+ file.write(" interior {\n")
+ file.write(" ior 1.45\n")
+ file.write(" media {\n")
+ file.write(" scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n")
+ file.write(" absorption 10/<0.83, 0.75, 0.15>\n")
+ file.write(" samples 1\n")
+ file.write(" method 2\n")
+ file.write(" density {cylindrical\n")
+ file.write(" color_map {\n")
+ file.write(" [0.0 rgb <0.83, 0.45, 0.35>]\n")
+ file.write(" [0.5 rgb <0.8, 0.8, 0.4>]\n")
+ file.write(" [1.0 rgb <1,1,1>]\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
+ file.write(" }\n")
- file.write(' #local I = I + HairStep;\n')
- file.write(' #end\n')
+ file.write(" #local I = I + HairStep;\n")
+ file.write(" #end\n")
- write_matrix(global_matrix @ ob.matrix_world)
+ write_matrix(file, global_matrix @ ob.matrix_world)
- file.write('}')
+ file.write("}")
print("Totals hairstrands written: %i" % total_number_of_strands)
- print("Number of tufts (particle systems)", len(ob.particle_systems))
-
- # Set back the displayed number of particles to preview count
- # p_sys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED
- # When you render, the entire dependency graph will be
- # evaluated at render resolution, including the particles.
- # In the viewport it will be at viewport resolution.
- # So there is no need fo render engines to use this function anymore,
- # it's automatic now.
+ print("Number of tufts (particle systems): %i" % len(ob.particle_systems))
diff --git a/render_povray/particles_properties.py b/render_povray/particles_properties.py
new file mode 100644
index 00000000..573ca48c
--- /dev/null
+++ b/render_povray/particles_properties.py
@@ -0,0 +1,718 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+"""Declare shading properties exported to POV textures."""
+import bpy
+from bpy.utils import register_class, unregister_class
+from bpy.types import PropertyGroup
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ FloatProperty,
+ PointerProperty,
+)
+
+
+class MaterialStrandSettings(PropertyGroup):
+ """Declare strand properties controllable in UI and translated to POV."""
+
+ bl_description = ("Strand settings for the material",)
+
+ blend_distance: FloatProperty(
+ name="Distance",
+ description="Worldspace distance over which to blend in the surface normal",
+ min=0.0,
+ max=10.0,
+ soft_min=0.0,
+ soft_max=10.0,
+ default=0.0,
+ precision=3,
+ )
+
+ root_size: FloatProperty(
+ name="Root",
+ description="Start size of strands in pixels or Blender units",
+ min=0.25,
+ default=1.0,
+ precision=5,
+ )
+
+ shape: FloatProperty(
+ name="Shape",
+ description="Positive values make strands rounder, negative ones make strands spiky",
+ min=-0.9,
+ max=0.9,
+ default=0.0,
+ precision=3,
+ )
+
+ size_min: FloatProperty(
+ name="Minimum",
+ description="Minimum size of strands in pixels",
+ min=0.001,
+ max=10.0,
+ default=1.0,
+ precision=3,
+ )
+
+ tip_size: FloatProperty(
+ name="Tip",
+ description="End size of strands in pixels or Blender units",
+ min=0.0,
+ default=1.0,
+ precision=5,
+ )
+
+ use_blender_units: BoolProperty(
+ name="Blender Units",
+ description="Use Blender units for widths instead of pixels",
+ default=False,
+ )
+
+ use_surface_diffuse: BoolProperty(
+ name="Surface diffuse",
+ description="Make diffuse shading more similar to shading the surface",
+ default=False,
+ )
+
+ use_tangent_shading: BoolProperty(
+ name="Tangent Shading",
+ description="Use direction of strands as normal for tangent-shading",
+ default=True,
+ )
+
+ uv_layer: StringProperty(
+ name="UV Layer",
+ # icon="GROUP_UVS",
+ description="Name of UV map to override",
+ default="",
+ )
+
+ width_fade: FloatProperty(
+ name="Width Fade",
+ description="Transparency along the width of the strand",
+ min=0.0,
+ max=2.0,
+ default=0.0,
+ precision=3,
+ )
+
+ # halo
+
+ # Halo settings for the material
+ # Type: MaterialHalo, (readonly, never None)
+
+
+ # darkness
+
+ # Minnaert darkness
+ # Type: float in [0, 2], default 0.0
+
+ # diffuse_color
+
+ # Diffuse color of the material
+ # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
+
+ # diffuse_fresnel
+
+ # Power of Fresnel
+ # Type: float in [0, 5], default 0.0
+
+ # diffuse_fresnel_factor
+
+ # Blending factor of Fresnel
+ # Type: float in [0, 5], default 0.0
+
+ # diffuse_intensity
+
+ # Amount of diffuse reflection
+ # Type: float in [0, 1], default 0.0
+
+ # diffuse_ramp
+
+ # Color ramp used to affect diffuse shading
+ # Type: ColorRamp, (readonly)
+
+ # diffuse_ramp_blend
+
+ # Blending method of the ramp and the diffuse color
+ # Type: enum in [‘MIX’, ‘ADD’, ‘MULTIPLY’, ‘SUBTRACT’, ‘SCREEN’, ‘DIVIDE’, ‘DIFFERENCE’, ‘DARKEN’, ‘LIGHTEN’, ‘OVERLAY’, ‘DODGE’, ‘BURN’, ‘HUE’, ‘SATURATION’, ‘VALUE’, ‘COLOR’, ‘SOFT_LIGHT’, ‘LINEAR_LIGHT’], default ‘MIX’
+
+ # diffuse_ramp_factor
+
+ # Blending factor (also uses alpha in Colorband)
+ # Type: float in [0, 1], default 0.0
+
+ # diffuse_ramp_input
+
+ # How the ramp maps on the surface
+ # Type: enum in [‘SHADER’, ‘ENERGY’, ‘NORMAL’, ‘RESULT’], default ‘SHADER’
+
+ # diffuse_shader
+
+ # LAMBERT Lambert, Use a Lambertian shader.
+ # OREN_NAYAR Oren-Nayar, Use an Oren-Nayar shader.
+ # TOON Toon, Use a toon shader.
+ # MINNAERT Minnaert, Use a Minnaert shader.
+ # FRESNEL Fresnel, Use a Fresnel shader.
+
+ # Type: enum in [‘LAMBERT’, ‘OREN_NAYAR’, ‘TOON’, ‘MINNAERT’, ‘FRESNEL’], default ‘LAMBERT’
+
+ # diffuse_toon_size
+
+ # Size of diffuse toon area
+ # Type: float in [0, 3.14], default 0.0
+
+ # diffuse_toon_smooth
+
+ # Smoothness of diffuse toon area
+ # Type: float in [0, 1], default 0.0
+
+ # emit
+
+ # Amount of light to emit
+ # Type: float in [0, inf], default 0.0
+
+ # halo
+
+ # Halo settings for the material
+ # Type: MaterialHalo, (readonly, never None)
+
+ # invert_z
+
+ # Render material’s faces with an inverted Z buffer (scanline only)
+ # Type: boolean, default False
+
+ # light_group
+
+ # Limit lighting to lamps in this Group
+ # Type: Group
+
+ # line_color
+
+ # Line color used for Freestyle line rendering
+ # Type: float array of 4 items in [0, inf], default (0.0, 0.0, 0.0, 0.0)
+
+ # line_priority
+
+ # The line color of a higher priority is used at material boundaries
+ # Type: int in [0, 32767], default 0
+
+ # mirror_color
+
+ # Mirror color of the material
+ # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
+
+ # node_tree
+
+ # Node tree for node based materials
+ # Type: NodeTree, (readonly)
+
+ # offset_z
+
+ # Give faces an artificial offset in the Z buffer for Z transparency
+ # Type: float in [-inf, inf], default 0.0
+
+ # paint_active_slot
+
+ # Index of active texture paint slot
+ # Type: int in [0, 32767], default 0
+
+ # paint_clone_slot
+
+ # Index of clone texture paint slot
+ # Type: int in [0, 32767], default 0
+
+ # pass_index
+
+ # Index number for the “Material Index” render pass
+ # Type: int in [0, 32767], default 0
+
+ # physics
+
+ # Game physics settings
+ # Type: MaterialPhysics, (readonly, never None)
+
+ # preview_render_type
+
+ # Type of preview render
+
+ # FLAT Flat, Flat XY plane.
+ # SPHERE Sphere, Sphere.
+ # CUBE Cube, Cube.
+ # MONKEY Monkey, Monkey.
+ # HAIR Hair, Hair strands.
+ # SPHERE_A World Sphere, Large sphere with sky.
+
+ # Type: enum in [‘FLAT’, ‘SPHERE’, ‘CUBE’, ‘MONKEY’, ‘HAIR’, ‘SPHERE_A’], default ‘FLAT’
+
+ # roughness
+
+ # Oren-Nayar Roughness
+ # Type: float in [0, 3.14], default 0.0
+
+ # shadow_buffer_bias
+
+ # Factor to multiply shadow buffer bias with (0 is ignore)
+ # Type: float in [0, 10], default 0.0
+
+ # shadow_cast_alpha
+
+ # Shadow casting alpha, in use for Irregular and Deep shadow buffer
+ # Type: float in [0.001, 1], default 0.0
+
+ # shadow_only_type
+
+ # How to draw shadows
+
+ # SHADOW_ONLY_OLD Shadow and Distance, Old shadow only method.
+ # SHADOW_ONLY Shadow Only, Improved shadow only method.
+ # SHADOW_ONLY_SHADED Shadow and Shading, Improved shadow only method which also renders lightless areas as shadows.
+
+ # Type: enum in [‘SHADOW_ONLY_OLD’, ‘SHADOW_ONLY’, ‘SHADOW_ONLY_SHADED’], default ‘SHADOW_ONLY_OLD’
+
+ # shadow_ray_bias
+
+ # Shadow raytracing bias to prevent terminator problems on shadow boundary
+ # Type: float in [0, 0.25], default 0.0
+
+ # specular_color
+
+ # Specular color of the material
+ # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
+
+ # specular_hardness
+
+ # How hard (sharp) the specular reflection is
+ # Type: int in [1, 511], default 0
+
+ # specular_intensity
+
+ # How intense (bright) the specular reflection is
+ # Type: float in [0, 1], default 0.0
+
+ # specular_ior
+
+ # Specular index of refraction
+ # Type: float in [1, 10], default 0.0
+
+ # specular_ramp
+
+ # Color ramp used to affect specular shading
+ # Type: ColorRamp, (readonly)
+
+ # specular_ramp_blend
+
+ # Blending method of the ramp and the specular color
+ # Type: enum in [‘MIX’, ‘ADD’, ‘MULTIPLY’, ‘SUBTRACT’, ‘SCREEN’, ‘DIVIDE’, ‘DIFFERENCE’, ‘DARKEN’, ‘LIGHTEN’, ‘OVERLAY’, ‘DODGE’, ‘BURN’, ‘HUE’, ‘SATURATION’, ‘VALUE’, ‘COLOR’, ‘SOFT_LIGHT’, ‘LINEAR_LIGHT’], default ‘MIX’
+
+ # specular_ramp_factor
+
+ # Blending factor (also uses alpha in Colorband)
+ # Type: float in [0, 1], default 0.0
+
+ # specular_ramp_input
+
+ # How the ramp maps on the surface
+ # Type: enum in [‘SHADER’, ‘ENERGY’, ‘NORMAL’, ‘RESULT’], default ‘SHADER’
+ # specular_shader
+
+ # COOKTORR CookTorr, Use a Cook-Torrance shader.
+ # PHONG Phong, Use a Phong shader.
+ # BLINN Blinn, Use a Blinn shader.
+ # TOON Toon, Use a toon shader.
+ # WARDISO WardIso, Use a Ward anisotropic shader.
+
+ # Type: enum in [‘COOKTORR’, ‘PHONG’, ‘BLINN’, ‘TOON’, ‘WARDISO’], default ‘COOKTORR’
+
+ # specular_slope
+
+ # The standard deviation of surface slope
+ # Type: float in [0, 0.4], default 0.0
+
+ # specular_toon_size
+
+ # Size of specular toon area
+ # Type: float in [0, 1.53], default 0.0
+
+ # specular_toon_smooth
+
+ # Smoothness of specular toon area
+ # Type: float in [0, 1], default 0.0
+
+ # strand
+
+ # Strand settings for the material
+ # Type: MaterialStrand, (readonly, never None)
+
+ # subsurface_scattering
+
+ # Subsurface scattering settings for the material
+ # Type: MaterialSubsurfaceScattering, (readonly, never None)
+
+ # texture_paint_images
+
+ # Texture images used for texture painting
+ # Type: bpy_prop_collection of Image, (readonly)
+
+ # texture_paint_slots
+
+ # Texture slots defining the mapping and influence of textures
+ # Type: bpy_prop_collection of TexPaintSlot, (readonly)
+
+ # texture_slots
+
+ # Texture slots defining the mapping and influence of textures
+ # Type: MaterialTextureSlots bpy_prop_collection of MaterialTextureSlot, (readonly)
+
+ # type
+
+ # Material type defining how the object is rendered
+
+ # SURFACE Surface, Render object as a surface.
+ # WIRE Wire, Render the edges of faces as wires (not supported in raytracing).
+ # VOLUME Volume, Render object as a volume.
+ # HALO Halo, Render object as halo particles.
+
+ # Type: enum in [‘SURFACE’, ‘WIRE’, ‘VOLUME’, ‘HALO’], default ‘SURFACE’
+
+ # use_cast_shadows
+
+ # Allow this material to cast shadows
+ # Type: boolean, default False
+
+ # use_cast_shadows_only
+
+ # Make objects with this material appear invisible (not rendered), only casting shadows
+ # Type: boolean, default False
+
+ # use_cubic
+
+ # Use cubic interpolation for diffuse values, for smoother transitions
+ # Type: boolean, default False
+
+ # use_diffuse_ramp
+
+ # Toggle diffuse ramp operations
+ # Type: boolean, default False
+
+ # use_face_texture
+
+ # Replace the object’s base color with color from UV map image textures
+ # Type: boolean, default False
+
+ # use_face_texture_alpha
+
+ # Replace the object’s base alpha value with alpha from UV map image textures
+ # Type: boolean, default False
+
+ # use_full_oversampling
+
+ # Force this material to render full shading/textures for all anti-aliasing samples
+ # Type: boolean, default False
+
+ # use_light_group_exclusive
+
+ # Material uses the light group exclusively - these lamps are excluded from other scene lighting
+ # Type: boolean, default False
+
+ # use_light_group_local
+
+ # When linked in, material uses local light group with the same name
+ # Type: boolean, default False
+
+ # use_mist
+
+ # Use mist with this material (in world settings)
+ # Type: boolean, default False
+
+ # use_nodes
+
+ # Use shader nodes to render the material
+ # Type: boolean, default False
+
+ # use_object_color
+
+ # Modulate the result with a per-object color
+ # Type: boolean, default False
+
+ # use_only_shadow
+
+ # Render shadows as the material’s alpha value, making the material transparent except for shadowed areas
+ # Type: boolean, default False
+
+ # use_ray_shadow_bias
+
+ # Prevent raytraced shadow errors on surfaces with smooth shaded normals (terminator problem)
+ # Type: boolean, default False
+
+ # use_raytrace
+
+ # Include this material and geometry that uses it in raytracing calculations
+ # Type: boolean, default False
+
+ # use_shadeless
+
+ # Make this material insensitive to light or shadow
+ # Type: boolean, default False
+
+ # use_shadows
+
+ # Allow this material to receive shadows
+ # Type: boolean, default False
+
+ # use_sky
+
+ # Render this material with zero alpha, with sky background in place (scanline only)
+ # Type: boolean, default False
+
+ # use_specular_ramp
+
+ # Toggle specular ramp operations
+ # Type: boolean, default False
+
+ # use_tangent_shading
+
+ # Use the material’s tangent vector instead of the normal for shading - for anisotropic shading effects
+ # Type: boolean, default False
+
+ # use_textures
+
+ # Enable/Disable each texture
+ # Type: boolean array of 18 items, default (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
+
+ # use_transparency
+
+ # Render material as transparent
+ # Type: boolean, default False
+
+ # use_transparent_shadows
+
+ # Allow this object to receive transparent shadows cast through other objects
+ # Type: boolean, default False
+
+ # use_uv_project
+
+ # Use to ensure UV interpolation is correct for camera projections (use with UV project modifier)
+ # Type: boolean, default False
+
+ # use_vertex_color_light
+
+ # Add vertex colors as additional lighting
+ # Type: boolean, default False
+
+ # use_vertex_color_paint
+
+ # Replace object base color with vertex colors (multiply with ‘texture face’ face assigned textures)
+ # Type: boolean, default False
+
+ # volume
+
+ # Volume settings for the material
+ # Type: MaterialVolume, (readonly, never None)
+ """
+ (mat.type in {'SURFACE', 'WIRE', 'VOLUME'})
+ "use_transparency")
+
+
+
+ mat.use_transparency and mat.pov.transparency_method == 'Z_TRANSPARENCY'
+
+
+
+
+ col.prop(mat, "use_raytrace")
+ col.prop(mat, "use_full_oversampling")
+
+ sub.prop(mat, "use_sky")
+
+
+ col.prop(mat, "use_cast_shadows", text="Cast")
+ col.prop(mat, "use_cast_shadows_only", text="Cast Only")
+ col.prop(mat, "use_cast_buffer_shadows")
+
+ sub.active = mat.use_cast_buffer_shadows
+ sub.prop(mat, "shadow_cast_alpha", text="Casting Alpha")
+ col.prop(mat, "use_cast_approximate")
+
+
+
+ col.prop(mat, "diffuse_color", text="")
+
+ sub.active = (not mat.use_shadeless)
+
+ sub.prop(mat, "diffuse_intensity", text="Intensity")
+
+
+ col.prop(mat, "diffuse_shader", text="")
+ col.prop(mat, "use_diffuse_ramp", text="Ramp")
+
+
+ if mat.diffuse_shader == 'OREN_NAYAR':
+ col.prop(mat, "roughness")
+ elif mat.diffuse_shader == 'MINNAERT':
+ col.prop(mat, "darkness")
+ elif mat.diffuse_shader == 'TOON':
+
+ row.prop(mat, "diffuse_toon_size", text="Size")
+ row.prop(mat, "diffuse_toon_smooth", text="Smooth")
+ elif mat.diffuse_shader == 'FRESNEL':
+
+ row.prop(mat, "diffuse_fresnel", text="Fresnel")
+ row.prop(mat, "diffuse_fresnel_factor", text="Factor")
+
+ if mat.use_diffuse_ramp:
+
+ col.template_color_ramp(mat, "diffuse_ramp", expand=True)
+
+
+
+ row.prop(mat, "diffuse_ramp_input", text="Input")
+ row.prop(mat, "diffuse_ramp_blend", text="Blend")
+
+ col.prop(mat, "diffuse_ramp_factor", text="Factor")
+
+
+
+
+ col.prop(mat, "specular_color", text="")
+ col.prop(mat, "specular_intensity", text="Intensity")
+
+ col.prop(mat, "specular_shader", text="")
+ col.prop(mat, "use_specular_ramp", text="Ramp")
+
+ if mat.pov.specular_shader in {'COOKTORR', 'PHONG'}:
+ col.prop(mat, "specular_hardness", text="Hardness")
+ elif mat.pov.specular_shader == 'BLINN':
+
+ row.prop(mat, "specular_hardness", text="Hardness")
+ row.prop(mat, "specular_ior", text="IOR")
+ elif mat.pov.specular_shader == 'WARDISO':
+ col.prop(mat, "specular_slope", text="Slope")
+ elif mat.pov.specular_shader == 'TOON':
+
+ row.prop(mat, "specular_toon_size", text="Size")
+ row.prop(mat, "specular_toon_smooth", text="Smooth")
+
+ if mat.use_specular_ramp:
+ layout.separator()
+ layout.template_color_ramp(mat, "specular_ramp", expand=True)
+ layout.separator()
+
+ row = layout.row()
+ row.prop(mat, "specular_ramp_input", text="Input")
+ row.prop(mat, "specular_ramp_blend", text="Blend")
+
+ layout.prop(mat, "specular_ramp_factor", text="Factor")
+
+
+ XXX remove unused props and relayout as done for transparent sky
+
+
+ class MATERIAL_PT_halo(MaterialButtonsPanel, Panel):
+ bl_label = "Halo"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ engine = context.scene.render.engine
+ return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material # don't use node material
+ halo = mat.pov.halo
+
+ def number_but(layout, toggle, number, name, color):
+ row = layout.row(align=True)
+ row.prop(halo, toggle, text="")
+ sub = row.column(align=True)
+ sub.active = getattr(halo, toggle)
+ sub.prop(halo, number, text=name, translate=False)
+ if not color == "":
+ sub.prop(mat, color, text="")
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(mat, "alpha")
+ col.prop(mat, "diffuse_color", text="")
+ col.prop(halo, "seed")
+
+ col = split.column()
+ col.prop(halo, "size")
+ col.prop(halo, "hardness")
+ col.prop(halo, "add")
+
+ layout.label(text="Options:")
+
+ split = layout.split()
+ col = split.column()
+ col.prop(halo, "use_texture")
+ col.prop(halo, "use_vertex_normal")
+ col.prop(halo, "use_extreme_alpha")
+ col.prop(halo, "use_shaded")
+ col.prop(halo, "use_soft")
+
+ col = split.column()
+ number_but(col, "use_ring", "ring_count", iface_("Rings"), "mirror_color")
+ number_but(col, "use_lines", "line_count", iface_("Lines"), "specular_color")
+ number_but(col, "use_star", "star_tip_count", iface_("Star Tips"), "")
+
+
+ class MATERIAL_PT_flare(MaterialButtonsPanel, Panel):
+ bl_label = "Flare"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ engine = context.scene.render.engine
+ return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES)
+
+ def draw_header(self, context):
+ halo = context.material.pov.halo
+
+ self.layout.prop(halo, "use_flare_mode", text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ mat = context.material # don't use node material
+ halo = mat.pov.halo
+
+ layout.active = halo.use_flare_mode
+
+ split = layout.split()
+
+ col = split.column()
+ col.prop(halo, "flare_size", text="Size")
+ col.prop(halo, "flare_boost", text="Boost")
+ col.prop(halo, "flare_seed", text="Seed")
+
+ col = split.column()
+ col.prop(halo, "flare_subflare_count", text="Subflares")
+ col.prop(halo, "flare_subflare_size", text="Subsize")
+
+ """
+
+
+classes = (
+ MaterialStrandSettings,
+)
+
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+ bpy.types.Material.strand = PointerProperty(type=MaterialStrandSettings)
+
+
+def unregister():
+ del bpy.types.Material.strand
+
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/render_povray/render.py b/render_povray/render.py
index 0f7da3c6..150d892d 100755
--- a/render_povray/render.py
+++ b/render_povray/render.py
@@ -8,12 +8,11 @@ import bpy
import subprocess
import os
from sys import platform
-import time
+#import time
from math import (
pi,
) # maybe move to scenography.py and topology_*****_data.py respectively with smoke and matrix
-
-import re
+import mathutils #import less than full
import tempfile # generate temporary files with random names
from bpy.types import Operator
from bpy.utils import register_class, unregister_class
@@ -21,19 +20,34 @@ from bpy.utils import register_class, unregister_class
from . import (
scripting,
) # for writing, importing and rendering directly POV Scene Description Language items
+from . import render_core
from . import scenography # for atmosphere, environment, effects, lighting, camera
from . import shading # for BI POV shaders emulation
-from . import object_mesh_topology # for mesh based geometry
-from . import object_curve_topology # for curves based geometry
+from . import nodes_fn
+from . import texturing_procedural # for Blender procedurals to POV patterns emulation
+from . import model_all # for mesh based geometry
+from . import model_meta_topology # for mesh based geometry
+from . import model_curve_topology # for curves based geometry
-# from . import object_primitives # for import and export of POV specific primitives
+# from . import model_primitives # for import and export of POV specific primitives
from .scenography import image_format, img_map, img_map_transforms, path_image
from .shading import write_object_material_interior
-from .object_primitives import write_object_modifiers
+from .model_primitives import write_object_modifiers
+
+tab_level = 0
+tab=""
+comments = False
+using_uberpov = False
+unpacked_images = []
+
+from .render_core import (
+ preview_dir,
+ PovRender,
+)
def string_strip_hyphen(name):
@@ -95,19 +109,31 @@ def renderable_objects():
return [ob for ob in bpy.data.objects if is_renderable(ob)]
-def no_renderable_objects():
+def non_renderable_objects():
"""Boolean operands only. Not to render"""
return list(csg_list)
-tab_level = 0
-unpacked_images = []
+def set_tab(tabtype, spaces):
+ """Apply the configured indentation all along the exported POV file
+
+ Arguments:
+ tabtype -- Specifies user preference between tabs or spaces indentation
+ spaces -- If using spaces, sets the number of space characters to use
+ Returns:
+ The beginning blank space for each line of the generated pov file
+ """
+ tab_str = ""
+ match tabtype:
+ case 'SPACE':
+ tab_str = spaces * " "
+ case 'NONE':
+ tab_str = ""
+ case 'TAB':
+ tab_str = "\t"
+ return tab_str
-user_dir = bpy.utils.resource_path('USER')
-preview_dir = os.path.join(user_dir, "preview")
-# Make sure Preview directory exists and is empty
-smoke_path = os.path.join(preview_dir, "smoke.df3")
'''
@@ -132,7 +158,7 @@ smoke_path = os.path.join(preview_dir, "smoke.df3")
# # Maybe return that string to be added instead of directly written.
# '''XXX WIP
-# import .object_mesh_topology.write_object_csg_inside_vector
+# import .model_all.write_object_csg_inside_vector
# write_object_csg_inside_vector(ob, file)
# '''
@@ -178,12 +204,55 @@ smoke_path = os.path.join(preview_dir, "smoke.df3")
# File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
# '''
+def tab_write(file, str_o, scene=None):
+ """write directly to exported file if user checked autonamed temp files (faster).
+ Otherwise, indent POV syntax from brackets levels and write to exported file"""
+
+ if not scene:
+ scene = bpy.data.scenes[0]
+ global tab
+ tab = set_tab(scene.pov.indentation_character, scene.pov.indentation_spaces)
+ if scene.pov.tempfiles_enable:
+ file.write(str_o)
+ else:
+ 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
+
+def write_matrix(file, matrix):
+ """Translate some transform matrix from Blender UI
+ to POV syntax and write to exported file """
+ tab_write(file,
+ "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],
+ )
+ )
+global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
def write_pov(filename, scene=None, info_callback=None):
"""Main export process from Blender UI to POV syntax and write to exported file """
- import mathutils
-
with open(filename, "w") as file:
# Only for testing
if not scene:
@@ -191,12 +260,13 @@ def write_pov(filename, scene=None, info_callback=None):
render = scene.render
world = scene.world
- global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
+ global comments
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
+ global using_uberpov
using_uberpov = feature_set == 'uberpov'
- pov_binary = PovrayRender._locate_binary()
+ pov_binary = PovRender._locate_binary()
if using_uberpov:
print("Unofficial UberPOV feature set chosen in preferences")
@@ -207,40 +277,6 @@ def write_pov(filename, scene=None, info_callback=None):
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':
- 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"""
@@ -257,329 +293,20 @@ def write_pov(filename, scene=None, info_callback=None):
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],
- )
- )
-
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)
- )
- 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 == '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,
- )
- )
- # 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")
- 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")
- 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(
- "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
- % (
- diffuse_color[0],
- diffuse_color[1],
- diffuse_color[2],
- pov_filter,
- trans,
- )
- )
- 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(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:
- 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)
+ tab_write(file, "global_settings {\n")
+ tab_write(file, "assumed_gamma 1.0\n")
+ tab_write(file, "max_trace_level %d\n" % scene.pov.max_trace_level)
if scene.pov.global_settings_advanced:
if not scene.pov.radio_enable:
@@ -589,24 +316,24 @@ def write_pov(filename, scene=None, info_callback=None):
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")
+ tab_write(file, "radiosity {\n")
+ tab_write(file, "adc_bailout %.4g\n" % scene.pov.radio_adc_bailout)
+ tab_write(file, "brightness %.4g\n" % scene.pov.radio_brightness)
+ tab_write(file, "count %d\n" % scene.pov.radio_count)
+ tab_write(file, "error_bound %.4g\n" % scene.pov.radio_error_bound)
+ tab_write(file, "gray_threshold %.4g\n" % scene.pov.radio_gray_threshold)
+ tab_write(file, "low_error_factor %.4g\n" % scene.pov.radio_low_error_factor)
+ tab_write(file, "maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse)
+ tab_write(file, "minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse)
+ tab_write(file, "nearest_count %d\n" % scene.pov.radio_nearest_count)
+ tab_write(file, "pretrace_start %.3g\n" % scene.pov.radio_pretrace_start)
+ tab_write(file, "pretrace_end %.3g\n" % scene.pov.radio_pretrace_end)
+ tab_write(file, "recursion_limit %d\n" % scene.pov.radio_recursion_limit)
+ tab_write(file, "always_sample %d\n" % scene.pov.radio_always_sample)
+ tab_write(file, "normal %d\n" % scene.pov.radio_normal)
+ tab_write(file, "media %d\n" % scene.pov.radio_media)
+ tab_write(file, "subsurface %d\n" % scene.pov.radio_subsurface)
+ tab_write(file, "}\n")
once_sss = 1
once_ambient = 1
once_photons = 1
@@ -614,7 +341,7 @@ def write_pov(filename, scene=None, info_callback=None):
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(
+ tab_write(file,
"mm_per_unit %.6f\n" % (material.pov_subsurface_scattering.scale * 1000.0)
)
# 1000 rather than scale * (-100.0) + 15.0))
@@ -624,44 +351,48 @@ def write_pov(filename, scene=None, info_callback=None):
# 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))
+ tab_write(file, "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[:])
+ tab_write(file, "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")
+ if (
+ scene.pov.photon_enable
+ and once_photons
+ and (
+ material.pov.refraction_type == "2"
+ or material.pov.photons_reflection
+ )
+ ):
+ tab_write(file, "photons {\n")
+ tab_write(file, "spacing %.6f\n" % scene.pov.photon_spacing)
+ tab_write(file, "max_trace_level %d\n" % scene.pov.photon_max_trace_level)
+ tab_write(file, "adc_bailout %.3g\n" % scene.pov.photon_adc_bailout)
+ tab_write(file,
+ "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(file, '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(file, 'load_file "%s"\n' % full_file_name)
+ tab_write(file, "}\n")
+ once_photons = 0
+
+ tab_write(file, "}\n")
# sel = renderable_objects() #removed for booleans
if comments:
@@ -697,17 +428,17 @@ def write_pov(filename, scene=None, info_callback=None):
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(texturing_procedural.export_pattern(texture))
file.write("\n")
if comments:
file.write("\n//--Background--\n\n")
- scenography.export_world(scene.world, scene, global_matrix, tab_write)
+ scenography.export_world(file, scene.world, scene, global_matrix, tab_write)
if comments:
file.write("\n//--Cameras--\n\n")
- scenography.export_camera(scene, global_matrix, render, tab_write)
+ scenography.export_camera(file, scene, global_matrix, render, tab_write)
if comments:
file.write("\n//--Lamps--\n\n")
@@ -719,32 +450,15 @@ def write_pov(filename, scene=None, info_callback=None):
csg_list.append(mod.object)
if csg_list:
csg = False
- sel = no_renderable_objects()
+ sel = non_renderable_objects()
# export non rendered boolean objects operands
- object_mesh_topology.export_meshes(
- preview_dir,
+ model_all.objects_loop(
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,
@@ -758,7 +472,6 @@ def write_pov(filename, scene=None, info_callback=None):
file,
scene,
global_matrix,
- write_matrix,
tab_write,
)
@@ -769,7 +482,6 @@ def write_pov(filename, scene=None, info_callback=None):
file,
scene,
global_matrix,
- write_matrix,
tab_write,
)
@@ -780,7 +492,7 @@ def write_pov(filename, scene=None, info_callback=None):
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)
+ model_curve_topology.export_curves(file, c, tab_write)
if comments:
file.write("\n//--Material Definitions--\n\n")
@@ -789,10 +501,10 @@ def write_pov(filename, scene=None, info_callback=None):
# Convert all materials to strings we can access directly per vertex.
# exportMaterials()
shading.write_material(
+ file,
using_uberpov,
DEF_MAT_NAME,
tab_write,
- safety,
comments,
unique_name,
material_names_dictionary,
@@ -809,7 +521,7 @@ def write_pov(filename, scene=None, info_callback=None):
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)
+ nodes_fn.write_nodes(pov_mat_name, ntree, file)
for node in ntree.nodes:
if node:
@@ -829,10 +541,10 @@ def write_pov(filename, scene=None, info_callback=None):
)
else:
shading.write_material(
+ file,
using_uberpov,
DEF_MAT_NAME,
tab_write,
- safety,
comments,
unique_name,
material_names_dictionary,
@@ -842,46 +554,32 @@ def write_pov(filename, scene=None, info_callback=None):
if comments:
file.write("\n")
- export_meta([m for m in sel if m.type == 'META'])
+ model_meta_topology.export_meta(file,
+ [m for m in sel if m.type == 'META'],
+ tab_write,
+ DEF_MAT_NAME,)
if comments:
file.write("//--Mesh objects--\n")
# tbefore = time.time()
- object_mesh_topology.export_meshes(
- preview_dir,
+ model_all.objects_loop(
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))
+ # print("objects_loop took" + str(totime))
# What follow used to happen here:
# export_camera()
- # scenography.export_world(scene.world, scene, global_matrix, tab_write)
+ # scenography.export_world(file, 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
@@ -893,6 +591,7 @@ def write_pov(filename, scene=None, info_callback=None):
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
+ global using_uberpov
using_uberpov = feature_set == 'uberpov'
# scene = bpy.data.scenes[0]
scene = bpy.context.scene
@@ -963,705 +662,6 @@ def write_pov_ini(filename_ini, filename_log, filename_pov, filename_image):
file.close()
-class PovrayRender(bpy.types.RenderEngine):
- """Define the external renderer"""
-
- bl_idname = 'POVRAY_RENDER'
- bl_label = "Persitence Of Vision"
- bl_use_shading_nodes_custom = False
- DELAY = 0.5
-
- @staticmethod
- def _locate_binary():
- """Identify POV engine"""
- addon_prefs = bpy.context.preferences.addons[__package__].preferences
-
- # Use the system preference if its set.
- pov_binary = addon_prefs.filepath_povray
- if pov_binary:
- if os.path.exists(pov_binary):
- return pov_binary
- # Implicit else, as here return was still not triggered:
- print("User Preferences path to povray %r NOT FOUND, checking $PATH" % pov_binary)
-
- # Windows Only
- # assume if there is a 64bit binary that the user has a 64bit capable OS
- if platform.startswith('win'):
- import winreg
-
- win_reg_key = winreg.OpenKey(
- winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows"
- )
- win_home = winreg.QueryValueEx(win_reg_key, "Home")[0]
-
- # First try 64bits UberPOV
- pov_binary = os.path.join(win_home, "bin", "uberpov64.exe")
- if os.path.exists(pov_binary):
- return pov_binary
-
- # Then try 64bits POV
- pov_binary = os.path.join(win_home, "bin", "pvengine64.exe")
- if os.path.exists(pov_binary):
- return pov_binary
-
- # search the path all os's
- pov_binary_default = "povray"
-
- os_path_ls = os.getenv("PATH").split(':') + [""]
-
- for dir_name in os_path_ls:
- pov_binary = os.path.join(dir_name, pov_binary_default)
- if os.path.exists(pov_binary):
- return pov_binary
- return ""
-
- def _export(self, depsgraph, pov_path, image_render_path):
- """gather all necessary output files paths user defined and auto generated and export there"""
-
- scene = bpy.context.scene
- if scene.pov.tempfiles_enable:
- self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name
- # PNG with POV 3.7, can show the background color with alpha. In the long run using the
- # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
- self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name
- # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
- self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name
- self._temp_file_log = os.path.join(tempfile.gettempdir(), "alltext.out")
- else:
- self._temp_file_in = pov_path + ".pov"
- # PNG with POV 3.7, can show the background color with alpha. In the long run using the
- # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
- self._temp_file_out = image_render_path + ".png"
- # self._temp_file_out = image_render_path + ".tga"
- self._temp_file_ini = pov_path + ".ini"
- log_path = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/')
- self._temp_file_log = os.path.join(log_path, "alltext.out")
- '''
- self._temp_file_in = "/test.pov"
- # PNG with POV 3.7, can show the background color with alpha. In the long run using the
- # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
- self._temp_file_out = "/test.png"
- #self._temp_file_out = "/test.tga"
- self._temp_file_ini = "/test.ini"
- '''
- if scene.pov.text_block == "":
-
- def info_callback(txt):
- self.update_stats("", "POV-Ray 3.7: " + txt)
-
- # os.makedirs(user_dir, exist_ok=True) # handled with previews
- os.makedirs(preview_dir, exist_ok=True)
-
- write_pov(self._temp_file_in, scene, info_callback)
- else:
- pass
-
- def _render(self, depsgraph):
- """Export necessary files and render image."""
- scene = bpy.context.scene
- try:
- os.remove(self._temp_file_out) # so as not to load the old file
- except OSError:
- pass
-
- pov_binary = PovrayRender._locate_binary()
- if not pov_binary:
- print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed")
- return False
-
- write_pov_ini(
- self._temp_file_ini, self._temp_file_log, self._temp_file_in, self._temp_file_out
- )
-
- print("***-STARTING-***")
-
- extra_args = []
-
- if scene.pov.command_line_switches != "":
- for new_arg in scene.pov.command_line_switches.split(" "):
- extra_args.append(new_arg)
-
- self._is_windows = False
- if platform.startswith('win'):
- self._is_windows = True
- if "/EXIT" not in extra_args and not scene.pov.pov_editor:
- extra_args.append("/EXIT")
- else:
- # added -d option to prevent render window popup which leads to segfault on linux
- extra_args.append("-d")
-
- # Start Rendering!
- try:
- self._process = subprocess.Popen(
- [pov_binary, self._temp_file_ini] + extra_args,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- )
- except OSError:
- # TODO, report api
- print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
- import traceback
-
- traceback.print_exc()
- print("***-DONE-***")
- return False
-
- else:
- print("Engine ready!...")
- print("Command line arguments passed: " + str(extra_args))
- return True
-
- # Now that we have a valid process
-
- def _cleanup(self):
- """Delete temp files and unpacked ones"""
- for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out):
- for i in range(5):
- try:
- os.unlink(f)
- break
- except OSError:
- # Wait a bit before retrying file might be still in use by Blender,
- # and Windows does not know how to delete a file in use!
- time.sleep(self.DELAY)
- for i in unpacked_images:
- for j in range(5):
- try:
- os.unlink(i)
- break
- except OSError:
- # Wait a bit before retrying file might be still in use by Blender,
- # and Windows does not know how to delete a file in use!
- time.sleep(self.DELAY)
-
- def render(self, depsgraph):
- """Export necessary files from text editor and render image."""
-
- scene = bpy.context.scene
- r = scene.render
- x = int(r.resolution_x * r.resolution_percentage * 0.01)
- y = int(r.resolution_y * r.resolution_percentage * 0.01)
- print("***INITIALIZING***")
-
- # This makes some tests on the render, returning True if all goes good, and False if
- # it was finished one way or the other.
- # It also pauses the script (time.sleep())
- def _test_wait():
- time.sleep(self.DELAY)
-
- # User interrupts the rendering
- if self.test_break():
- try:
- self._process.terminate()
- print("***POV INTERRUPTED***")
- except OSError:
- pass
- return False
- try:
- poll_result = self._process.poll()
- except AttributeError:
- print("***CHECK POV PATH IN PREFERENCES***")
- return False
- # POV process is finisehd, one way or the other
- if poll_result is not None:
- if poll_result < 0:
- print("***POV PROCESS FAILED : %s ***" % poll_result)
- self.update_stats("", "POV-Ray 3.7: Failed")
- return False
-
- return True
-
- if bpy.context.scene.pov.text_block != "":
- if scene.pov.tempfiles_enable:
- self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name
- self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name
- # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
- self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name
- self._temp_file_log = os.path.join(tempfile.gettempdir(), "alltext.out")
- else:
- pov_path = scene.pov.text_block
- image_render_path = os.path.splitext(pov_path)[0]
- self._temp_file_out = os.path.join(preview_dir, image_render_path)
- self._temp_file_in = os.path.join(preview_dir, pov_path)
- self._temp_file_ini = os.path.join(
- preview_dir, (os.path.splitext(self._temp_file_in)[0] + ".INI")
- )
- self._temp_file_log = os.path.join(preview_dir, "alltext.out")
-
- '''
- try:
- os.remove(self._temp_file_in) # so as not to load the old file
- except OSError:
- pass
- '''
- print(scene.pov.text_block)
- text = bpy.data.texts[scene.pov.text_block]
- 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)
-
- pov_binary = PovrayRender._locate_binary()
-
- if not pov_binary:
- print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed")
- return False
-
- # start ini UI options export
- self.update_stats("", "POV-Ray 3.7: Exporting ini options from Blender")
-
- write_pov_ini(
- self._temp_file_ini,
- self._temp_file_log,
- self._temp_file_in,
- self._temp_file_out,
- )
-
- print("***-STARTING-***")
-
- extra_args = []
-
- if scene.pov.command_line_switches != "":
- for new_arg in scene.pov.command_line_switches.split(" "):
- extra_args.append(new_arg)
-
- if platform.startswith('win'):
- if "/EXIT" not in extra_args and not scene.pov.pov_editor:
- extra_args.append("/EXIT")
- else:
- # added -d option to prevent render window popup which leads to segfault on linux
- extra_args.append("-d")
-
- # Start Rendering!
- try:
- if scene.pov.sdl_window_enable and not platform.startswith(
- 'win'
- ): # segfault on linux == False !!!
- env = {'POV_DISPLAY_SCALED': 'off'}
- env.update(os.environ)
- self._process = subprocess.Popen(
- [pov_binary, self._temp_file_ini],
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- env=env,
- )
- else:
- self._process = subprocess.Popen(
- [pov_binary, self._temp_file_ini] + extra_args,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- )
- except OSError:
- # TODO, report api
- print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
- import traceback
-
- traceback.print_exc()
- print("***-DONE-***")
- return False
-
- else:
- print("Engine ready!...")
- print("Command line arguments passed: " + str(extra_args))
- # return True
- self.update_stats("", "POV-Ray 3.7: Parsing File")
-
- # Indented in main function now so repeated here but still not working
- # to bring back render result to its buffer
-
- if os.path.exists(self._temp_file_out):
- xmin = int(r.border_min_x * x)
- ymin = int(r.border_min_y * y)
- xmax = int(r.border_max_x * x)
- ymax = int(r.border_max_y * y)
- result = self.begin_result(0, 0, x, y)
- lay = result.layers[0]
-
- time.sleep(self.DELAY)
- try:
- lay.load_from_file(self._temp_file_out)
- except RuntimeError:
- print("***POV ERROR WHILE READING OUTPUT FILE***")
- self.end_result(result)
- # print(self._temp_file_log) #bring the pov log to blender console with proper path?
- with open(
- self._temp_file_log
- ) as f: # The with keyword automatically closes the file when you are done
- print(f.read())
-
- self.update_stats("", "")
-
- if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
- self._cleanup()
- else:
-
- # WIP output format
- # if r.image_settings.file_format == 'OPENEXR':
- # fformat = 'EXR'
- # render.image_settings.color_mode = 'RGBA'
- # else:
- # fformat = 'TGA'
- # r.image_settings.file_format = 'TARGA'
- # r.image_settings.color_mode = 'RGBA'
-
- blend_scene_name = bpy.data.filepath.split(os.path.sep)[-1].split(".")[0]
- pov_scene_name = ""
- pov_path = ""
- image_render_path = ""
-
- # has to be called to update the frame on exporting animations
- scene.frame_set(scene.frame_current)
-
- if not scene.pov.tempfiles_enable:
-
- # check paths
- pov_path = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/')
- if pov_path == "":
- if bpy.data.is_saved:
- pov_path = bpy.path.abspath("//")
- else:
- pov_path = tempfile.gettempdir()
- elif pov_path.endswith("/"):
- if pov_path == "/":
- pov_path = bpy.path.abspath("//")
- else:
- pov_path = bpy.path.abspath(scene.pov.scene_path)
-
- if not os.path.exists(pov_path):
- try:
- os.makedirs(pov_path)
- except BaseException as e:
- print(e.__doc__)
- print('An exception occurred: {}'.format(e))
- import traceback
-
- traceback.print_exc()
-
- print("POV-Ray 3.7: Cannot create scenes directory: %r" % pov_path)
- self.update_stats(
- "", "POV-Ray 3.7: Cannot create scenes directory %r" % pov_path
- )
- time.sleep(2.0)
- # return
-
- '''
- # Bug in POV-Ray RC3
- image_render_path = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/')
- if image_render_path == "":
- if bpy.data.is_saved:
- image_render_path = bpy.path.abspath("//")
- else:
- image_render_path = tempfile.gettempdir()
- #print("Path: " + image_render_path)
- elif path.endswith("/"):
- if image_render_path == "/":
- image_render_path = bpy.path.abspath("//")
- else:
- image_render_path = bpy.path.abspath(scene.pov.)
- if not os.path.exists(path):
- print("POV-Ray 3.7: Cannot find render image directory")
- self.update_stats("", "POV-Ray 3.7: Cannot find render image directory")
- time.sleep(2.0)
- return
- '''
-
- # check name
- if scene.pov.scene_name == "":
- if blend_scene_name != "":
- pov_scene_name = blend_scene_name
- else:
- pov_scene_name = "untitled"
- else:
- pov_scene_name = scene.pov.scene_name
- if os.path.isfile(pov_scene_name):
- pov_scene_name = os.path.basename(pov_scene_name)
- pov_scene_name = pov_scene_name.split('/')[-1].split('\\')[-1]
- if not pov_scene_name:
- print("POV-Ray 3.7: Invalid scene name")
- self.update_stats("", "POV-Ray 3.7: Invalid scene name")
- time.sleep(2.0)
- # return
- pov_scene_name = os.path.splitext(pov_scene_name)[0]
-
- print("Scene name: " + pov_scene_name)
- print("Export path: " + pov_path)
- pov_path = os.path.join(pov_path, pov_scene_name)
- pov_path = os.path.realpath(pov_path)
-
- image_render_path = pov_path
- # print("Render Image path: " + image_render_path)
-
- # start export
- self.update_stats("", "POV-Ray 3.7: Exporting data from Blender")
- self._export(depsgraph, pov_path, image_render_path)
- self.update_stats("", "POV-Ray 3.7: Parsing File")
-
- if not self._render(depsgraph):
- self.update_stats("", "POV-Ray 3.7: Not found")
- # return
-
- # r = scene.render
- # compute resolution
- # x = int(r.resolution_x * r.resolution_percentage * 0.01)
- # y = int(r.resolution_y * r.resolution_percentage * 0.01)
-
- # Wait for the file to be created
- # XXX This is no more valid, as 3.7 always creates output file once render is finished!
- parsing = re.compile(br"= \[Parsing\.\.\.\] =")
- rendering = re.compile(br"= \[Rendering\.\.\.\] =")
- percent = re.compile(r"\(([0-9]{1,3})%\)")
- # print("***POV WAITING FOR FILE***")
-
- data = b""
- last_line = ""
- while _test_wait():
- # POV in Windows did not output its stdout/stderr, it displayed them in its GUI
- # But now writes file
- if self._is_windows:
- self.update_stats("", "POV-Ray 3.7: Rendering File")
- else:
- t_data = self._process.stdout.read(10000)
- if not t_data:
- continue
-
- data += t_data
- # XXX This is working for UNIX, not sure whether it might need adjustments for
- # other OSs
- # First replace is for windows
- t_data = str(t_data).replace('\\r\\n', '\\n').replace('\\r', '\r')
- lines = t_data.split('\\n')
- last_line += lines[0]
- lines[0] = last_line
- print('\n'.join(lines), end="")
- last_line = lines[-1]
-
- if rendering.search(data):
- _pov_rendering = True
- match = percent.findall(str(data))
- if match:
- self.update_stats("", "POV-Ray 3.7: Rendering File (%s%%)" % match[-1])
- else:
- self.update_stats("", "POV-Ray 3.7: Rendering File")
-
- elif parsing.search(data):
- self.update_stats("", "POV-Ray 3.7: Parsing File")
-
- if os.path.exists(self._temp_file_out):
- # print("***POV FILE OK***")
- # self.update_stats("", "POV-Ray 3.7: Rendering")
-
- # prev_size = -1
-
- xmin = int(r.border_min_x * x)
- ymin = int(r.border_min_y * y)
- xmax = int(r.border_max_x * x)
- ymax = int(r.border_max_y * y)
-
- # print("***POV UPDATING IMAGE***")
- result = self.begin_result(0, 0, x, y)
- # XXX, tests for border render.
- # result = self.begin_result(xmin, ymin, xmax - xmin, ymax - ymin)
- # result = self.begin_result(0, 0, xmax - xmin, ymax - ymin)
- lay = result.layers[0]
-
- # This assumes the file has been fully written We wait a bit, just in case!
- time.sleep(self.DELAY)
- try:
- lay.load_from_file(self._temp_file_out)
- # XXX, tests for border render.
- # lay.load_from_file(self._temp_file_out, xmin, ymin)
- except RuntimeError:
- print("***POV ERROR WHILE READING OUTPUT FILE***")
-
- # Not needed right now, might only be useful if we find a way to use temp raw output of
- # pov 3.7 (in which case it might go under _test_wait()).
- '''
- def update_image():
- # possible the image wont load early on.
- try:
- lay.load_from_file(self._temp_file_out)
- # XXX, tests for border render.
- #lay.load_from_file(self._temp_file_out, xmin, ymin)
- #lay.load_from_file(self._temp_file_out, xmin, ymin)
- except RuntimeError:
- pass
-
- # Update while POV-Ray renders
- while True:
- # print("***POV RENDER LOOP***")
-
- # test if POV-Ray exists
- if self._process.poll() is not None:
- print("***POV PROCESS FINISHED***")
- update_image()
- break
-
- # user exit
- if self.test_break():
- try:
- self._process.terminate()
- print("***POV PROCESS INTERRUPTED***")
- except OSError:
- pass
-
- break
-
- # Would be nice to redirect the output
- # stdout_value, stderr_value = self._process.communicate() # locks
-
- # check if the file updated
- new_size = os.path.getsize(self._temp_file_out)
-
- if new_size != prev_size:
- update_image()
- prev_size = new_size
-
- time.sleep(self.DELAY)
- '''
-
- self.end_result(result)
-
- else:
- print("***POV FILE NOT FOUND***")
-
- print("***POV FILE FINISHED***")
-
- # print(filename_log) #bring the pov log to blender console with proper path?
- with open(
- self._temp_file_log, encoding='utf-8'
- ) as f: # The with keyword automatically closes the file when you are done
- msg = f.read()
- # if isinstance(msg, str):
- # stdmsg = msg
- # decoded = False
- # else:
- # if type(msg) == bytes:
- # stdmsg = msg.split('\n')
- # stdmsg = msg.encode('utf-8', "replace")
- # stdmsg = msg.encode("utf-8", "replace")
-
- # stdmsg = msg.decode(encoding)
- # decoded = True
- # msg.encode('utf-8').decode('utf-8')
- msg.replace("\t", " ")
- print(msg)
- # Also print to the interactive console used in POV centric workspace
- # To do: get a grip on new line encoding
- # and make this a function to be used elsewhere
- for win in bpy.context.window_manager.windows:
- if win.screen is not None:
- scr = win.screen
- for area in scr.areas:
- if area.type == 'CONSOLE':
- try:
- # context override
- ctx = {
- 'area': area,
- 'screen': scr,
- 'window': win
- }
-
- # bpy.ops.console.banner(ctx, text = "Hello world")
- bpy.ops.console.clear_line(ctx)
- 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__)
- print('An exception occurred: {}'.format(e))
- pass
-
- self.update_stats("", "")
-
- if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
- self._cleanup()
-
- sound_on = bpy.context.preferences.addons[__package__].preferences.use_sounds
- finished_render_message = "\'Et Voilà!\'"
-
- if platform.startswith('win') and sound_on:
- # Could not find tts Windows command so playing beeps instead :-)
- # "Korobeiniki"(Коробе́йники)
- # aka "A-Type" Tetris theme
- import winsound
-
- winsound.Beep(494, 250) # B
- winsound.Beep(370, 125) # F
- winsound.Beep(392, 125) # G
- winsound.Beep(440, 250) # A
- winsound.Beep(392, 125) # G
- winsound.Beep(370, 125) # F#
- winsound.Beep(330, 275) # E
- winsound.Beep(330, 125) # E
- winsound.Beep(392, 125) # G
- winsound.Beep(494, 275) # B
- winsound.Beep(440, 125) # A
- winsound.Beep(392, 125) # G
- winsound.Beep(370, 275) # F
- winsound.Beep(370, 125) # F
- winsound.Beep(392, 125) # G
- winsound.Beep(440, 250) # A
- winsound.Beep(494, 250) # B
- winsound.Beep(392, 250) # G
- winsound.Beep(330, 350) # E
- time.sleep(0.5)
- winsound.Beep(440, 250) # A
- winsound.Beep(440, 150) # A
- winsound.Beep(523, 125) # D8
- winsound.Beep(659, 250) # E8
- winsound.Beep(587, 125) # D8
- winsound.Beep(523, 125) # C8
- winsound.Beep(494, 250) # B
- winsound.Beep(494, 125) # B
- winsound.Beep(392, 125) # G
- winsound.Beep(494, 250) # B
- winsound.Beep(440, 150) # A
- winsound.Beep(392, 125) # G
- winsound.Beep(370, 250) # F#
- winsound.Beep(370, 125) # F#
- winsound.Beep(392, 125) # G
- winsound.Beep(440, 250) # A
- winsound.Beep(494, 250) # B
- winsound.Beep(392, 250) # G
- winsound.Beep(330, 300) # E
-
- # Mac supports natively say command
- elif platform == "darwin":
- # We don't want the say command to block Python,
- # so we add an ampersand after the message
- # 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 espeak command to block Python,
- # so we add an ampersand after the 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
-
-
-
# --------------------------------------------------------------------------------- #
# ----------------------------------- Operators ----------------------------------- #
# --------------------------------------------------------------------------------- #
@@ -1703,7 +703,7 @@ class RenderPovTexturePreview(Operator):
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(texturing_procedural.export_pattern(tex))
file_pov.write("#declare Plane =\n")
file_pov.write("mesh {\n")
@@ -1735,21 +735,22 @@ class RenderPovTexturePreview(Operator):
file_pov.close()
# ------------------------------- end write ------------------------------- #
- pov_binary = PovrayRender._locate_binary()
+ pov_binary = PovRender._locate_binary()
if platform.startswith('win'):
- p1 = subprocess.Popen(
+ with subprocess.Popen(
["%s" % pov_binary, "/EXIT", "%s" % ini_prev_file],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
- )
+ ) as p1:
+ p1.wait()
else:
- p1 = subprocess.Popen(
+ with subprocess.Popen(
["%s" % pov_binary, "-d", "%s" % ini_prev_file],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
- )
- p1.wait()
+ ) as p1:
+ p1.wait()
tex.use_nodes = True
tree = tex.node_tree
@@ -1793,7 +794,7 @@ class RunPovTextRender(Operator):
classes = (
- PovrayRender,
+ #PovRender,
RenderPovTexturePreview,
RunPovTextRender,
)
diff --git a/render_povray/render_core.py b/render_povray/render_core.py
new file mode 100644
index 00000000..77714c6f
--- /dev/null
+++ b/render_povray/render_core.py
@@ -0,0 +1,784 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Define the POV render engine from generic Blender RenderEngine class."""
+import faulthandler
+faulthandler.enable()
+import bpy
+
+import builtins as __builtin__
+import subprocess
+import os
+from sys import platform
+import time
+import re
+import tempfile
+from bpy.utils import register_class, unregister_class
+from . import render
+
+def console_get(context):
+ #context = bpy.context
+ for win in context.window_manager.windows:
+ if win.screen is not None:
+ scr = win.screen
+ for area in scr.areas:
+ if area.type == 'CONSOLE':
+ for space in area.spaces:
+ if space.type == 'CONSOLE':
+ return area, space, win, scr
+ return None, None, None, None
+
+def console_write(context, txt):
+ area, space, window, screen = console_get()
+ if space is None:
+ return
+ #context = bpy.context.copy()
+ context.update(dict(
+ area=area,
+ space_data=space,
+ region=area.regions[-1],
+ window=window,
+ screen=screen,
+ ))
+ for line in txt.split("\n"):
+ bpy.ops.console.scrollback_append(context, text=line, type='INFO')
+"""
+class RENDER_OT_test(bpy.types.Operator):
+ bl_idname = 'pov.oha_test'
+ bl_label = 'Test'
+ bl_options = {'REGISTER', 'UNDO'}
+
+ txt: bpy.props.StringProperty(
+ name='text',
+ default='what?'
+ )
+ def execute(self, context):
+ try:
+ console_write(context, self.txt)
+ return {'FINISHED'}
+ except:
+ self.report({'INFO'}, 'Printing report to Info window.')
+ return {'CANCELLED'}
+
+def console_print(*args, **kwargs):
+ context = bpy.context
+ #screens = (win.screen for win in context.window_manager.windows if win.screen is not None)
+ for win in context.window_manager.windows:
+ if win.screen is not None:
+ scr = win.screen
+ for a in scr.areas:
+ if a.type == 'CONSOLE':
+ try:
+ c = {}
+ c['area'] = a
+ c['space_data'] = a.spaces.active
+ c['region'] = a.regions[-1]
+ c['window'] = win
+ c['screen'] = scr
+ s = " ".join([str(arg) for arg in args])
+ for line in s.split("\n"):
+ bpy.ops.console.scrollback_append(c, text=line, type='INFO')
+
+ except BaseException as e:
+ print(e.__doc__)
+ print('An exception occurred: {}'.format(e))
+ pass
+
+
+def print(*args, **kwargs):
+ console_print(*args, **kwargs) # to Python Console
+ __builtin__.print(*args, **kwargs) # to System Console
+"""
+
+user_dir = bpy.utils.resource_path('USER')
+preview_dir = os.path.join(user_dir, "preview")
+# Make sure Preview directory exists and is empty
+smoke_path = os.path.join(preview_dir, "smoke.df3")
+
+class PovRender(bpy.types.RenderEngine):
+ """Define the external renderer"""
+
+ bl_idname = 'POVRAY_RENDER'
+ bl_label = "Persitence Of Vision"
+ bl_use_eevee_viewport = True
+ bl_use_shading_nodes_custom = False
+ DELAY = 0.5
+
+ @staticmethod
+ def _locate_binary():
+ """Identify POV engine"""
+ addon_prefs = bpy.context.preferences.addons[__package__].preferences
+
+ # Use the system preference if its set.
+ if pov_binary:= addon_prefs.filepath_povray:
+ if os.path.exists(pov_binary):
+ return pov_binary
+ # Implicit else, as here return was still not triggered:
+ print("User Preferences path to povray %r NOT FOUND, checking $PATH" % pov_binary)
+
+ # Windows Only
+ # assume if there is a 64bit binary that the user has a 64bit capable OS
+ if platform.startswith('win'):
+ import winreg
+
+ win_reg_key = winreg.OpenKey(
+ winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows"
+ )
+ win_home = winreg.QueryValueEx(win_reg_key, "Home")[0]
+
+ # First try 64bits UberPOV
+ pov_binary = os.path.join(win_home, "bin", "uberpov64.exe")
+ if os.path.exists(pov_binary):
+ return pov_binary
+
+ # Then try 64bits POV
+ pov_binary = os.path.join(win_home, "bin", "pvengine64.exe")
+ if os.path.exists(pov_binary):
+ return pov_binary
+
+ # search the path all os's
+ pov_binary_default = "povray"
+
+ os_path_ls = os.getenv("PATH").split(':') + [""]
+
+ for dir_name in os_path_ls:
+ pov_binary = os.path.join(dir_name, pov_binary_default)
+ if os.path.exists(pov_binary):
+ return pov_binary
+ return ""
+
+ def _export(self, depsgraph, pov_path, image_render_path):
+ """gather all necessary output files paths user defined and auto generated and export there"""
+
+ scene = bpy.context.scene
+ if scene.pov.tempfiles_enable:
+ self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name
+ # PNG with POV 3.7, can show the background color with alpha. In the long run using the
+ # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
+ self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name
+ # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
+ self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name
+ log_path = os.path.join(tempfile.gettempdir(), "alltext.out")
+ else:
+ self._temp_file_in = pov_path + ".pov"
+ # PNG with POV 3.7, can show the background color with alpha. In the long run using the
+ # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
+ self._temp_file_out = image_render_path + ".png"
+ # self._temp_file_out = image_render_path + ".tga"
+ self._temp_file_ini = pov_path + ".ini"
+ scene_path = scene.pov.scene_path
+ abs_log_path = bpy.path.abspath(scene_path)
+ log_path= os.path.join(abs_log_path, "alltext.out")
+ '''
+ self._temp_file_in = "/test.pov"
+ # PNG with POV 3.7, can show the background color with alpha. In the long run using the
+ # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
+ self._temp_file_out = "/test.png"
+ #self._temp_file_out = "/test.tga"
+ self._temp_file_ini = "/test.ini"
+ '''
+
+ self._temp_file_log = log_path
+ # self._temp_file_log = log_path.replace('\\', '/') # unnecessary relying on os.path
+
+ if scene.pov.text_block == "":
+
+ def info_callback(txt):
+ self.update_stats("", "POV-Ray 3.7: " + txt)
+
+ # os.makedirs(user_dir, exist_ok=True) # handled with previews
+ os.makedirs(preview_dir, exist_ok=True)
+
+ render.write_pov(self._temp_file_in, scene, info_callback)
+ else:
+ pass
+
+ def _render(self, depsgraph):
+ """Export necessary files and render image."""
+ scene = bpy.context.scene
+ try:
+ os.remove(self._temp_file_out) # so as not to load the old file
+ except OSError:
+ pass
+
+ pov_binary = PovRender._locate_binary()
+ if not pov_binary:
+ print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed")
+ return False
+
+ render.write_pov_ini(
+ self._temp_file_ini, self._temp_file_log, self._temp_file_in, self._temp_file_out
+ )
+
+ print("***-STARTING-***")
+
+ extra_args = []
+ # Always add user preferences include path field when specified
+ if (pov_documents := bpy.context.preferences.addons[__package__].preferences.docpath_povray)!="":
+ extra_args.append("+L"+ pov_documents)
+ if scene.pov.command_line_switches != "":
+ extra_args.extend(iter(scene.pov.command_line_switches.split(" ")))
+ self._is_windows = False
+ if platform.startswith('win'):
+ self._is_windows = True
+ if "/EXIT" not in extra_args and not scene.pov.pov_editor:
+ extra_args.append("/EXIT")
+ else:
+ # added -d option to prevent render window popup which leads to segfault on linux
+ extra_args.append("-d")
+
+ # Start Rendering!
+ try:
+ self._process = subprocess.Popen(
+ [pov_binary, self._temp_file_ini] + extra_args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ except OSError:
+ # TODO, report api
+ print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
+ import traceback
+
+ traceback.print_exc()
+ print("***-DONE-***")
+ return False
+
+ else:
+ print("Engine ready!...")
+ print("Command line arguments passed: " + str(extra_args))
+ return True
+
+ def _cleanup(self):
+ """Delete temp files and unpacked ones"""
+ for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out):
+ for i in range(5):
+ try:
+ os.unlink(f)
+ break
+ except OSError:
+ # Wait a bit before retrying file might be still in use by Blender,
+ # and Windows does not know how to delete a file in use!
+ time.sleep(self.DELAY)
+ for i in render.unpacked_images:
+ for j in range(5):
+ try:
+ os.unlink(i)
+ break
+ except OSError:
+ # Wait a bit before retrying file might be still in use by Blender,
+ # and Windows does not know how to delete a file in use!
+ time.sleep(self.DELAY)
+ # avoid some crashes if memory leaks from one render to the next?
+ #self.free_blender_memory()
+
+ def render(self, depsgraph):
+ """Export necessary files from text editor and render image."""
+
+ scene = bpy.context.scene
+ r = scene.render
+ x = int(r.resolution_x * r.resolution_percentage * 0.01)
+ y = int(r.resolution_y * r.resolution_percentage * 0.01)
+ print("\n***INITIALIZING***")
+
+ # This makes some tests on the render, returning True if all goes good, and False if
+ # it was finished one way or the other.
+ # It also pauses the script (time.sleep())
+ def _test_wait():
+ time.sleep(self.DELAY)
+
+ # User interrupts the rendering
+ if self.test_break():
+ try:
+ self._process.terminate()
+ print("***POV INTERRUPTED***")
+ except OSError:
+ pass
+ return False
+ try:
+ poll_result = self._process.poll()
+ except AttributeError:
+ print("***CHECK POV PATH IN PREFERENCES***")
+ return False
+ # POV process is finisehd, one way or the other
+ if poll_result is not None:
+ if poll_result < 0:
+ print("***POV PROCESS FAILED : %s ***" % poll_result)
+ self.update_stats("", "POV-Ray 3.7: Failed")
+ return False
+
+ return True
+
+ if bpy.context.scene.pov.text_block != "":
+ if scene.pov.tempfiles_enable:
+ self._temp_file_in = tempfile.NamedTemporaryFile(suffix=".pov", delete=False).name
+ self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name
+ # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
+ self._temp_file_ini = tempfile.NamedTemporaryFile(suffix=".ini", delete=False).name
+ self._temp_file_log = os.path.join(tempfile.gettempdir(), "alltext.out")
+ else:
+ pov_path = scene.pov.text_block
+ image_render_path = os.path.splitext(pov_path)[0]
+ self._temp_file_out = os.path.join(preview_dir, image_render_path)
+ self._temp_file_in = os.path.join(preview_dir, pov_path)
+ self._temp_file_ini = os.path.join(
+ preview_dir, (os.path.splitext(self._temp_file_in)[0] + ".INI")
+ )
+ self._temp_file_log = os.path.join(preview_dir, "alltext.out")
+
+ '''
+ try:
+ os.remove(self._temp_file_in) # so as not to load the old file
+ except OSError:
+ pass
+ '''
+ print(scene.pov.text_block)
+ text = bpy.data.texts[scene.pov.text_block]
+ 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")
+
+
+ # has to be called to update the frame on exporting animations
+ scene.frame_set(scene.frame_current)
+
+ pov_binary = PovRender._locate_binary()
+
+ if not pov_binary:
+ print("Could not execute POV-Ray, which installation possibly isn't standard ?")
+ return False
+
+ # start ini UI options export
+ self.update_stats("", "POV-Ray 3.7: Exporting ini options from Blender")
+
+ render.write_pov_ini(
+ self._temp_file_ini,
+ self._temp_file_log,
+ self._temp_file_in,
+ self._temp_file_out,
+ )
+
+ print("***-STARTING-***")
+
+ extra_args = []
+
+ if scene.pov.command_line_switches != "":
+ for new_arg in scene.pov.command_line_switches.split(" "):
+ extra_args.append(new_arg)
+
+ if platform.startswith('win'):
+ if "/EXIT" not in extra_args and not scene.pov.pov_editor:
+ extra_args.append("/EXIT")
+ else:
+ # added -d option to prevent render window popup which leads to segfault on linux
+ extra_args.append("-d")
+
+ # Start Rendering!
+ try:
+ if scene.pov.sdl_window_enable and not platform.startswith(
+ 'win'
+ ): # segfault on linux == False !!!
+ env = {'POV_DISPLAY_SCALED': 'off'}
+ env.update(os.environ)
+ self._process = subprocess.Popen(
+ [pov_binary, self._temp_file_ini],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ env=env,
+ )
+ else:
+ self._process = subprocess.Popen(
+ [pov_binary, self._temp_file_ini] + extra_args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ except OSError:
+ # TODO, report api
+ print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
+ import traceback
+
+ traceback.print_exc()
+ print("***-DONE-***")
+ return False
+
+ else:
+ print("Engine ready!...")
+ print("Command line arguments passed: " + str(extra_args))
+ # return True
+ self.update_stats("", "POV-Ray 3.7: Parsing File")
+
+ # Indented in main function now so repeated here but still not working
+ # to bring back render result to its buffer
+
+ if os.path.exists(self._temp_file_out):
+ xmin = int(r.border_min_x * x)
+ ymin = int(r.border_min_y * y)
+ xmax = int(r.border_max_x * x)
+ ymax = int(r.border_max_y * y)
+ result = self.begin_result(0, 0, x, y)
+ lay = result.layers[0]
+
+ time.sleep(self.DELAY)
+ try:
+ lay.load_from_file(self._temp_file_out)
+ except RuntimeError:
+ print("***POV ERROR WHILE READING OUTPUT FILE***")
+ self.end_result(result)
+ # print(self._temp_file_log) #bring the pov log to blender console with proper path?
+ with open(
+ self._temp_file_log
+ ) as f: # The with keyword automatically closes the file when you are done
+ print(f.read()) # console_write(f.read())
+
+ self.update_stats("", "")
+
+ if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
+ self._cleanup()
+ else:
+
+ # WIP output format
+ # if r.image_settings.file_format == 'OPENEXR':
+ # fformat = 'EXR'
+ # render.image_settings.color_mode = 'RGBA'
+ # else:
+ # fformat = 'TGA'
+ # r.image_settings.file_format = 'TARGA'
+ # r.image_settings.color_mode = 'RGBA'
+
+ blend_scene_name = bpy.data.filepath.split(os.path.sep)[-1].split(".")[0]
+ pov_scene_name = ""
+ pov_path = ""
+ image_render_path = ""
+
+ # has to be called to update the frame on exporting animations
+ scene.frame_set(scene.frame_current)
+
+ if not scene.pov.tempfiles_enable:
+
+ # check paths
+ pov_path = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/')
+ if pov_path == "":
+ if bpy.data.is_saved:
+ pov_path = bpy.path.abspath("//")
+ else:
+ pov_path = tempfile.gettempdir()
+ elif pov_path.endswith("/"):
+ if pov_path == "/":
+ pov_path = bpy.path.abspath("//")
+ else:
+ pov_path = bpy.path.abspath(scene.pov.scene_path)
+
+ if not os.path.exists(pov_path):
+ try:
+ os.makedirs(pov_path)
+ except BaseException as e:
+ print(e.__doc__)
+ print('An exception occurred: {}'.format(e))
+ import traceback
+
+ traceback.print_exc()
+
+ print("POV-Ray 3.7: Cannot create scenes directory: %r" % pov_path)
+ self.update_stats(
+ "", "POV-Ray 3.7: Cannot create scenes directory %r" % pov_path
+ )
+ time.sleep(2.0)
+ # return
+
+ '''
+ # Bug in POV-Ray RC3
+ image_render_path = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/')
+ if image_render_path == "":
+ if bpy.data.is_saved:
+ image_render_path = bpy.path.abspath("//")
+ else:
+ image_render_path = tempfile.gettempdir()
+ #print("Path: " + image_render_path)
+ elif path.endswith("/"):
+ if image_render_path == "/":
+ image_render_path = bpy.path.abspath("//")
+ else:
+ image_render_path = bpy.path.abspath(scene.pov.)
+ if not os.path.exists(path):
+ print("POV-Ray 3.7: Cannot find render image directory")
+ self.update_stats("", "POV-Ray 3.7: Cannot find render image directory")
+ time.sleep(2.0)
+ return
+ '''
+
+ # check name
+ if scene.pov.scene_name == "":
+ if blend_scene_name != "":
+ pov_scene_name = blend_scene_name
+ else:
+ pov_scene_name = "untitled"
+ else:
+ pov_scene_name = scene.pov.scene_name
+ if os.path.isfile(pov_scene_name):
+ pov_scene_name = os.path.basename(pov_scene_name)
+ pov_scene_name = pov_scene_name.split('/')[-1].split('\\')[-1]
+ if not pov_scene_name:
+ print("POV-Ray 3.7: Invalid scene name")
+ self.update_stats("", "POV-Ray 3.7: Invalid scene name")
+ time.sleep(2.0)
+ # return
+ pov_scene_name = os.path.splitext(pov_scene_name)[0]
+
+ print("Scene name: " + pov_scene_name)
+ print("Export path: " + pov_path)
+ pov_path = os.path.join(pov_path, pov_scene_name)
+ pov_path = os.path.realpath(pov_path)
+
+ image_render_path = pov_path
+ # print("Render Image path: " + image_render_path)
+
+ # start export
+ self.update_stats("", "POV-Ray 3.7: Exporting data from Blender")
+ self._export(depsgraph, pov_path, image_render_path)
+ self.update_stats("", "POV-Ray 3.7: Parsing File")
+
+ if not self._render(depsgraph):
+ self.update_stats("", "POV-Ray 3.7: Not found")
+ # return
+
+ # r = scene.render
+ # compute resolution
+ # x = int(r.resolution_x * r.resolution_percentage * 0.01)
+ # y = int(r.resolution_y * r.resolution_percentage * 0.01)
+
+ # Wait for the file to be created
+ # XXX This is no more valid, as 3.7 always creates output file once render is finished!
+ parsing = re.compile(br"= \[Parsing\.\.\.\] =")
+ rendering = re.compile(br"= \[Rendering\.\.\.\] =")
+ percent = re.compile(r"\(([0-9]{1,3})%\)")
+ # print("***POV WAITING FOR FILE***")
+
+ data = b""
+ last_line = ""
+ while _test_wait():
+ # POV in Windows did not output its stdout/stderr, it displayed them in its GUI
+ # But now writes file
+ if self._is_windows:
+ self.update_stats("", "POV-Ray 3.7: Rendering File")
+ else:
+ t_data = self._process.stdout.read(10000)
+ if not t_data:
+ continue
+
+ data += t_data
+ # XXX This is working for UNIX, not sure whether it might need adjustments for
+ # other OSs
+ # First replace is for windows
+ t_data = str(t_data).replace('\\r\\n', '\\n').replace('\\r', '\r')
+ lines = t_data.split('\\n')
+ last_line += lines[0]
+ lines[0] = last_line
+ print('\n'.join(lines), end="")
+ last_line = lines[-1]
+
+ if rendering.search(data):
+ _pov_rendering = True
+ match = percent.findall(str(data))
+ if match:
+ self.update_stats("", "POV-Ray 3.7: Rendering File (%s%%)" % match[-1])
+ else:
+ self.update_stats("", "POV-Ray 3.7: Rendering File")
+
+ elif parsing.search(data):
+ self.update_stats("", "POV-Ray 3.7: Parsing File")
+
+ if os.path.exists(self._temp_file_out):
+ # print("***POV FILE OK***")
+ # self.update_stats("", "POV-Ray 3.7: Rendering")
+
+ # prev_size = -1
+
+ xmin = int(r.border_min_x * x)
+ ymin = int(r.border_min_y * y)
+ xmax = int(r.border_max_x * x)
+ ymax = int(r.border_max_y * y)
+
+ # print("***POV UPDATING IMAGE***")
+ result = self.begin_result(0, 0, x, y)
+ # XXX, tests for border render.
+ # result = self.begin_result(xmin, ymin, xmax - xmin, ymax - ymin)
+ # result = self.begin_result(0, 0, xmax - xmin, ymax - ymin)
+ lay = result.layers[0]
+
+ # This assumes the file has been fully written We wait a bit, just in case!
+ time.sleep(self.DELAY)
+ try:
+ lay.load_from_file(self._temp_file_out)
+ # XXX, tests for border render.
+ # lay.load_from_file(self._temp_file_out, xmin, ymin)
+ except RuntimeError:
+ print("***POV ERROR WHILE READING OUTPUT FILE***")
+
+ # Not needed right now, might only be useful if we find a way to use temp raw output of
+ # pov 3.7 (in which case it might go under _test_wait()).
+ '''
+ def update_image():
+ # possible the image wont load early on.
+ try:
+ lay.load_from_file(self._temp_file_out)
+ # XXX, tests for border render.
+ #lay.load_from_file(self._temp_file_out, xmin, ymin)
+ #lay.load_from_file(self._temp_file_out, xmin, ymin)
+ except RuntimeError:
+ pass
+
+ # Update while POV-Ray renders
+ while True:
+ # print("***POV RENDER LOOP***")
+
+ # test if POV-Ray exists
+ if self._process.poll() is not None:
+ print("***POV PROCESS FINISHED***")
+ update_image()
+ break
+
+ # user exit
+ if self.test_break():
+ try:
+ self._process.terminate()
+ print("***POV PROCESS INTERRUPTED***")
+ except OSError:
+ pass
+
+ break
+
+ # Would be nice to redirect the output
+ # stdout_value, stderr_value = self._process.communicate() # locks
+
+ # check if the file updated
+ new_size = os.path.getsize(self._temp_file_out)
+
+ if new_size != prev_size:
+ update_image()
+ prev_size = new_size
+
+ time.sleep(self.DELAY)
+ '''
+
+ self.end_result(result)
+ else:
+ print("***NO POV OUTPUT IMAGE***")
+
+ print("***POV INPUT FILE WRITTEN***")
+
+ # print(filename_log) #bring the pov log to blender console with proper path?
+ try:
+ with open(
+ self._temp_file_log, encoding='utf-8'
+ ) as f: # The with keyword automatically closes the file when you are done
+ msg = f.read()
+ if isinstance(msg, str):
+ stdmsg = msg
+ #decoded = False
+ elif type(msg) == bytes:
+ #stdmsg = msg.split('\n')
+ stdmsg = msg.encode('utf-8', "replace")
+ # stdmsg = msg.encode("utf-8", "replace")
+
+ # stdmsg = msg.decode(encoding)
+ # decoded = True
+ # msg.encode('utf-8').decode('utf-8')
+ stdmsg.replace("\t", " ")
+ print(stdmsg) # console_write(stdmsg) # todo fix segfault and use
+ except FileNotFoundError:
+ print("No render log to read")
+ self.update_stats("", "")
+
+ if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
+ self._cleanup()
+
+ sound_on = bpy.context.preferences.addons[__package__].preferences.use_sounds
+ finished_render_message = "\'Et Voilà!\'"
+
+ if platform.startswith('win') and sound_on:
+ # Could not find tts Windows command so playing beeps instead :-)
+ # "Korobeiniki"(Коробе́йники)
+ # aka "A-Type" Tetris theme
+ import winsound
+
+ winsound.Beep(494, 250) # B
+ winsound.Beep(370, 125) # F
+ winsound.Beep(392, 125) # G
+ winsound.Beep(440, 250) # A
+ winsound.Beep(392, 125) # G
+ winsound.Beep(370, 125) # F#
+ winsound.Beep(330, 275) # E
+ winsound.Beep(330, 125) # E
+ winsound.Beep(392, 125) # G
+ winsound.Beep(494, 275) # B
+ winsound.Beep(440, 125) # A
+ winsound.Beep(392, 125) # G
+ winsound.Beep(370, 275) # F
+ winsound.Beep(370, 125) # F
+ winsound.Beep(392, 125) # G
+ winsound.Beep(440, 250) # A
+ winsound.Beep(494, 250) # B
+ winsound.Beep(392, 250) # G
+ winsound.Beep(330, 350) # E
+ time.sleep(0.5)
+ winsound.Beep(440, 250) # A
+ winsound.Beep(440, 150) # A
+ winsound.Beep(523, 125) # D8
+ winsound.Beep(659, 250) # E8
+ winsound.Beep(587, 125) # D8
+ winsound.Beep(523, 125) # C8
+ winsound.Beep(494, 250) # B
+ winsound.Beep(494, 125) # B
+ winsound.Beep(392, 125) # G
+ winsound.Beep(494, 250) # B
+ winsound.Beep(440, 150) # A
+ winsound.Beep(392, 125) # G
+ winsound.Beep(370, 250) # F#
+ winsound.Beep(370, 125) # F#
+ winsound.Beep(392, 125) # G
+ winsound.Beep(440, 250) # A
+ winsound.Beep(494, 250) # B
+ winsound.Beep(392, 250) # G
+ winsound.Beep(330, 300) # E
+
+ # Mac supports natively say command
+ elif platform == "darwin":
+ # We don't want the say command to block Python,
+ # so we add an ampersand after the message
+ # but if the os TTS package isn't up to date it
+ # still does thus, the try except clause
+ try:
+ os.system("say -v Amelie %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 espeak command to block Python,
+ # so we add an ampersand after the 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
+
+classes = (
+ PovRender,
+)
+
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+
+
+def unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/render_povray/render_gui.py b/render_povray/render_gui.py
index cc3dcf23..465f5a48 100755
--- a/render_povray/render_gui.py
+++ b/render_povray/render_gui.py
@@ -21,7 +21,7 @@ from bl_ui import properties_output
for member in dir(properties_output):
subclass = getattr(properties_output, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_output
from bl_ui import properties_freestyle
@@ -29,10 +29,9 @@ from bl_ui import properties_freestyle
for member in dir(properties_freestyle):
subclass = getattr(properties_freestyle, member)
if hasattr(subclass, "COMPAT_ENGINES") and (
- subclass.bl_space_type != 'PROPERTIES'
- or subclass.bl_context != "render"
+ subclass.bl_space_type != "PROPERTIES" or subclass.bl_context != "render"
):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
# subclass.bl_parent_id = "RENDER_PT_POV_filter"
del properties_freestyle
@@ -41,7 +40,7 @@ from bl_ui import properties_view_layer
for member in dir(properties_view_layer):
subclass = getattr(properties_view_layer, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_view_layer
# Use some of the existing buttons.
@@ -68,10 +67,10 @@ class RenderButtonsPanel:
"""Use this class to define buttons from the render tab of
properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "render"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -82,16 +81,16 @@ class RenderButtonsPanel:
class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel):
"""Use this class to define pov ini settings buttons."""
- bl_options = {'DEFAULT_CLOSED'}
+ bl_options = {"DEFAULT_CLOSED"}
bl_label = "Auto Start"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
scene = context.scene
if scene.pov.tempfiles_enable:
- self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='AUTO')
+ self.layout.prop(scene.pov, "tempfiles_enable", text="", icon="AUTO")
else:
- self.layout.prop(scene.pov, "tempfiles_enable", text="", icon='CONSOLE')
+ self.layout.prop(scene.pov, "tempfiles_enable", text="", icon="CONSOLE")
def draw(self, context):
@@ -104,13 +103,15 @@ class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel):
col = split.column()
col.label(text="Command line options:")
- col.prop(scene.pov, "command_line_switches", text="", icon='RIGHTARROW')
+ col.prop(scene.pov, "command_line_switches", text="", icon="RIGHTARROW")
split = layout.split()
# layout.active = not scene.pov.tempfiles_enable
if not scene.pov.tempfiles_enable:
- split.prop(scene.pov, "deletefiles_enable", text="Delete files")
- split.prop(scene.pov, "pov_editor", text="POV Editor")
+ split.prop(scene.pov, "deletefiles_enable", text="Delete")
+ if not platform.startswith("win"):
+ split.prop(scene.pov, "sdl_window_enable", text="Show")
+ split.prop(scene.pov, "pov_editor", text="Edit")
col = layout.column()
col.prop(scene.pov, "scene_name", text="Name")
@@ -120,7 +121,7 @@ class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel):
split = layout.split()
split.prop(scene.pov, "indentation_character", text="Indent")
- if scene.pov.indentation_character == 'SPACE':
+ if scene.pov.indentation_character == "SPACE":
split.prop(scene.pov, "indentation_spaces", text="Spaces")
row = layout.row()
@@ -129,19 +130,35 @@ class RENDER_PT_POV_export_settings(RenderButtonsPanel, Panel):
class RENDER_PT_POV_render_settings(RenderButtonsPanel, Panel):
- """Use this class to define pov render settings buttons."""
+ """Use this class to host pov render settings buttons from other sub panels."""
bl_label = "Global Settings"
- bl_icon = 'SETTINGS'
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_icon = "SETTINGS"
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
scene = context.scene
if scene.pov.global_settings_advanced:
- self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='SETTINGS')
+ self.layout.prop(scene.pov, "global_settings_advanced", text="", icon="PREFERENCES")
else:
- self.layout.prop(scene.pov, "global_settings_advanced", text="", icon='PREFERENCES')
+ self.layout.prop(scene.pov, "global_settings_advanced", text="", icon="SETTINGS")
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+
+ layout.active = scene.pov.global_settings_advanced
+
+
+class RENDER_PT_POV_light_paths(RenderButtonsPanel, Panel):
+ """Use this class to define pov's main light ray relative settings buttons."""
+
+ bl_label = "Light Paths Tracing"
+ bl_parent_id = "RENDER_PT_POV_render_settings"
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
@@ -149,41 +166,110 @@ class RENDER_PT_POV_render_settings(RenderButtonsPanel, Panel):
scene = context.scene
# rd = context.scene.render
# layout.active = (scene.pov.max_trace_level != 0)
+ layout.active = scene.pov.global_settings_advanced
+ if scene.pov.use_shadows:
+ layout.prop(scene.pov, "use_shadows", icon="COMMUNITY")
+ else:
+ layout.prop(scene.pov, "use_shadows", icon="USER")
+ col = layout.column()
+ col.prop(scene.pov, "max_trace_level", text="Ray Depth")
+ row = layout.row(align=True)
+ row.prop(scene.pov, "adc_bailout")
+
+
+class RENDER_PT_POV_film(RenderButtonsPanel, Panel):
+ """Use this class to define pov film settings buttons."""
+
+ bl_label = "Film"
+ bl_parent_id = "RENDER_PT_POV_render_settings"
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
- if not platform.startswith('win'):
- layout.prop(scene.pov, "sdl_window_enable", text="POV-Ray SDL Window")
+ def draw(self, context):
+ layout = self.layout
+
+ povprops = context.scene.pov
+ agnosticprops = context.scene.render
+ layout.active = povprops.global_settings_advanced
col = layout.column()
- col.label(text="Main Path Tracing:")
- col.prop(scene.pov, "max_trace_level", text="Ray Depth")
- align = True
+ col.label(text="Background")
+ row = layout.row(align=True)
+ if agnosticprops.film_transparent:
+ row.prop(
+ agnosticprops,
+ "film_transparent",
+ text="Blender alpha",
+ icon="NODE_COMPOSITING",
+ invert_checkbox=True,
+ )
+ else:
+ row.prop(
+ agnosticprops,
+ "film_transparent",
+ text="POV alpha",
+ icon="IMAGE_ALPHA",
+ invert_checkbox=True,
+ )
+ row.prop(povprops, "alpha_mode", text="")
+ if povprops.alpha_mode == "SKY":
+ row.label(text=" (color only)")
+ elif povprops.alpha_mode == "TRANSPARENT":
+ row.prop(povprops, "alpha_filter", text="(premultiplied)", slider=True)
+ else:
+ # povprops.alpha_mode == 'STRAIGHT'
+ row.label(text=" (unassociated)")
+
+
+class RENDER_PT_POV_hues(RenderButtonsPanel, Panel):
+ """Use this class to define pov RGB tweaking buttons."""
+
+ bl_label = "Hues"
+ bl_parent_id = "RENDER_PT_POV_render_settings"
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+
layout.active = scene.pov.global_settings_advanced
- row = layout.row(align=align)
- row.prop(scene.pov, "adc_bailout")
- row = layout.row(align=align)
+
+ row = layout.row(align=True)
row.prop(scene.pov, "ambient_light")
- row = layout.row(align=align)
+ row = layout.row(align=True)
row.prop(scene.pov, "irid_wavelength")
- row = layout.row(align=align)
- row.prop(scene.pov, "number_of_waves")
- row = layout.row(align=align)
- row.prop(scene.pov, "noise_generator")
+ row = layout.row(align=True)
- split = layout.split()
- split.label(text="Shading:")
- split = layout.split()
- row = split.row(align=align)
- row.prop(scene.pov, "use_shadows")
- row.prop(scene.pov, "alpha_mode")
+class RENDER_PT_POV_pattern_rules(RenderButtonsPanel, Panel):
+ """Use this class to change pov sets of texture generating algorythms."""
+
+ bl_label = "Pattern Rules"
+ bl_parent_id = "RENDER_PT_POV_render_settings"
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ def draw(self, context):
+ layout = self.layout
+
+ scene = context.scene
+
+ layout.active = scene.pov.global_settings_advanced
+
+ row = layout.row(align=True)
+ row.prop(scene.pov, "number_of_waves")
+ row = layout.row(align=True)
+ row.prop(scene.pov, "noise_generator")
class RENDER_PT_POV_photons(RenderButtonsPanel, Panel):
"""Use this class to define pov photons buttons."""
bl_label = "Photons"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# def draw_header(self, context):
# self.layout.label(icon='SETTINGS')
@@ -191,9 +277,9 @@ class RENDER_PT_POV_photons(RenderButtonsPanel, Panel):
def draw_header(self, context):
scene = context.scene
if scene.pov.photon_enable:
- self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER_ACT')
+ self.layout.prop(scene.pov, "photon_enable", text="", icon="PARTICLES")
else:
- self.layout.prop(scene.pov, "photon_enable", text="", icon='PMARKER')
+ self.layout.prop(scene.pov, "photon_enable", text="", icon="MOD_PARTICLES")
def draw(self, context):
scene = context.scene
@@ -214,20 +300,20 @@ class RENDER_PT_POV_photons(RenderButtonsPanel, Panel):
col.prop(scene.pov, "photon_gather_max")
box = layout.box()
- box.label(text='Photon Map File:')
+ box.label(text="Photon Map File:")
row = box.row()
row.prop(scene.pov, "photon_map_file_save_load", expand=True)
- if scene.pov.photon_map_file_save_load in {'save'}:
+ if scene.pov.photon_map_file_save_load in {"save"}:
box.prop(scene.pov, "photon_map_dir")
box.prop(scene.pov, "photon_map_filename")
- if scene.pov.photon_map_file_save_load in {'load'}:
+ if scene.pov.photon_map_file_save_load in {"load"}:
box.prop(scene.pov, "photon_map_file")
# end main photons
def uberpov_only_qmc_til_pov38release(layout):
col = layout.column()
- col.alignment = 'CENTER'
+ 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.")
@@ -252,18 +338,18 @@ class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel):
"""Use this class to define pov antialiasing buttons."""
bl_label = "Anti-Aliasing"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
scene = context.scene
- if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2':
- self.layout.prop(scene.pov, "antialias_enable", text="", icon='ERROR')
+ if prefs.branch_feature_set_povray != "uberpov" and scene.pov.antialias_method == "2":
+ self.layout.prop(scene.pov, "antialias_enable", text="", icon="ERROR")
elif scene.pov.antialias_enable:
- self.layout.prop(scene.pov, "antialias_enable", text="", icon='ANTIALIASED')
+ self.layout.prop(scene.pov, "antialias_enable", text="", icon="ANTIALIASED")
else:
- self.layout.prop(scene.pov, "antialias_enable", text="", icon='ALIASED')
+ self.layout.prop(scene.pov, "antialias_enable", text="", icon="ALIASED")
def draw(self, context):
prefs = bpy.context.preferences.addons[__package__].preferences
@@ -275,29 +361,29 @@ class RENDER_PT_POV_antialias(RenderButtonsPanel, Panel):
row = layout.row()
row.prop(scene.pov, "antialias_method", text="")
- if prefs.branch_feature_set_povray != 'uberpov' and scene.pov.antialias_method == '2':
+ if prefs.branch_feature_set_povray != "uberpov" and scene.pov.antialias_method == "2":
uberpov_only_qmc_til_pov38release(layout)
else:
no_qmc_fallbacks(row, scene, layout)
- if prefs.branch_feature_set_povray == 'uberpov':
+ 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'
+ row.enabled = scene.pov.antialias_method == "2"
class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel):
"""Use this class to define pov radiosity buttons."""
bl_label = "Diffuse Radiosity"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
scene = context.scene
if scene.pov.radio_enable:
- self.layout.prop(scene.pov, "radio_enable", text="", icon='OUTLINER_OB_LIGHTPROBE')
+ self.layout.prop(scene.pov, "radio_enable", text="", icon="OUTLINER_OB_LIGHTPROBE")
else:
- self.layout.prop(scene.pov, "radio_enable", text="", icon='LIGHTPROBE_CUBEMAP')
+ self.layout.prop(scene.pov, "radio_enable", text="", icon="LIGHTPROBE_CUBEMAP")
def draw(self, context):
layout = self.layout
@@ -340,7 +426,7 @@ class RENDER_PT_POV_radiosity(RenderButtonsPanel, Panel):
col.prop(scene.pov, "radio_subsurface")
-class POV_RADIOSITY_MT_presets(Menu):
+class RADIOSITY_MT_POV_presets(Menu):
"""Use this class to define pov radiosity presets menu."""
bl_label = "Radiosity Presets"
@@ -352,10 +438,10 @@ class POV_RADIOSITY_MT_presets(Menu):
class RENDER_OT_POV_radiosity_add_preset(AddPresetBase, Operator):
"""Use this class to define pov radiosity add presets button"""
- '''Add a Radiosity Preset'''
+ """Add a Radiosity Preset"""
bl_idname = "scene.radiosity_preset_add"
bl_label = "Add Radiosity Preset"
- preset_menu = "POV_RADIOSITY_MT_presets"
+ preset_menu = "RADIOSITY_MT_POV_presets"
# variable used for all preset values
preset_defines = ["scene = bpy.context.scene"]
@@ -391,10 +477,10 @@ def rad_panel_func(self, context):
layout = self.layout
row = layout.row(align=True)
- row.menu(POV_RADIOSITY_MT_presets.__name__, text=POV_RADIOSITY_MT_presets.bl_label)
- row.operator(RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='ADD')
+ row.menu(RADIOSITY_MT_POV_presets.__name__, text=RADIOSITY_MT_POV_presets.bl_label)
+ row.operator(RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon="ADD")
row.operator(
- RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon='REMOVE'
+ RENDER_OT_POV_radiosity_add_preset.bl_idname, text="", icon="REMOVE"
).remove_active = True
@@ -408,27 +494,27 @@ def rad_panel_func(self, context):
bpy.utils.script_paths(subdir="addons")
# render_freestyle_svg = os.path.join(bpy.utils.script_paths(subdir="addons"), "render_freestyle_svg.py")
-render_freestyle_svg = bpy.context.preferences.addons.get('render_freestyle_svg')
+render_freestyle_svg = bpy.context.preferences.addons.get("render_freestyle_svg")
# mpath=addon_utils.paths()[0].render_freestyle_svg
# import mpath
# from mpath import render_freestyle_svg #= addon_utils.modules(module_cache=['Freestyle SVG Exporter'])
# from scripts\\addons import render_freestyle_svg
if check_render_freestyle_svg():
- '''
+ """
snippetsWIP
import myscript
import importlib
importlib.reload(myscript)
myscript.main()
- '''
+ """
for member in dir(render_freestyle_svg):
subclass = getattr(render_freestyle_svg, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
if subclass.bl_idname == "RENDER_PT_SVGExporterPanel":
subclass.bl_parent_id = "RENDER_PT_POV_filter"
- subclass.bl_options = {'HIDE_HEADER'}
+ subclass.bl_options = {"HIDE_HEADER"}
# subclass.bl_order = 11
print(subclass.bl_info)
@@ -439,14 +525,14 @@ class RENDER_PT_POV_filter(RenderButtonsPanel, Panel):
"""Use this class to invoke stuff like Freestyle UI."""
bl_label = "Freestyle"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
with_freestyle = bpy.app.build_options.freestyle
engine = context.scene.render.engine
- return with_freestyle and engine == 'POVRAY_RENDER'
+ return with_freestyle and engine == "POVRAY_RENDER"
def draw_header(self, context):
@@ -455,10 +541,10 @@ class RENDER_PT_POV_filter(RenderButtonsPanel, Panel):
layout = self.layout
if rd.use_freestyle:
- layout.prop(rd, "use_freestyle", text="", icon='LINE_DATA')
+ layout.prop(rd, "use_freestyle", text="", icon="LINE_DATA")
else:
- layout.prop(rd, "use_freestyle", text="", icon='OUTLINER_OB_IMAGE')
+ layout.prop(rd, "use_freestyle", text="", icon="MOD_LINEART")
def draw(self, context):
rd = context.scene.render
@@ -472,7 +558,7 @@ class RENDER_PT_POV_filter(RenderButtonsPanel, Panel):
flow.prop(rd, "line_thickness_mode", expand=True)
- if rd.line_thickness_mode == 'ABSOLUTE':
+ if rd.line_thickness_mode == "ABSOLUTE":
flow.prop(rd, "line_thickness")
# Warning if the Freestyle SVG Exporter addon is not enabled
@@ -508,12 +594,16 @@ class RENDER_PT_POV_filter(RenderButtonsPanel, Panel):
classes = (
RENDER_PT_POV_export_settings,
RENDER_PT_POV_render_settings,
+ RENDER_PT_POV_light_paths,
+ RENDER_PT_POV_film,
+ RENDER_PT_POV_hues,
+ RENDER_PT_POV_pattern_rules,
RENDER_PT_POV_photons,
RENDER_PT_POV_antialias,
RENDER_PT_POV_radiosity,
RENDER_PT_POV_filter,
# RENDER_PT_povray_baking,
- POV_RADIOSITY_MT_presets,
+ RADIOSITY_MT_POV_presets,
RENDER_OT_POV_radiosity_add_preset,
)
diff --git a/render_povray/render_properties.py b/render_povray/render_properties.py
index 339d1301..1d084aa1 100755
--- a/render_povray/render_properties.py
+++ b/render_povray/render_properties.py
@@ -332,15 +332,26 @@ class RenderPovSettingsScene(PropertyGroup):
default=2.5,
)
+ alpha_filter: FloatProperty(
+ name="Alpha",
+ description="User defined color associated background alpha",
+ min=0.0,
+ max=1.0,
+ soft_min=0.01,
+ soft_max=1.0,
+ default=0.75,
+ )
+
alpha_mode: EnumProperty(
name="Alpha",
description="Representation of alpha information in the RGBA pixels",
items=(
("SKY", "Sky", "Transparent pixels are filled with sky color"),
+ ("STRAIGHT", "Straight", "Transparent pixels are not premultiplied"),
(
"TRANSPARENT",
"Transparent",
- "Transparent, World background is transparent with premultiplied alpha",
+ "World background has user defined premultiplied alpha",
),
),
default="SKY",
@@ -657,9 +668,7 @@ class RenderPovSettingsScene(PropertyGroup):
)
-classes = (
- RenderPovSettingsScene,
-)
+classes = (RenderPovSettingsScene,)
def register():
diff --git a/render_povray/scenography.py b/render_povray/scenography.py
index e744b266..1a5142f6 100755
--- a/render_povray/scenography.py
+++ b/render_povray/scenography.py
@@ -11,8 +11,8 @@ import bpy
import os
from imghdr import what # imghdr is a python lib to identify image file types
from math import atan, pi, sqrt, degrees
-from . import df3_library # for smoke rendering
-from .object_primitives import write_object_modifiers
+from . import voxel_lib # for smoke rendering
+from .model_primitives import write_object_modifiers
# -------- find image texture # used for export_world -------- #
@@ -22,18 +22,18 @@ 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 = {
- 'JPG': "jpeg",
- 'JPEG': "jpeg",
- 'GIF': "gif",
- 'TGA': "tga",
- 'IFF': "iff",
- 'PPM': "ppm",
- 'PNG': "png",
- 'SYS': "sys",
- 'TIFF': "tiff",
- 'TIF': "tiff",
- 'EXR': "exr",
- 'HDR': "hdr",
+ "JPG": "jpeg",
+ "JPEG": "jpeg",
+ "GIF": "gif",
+ "TGA": "tga",
+ "IFF": "iff",
+ "PPM": "ppm",
+ "PNG": "png",
+ "SYS": "sys",
+ "TIFF": "tiff",
+ "TIF": "tiff",
+ "EXR": "exr",
+ "HDR": "hdr",
}.get(os.path.splitext(img_f)[-1].upper(), "")
# Then, use imghdr to really identify the filetype as it can be different
if not ext:
@@ -48,11 +48,11 @@ def img_map(ts):
"""Translate mapping type from Blender UI to POV syntax and return that string."""
image_map = ""
texdata = bpy.data.textures[ts.texture]
- if ts.mapping == 'FLAT':
+ if ts.mapping == "FLAT":
image_map = "map_type 0 "
- elif ts.mapping == 'SPHERE':
+ elif ts.mapping == "SPHERE":
image_map = "map_type 1 "
- elif ts.mapping == 'TUBE':
+ elif ts.mapping == "TUBE":
image_map = "map_type 2 "
# map_type 3 and 4 in development (?) (ENV in pov 3.8)
@@ -63,7 +63,7 @@ def img_map(ts):
# image_map = " map_type 4 "
if ts.use_interpolation: # Available if image sampling class reactivated?
image_map += " interpolate 2 "
- if texdata.extension == 'CLIP':
+ if texdata.extension == "CLIP":
image_map += " once "
# image_map += "}"
# if ts.mapping=='CUBE':
@@ -120,16 +120,16 @@ def img_map_bg(wts):
tex = bpy.data.textures[wts.texture]
image_mapBG = ""
# texture_coords refers to the mapping of world textures:
- if wts.texture_coords in ['VIEW', 'GLOBAL']:
+ if wts.texture_coords in ["VIEW", "GLOBAL"]:
image_mapBG = " map_type 0 "
- elif wts.texture_coords == 'ANGMAP':
+ elif wts.texture_coords == "ANGMAP":
image_mapBG = " map_type 1 "
- elif wts.texture_coords == 'TUBE':
+ elif wts.texture_coords == "TUBE":
image_mapBG = " map_type 2 "
if tex.use_interpolation:
image_mapBG += " interpolate 2 "
- if tex.extension == 'CLIP':
+ if tex.extension == "CLIP":
image_mapBG += " once "
# image_mapBG += "}"
# if wts.mapping == 'CUBE':
@@ -152,7 +152,7 @@ def path_image(image):
# -----------------------------------------------------------------------------
-def export_camera(scene, global_matrix, render, tab_write):
+def export_camera(file, scene, global_matrix, render, tab_write):
"""Translate camera from Blender UI to POV syntax and write to exported file."""
camera = scene.camera
@@ -163,91 +163,103 @@ def export_camera(scene, global_matrix, render, tab_write):
# compute resolution
q_size = render.resolution_x / render.resolution_y
- tab_write("#declare camLocation = <%.6f, %.6f, %.6f>;\n" % matrix.translation[:])
+ tab_write(file, "#declare camLocation = <%.6f, %.6f, %.6f>;\n" % matrix.translation[:])
tab_write(
- "#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
- % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
+ file,
+ (
+ "#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
+ % tuple(degrees(e) for e in matrix.to_3x3().to_euler())
+ ),
)
- tab_write("camera {\n")
- if scene.pov.baking_enable and active_object and active_object.type == 'MESH':
- tab_write("mesh_camera{ 1 3\n") # distribution 3 is what we want here
- tab_write("mesh{%s}\n" % active_object.name)
- tab_write("}\n")
- tab_write("location <0,0,.01>")
- tab_write("direction <0,0,-1>")
+ tab_write(file, "camera {\n")
+ if scene.pov.baking_enable and active_object and active_object.type == "MESH":
+ tab_write(file, "mesh_camera{ 1 3\n") # distribution 3 is what we want here
+ tab_write(file, "mesh{%s}\n" % active_object.name)
+ tab_write(file, "}\n")
+ tab_write(file, "location <0,0,.01>")
+ tab_write(file, "direction <0,0,-1>")
else:
- if camera.data.type == 'ORTHO':
+ if camera.data.type == "ORTHO":
# XXX todo: track when SensorHeightRatio was added to see if needed (not used)
sensor_height_ratio = (
render.resolution_x * camera.data.ortho_scale / render.resolution_y
)
- tab_write("orthographic\n")
+ tab_write(file, "orthographic\n")
# Blender angle is radian so should be converted to degrees:
# % (camera.data.angle * (180.0 / pi) )
# but actually argument is not compulsory after angle in pov ortho mode
- tab_write("angle\n")
- tab_write("right <%6f, 0, 0>\n" % -camera.data.ortho_scale)
- tab_write("location <0, 0, 0>\n")
- tab_write("look_at <0, 0, -1>\n")
- tab_write("up <0, %6f, 0>\n" % (camera.data.ortho_scale / q_size))
-
- elif camera.data.type == 'PANO':
- tab_write("panoramic\n")
- tab_write("location <0, 0, 0>\n")
- tab_write("look_at <0, 0, -1>\n")
- tab_write("right <%s, 0, 0>\n" % -q_size)
- tab_write("up <0, 1, 0>\n")
- tab_write("angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi))
- elif camera.data.type == 'PERSP':
+ tab_write(file, "angle\n")
+ tab_write(file, "right <%6f, 0, 0>\n" % -camera.data.ortho_scale)
+ tab_write(file, "location <0, 0, 0>\n")
+ tab_write(file, "look_at <0, 0, -1>\n")
+ tab_write(file, "up <0, %6f, 0>\n" % (camera.data.ortho_scale / q_size))
+
+ elif camera.data.type == "PANO":
+ tab_write(file, "panoramic\n")
+ tab_write(file, "location <0, 0, 0>\n")
+ tab_write(file, "look_at <0, 0, -1>\n")
+ tab_write(file, "right <%s, 0, 0>\n" % -q_size)
+ tab_write(file, "up <0, 1, 0>\n")
+ tab_write(file, "angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi))
+ elif camera.data.type == "PERSP":
# Standard camera otherwise would be default in pov
- tab_write("location <0, 0, 0>\n")
- tab_write("look_at <0, 0, -1>\n")
- tab_write("right <%s, 0, 0>\n" % -q_size)
- tab_write("up <0, 1, 0>\n")
+ tab_write(file, "location <0, 0, 0>\n")
+ tab_write(file, "look_at <0, 0, -1>\n")
+ tab_write(file, "right <%s, 0, 0>\n" % -q_size)
+ tab_write(file, "up <0, 1, 0>\n")
tab_write(
+ file,
"angle %f\n"
- % (2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi)
+ % (2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi),
)
tab_write(
- "rotate <%.6f, %.6f, %.6f>\n" % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
+ file,
+ "rotate <%.6f, %.6f, %.6f>\n" % tuple(degrees(e) for e in matrix.to_3x3().to_euler()),
)
- tab_write("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
+
+ tab_write(file, "translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
if camera.data.dof.use_dof and (focal_point != 0 or camera.data.dof.focus_object):
- tab_write("aperture %.3g\n" % (1 / (camera.data.dof.aperture_fstop * 10000) * 1000))
tab_write(
+ file, "aperture %.3g\n" % (1 / (camera.data.dof.aperture_fstop * 10000) * 1000)
+ )
+ tab_write(
+ file,
"blur_samples %d %d\n"
- % (camera.data.pov.dof_samples_min, camera.data.pov.dof_samples_max)
+ % (camera.data.pov.dof_samples_min, camera.data.pov.dof_samples_max),
)
- tab_write("variance 1/%d\n" % camera.data.pov.dof_variance)
- tab_write("confidence %.3g\n" % camera.data.pov.dof_confidence)
+ tab_write(file, "variance 1/%d\n" % camera.data.pov.dof_variance)
+ tab_write(file, "confidence %.3g\n" % camera.data.pov.dof_confidence)
if camera.data.dof.focus_object:
focal_ob = scene.objects[camera.data.dof.focus_object.name]
matrix_blur = global_matrix @ focal_ob.matrix_world
- tab_write("focal_point <%.4f,%.4f,%.4f>\n" % matrix_blur.translation[:])
+ tab_write(file, "focal_point <%.4f,%.4f,%.4f>\n" % matrix_blur.translation[:])
else:
- tab_write("focal_point <0, 0, %f>\n" % focal_point)
+ tab_write(file, "focal_point <0, 0, %f>\n" % focal_point)
if camera.data.pov.normal_enable:
tab_write(
+ file,
"normal {%s %.4f turbulence %.4f scale %.4f}\n"
% (
camera.data.pov.normal_patterns,
camera.data.pov.cam_normal,
camera.data.pov.turbulence,
camera.data.pov.scale,
- )
+ ),
)
- tab_write("}\n")
+ tab_write(file, "}\n")
exported_lights_count = 0
-def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write):
+def export_lights(lamps, file, scene, global_matrix, tab_write):
"""Translate lights from Blender UI to POV syntax and write to exported file."""
+ from .render import 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
@@ -261,60 +273,62 @@ def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write):
# any way to directly get bpy_prop_array as tuple?
color = tuple(lamp.color)
- tab_write("light_source {\n")
- tab_write("< 0,0,0 >\n")
- tab_write("color srgb<%.3g, %.3g, %.3g>\n" % color)
+ tab_write(file, "light_source {\n")
+ tab_write(file, "< 0,0,0 >\n")
+ tab_write(file, "color srgb<%.3g, %.3g, %.3g>\n" % color)
- if lamp.type == 'POINT':
+ if lamp.type == "POINT":
pass
- elif lamp.type == 'SPOT':
- tab_write("spotlight\n")
+ elif lamp.type == "SPOT":
+ tab_write(file, "spotlight\n")
# Falloff is the main radius from the centre line
- tab_write("falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)) # 1 TO 179 FOR BOTH
- tab_write("radius %.6f\n" % ((degrees(lamp.spot_size) / 2.0) * (1.0 - lamp.spot_blend)))
+ tab_write(file, "falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)) # 1 TO 179 FOR BOTH
+ tab_write(
+ file, "radius %.6f\n" % ((degrees(lamp.spot_size) / 2.0) * (1.0 - lamp.spot_blend))
+ )
# Blender does not have a tightness equivalent, 0 is most like blender default.
- tab_write("tightness 0\n") # 0:10f
+ tab_write(file, "tightness 0\n") # 0:10f
- tab_write("point_at <0, 0, -1>\n")
+ tab_write(file, "point_at <0, 0, -1>\n")
if lamp.pov.use_halo:
- tab_write("looks_like{\n")
- tab_write("sphere{<0,0,0>,%.6f\n" % lamp.distance)
- tab_write("hollow\n")
- tab_write("material{\n")
- tab_write("texture{\n")
- tab_write("pigment{rgbf<1,1,1,%.4f>}\n" % (lamp.pov.halo_intensity * 5.0))
- tab_write("}\n")
- tab_write("interior{\n")
- tab_write("media{\n")
- tab_write("emission 1\n")
- tab_write("scattering {1, 0.5}\n")
- tab_write("density{\n")
- tab_write("spherical\n")
- tab_write("color_map{\n")
- tab_write("[0.0 rgb <0,0,0>]\n")
- tab_write("[0.5 rgb <1,1,1>]\n")
- tab_write("[1.0 rgb <1,1,1>]\n")
- tab_write("}\n")
- tab_write("}\n")
- tab_write("}\n")
- tab_write("}\n")
- tab_write("}\n")
- tab_write("}\n")
- tab_write("}\n")
- elif lamp.type == 'SUN':
- tab_write("parallel\n")
- tab_write("point_at <0, 0, -1>\n") # *must* be after 'parallel'
-
- elif lamp.type == 'AREA':
- tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
+ tab_write(file, "looks_like{\n")
+ tab_write(file, "sphere{<0,0,0>,%.6f\n" % lamp.distance)
+ tab_write(file, "hollow\n")
+ tab_write(file, "material{\n")
+ tab_write(file, "texture{\n")
+ tab_write(file, "pigment{rgbf<1,1,1,%.4f>}\n" % (lamp.pov.halo_intensity * 5.0))
+ tab_write(file, "}\n")
+ tab_write(file, "interior{\n")
+ tab_write(file, "media{\n")
+ tab_write(file, "emission 1\n")
+ tab_write(file, "scattering {1, 0.5}\n")
+ tab_write(file, "density{\n")
+ tab_write(file, "spherical\n")
+ tab_write(file, "color_map{\n")
+ tab_write(file, "[0.0 rgb <0,0,0>]\n")
+ tab_write(file, "[0.5 rgb <1,1,1>]\n")
+ tab_write(file, "[1.0 rgb <1,1,1>]\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ elif lamp.type == "SUN":
+ tab_write(file, "parallel\n")
+ tab_write(file, "point_at <0, 0, -1>\n") # *must* be after 'parallel'
+
+ elif lamp.type == "AREA":
+ tab_write(file, "fade_distance %.6f\n" % (lamp.distance / 2.0))
# Area lights have no falloff type, so always use blenders lamp quad equivalent
# for those?
- tab_write("fade_power %d\n" % 2)
+ tab_write(file, "fade_power %d\n" % 2)
size_x = lamp.size
samples_x = lamp.pov.shadow_ray_samples_x
- if lamp.shape == 'SQUARE':
+ if lamp.shape == "SQUARE":
size_y = size_x
samples_y = samples_x
else:
@@ -322,40 +336,42 @@ def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write):
samples_y = lamp.pov.shadow_ray_samples_y
tab_write(
- "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n" % (size_x, size_y, samples_x, samples_y)
+ file,
+ "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n"
+ % (size_x, size_y, samples_x, samples_y),
)
- tab_write("area_illumination\n")
- if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED':
+ tab_write(file, "area_illumination\n")
+ if lamp.pov.shadow_ray_sample_method == "CONSTANT_JITTERED":
if lamp.pov.use_jitter:
- tab_write("jitter\n")
+ tab_write(file, "jitter\n")
else:
- tab_write("adaptive 1\n")
- tab_write("jitter\n")
+ tab_write(file, "adaptive 1\n")
+ tab_write(file, "jitter\n")
# No shadow checked either at global or light level:
- if not scene.pov.use_shadows or (lamp.pov.shadow_method == 'NOSHADOW'):
- tab_write("shadowless\n")
+ if not scene.pov.use_shadows or (lamp.pov.shadow_method == "NOSHADOW"):
+ tab_write(file, "shadowless\n")
# Sun shouldn't be attenuated. Area lights have no falloff attribute so they
# are put to type 2 attenuation a little higher above.
- if lamp.type not in {'SUN', 'AREA'}:
- if lamp.falloff_type == 'INVERSE_SQUARE':
- tab_write("fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0)))
- tab_write("fade_power %d\n" % 2) # Use blenders lamp quad equivalent
- elif lamp.falloff_type == 'INVERSE_LINEAR':
- tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
- tab_write("fade_power %d\n" % 1) # Use blenders lamp linear
- elif lamp.falloff_type == 'CONSTANT':
- tab_write("fade_distance %.6f\n" % (lamp.distance / 2.0))
- tab_write("fade_power %d\n" % 3)
+ if lamp.type not in {"SUN", "AREA"}:
+ if lamp.falloff_type == "INVERSE_SQUARE":
+ tab_write(file, "fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0)))
+ tab_write(file, "fade_power %d\n" % 2) # Use blenders lamp quad equivalent
+ elif lamp.falloff_type == "INVERSE_LINEAR":
+ tab_write(file, "fade_distance %.6f\n" % (lamp.distance / 2.0))
+ tab_write(file, "fade_power %d\n" % 1) # Use blenders lamp linear
+ elif lamp.falloff_type == "CONSTANT":
+ tab_write(file, "fade_distance %.6f\n" % (lamp.distance / 2.0))
+ tab_write(file, "fade_power %d\n" % 3)
# Use blenders lamp constant equivalent no attenuation.
# Using Custom curve for fade power 3 for now.
- elif lamp.falloff_type == 'CUSTOM_CURVE':
- tab_write("fade_power %d\n" % 4)
+ elif lamp.falloff_type == "CUSTOM_CURVE":
+ tab_write(file, "fade_power %d\n" % 4)
- write_matrix(matrix)
+ write_matrix(file, matrix)
- tab_write("}\n")
+ tab_write(file, "}\n")
# v(A,B) rotates vector A about origin by vector B.
file.write(
@@ -372,190 +388,224 @@ def export_lights(lamps, file, scene, global_matrix, write_matrix, tab_write):
)
-def export_world(world, scene, global_matrix, tab_write):
- """write world as POV backgrounbd and sky_sphere to exported file """
+def export_world(file, world, scene, global_matrix, tab_write):
+ """write world as POV background and sky_sphere to exported file"""
render = scene.pov
+ agnosticrender = scene.render
camera = scene.camera
# matrix = global_matrix @ camera.matrix_world # view dependant for later use NOT USED
if not world:
return
# These lines added to get sky gradient (visible with PNG output)
- if world:
- # For simple flat background:
- if not world.pov.use_sky_blend:
+
+ # For simple flat background:
+ if not world.pov.use_sky_blend:
+ # No alpha with Sky option:
+ if render.alpha_mode == "SKY" and not agnosticrender.film_transparent:
+ tab_write(
+ file, "background {rgbt<%.3g, %.3g, %.3g, 0>}\n" % (world.pov.horizon_color[:])
+ )
+
+ elif render.alpha_mode == "STRAIGHT" or agnosticrender.film_transparent:
+ tab_write(
+ file, "background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:])
+ )
+ else:
# Non fully transparent background could premultiply alpha and avoid
- # anti-aliasing display issue:
- if render.alpha_mode == 'TRANSPARENT':
- tab_write(
- "background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n" % (world.pov.horizon_color[:])
- )
- # Currently using no alpha with Sky option:
- elif render.alpha_mode == 'SKY':
- tab_write("background {rgbt<%.3g, %.3g, %.3g, 0>}\n" % (world.pov.horizon_color[:]))
- # StraightAlpha:
- # XXX Does not exists anymore
- # else:
- # tab_write("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:]))
-
- world_tex_count = 0
- # For Background image textures
- for t in world.pov_texture_slots: # risk to write several sky_spheres but maybe ok.
- if t:
- tex = bpy.data.textures[t.texture]
- if tex.type is not None:
- world_tex_count += 1
- # XXX No enable checkbox for world textures yet (report it?)
- # if t and tex.type == 'IMAGE' and t.use:
- if tex.type == 'IMAGE':
- image_filename = path_image(tex.image)
- if tex.image.filepath != image_filename:
- tex.image.filepath = image_filename
- if image_filename != "" and t.use_map_blend:
- textures_blend = image_filename
- # colvalue = t.default_value
- t_blend = t
-
- # Commented below was an idea to make the Background image oriented as camera
- # taken here:
- # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/
- # Replace 4/3 by the ratio of each image found by some custom or existing
- # function
- # mapping_blend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \
- # "(atan((camLocation - camLookAt).x/(camLocation - " \
- # "camLookAt).y)) rotate x*degrees(atan((camLocation - " \
- # "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \
- # "degrees(atan((camLocation - camLookAt).z/(camLocation - " \
- # "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \
- # (t_blend.offset.x / 10 , t_blend.offset.y / 10 ,
- # t_blend.offset.z / 10, t_blend.scale.x ,
- # t_blend.scale.y , t_blend.scale.z))
- # using camera rotation valuesdirectly from blender seems much easier
- if t_blend.texture_coords == 'ANGMAP':
- mapping_blend = ""
- else:
- # POV-Ray "scale" is not a number of repetitions factor, but its
- # inverse, a standard scale factor.
- # 0.5 Offset is needed relatively to scale because center of the
- # UV scale is 0.5,0.5 in blender and 0,0 in POV
- # Further Scale by 2 and translate by -1 are
- # required for the sky_sphere not to repeat
-
- mapping_blend = (
- "scale 2 scale <%.4g,%.4g,%.4g> translate -1 "
- "translate <%.4g,%.4g,%.4g> rotate<0,0,0> "
- % (
- (1.0 / t_blend.scale.x),
- (1.0 / t_blend.scale.y),
- (1.0 / t_blend.scale.z),
- 0.5 - (0.5 / t_blend.scale.x) - t_blend.offset.x,
- 0.5 - (0.5 / t_blend.scale.y) - t_blend.offset.y,
- t_blend.offset.z,
- )
- )
+ # anti-aliasing display issue
+ tab_write(
+ file,
+ "background {rgbft<%.3g, %.3g, %.3g, %.3g, 0>}\n"
+ % (
+ world.pov.horizon_color[0],
+ world.pov.horizon_color[1],
+ world.pov.horizon_color[2],
+ render.alpha_filter,
+ ),
+ )
- # The initial position and rotation of the pov camera is probably creating
- # the rotation offset should look into it someday but at least background
- # won't rotate with the camera now.
- # Putting the map on a plane would not introduce the skysphere distortion and
- # allow for better image scale matching but also some waay to chose depth and
- # size of the plane relative to camera.
- tab_write("sky_sphere {\n")
- tab_write("pigment {\n")
- tab_write(
- "image_map{%s \"%s\" %s}\n"
- % (image_format(textures_blend), textures_blend, img_map_bg(t_blend))
+ world_tex_count = 0
+ # For Background image textures
+ for t in world.pov_texture_slots: # risk to write several sky_spheres but maybe ok.
+ if t:
+ tex = bpy.data.textures[t.texture]
+ if tex.type is not None:
+ world_tex_count += 1
+ # XXX No enable checkbox for world textures yet (report it?)
+ # if t and tex.type == 'IMAGE' and t.use:
+ if tex.type == "IMAGE":
+ image_filename = path_image(tex.image)
+ if tex.image.filepath != image_filename:
+ tex.image.filepath = image_filename
+ if image_filename != "" and t.use_map_blend:
+ textures_blend = image_filename
+ # colvalue = t.default_value
+ t_blend = t
+
+ # Commented below was an idea to make the Background image oriented as camera
+ # taken here:
+ # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/
+ # Replace 4/3 by the ratio of each image found by some custom or existing
+ # function
+ # mapping_blend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \
+ # "(atan((camLocation - camLookAt).x/(camLocation - " \
+ # "camLookAt).y)) rotate x*degrees(atan((camLocation - " \
+ # "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \
+ # "degrees(atan((camLocation - camLookAt).z/(camLocation - " \
+ # "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \
+ # (t_blend.offset.x / 10 , t_blend.offset.y / 10 ,
+ # t_blend.offset.z / 10, t_blend.scale.x ,
+ # t_blend.scale.y , t_blend.scale.z))
+ # using camera rotation valuesdirectly from blender seems much easier
+ if t_blend.texture_coords == "ANGMAP":
+ mapping_blend = ""
+ else:
+ # POV-Ray "scale" is not a number of repetitions factor, but its
+ # inverse, a standard scale factor.
+ # 0.5 Offset is needed relatively to scale because center of the
+ # UV scale is 0.5,0.5 in blender and 0,0 in POV
+ # Further Scale by 2 and translate by -1 are
+ # required for the sky_sphere not to repeat
+
+ mapping_blend = (
+ "scale 2 scale <%.4g,%.4g,%.4g> translate -1 "
+ "translate <%.4g,%.4g,%.4g> rotate<0,0,0> "
+ % (
+ (1.0 / t_blend.scale.x),
+ (1.0 / t_blend.scale.y),
+ (1.0 / t_blend.scale.z),
+ 0.5 - (0.5 / t_blend.scale.x) - t_blend.offset.x,
+ 0.5 - (0.5 / t_blend.scale.y) - t_blend.offset.y,
+ t_blend.offset.z,
+ )
)
- tab_write("}\n")
- tab_write("%s\n" % mapping_blend)
- # The following layered pigment opacifies to black over the texture for
- # transmit below 1 or otherwise adds to itself
- tab_write("pigment {rgb 0 transmit %s}\n" % tex.intensity)
- tab_write("}\n")
- # tab_write("scale 2\n")
- # tab_write("translate -1\n")
-
- # For only Background gradient
-
- if world_tex_count == 0 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
-
- # Maybe change the above to a function copyInternalRenderer settings when
- # user pushes a button, then:
- # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
- # and other such translations but maybe this would not be allowed either?
+
+ # The initial position and rotation of the pov camera is probably creating
+ # the rotation offset should look into it someday but at least background
+ # won't rotate with the camera now.
+ # Putting the map on a plane would not introduce the skysphere distortion and
+ # allow for better image scale matching but also some waay to chose depth and
+ # size of the plane relative to camera.
+ tab_write(file, "sky_sphere {\n")
+ tab_write(file, "pigment {\n")
+ tab_write(
+ file,
+ 'image_map{%s "%s" %s}\n'
+ % (image_format(textures_blend), textures_blend, img_map_bg(t_blend)),
+ )
+ tab_write(file, "}\n")
+ tab_write(file, "%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(file, "pigment {rgb 0 transmit %s}\n" % tex.intensity)
+ tab_write(file, "}\n")
+ # tab_write(file, "scale 2\n")
+ # tab_write(file, "translate -1\n")
+
+ # For only Background gradient
+
+ if world_tex_count == 0 and world.pov.use_sky_blend:
+ tab_write(file, "sky_sphere {\n")
+ tab_write(file, "pigment {\n")
+ # maybe Should follow the advice of POV doc about replacing gradient
+ # for skysphere..5.5
+ tab_write(file, "gradient y\n")
+ tab_write(file, "color_map {\n")
+
+ if render.alpha_mode == "TRANSPARENT":
+ tab_write(
+ file,
+ "[0.0 rgbft<%.3g, %.3g, %.3g, %.3g, 0>]\n"
+ % (
+ world.pov.horizon_color[0],
+ world.pov.horizon_color[1],
+ world.pov.horizon_color[2],
+ render.alpha_filter,
+ ),
+ )
+ tab_write(
+ file,
+ "[1.0 rgbft<%.3g, %.3g, %.3g, %.3g, 0>]\n"
+ % (
+ world.pov.zenith_color[0],
+ world.pov.zenith_color[1],
+ world.pov.zenith_color[2],
+ render.alpha_filter,
+ ),
+ )
+ if agnosticrender.film_transparent or render.alpha_mode == "STRAIGHT":
+ tab_write(file, "[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.horizon_color[:]))
+ # aa premult not solved with transmit 1
+ tab_write(file, "[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.zenith_color[:]))
+ else:
+ tab_write(file, "[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.horizon_color[:]))
+ tab_write(file, "[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n" % (world.pov.zenith_color[:]))
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
+ # Sky_sphere alpha (transmit) is not translating into image alpha the same
+ # way as 'background'
+
+ # if world.pov.light_settings.use_indirect_light:
+ # scene.pov.radio_enable=1
+
+ # Maybe change the above to a function copyInternalRenderer settings when
+ # user pushes a button, then:
+ # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
+ # and other such translations but maybe this would not be allowed either?
# -----------------------------------------------------------------------------
mist = world.mist_settings
if mist.use_mist:
- tab_write("fog {\n")
- if mist.falloff == 'LINEAR':
- tab_write("distance %.6f\n" % ((mist.start + mist.depth) * 0.368))
- elif mist.falloff == 'QUADRATIC': # n**2 or squrt(n)?
- tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368))
- elif mist.falloff == 'INVERSE_QUADRATIC': # n**2 or squrt(n)?
- tab_write("distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368))
+ tab_write(file, "fog {\n")
+ if mist.falloff == "LINEAR":
+ tab_write(file, "distance %.6f\n" % ((mist.start + mist.depth) * 0.368))
+ elif mist.falloff in ["QUADRATIC", "INVERSE_QUADRATIC"]: # n**2 or squrt(n)?
+ tab_write(file, "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368))
tab_write(
+ file,
"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?
- # tab_write("turbulence 0.2\n")
- # tab_write("turb_depth 0.3\n")
- tab_write("fog_type 1\n") # type2 for height
- tab_write("}\n")
+ # tab_write(file, "fog_offset %.6f\n" % mist.start) #create a pov property to prepend
+ # tab_write(file, "fog_alt %.6f\n" % mist.height) #XXX right?
+ # tab_write(file, "turbulence 0.2\n")
+ # tab_write(file, "turb_depth 0.3\n")
+ tab_write(file, "fog_type 1\n") # type2 for height
+ tab_write(file, "}\n")
if scene.pov.media_enable:
- tab_write("media {\n")
+ tab_write(file, "media {\n")
tab_write(
+ file,
"scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n"
% (
int(scene.pov.media_scattering_type),
scene.pov.media_diffusion_scale,
*(scene.pov.media_diffusion_color[:]),
- )
+ ),
)
- if scene.pov.media_scattering_type == '5':
- tab_write("eccentricity %.3g\n" % scene.pov.media_eccentricity)
- tab_write("}\n")
+ if scene.pov.media_scattering_type == "5":
+ tab_write(file, "eccentricity %.3g\n" % scene.pov.media_eccentricity)
+ tab_write(file, "}\n")
tab_write(
+ file,
"absorption %.12f*<%.4g, %.4g, %.4g>\n"
- % (scene.pov.media_absorption_scale, *(scene.pov.media_absorption_color[:]))
+ % (scene.pov.media_absorption_scale, *(scene.pov.media_absorption_color[:])),
)
- tab_write("\n")
- tab_write("samples %.d\n" % scene.pov.media_samples)
- tab_write("}\n")
+ tab_write(file, "\n")
+ tab_write(file, "samples %.d\n" % scene.pov.media_samples)
+ tab_write(file, "}\n")
# -----------------------------------------------------------------------------
-def export_rainbows(rainbows, file, scene, global_matrix, write_matrix, tab_write):
- """write all POV rainbows primitives to exported file """
+def export_rainbows(rainbows, file, scene, global_matrix, tab_write):
+ """write all POV rainbows primitives to exported file"""
+
+ from .render import write_matrix, tab_write
+
pov_mat_name = "Default_texture"
for ob in rainbows:
povdataname = ob.data.name # enough? XXX not used nor matrix fn?
@@ -588,45 +638,47 @@ def export_rainbows(rainbows, file, scene, global_matrix, write_matrix, tab_writ
# XXX TO CHANGE:
# formerly:
- # tab_write("#declare %s = rainbow {\n"%povdataname)
+ # tab_write(file, "#declare %s = rainbow {\n"%povdataname)
# clumsy for now but remove the rainbow from instancing
# system because not an object. use lamps later instead of meshes
# del data_ref[dataname]
- tab_write("rainbow {\n")
-
- tab_write("angle %.4f\n" % angle)
- tab_write("width %.4f\n" % width)
- tab_write("distance %.4f\n" % distance)
- tab_write("arc_angle %.4f\n" % ob.pov.arc_angle)
- tab_write("falloff_angle %.4f\n" % ob.pov.falloff_angle)
- tab_write("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
- tab_write("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
- tab_write("color_map {\n")
- tab_write("[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
- tab_write("[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
- tab_write("[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
- tab_write("[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
- tab_write("[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
- tab_write("[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
- tab_write("[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
- tab_write("[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
- tab_write("[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
- tab_write("}\n")
-
- # tab_write("texture {%s}\n"%pov_mat_name)
+ tab_write(file, "rainbow {\n")
+
+ tab_write(file, "angle %.4f\n" % angle)
+ tab_write(file, "width %.4f\n" % width)
+ tab_write(file, "distance %.4f\n" % distance)
+ tab_write(file, "arc_angle %.4f\n" % ob.pov.arc_angle)
+ tab_write(file, "falloff_angle %.4f\n" % ob.pov.falloff_angle)
+ tab_write(file, "direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
+ tab_write(file, "up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
+ tab_write(file, "color_map {\n")
+ tab_write(file, "[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
+ tab_write(file, "[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
+ tab_write(file, "[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
+ tab_write(file, "[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
+ tab_write(file, "[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
+ tab_write(file, "[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
+ tab_write(file, "[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
+ tab_write(file, "[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
+ tab_write(file, "[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
+ tab_write(file, "}\n")
+
+ # tab_write(file, "texture {%s}\n"%pov_mat_name)
write_object_modifiers(ob, file)
- # tab_write("rotate x*90\n")
+ # tab_write(file, "rotate x*90\n")
# matrix = global_matrix @ ob.matrix_world
- # write_matrix(matrix)
- tab_write("}\n")
+ # write_matrix(file, matrix)
+ tab_write(file, "}\n")
# continue #Don't render proxy mesh, skip to next object
-def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, write_matrix):
+def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix):
"""export Blender smoke type fluids to pov media using df3 library"""
+ from .render import write_matrix, tab_write
+
flowtype = -1 # XXX todo: not used yet? should trigger emissive for fire type
depsgraph = bpy.context.evaluated_depsgraph_get()
smoke_obj = bpy.data.objects[smoke_obj_name].evaluated_get(depsgraph)
@@ -634,17 +686,17 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ
smoke_modifier = None
# Search smoke domain target for smoke modifiers
for mod in smoke_obj.modifiers:
- if mod.type == 'FLUID':
- if mod.fluid_type == 'DOMAIN':
+ if mod.type == "FLUID":
+ if mod.fluid_type == "DOMAIN":
domain = smoke_obj
smoke_modifier = mod
- elif mod.fluid_type == 'FLOW':
- if mod.flow_settings.flow_type == 'BOTH':
+ elif mod.fluid_type == "FLOW":
+ if mod.flow_settings.flow_type == "BOTH":
flowtype = 2
- elif mod.flow_settings.flow_type == 'FIRE':
+ elif mod.flow_settings.flow_type == "FIRE":
flowtype = 1
- elif mod.flow_settings.flow_type == 'SMOKE':
+ elif mod.flow_settings.flow_type == "SMOKE":
flowtype = 0
eps = 0.000001 # XXX not used currently. restore from corner case ... zero div?
if domain is not None:
@@ -667,7 +719,7 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ
big_res = [
mod_set.domain_resolution[0],
mod_set.domain_resolution[1],
- mod_set.domain_resolution[2]
+ mod_set.domain_resolution[2],
]
if mod_set.use_noise:
@@ -746,7 +798,7 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ
# return big_res[0], big_res[1], big_res[2], channeldata
- mydf3 = df3_library.df3(big_res[0], big_res[1], big_res[2])
+ mydf3 = voxel_lib.df3(big_res[0], big_res[1], big_res[2])
sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size()
for x in range(sim_sizeX):
for y in range(sim_sizeY):
@@ -756,7 +808,7 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ
mydf3.exportDF3(smoke_path)
except ZeroDivisionError:
print("Show smoke simulation in 3D view before export")
- print('Binary smoke.df3 file written in preview directory')
+ print("Binary smoke.df3 file written in preview directory")
if comments:
file.write("\n//--Smoke--\n\n")
@@ -772,7 +824,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")
@@ -805,7 +857,7 @@ def export_smoke(file, smoke_obj_name, smoke_path, comments, global_matrix, writ
# We apply object's transformations to get final loc/rot/size in world space!
# Note: we could combine the two previous transformations with this matrix directly...
- write_matrix(global_matrix @ smoke_obj.matrix_world)
+ write_matrix(file, global_matrix @ smoke_obj.matrix_world)
# END OF TRANSFORMATIONS
diff --git a/render_povray/scenography_gui.py b/render_povray/scenography_gui.py
index 4adc4ade..097c6357 100755
--- a/render_povray/scenography_gui.py
+++ b/render_povray/scenography_gui.py
@@ -16,7 +16,7 @@ from bl_ui import properties_data_camera
for member in dir(properties_data_camera):
subclass = getattr(properties_data_camera, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_data_camera
# -------- Use only a subset of the world panels
@@ -34,7 +34,7 @@ from bl_ui import properties_physics_common
for member in dir(properties_physics_common):
subclass = getattr(properties_physics_common, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_common
# Physics Rigid Bodies wrapping every class 'as is'
@@ -43,7 +43,7 @@ from bl_ui import properties_physics_rigidbody
for member in dir(properties_physics_rigidbody):
subclass = getattr(properties_physics_rigidbody, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_rigidbody
# Physics Rigid Body Constraint wrapping every class 'as is'
@@ -52,7 +52,7 @@ from bl_ui import properties_physics_rigidbody_constraint
for member in dir(properties_physics_rigidbody_constraint):
subclass = getattr(properties_physics_rigidbody_constraint, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_rigidbody_constraint
# Physics Smoke and fluids wrapping every class 'as is'
@@ -61,7 +61,7 @@ from bl_ui import properties_physics_fluid
for member in dir(properties_physics_fluid):
subclass = getattr(properties_physics_fluid, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_fluid
# Physics softbody wrapping every class 'as is'
@@ -70,7 +70,7 @@ from bl_ui import properties_physics_softbody
for member in dir(properties_physics_softbody):
subclass = getattr(properties_physics_softbody, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_softbody
# Physics Field wrapping every class 'as is'
@@ -79,7 +79,7 @@ from bl_ui import properties_physics_field
for member in dir(properties_physics_field):
subclass = getattr(properties_physics_field, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_field
# Physics Cloth wrapping every class 'as is'
@@ -88,7 +88,7 @@ from bl_ui import properties_physics_cloth
for member in dir(properties_physics_cloth):
subclass = getattr(properties_physics_cloth, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_cloth
# Physics Dynamic Paint wrapping every class 'as is'
@@ -97,7 +97,7 @@ from bl_ui import properties_physics_dynamicpaint
for member in dir(properties_physics_dynamicpaint):
subclass = getattr(properties_physics_dynamicpaint, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_physics_dynamicpaint
from bl_ui import properties_particle
@@ -105,7 +105,7 @@ from bl_ui import properties_particle
for member in dir(properties_particle): # add all "particle" panels from blender
subclass = getattr(properties_particle, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_particle
@@ -113,10 +113,10 @@ class CameraDataButtonsPanel:
"""Use this class to define buttons from the camera data tab of
properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "data"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -129,10 +129,10 @@ class WorldButtonsPanel:
"""Use this class to define buttons from the world tab of
properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "world"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -148,9 +148,9 @@ class CAMERA_PT_POV_cam_dof(CameraDataButtonsPanel, Panel):
"""Use this class for camera depth of field focal blur buttons."""
bl_label = "POV Aperture"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
bl_parent_id = "DATA_PT_camera_dof_aperture"
- bl_options = {'HIDE_HEADER'}
+ bl_options = {"HIDE_HEADER"}
# def draw_header(self, context):
# cam = context.camera
@@ -183,7 +183,7 @@ class CAMERA_PT_POV_cam_nor(CameraDataButtonsPanel, Panel):
"""Use this class for camera normal perturbation buttons."""
bl_label = "POV Perturbation"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
cam = context.camera
@@ -207,7 +207,7 @@ class CAMERA_PT_POV_replacement_text(CameraDataButtonsPanel, Panel):
"""Use this class for camera text replacement field."""
bl_label = "Custom POV Code"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
@@ -229,7 +229,7 @@ class WORLD_PT_POV_world(WorldButtonsPanel, Panel):
bl_label = "World"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
@@ -238,8 +238,8 @@ class WORLD_PT_POV_world(WorldButtonsPanel, Panel):
row = layout.row(align=True)
row.menu(WORLD_MT_POV_presets.__name__, text=WORLD_MT_POV_presets.bl_label)
- row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='ADD')
- row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True
+ row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon="ADD")
+ row.operator(WORLD_OT_POV_add_preset.bl_idname, text="", icon="REMOVE").remove_active = True
row = layout.row()
row.prop(world, "use_sky_paper")
@@ -262,8 +262,8 @@ class WORLD_PT_POV_mist(WorldButtonsPanel, Panel):
"""Use this class to define pov mist buttons."""
bl_label = "Mist"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
world = context.world
@@ -337,7 +337,7 @@ class RENDER_PT_POV_media(WorldButtonsPanel, Panel):
"""Use this class to define a pov global atmospheric media buttons."""
bl_label = "Atmosphere Media"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
scene = context.scene
@@ -364,7 +364,7 @@ class RENDER_PT_POV_media(WorldButtonsPanel, Panel):
col.label(text="Absorption:")
col.prop(scene.pov, "media_absorption_scale")
col.prop(scene.pov, "media_absorption_color", text="")
- if scene.pov.media_scattering_type == '5':
+ if scene.pov.media_scattering_type == "5":
col = layout.column()
col.prop(scene.pov, "media_eccentricity", text="Eccentricity")
@@ -400,8 +400,8 @@ class PovLightButtonsPanel(properties_data_light.DataButtonsPanel):
"""Use this class to define buttons from the light data tab of
properties window."""
- COMPAT_ENGINES = {'POVRAY_RENDER'}
- POV_OBJECT_TYPES = {'RAINBOW'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+ POV_OBJECT_TYPES = {"RAINBOW"}
@classmethod
def poll(cls, context):
@@ -429,8 +429,8 @@ from bl_ui import properties_data_light
# pass
# Now only These panels are kept
-properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add('POVRAY_RENDER')
-properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add('POVRAY_RENDER')
+properties_data_light.DATA_PT_custom_props_light.COMPAT_ENGINES.add("POVRAY_RENDER")
+properties_data_light.DATA_PT_context_light.COMPAT_ENGINES.add("POVRAY_RENDER")
class LIGHT_PT_POV_preview(PovLightButtonsPanel, Panel):
@@ -464,25 +464,25 @@ class LIGHT_PT_POV_light(PovLightButtonsPanel, Panel):
sub.prop(light, "color", text="")
sub.prop(light, "energy")
- if light.type in {'POINT', 'SPOT'}:
+ if light.type in {"POINT", "SPOT"}:
sub.label(text="Falloff:")
sub.prop(light, "falloff_type", text="")
sub.prop(light, "distance")
- if light.falloff_type == 'LINEAR_QUADRATIC_WEIGHTED':
+ if light.falloff_type == "LINEAR_QUADRATIC_WEIGHTED":
col.label(text="Attenuation Factors:")
sub = col.column(align=True)
sub.prop(light, "linear_attenuation", slider=True, text="Linear")
sub.prop(light, "quadratic_attenuation", slider=True, text="Quadratic")
- elif light.falloff_type == 'INVERSE_COEFFICIENTS':
+ elif light.falloff_type == "INVERSE_COEFFICIENTS":
col.label(text="Inverse Coefficients:")
sub = col.column(align=True)
sub.prop(light, "constant_coefficient", text="Constant")
sub.prop(light, "linear_coefficient", text="Linear")
sub.prop(light, "quadratic_coefficient", text="Quadratic")
- if light.type == 'AREA':
+ if light.type == "AREA":
col.prop(light, "distance")
# restore later as interface to POV light groups ?
@@ -523,11 +523,11 @@ def light_panel_func(self, context):
row = layout.row(align=True)
row.menu(LIGHT_MT_POV_presets.__name__, text=LIGHT_MT_POV_presets.bl_label)
- row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='ADD')
- row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon='REMOVE').remove_active = True
+ row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon="ADD")
+ row.operator(LIGHT_OT_POV_add_preset.bl_idname, text="", icon="REMOVE").remove_active = True
-'''#TORECREATE##DEPRECATED#
+"""#TORECREATE##DEPRECATED#
class LIGHT_PT_POV_sunsky(PovLightButtonsPanel, Panel):
bl_label = properties_data_light.DATA_PT_sunsky.bl_label
@@ -539,7 +539,7 @@ class LIGHT_PT_POV_sunsky(PovLightButtonsPanel, Panel):
draw = properties_data_light.DATA_PT_sunsky.draw
-'''
+"""
class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
@@ -567,7 +567,7 @@ class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
sub.active = light.pov.use_halo
sub.prop(light.pov, "halo_intensity", text="Intensity")
- if light.pov.shadow_method == 'NOSHADOW' and light.type == 'AREA':
+ if light.pov.shadow_method == "NOSHADOW" and light.type == "AREA":
split = layout.split()
col = split.column()
@@ -575,13 +575,13 @@ class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
sub = col.row(align=True)
- if light.shape == 'SQUARE':
+ if light.shape == "SQUARE":
sub.prop(light, "shadow_ray_samples_x", text="Samples")
- elif light.shape == 'RECTANGLE':
+ elif light.shape == "RECTANGLE":
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
- if light.pov.shadow_method != 'NOSHADOW':
+ if light.pov.shadow_method != "NOSHADOW":
split = layout.split()
col = split.column()
@@ -591,25 +591,25 @@ class LIGHT_PT_POV_shadow(PovLightButtonsPanel, Panel):
# col.prop(light.pov, "use_shadow_layer", text="This Layer Only")
# col.prop(light.pov, "use_only_shadow")
- if light.pov.shadow_method == 'RAY_SHADOW':
+ if light.pov.shadow_method == "RAY_SHADOW":
split = layout.split()
col = split.column()
col.label(text="Sampling:")
- if light.type in {'POINT', 'SUN', 'SPOT'}:
+ if light.type in {"POINT", "SUN", "SPOT"}:
sub = col.row()
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
# any equivalent in pov?
# sub.prop(light, "shadow_soft_size", text="Soft Size")
- elif light.type == 'AREA':
+ elif light.type == "AREA":
sub = col.row(align=True)
- if light.shape == 'SQUARE':
+ if light.shape == "SQUARE":
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples")
- elif light.shape == 'RECTANGLE':
+ elif light.shape == "RECTANGLE":
sub.prop(light.pov, "shadow_ray_samples_x", text="Samples X")
sub.prop(light.pov, "shadow_ray_samples_y", text="Samples Y")
@@ -625,7 +625,7 @@ class LIGHT_PT_POV_area(PovLightButtonsPanel, Panel):
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
- return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
+ return (lamp and lamp.type == "AREA") and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_area.draw
@@ -639,7 +639,7 @@ class LIGHT_PT_POV_spot(PovLightButtonsPanel, Panel):
def poll(cls, context):
lamp = context.light
engine = context.scene.render.engine
- return (lamp and lamp.type == 'SPOT') and (engine in cls.COMPAT_ENGINES)
+ return (lamp and lamp.type == "SPOT") and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_spot.draw
@@ -654,7 +654,7 @@ class LIGHT_PT_POV_falloff_curve(PovLightButtonsPanel, Panel):
engine = context.scene.render.engine
return (
- lamp and lamp.type in {'POINT', 'SPOT'} and lamp.falloff_type == 'CUSTOM_CURVE'
+ lamp and lamp.type in {"POINT", "SPOT"} and lamp.falloff_type == "CUSTOM_CURVE"
) and (engine in cls.COMPAT_ENGINES)
draw = properties_data_light.DATA_PT_falloff_curve.draw
@@ -665,14 +665,14 @@ class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel):
properties window. inheriting lamp buttons panel class"""
bl_label = "POV-Ray Rainbow"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return obj and obj.pov.object_as == 'RAINBOW' and (engine in cls.COMPAT_ENGINES)
+ return obj and obj.pov.object_as == "RAINBOW" and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -681,10 +681,10 @@ class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel):
col = layout.column()
- if obj.pov.object_as == 'RAINBOW':
+ if obj.pov.object_as == "RAINBOW":
if not obj.pov.unlock_parameters:
col.prop(
- obj.pov, "unlock_parameters", text="Exported parameters below", icon='LOCKED'
+ obj.pov, "unlock_parameters", text="Exported parameters below", icon="LOCKED"
)
col.label(text="Rainbow projection angle: " + str(obj.data.spot_size))
col.label(text="Rainbow width: " + str(obj.data.spot_blend))
@@ -694,7 +694,7 @@ class OBJECT_PT_POV_rainbow(PovLightButtonsPanel, Panel):
else:
col.prop(
- obj.pov, "unlock_parameters", text="Edit exported parameters", icon='UNLOCKED'
+ obj.pov, "unlock_parameters", text="Edit exported parameters", icon="UNLOCKED"
)
col.label(text="3D view proxy may get out of synch")
col.active = obj.pov.unlock_parameters
diff --git a/render_povray/scripting.py b/render_povray/scripting.py
index 698dbb5f..f9aa3391 100755
--- a/render_povray/scripting.py
+++ b/render_povray/scripting.py
@@ -16,13 +16,13 @@ from math import pi, sqrt
def export_custom_code(file):
- """write all POV user defined custom code to exported file """
+ """write all POV user defined custom code to exported file"""
# Write CurrentAnimation Frame for use in Custom POV Code
file.write("#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current)
# Change path and uncomment to add an animated include file by hand:
- file.write("//#include \"/home/user/directory/animation_include_file.inc\"\n")
+ file.write('//#include "/home/user/directory/animation_include_file.inc"\n')
for txt in bpy.data.texts:
- if txt.pov.custom_code == 'both':
+ if txt.pov.custom_code == "both":
# Why are the newlines needed?
file.write("\n")
file.write(txt.as_string())
@@ -32,23 +32,23 @@ def export_custom_code(file):
# ----------------------------------- IMPORT
-class ImportPOV(bpy.types.Operator, ImportHelper):
+class SCENE_OT_POV_Import(bpy.types.Operator, ImportHelper):
"""Load Povray files"""
bl_idname = "import_scene.pov"
bl_label = "POV-Ray files (.pov/.inc)"
- bl_options = {'PRESET', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"PRESET", "UNDO"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# -----------
# File props.
files: CollectionProperty(
- type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}
+ type=bpy.types.OperatorFileListElement, options={"HIDDEN", "SKIP_SAVE"}
)
- directory: StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
+ directory: StringProperty(maxlen=1024, subtype="FILE_PATH", options={"HIDDEN", "SKIP_SAVE"})
filename_ext = {".pov", ".inc"}
- filter_glob: StringProperty(default="*.pov;*.inc", options={'HIDDEN'})
+ filter_glob: StringProperty(default="*.pov;*.inc", options={"HIDDEN"})
import_at_cur: BoolProperty(
name="Import at Cursor Location", description="Ignore Object Matrix", default=False
@@ -67,7 +67,7 @@ class ImportPOV(bpy.types.Operator, ImportHelper):
lenverts = None
lenfaces = None
suffix = -1
- name = 'Mesh2_%s' % suffix
+ name = "Mesh2_%s" % suffix
name_search = False
verts_search = False
faces_search = False
@@ -90,15 +90,15 @@ class ImportPOV(bpy.types.Operator, ImportHelper):
color = None
for item, value in enumerate(cache):
# if value == 'texture': # add more later
- if value == 'pigment':
+ if value == "pigment":
# Todo: create function for all color models.
# instead of current pass statements
# distinguish srgb from rgb into blend option
- if cache[item + 2] in {'rgb', 'srgb'}:
+ if cache[item + 2] in {"rgb", "srgb"}:
pass
- elif cache[item + 2] in {'rgbf', 'srgbf'}:
+ elif cache[item + 2] in {"rgbf", "srgbf"}:
pass
- elif cache[item + 2] in {'rgbt', 'srgbt'}:
+ elif cache[item + 2] in {"rgbt", "srgbt"}:
try:
r, g, b, t = (
float(cache[item + 3]),
@@ -108,25 +108,25 @@ class ImportPOV(bpy.types.Operator, ImportHelper):
)
except BaseException as e:
print(e.__doc__)
- print('An exception occurred: {}'.format(e))
+ print("An exception occurred: {}".format(e))
r = g = b = t = float(cache[item + 2])
color = (r, g, b, t)
- elif cache[item + 2] in {'rgbft', 'srgbft'}:
+ elif cache[item + 2] in {"rgbft", "srgbft"}:
pass
else:
pass
- if colors == [] or (colors != [] and color not in colors):
+ if colors == [] or color not in colors:
colors.append(color)
name = ob.name + "_mat"
mat_names.append(name)
mat = bpy.data.materials.new(name)
mat.diffuse_color = (r, g, b)
- mat.alpha = 1 - t
- if mat.alpha != 1:
- mat.use_transparency = True
+ mat.pov.alpha = 1 - t
+ if mat.pov.alpha != 1:
+ mat.pov.use_transparency = True
ob.data.materials.append(mat)
else:
@@ -138,361 +138,361 @@ class ImportPOV(bpy.types.Operator, ImportHelper):
print("Importing file: " + file.name)
file_pov = self.directory + file.name
# Ignore any non unicode character
- for line in open(file_pov, encoding='utf-8', errors='ignore'):
- string = line.replace("{", " ")
- string = string.replace("}", " ")
- string = string.replace("<", " ")
- string = string.replace(">", " ")
- string = string.replace(",", " ")
- lw = string.split()
- # lenwords = len(lw) # Not used... why written?
- if lw:
- if lw[0] == "object":
- write_matrix = True
- if write_matrix:
- if lw[0] not in {"object", "matrix"}:
- index = lw[0]
- if lw[0] in {"matrix"}:
- value = [
- float(lw[1]),
- float(lw[2]),
- float(lw[3]),
- float(lw[4]),
- float(lw[5]),
- float(lw[6]),
- float(lw[7]),
- float(lw[8]),
- float(lw[9]),
- float(lw[10]),
- float(lw[11]),
- float(lw[12]),
- ]
- matrixes[index] = value
- write_matrix = False
- for line in open(file_pov, encoding='utf-8', errors='ignore'):
- S = line.replace("{", " { ")
- S = S.replace("}", " } ")
- S = S.replace(",", " ")
- S = S.replace("<", "")
- S = S.replace(">", " ")
- S = S.replace("=", " = ")
- S = S.replace(";", " ; ")
- S = S.split()
- # lenS = len(S) # Not used... why written?
- for word in S:
- # -------- Primitives Import -------- #
- if word == 'cone':
- cone_search = True
- name_search = False
- if cone_search:
- cache.append(word)
- if cache[-1] == '}':
- try:
- x0 = float(cache[2])
- y0 = float(cache[3])
- z0 = float(cache[4])
- r0 = float(cache[5])
- x1 = float(cache[6])
- y1 = float(cache[7])
- z1 = float(cache[8])
- r1 = float(cache[9])
- # Y is height in most pov files, not z
- bpy.ops.pov.addcone(base=r0, cap=r1, height=(y1 - y0))
- ob = context.object
- ob.location = (x0, y0, z0)
- # ob.scale = (r,r,r)
- mat_search(cache)
- except ValueError:
- pass
- cache = []
- cone_search = False
- if word == 'plane':
- plane_search = True
- name_search = False
- if plane_search:
- cache.append(word)
- if cache[-1] == '}':
- try:
- bpy.ops.pov.addplane()
- ob = context.object
- mat_search(cache)
- except ValueError:
- pass
- cache = []
- plane_search = False
- if word == 'box':
- box_search = True
- name_search = False
- if box_search:
- cache.append(word)
- if cache[-1] == '}':
- try:
- x0 = float(cache[2])
- y0 = float(cache[3])
- z0 = float(cache[4])
- x1 = float(cache[5])
- y1 = float(cache[6])
- z1 = float(cache[7])
- # imported_corner_1=(x0, y0, z0)
- # imported_corner_2 =(x1, y1, z1)
- center = ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2)
- bpy.ops.pov.addbox()
- ob = context.object
- ob.location = center
- mat_search(cache)
-
- except ValueError:
- pass
- cache = []
- box_search = False
- if word == 'cylinder':
- cylinder_search = True
- name_search = False
- if cylinder_search:
- cache.append(word)
- if cache[-1] == '}':
- try:
- x0 = float(cache[2])
- y0 = float(cache[3])
- z0 = float(cache[4])
- x1 = float(cache[5])
- y1 = float(cache[6])
- z1 = float(cache[7])
- imported_cyl_loc = (x0, y0, z0)
- imported_cyl_loc_cap = (x1, y1, z1)
-
- r = float(cache[8])
-
- vec = Vector(imported_cyl_loc_cap) - Vector(imported_cyl_loc)
- depth = vec.length
- rot = Vector((0, 0, 1)).rotation_difference(
- vec
- ) # Rotation from Z axis.
- trans = rot @ Vector( # XXX Not used, why written?
- (0, 0, depth / 2)
- ) # Such that origin is at center of the base of the cylinder.
- # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
- scale_z = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2) / 2
- bpy.ops.pov.addcylinder(
- R=r,
- imported_cyl_loc=imported_cyl_loc,
- imported_cyl_loc_cap=imported_cyl_loc_cap,
- )
- ob = context.object
- ob.location = (x0, y0, z0)
- ob.rotation_euler = rot.to_euler()
- ob.scale = (1, 1, scale_z)
-
- # scale data rather than obj?
- # bpy.ops.object.mode_set(mode='EDIT')
- # bpy.ops.mesh.reveal()
- # bpy.ops.mesh.select_all(action='SELECT')
- # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
- # bpy.ops.mesh.hide(unselected=False)
- # bpy.ops.object.mode_set(mode='OBJECT')
-
- mat_search(cache)
-
- except ValueError:
- pass
- cache = []
- cylinder_search = False
- if word == 'sphere':
- sphere_search = True
- name_search = False
- if sphere_search:
- cache.append(word)
- if cache[-1] == '}':
- x = y = z = r = 0
- try:
- x = float(cache[2])
- y = float(cache[3])
- z = float(cache[4])
- r = float(cache[5])
-
- except ValueError:
- pass
- except 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))
- ob = context.object
- ob.location = (x, y, z)
- ob.scale = (r, r, r)
- mat_search(cache)
- cache = []
- sphere_search = False
- # -------- End Primitives Import -------- #
- if word == '#declare':
- name_search = True
- if name_search:
- cache.append(word)
- if word == 'mesh2':
+ with open(file_pov, 'r', encoding='utf-8', errors="ignore") as infile:
+ for line in infile:
+ string = line.replace("{", " ")
+ string = string.replace("}", " ")
+ string = string.replace("<", " ")
+ string = string.replace(">", " ")
+ string = string.replace(",", " ")
+ lw = string.split()
+ # lenwords = len(lw) # Not used... why written?
+ if lw:
+ if lw[0] == "object":
+ write_matrix = True
+ if write_matrix:
+ if lw[0] not in {"object", "matrix"}:
+ index = lw[0]
+ if lw[0] in {"matrix"}:
+ value = [
+ float(lw[1]),
+ float(lw[2]),
+ float(lw[3]),
+ float(lw[4]),
+ float(lw[5]),
+ float(lw[6]),
+ float(lw[7]),
+ float(lw[8]),
+ float(lw[9]),
+ float(lw[10]),
+ float(lw[11]),
+ float(lw[12]),
+ ]
+ matrixes[index] = value
+ write_matrix = False
+ with open(file_pov, 'r', encoding='utf-8', errors="ignore") as infile:
+ for line in infile:
+ S = line.replace("{", " { ")
+ S = S.replace("}", " } ")
+ S = S.replace(",", " ")
+ S = S.replace("<", "")
+ S = S.replace(">", " ")
+ S = S.replace("=", " = ")
+ S = S.replace(";", " ; ")
+ S = S.split()
+ # lenS = len(S) # Not used... why written?
+ for word in S:
+ # -------- Primitives Import -------- #
+ if word == "cone":
+ cone_search = True
name_search = False
- if cache[-2] == '=':
- name = cache[-3]
- else:
- suffix += 1
- cache = []
- if word in {'texture', ';'}:
+ if cone_search:
+ cache.append(word)
+ if cache[-1] == "}":
+ try:
+ x0 = float(cache[2])
+ y0 = float(cache[3])
+ z0 = float(cache[4])
+ r0 = float(cache[5])
+ x1 = float(cache[6])
+ y1 = float(cache[7])
+ z1 = float(cache[8])
+ r1 = float(cache[9])
+ # Y is height in most pov files, not z
+ bpy.ops.pov.addcone(base=r0, cap=r1, height=(y1 - y0))
+ ob = context.object
+ ob.location = (x0, y0, z0)
+ # ob.scale = (r,r,r)
+ mat_search(cache)
+ except ValueError:
+ pass
+ cache = []
+ cone_search = False
+ if word == "plane":
+ plane_search = True
+ name_search = False
+ if plane_search:
+ cache.append(word)
+ if cache[-1] == "}":
+ try:
+ bpy.ops.pov.addplane()
+ ob = context.object
+ mat_search(cache)
+ except ValueError:
+ pass
+ cache = []
+ plane_search = False
+ if word == "box":
+ box_search = True
+ name_search = False
+ if box_search:
+ cache.append(word)
+ if cache[-1] == "}":
+ try:
+ x0 = float(cache[2])
+ y0 = float(cache[3])
+ z0 = float(cache[4])
+ x1 = float(cache[5])
+ y1 = float(cache[6])
+ z1 = float(cache[7])
+ # imported_corner_1=(x0, y0, z0)
+ # imported_corner_2 =(x1, y1, z1)
+ center = ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2)
+ bpy.ops.pov.addbox()
+ ob = context.object
+ ob.location = center
+ mat_search(cache)
+
+ except ValueError:
+ pass
+ cache = []
+ box_search = False
+ if word == "cylinder":
+ cylinder_search = True
name_search = False
- cache = []
- if word == 'vertex_vectors':
- verts_search = True
- if verts_search:
- cache.append(word)
- if word == '}':
- verts_search = False
- lenverts = cache[2]
- cache.pop()
- cache.pop(0)
- cache.pop(0)
- cache.pop(0)
- for j in range(int(lenverts)):
- x = j * 3
- y = (j * 3) + 1
- z = (j * 3) + 2
- verts.append((float(cache[x]), float(cache[y]), float(cache[z])))
- cache = []
- # if word == 'face_indices':
- # faces_search = True
- if word == 'texture_list': # XXX
- tex_search = True # XXX
- if tex_search: # XXX
- if (
- word not in {'texture_list', 'texture', '{', '}', 'face_indices'}
- and not word.isdigit()
- ): # XXX
- pov_mats.append(word) # XXX
- if word == 'face_indices':
- tex_search = False # XXX
- faces_search = True
- if faces_search:
- cache.append(word)
- if word == '}':
- faces_search = False
- lenfaces = cache[2]
- cache.pop()
- cache.pop(0)
- cache.pop(0)
- cache.pop(0)
- lf = int(lenfaces)
- var = int(len(cache) / lf)
- for k in range(lf):
- if var == 3:
- v0 = k * 3
- v1 = k * 3 + 1
- v2 = k * 3 + 2
- faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
- if var == 4:
- v0 = k * 4
- v1 = k * 4 + 1
- v2 = k * 4 + 2
- m = k * 4 + 3
- materials.append((int(cache[m])))
- faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
- if var == 6:
- v0 = k * 6
- v1 = k * 6 + 1
- v2 = k * 6 + 2
- m0 = k * 6 + 3
- m1 = k * 6 + 4
- m2 = k * 6 + 5
- materials.append(
- (int(cache[m0]), int(cache[m1]), int(cache[m2]))
+ if cylinder_search:
+ cache.append(word)
+ if cache[-1] == "}":
+ try:
+ x0 = float(cache[2])
+ y0 = float(cache[3])
+ z0 = float(cache[4])
+ x1 = float(cache[5])
+ y1 = float(cache[6])
+ z1 = float(cache[7])
+ imported_cyl_loc = (x0, y0, z0)
+ imported_cyl_loc_cap = (x1, y1, z1)
+
+ r = float(cache[8])
+
+ vec = Vector(imported_cyl_loc_cap) - Vector(imported_cyl_loc)
+ depth = vec.length
+ rot = Vector((0, 0, 1)).rotation_difference(
+ vec
+ ) # Rotation from Z axis.
+ trans = rot @ Vector( # XXX Not used, why written?
+ (0, 0, depth / 2)
+ ) # Such that origin is at center of the base of the cylinder.
+ # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
+ scale_z = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2) / 2
+ bpy.ops.pov.addcylinder(
+ R=r,
+ imported_cyl_loc=imported_cyl_loc,
+ imported_cyl_loc_cap=imported_cyl_loc_cap,
)
- faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
- # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
- # ob = object_utils.object_data_add(context, mesh, operator=None)
-
- me = bpy.data.meshes.new(name) # XXX
- ob = bpy.data.objects.new(name, me) # XXX
- bpy.context.collection.objects.link(ob) # XXX
- me.from_pydata(verts, [], faces) # XXX
-
- for mat in bpy.data.materials: # XXX
- blend_mats.append(mat.name) # XXX
- for m_name in pov_mats: # XXX
- if m_name not in blend_mats: # XXX
- bpy.data.materials.new(m_name) # XXX
+ ob = context.object
+ ob.location = (x0, y0, z0)
+ # todo: test and search where to add the below currently commented
+ # since Blender defers the evaluation until the results are needed.
+ # bpy.context.view_layer.update()
+ # as explained here: https://docs.blender.org/api/current/info_gotcha.html?highlight=gotcha#no-updates-after-setting-values
+ ob.rotation_euler = rot.to_euler()
+ ob.scale = (1, 1, scale_z)
+
+ # scale data rather than obj?
+ # bpy.ops.object.mode_set(mode='EDIT')
+ # bpy.ops.mesh.reveal()
+ # bpy.ops.mesh.select_all(action='SELECT')
+ # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
+ # bpy.ops.mesh.hide(unselected=False)
+ # bpy.ops.object.mode_set(mode='OBJECT')
+
mat_search(cache)
- ob.data.materials.append(
- bpy.data.materials[m_name]
- ) # XXX
- if materials: # XXX
- for idx, val in enumerate(materials): # XXX
- try: # XXX
- ob.data.polygons[
- idx
- ].material_index = val # XXX
- except TypeError: # XXX
- ob.data.polygons[idx].material_index = int(
- val[0]
- ) # XXX
-
- blend_mats = [] # XXX
- pov_mats = [] # XXX
- materials = [] # XXX
- cache = []
+
+ except ValueError:
+ pass
+ cache = []
+ cylinder_search = False
+ if word == "sphere":
+ sphere_search = True
+ name_search = False
+ if sphere_search:
+ cache.append(word)
+ if cache[-1] == "}":
+ x = y = z = r = 0
+ try:
+ x = float(cache[2])
+ y = float(cache[3])
+ z = float(cache[4])
+ r = float(cache[5])
+
+ except ValueError:
+ pass
+ except 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))
+ ob = context.object
+ ob.location = (x, y, z)
+ ob.scale = (r, r, r)
+ mat_search(cache)
+ cache = []
+ sphere_search = False
+ # -------- End Primitives Import -------- #
+ if word == "#declare":
name_search = True
- if name in matrixes and not self.import_at_cur:
- global_matrix = Matrix.Rotation(pi / 2.0, 4, 'X')
- ob = bpy.context.object
- matrix = ob.matrix_world
- v = matrixes[name]
- matrix[0][0] = v[0]
- matrix[1][0] = v[1]
- matrix[2][0] = v[2]
- matrix[0][1] = v[3]
- matrix[1][1] = v[4]
- matrix[2][1] = v[5]
- matrix[0][2] = v[6]
- matrix[1][2] = v[7]
- matrix[2][2] = v[8]
- matrix[0][3] = v[9]
- matrix[1][3] = v[10]
- matrix[2][3] = v[11]
- matrix = global_matrix * ob.matrix_world
- ob.matrix_world = matrix
- verts = []
- faces = []
-
- # if word == 'pigment':
- # try:
- # #all indices have been incremented once to fit a bad test file
- # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
- # color = (r,g,b,t)
-
- # except IndexError:
- # #all indices have been incremented once to fit alternate test file
- # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
- # color = (r,g,b,t)
- # except UnboundLocalError:
- # # In case no transmit is specified ? put it to 0
- # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
- # color = (r,g,b,t)
-
- # except ValueError:
- # color = (0.8,0.8,0.8,0)
- # pass
-
- # if colors == [] or (colors != [] and color not in colors):
- # colors.append(color)
- # name = ob.name+"_mat"
- # mat_names.append(name)
- # mat = bpy.data.materials.new(name)
- # mat.diffuse_color = (r,g,b)
- # mat.alpha = 1-t
- # if mat.alpha != 1:
- # mat.use_transparency=True
- # ob.data.materials.append(mat)
- # print (colors)
- # else:
- # for m in range(len(colors)):
- # if color == colors[m]:
- # ob.data.materials.append(bpy.data.materials[mat_names[m]])
+ if name_search:
+ cache.append(word)
+ if word == "mesh2":
+ name_search = False
+ if cache[-2] == "=":
+ name = cache[-3]
+ else:
+ suffix += 1
+ cache = []
+ if word in {"texture", ";"}:
+ name_search = False
+ cache = []
+ if word == "vertex_vectors":
+ verts_search = True
+ if verts_search:
+ cache.append(word)
+ if word == "}":
+ verts_search = False
+ lenverts = cache[2]
+ cache.pop()
+ cache.pop(0)
+ cache.pop(0)
+ cache.pop(0)
+ for j in range(int(lenverts)):
+ x = j * 3
+ y = (j * 3) + 1
+ z = (j * 3) + 2
+ verts.append((float(cache[x]), float(cache[y]), float(cache[z])))
+ cache = []
+ # if word == 'face_indices':
+ # faces_search = True
+ if word == "texture_list": # XXX
+ tex_search = True # XXX
+ if tex_search: # XXX
+ if (
+ word not in {"texture_list", "texture", "{", "}", "face_indices"}
+ and not word.isdigit()
+ ): # XXX
+ pov_mats.append(word) # XXX
+ if word == "face_indices":
+ tex_search = False # XXX
+ faces_search = True
+ if faces_search:
+ cache.append(word)
+ if word == "}":
+ faces_search = False
+ lenfaces = cache[2]
+ cache.pop()
+ cache.pop(0)
+ cache.pop(0)
+ cache.pop(0)
+ lf = int(lenfaces)
+ var = int(len(cache) / lf)
+ for k in range(lf):
+ if var == 3:
+ v0 = k * 3
+ v1 = k * 3 + 1
+ v2 = k * 3 + 2
+ faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
+ if var == 4:
+ v0 = k * 4
+ v1 = k * 4 + 1
+ v2 = k * 4 + 2
+ m = k * 4 + 3
+ materials.append((int(cache[m])))
+ faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
+ if var == 6:
+ v0 = k * 6
+ v1 = k * 6 + 1
+ v2 = k * 6 + 2
+ m0 = k * 6 + 3
+ m1 = k * 6 + 4
+ m2 = k * 6 + 5
+ materials.append(
+ (int(cache[m0]), int(cache[m1]), int(cache[m2]))
+ )
+ faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
+ # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
+ # ob = object_utils.object_data_add(context, mesh, operator=None)
+
+ me = bpy.data.meshes.new(name) # XXX
+ ob = bpy.data.objects.new(name, me) # XXX
+ bpy.context.collection.objects.link(ob) # XXX
+ me.from_pydata(verts, [], faces) # XXX
+
+ for mat in bpy.data.materials: # XXX
+ blend_mats.append(mat.name) # XXX
+ for m_name in pov_mats: # XXX
+ if m_name not in blend_mats: # XXX
+ bpy.data.materials.new(m_name) # XXX
+ mat_search(cache)
+ ob.data.materials.append(bpy.data.materials[m_name]) # XXX
+ if materials: # XXX
+ for idx, val in enumerate(materials): # XXX
+ try: # XXX
+ ob.data.polygons[idx].material_index = val # XXX
+ except TypeError: # XXX
+ ob.data.polygons[idx].material_index = int(val[0]) # XXX
+
+ blend_mats = [] # XXX
+ pov_mats = [] # XXX
+ materials = [] # XXX
+ cache = []
+ name_search = True
+ if name in matrixes and not self.import_at_cur:
+ global_matrix = Matrix.Rotation(pi / 2.0, 4, "X")
+ ob = bpy.context.object
+ matrix = ob.matrix_world
+ v = matrixes[name]
+ matrix[0][0] = v[0]
+ matrix[1][0] = v[1]
+ matrix[2][0] = v[2]
+ matrix[0][1] = v[3]
+ matrix[1][1] = v[4]
+ matrix[2][1] = v[5]
+ matrix[0][2] = v[6]
+ matrix[1][2] = v[7]
+ matrix[2][2] = v[8]
+ matrix[0][3] = v[9]
+ matrix[1][3] = v[10]
+ matrix[2][3] = v[11]
+ matrix = global_matrix * ob.matrix_world
+ ob.matrix_world = matrix
+ verts = []
+ faces = []
+
+ # if word == 'pigment':
+ # try:
+ # #all indices have been incremented once to fit a bad test file
+ # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
+ # color = (r,g,b,t)
+
+ # except IndexError:
+ # #all indices have been incremented once to fit alternate test file
+ # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
+ # color = (r,g,b,t)
+ # except UnboundLocalError:
+ # # In case no transmit is specified ? put it to 0
+ # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
+ # color = (r,g,b,t)
+
+ # except ValueError:
+ # color = (0.8,0.8,0.8,0)
+ # pass
+
+ # if colors == [] or (colors != [] and color not in colors):
+ # colors.append(color)
+ # name = ob.name+"_mat"
+ # mat_names.append(name)
+ # mat = bpy.data.materials.new(name)
+ # mat.diffuse_color = (r,g,b)
+ # mat.pov.alpha = 1-t
+ # if mat.pov.alpha != 1:
+ # mat.pov.use_transparency=True
+ # ob.data.materials.append(mat)
+ # print (colors)
+ # else:
+ # for m in range(len(colors)):
+ # if color == colors[m]:
+ # ob.data.materials.append(bpy.data.materials[mat_names[m]])
# To keep Avogadro Camera angle:
# for obj in bpy.context.view_layer.objects:
@@ -502,12 +502,10 @@ class ImportPOV(bpy.types.Operator, ImportHelper):
# track.track_axis ="TRACK_NEGATIVE_Z"
# track.up_axis = "UP_Y"
# obj.location = (0,0,0)
- return {'FINISHED'}
+ return {"FINISHED"}
-classes = (
- ImportPOV,
-)
+classes = (SCENE_OT_POV_Import,)
def register():
diff --git a/render_povray/scripting_gui.py b/render_povray/scripting_gui.py
index 0ca2d949..ed083101 100755
--- a/render_povray/scripting_gui.py
+++ b/render_povray/scripting_gui.py
@@ -21,8 +21,7 @@ def locate_docpath():
addon_prefs = bpy.context.preferences.addons[__package__].preferences
# Use the system preference if its set.
- pov_documents = addon_prefs.docpath_povray
- if pov_documents:
+ if pov_documents := addon_prefs.docpath_povray:
if os.path.exists(pov_documents):
return pov_documents
# Implicit else, as here return was still not triggered:
@@ -31,7 +30,7 @@ def locate_docpath():
)
# Windows Only
- if platform.startswith('win'):
+ if platform.startswith("win"):
import winreg
try:
@@ -47,7 +46,7 @@ def locate_docpath():
# search the path all os's
pov_documents_default = "include"
- os_path_ls = os.getenv("PATH").split(':') + [""]
+ os_path_ls = os.getenv("PATH").split(":") + [""]
for dir_name in os_path_ls:
pov_documents = os.path.join(dir_name, pov_documents_default)
@@ -63,10 +62,10 @@ class TextButtonsPanel:
"""Use this class to define buttons from the side tab of
text window."""
- bl_space_type = 'TEXT_EDITOR'
- bl_region_type = 'UI'
+ bl_space_type = "TEXT_EDITOR"
+ bl_region_type = "UI"
bl_label = "POV-Ray"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -86,12 +85,12 @@ class TEXT_OT_POV_insert(Operator):
bl_idname = "text.povray_insert"
bl_label = "Insert"
- filepath: bpy.props.StringProperty(name="Filepath", subtype='FILE_PATH')
+ filepath: bpy.props.StringProperty(name="Filepath", subtype="FILE_PATH")
@classmethod
def poll(cls, context):
text = context.space_data.text
- return context.area.type == 'TEXT_EDITOR' and text is not None
+ return context.area.type == "TEXT_EDITOR" and text is not None
# return bpy.ops.text.insert.poll() this Bpy op has no poll()
def execute(self, context):
@@ -103,7 +102,7 @@ class TEXT_OT_POV_insert(Operator):
# context.space_data.text.write(file.read())
if not file.closed:
file.close()
- return {'FINISHED'}
+ return {"FINISHED"}
def validinsert(ext):
@@ -119,7 +118,7 @@ class TEXT_MT_POV_insert(Menu):
def draw(self, context):
pov_documents = locate_docpath()
- prop = self.layout.operator("wm.path_open", text="Open folder", icon='FILE_FOLDER')
+ prop = self.layout.operator("wm.path_open", text="Open folder", icon="FILE_FOLDER")
prop.filepath = pov_documents
self.layout.separator()
@@ -140,15 +139,18 @@ class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel):
only or adds to 3d scene."""
bl_label = "POV"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
text = context.space_data.text
- pov_documents = locate_docpath()
- if not pov_documents:
+ if pov_documents := locate_docpath():
+ # print(pov_documents)
+ layout.menu(TEXT_MT_POV_insert.bl_idname)
+
+ else:
layout.label(text="Please configure ", icon="INFO")
layout.label(text="default pov include path ")
layout.label(text="in addon preferences")
@@ -159,24 +161,19 @@ class TEXT_PT_POV_custom_code(TextButtonsPanel, Panel):
icon="PREFERENCES",
).module = "render_povray"
- # layout.separator()
- else:
- # print(pov_documents)
- layout.menu(TEXT_MT_POV_insert.bl_idname)
-
if text:
box = layout.box()
- box.label(text='Source to render:', icon='RENDER_STILL')
+ box.label(text="Source to render:", icon="RENDER_STILL")
row = box.row()
row.prop(text.pov, "custom_code", expand=True)
- if text.pov.custom_code in {'3dview'}:
- box.operator("render.render", icon='OUTLINER_DATA_ARMATURE')
- if text.pov.custom_code in {'text'}:
+ if text.pov.custom_code in {"3dview"}:
+ box.operator("render.render", icon="OUTLINER_DATA_ARMATURE")
+ if text.pov.custom_code in {"text"}:
rtext = bpy.context.space_data.text # is r a typo ? or why written, not used
- box.operator("text.run", icon='ARMATURE_DATA')
+ box.operator("text.run", icon="ARMATURE_DATA")
# layout.prop(text.pov, "custom_code")
- elif text.pov.custom_code in {'both'}:
- box.operator("render.render", icon='POSE_HLT')
+ elif text.pov.custom_code in {"both"}:
+ box.operator("render.render", icon="POSE_HLT")
layout.label(text="Please specify declared", icon="INFO")
layout.label(text="items in properties ")
# layout.label(text="")
@@ -218,14 +215,14 @@ class VIEW_MT_POV_import(Menu):
def draw(self, context):
layout = self.layout
- layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator_context = "INVOKE_REGION_WIN"
layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
def menu_func_import(self, context):
"""Add the import operator to menu"""
engine = context.scene.render.engine
- if engine == 'POVRAY_RENDER':
+ if engine == "POVRAY_RENDER":
self.layout.operator("import_scene.pov", icon="FORCE_LENNARDJONES")
diff --git a/render_povray/scripting_properties.py b/render_povray/scripting_properties.py
index 59c37381..cfcaf658 100755
--- a/render_povray/scripting_properties.py
+++ b/render_povray/scripting_properties.py
@@ -26,9 +26,7 @@ class RenderPovSettingsText(PropertyGroup):
)
-classes = (
- RenderPovSettingsText,
-)
+classes = (RenderPovSettingsText,)
def register():
diff --git a/render_povray/shading.py b/render_povray/shading.py
index 67ee8e51..cc61c5b8 100755
--- a/render_povray/shading.py
+++ b/render_povray/shading.py
@@ -7,102 +7,101 @@
import bpy
-def write_object_material_interior(material, ob, tab_write):
+def write_object_material_interior(file, material, ob, tab_write):
"""Translate some object level material from Blender UI (VS data level)
to POV interior{} syntax and write it to exported file.
- This is called in object_mesh_topology.export_meshes
+ This is called in model_all.objects_loop
"""
# DH - modified some variables to be function local, avoiding RNA write
# this should be checked to see if it is functionally correct
# Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
- # if material and material.transparency_method == 'RAYTRACE':
- if material:
- # But there can be only one!
- if material.pov_subsurface_scattering.use: # SSS IOR get highest priority
- tab_write("interior {\n")
- tab_write("ior %.6f\n" % material.pov_subsurface_scattering.ior)
- # Then the raytrace IOR taken from raytrace transparency properties and used for
- # reflections if IOR Mirror option is checked.
- elif material.pov.mirror_use_IOR:
- tab_write("interior {\n")
- tab_write("ior %.6f\n" % material.pov_raytrace_transparency.ior)
- elif material.pov.transparency_method == 'Z_TRANSPARENCY':
- tab_write("interior {\n")
- tab_write("ior 1.0\n")
- else:
- tab_write("interior {\n")
- tab_write("ior %.6f\n" % material.pov_raytrace_transparency.ior)
+ # if material and material.pov.transparency_method == 'RAYTRACE':
+ if not material:
+ return
+ # implicit if material:
+ # But there can be only one ior!
+ if material.pov_subsurface_scattering.use: # SSS IOR get highest priority
+ tab_write(file, "interior {\n")
+ tab_write(file, "ior %.6f\n" % material.pov_subsurface_scattering.ior)
+ # Then the raytrace IOR taken from raytrace transparency properties and used for
+ # reflections if IOR Mirror option is checked.
+ elif material.pov.mirror_use_IOR or material.pov.transparency_method != "Z_TRANSPARENCY":
+ tab_write(file, "interior {\n")
+ tab_write(file, "ior %.6f\n" % material.pov_raytrace_transparency.ior)
+ else:
+ tab_write(file, "interior {\n")
+ tab_write(file, "ior 1.0\n")
+ pov_fake_caustics = False
+ pov_photons_refraction = False
+ pov_photons_reflection = bool(material.pov.photons_reflection)
+ if not material.pov.refraction_caustics:
pov_fake_caustics = False
pov_photons_refraction = False
- pov_photons_reflection = False
-
- if material.pov.photons_reflection:
- pov_photons_reflection = True
- if not material.pov.refraction_caustics:
- pov_fake_caustics = False
- pov_photons_refraction = False
- elif material.pov.refraction_type == "1":
- pov_fake_caustics = True
- pov_photons_refraction = False
- elif material.pov.refraction_type == "2":
- pov_fake_caustics = False
- pov_photons_refraction = True
-
- # If only Raytrace transparency is set, its IOR will be used for refraction, but user
- # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
- # Last, if none of the above is specified, user can set up 'un-physical' fresnel
- # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
- if material.pov.caustics_enable:
- if pov_fake_caustics:
- tab_write("caustics %.3g\n" % material.pov.fake_caustics_power)
- if pov_photons_refraction:
- # Default of 1 means no dispersion
- tab_write("dispersion %.6f\n" % material.pov.photons_dispersion)
- tab_write("dispersion_samples %.d\n" % material.pov.photons_dispersion_samples)
- # TODO
- # Other interior args
- if material.pov.use_transparency and material.pov.transparency_method == 'RAYTRACE':
- # fade_distance
- # In Blender this value has always been reversed compared to what tooltip says.
- # 100.001 rather than 100 so that it does not get to 0
- # which deactivates the feature in POV
- tab_write(
- "fade_distance %.3g\n" % (100.001 - material.pov_raytrace_transparency.depth_max)
- )
- # fade_power
- tab_write("fade_power %.3g\n" % material.pov_raytrace_transparency.falloff)
- # fade_color
- tab_write("fade_color <%.3g, %.3g, %.3g>\n" % material.pov.interior_fade_color[:])
-
- # (variable) dispersion_samples (constant count for now)
- tab_write("}\n")
- if material.pov.photons_reflection or material.pov.refraction_type == "2":
- tab_write("photons{")
- tab_write("target %.3g\n" % ob.pov.spacing_multiplier)
- if not ob.pov.collect_photons:
- tab_write("collect off\n")
- if pov_photons_refraction:
- tab_write("refraction on\n")
- if pov_photons_reflection:
- tab_write("reflection on\n")
- tab_write("}\n")
+ elif material.pov.refraction_type == "1":
+ pov_fake_caustics = True
+ pov_photons_refraction = False
+ elif material.pov.refraction_type == "2":
+ pov_fake_caustics = False
+ pov_photons_refraction = True
+
+ # If only Raytrace transparency is set, its IOR will be used for refraction, but user
+ # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
+ # Last, if none of the above is specified, user can set up 'un-physical' fresnel
+ # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
+ if material.pov.caustics_enable:
+ if pov_fake_caustics:
+ tab_write(file, "caustics %.3g\n" % material.pov.fake_caustics_power)
+ if pov_photons_refraction:
+ # Default of 1 means no dispersion
+ tab_write(file, "dispersion %.6f\n" % material.pov.photons_dispersion)
+ tab_write(file, "dispersion_samples %.d\n" % material.pov.photons_dispersion_samples)
+ # TODO
+ # Other interior args
+ if material.pov.use_transparency and material.pov.transparency_method == "RAYTRACE":
+ # fade_distance
+ # In Blender this value has always been reversed compared to what tooltip says.
+ # 100.001 rather than 100 so that it does not get to 0
+ # which deactivates the feature in POV
+ tab_write(
+ file, "fade_distance %.3g\n" % (100.001 - material.pov_raytrace_transparency.depth_max)
+ )
+ # fade_power
+ tab_write(file, "fade_power %.3g\n" % material.pov_raytrace_transparency.falloff)
+ # fade_color
+ tab_write(file, "fade_color <%.3g, %.3g, %.3g>\n" % material.pov.interior_fade_color[:])
+
+ # (variable) dispersion_samples (constant count for now)
+ tab_write(file, "}\n")
+ if material.pov.photons_reflection or material.pov.refraction_type == "2":
+ tab_write(file, "photons{")
+ tab_write(file, "target %.3g\n" % ob.pov.spacing_multiplier)
+ if not ob.pov.collect_photons:
+ tab_write(file, "collect off\n")
+ if pov_photons_refraction:
+ tab_write(file, "refraction on\n")
+ if pov_photons_reflection:
+ tab_write(file, "reflection on\n")
+ tab_write(file, "}\n")
def write_material(
+ file,
using_uberpov,
DEF_MAT_NAME,
tab_write,
- safety,
comments,
unique_name,
material_names_dictionary,
- material
+ material,
):
"""Translate Blender material to POV texture{} block and write in exported file."""
# Assumes only called once on each material
+
+ from .render import safety
+
if material:
name_orig = material.name
name = material_names_dictionary[name_orig] = unique_name(
@@ -122,30 +121,31 @@ def write_material(
# ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
# ref_level_bound=3 Means Maximum Spec and Mirror
- def pov_has_no_specular_maps(ref_level_bound):
+ def pov_has_no_specular_maps(file, ref_level_bound):
"""Translate Blender specular map influence to POV finish map trick and write to file."""
if ref_level_bound == 1:
if comments:
- tab_write("//--No specular nor Mirror reflection--\n")
+ tab_write(file, "//--No specular nor Mirror reflection--\n")
else:
- tab_write("\n")
- tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=1))
+ tab_write(file, "\n")
+ tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=1))
elif ref_level_bound == 2:
if comments:
tab_write(
- "//--translation of spec and mir levels for when no map " "influences them--\n"
+ file,
+ "//--translation of spec and mir levels for when no map " "influences them--\n",
)
else:
- tab_write("\n")
- tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=2))
+ tab_write(file, "\n")
+ tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=2))
elif ref_level_bound == 3:
if comments:
- tab_write("//--Maximum Spec and Mirror--\n")
+ tab_write(file, "//--Maximum Spec and Mirror--\n")
else:
- tab_write("\n")
- tab_write("#declare %s = finish {\n" % safety(name, ref_level_bound=3))
+ tab_write(file, "\n")
+ tab_write(file, "#declare %s = finish {\n" % safety(name, ref_level_bound=3))
if material:
# POV-Ray 3.7 now uses two diffuse values respectively for front and back shading
# (the back diffuse is like blender translucency)
@@ -180,188 +180,198 @@ def write_material(
if material.pov.diffuse_shader == "OREN_NAYAR" and ref_level_bound != 3:
# Blender roughness is what is generally called oren nayar Sigma,
# and brilliance in POV-Ray.
- tab_write("brilliance %.3g\n" % (0.9 + material.roughness))
+ tab_write(file, "brilliance %.3g\n" % (0.9 + material.roughness))
if material.pov.diffuse_shader == "TOON" and ref_level_bound != 3:
- tab_write("brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25))
+ tab_write(file, "brilliance %.3g\n" % (0.01 + material.diffuse_toon_smooth * 0.25))
# Lower diffuse and increase specular for toon effect seems to look better
# in POV-Ray.
front_diffuse *= 0.5
if material.pov.diffuse_shader == "MINNAERT" and ref_level_bound != 3:
- # tab_write("aoi %.3g\n" % material.darkness)
+ # tab_write(file, "aoi %.3g\n" % material.pov.darkness)
pass # let's keep things simple for now
if material.pov.diffuse_shader == "FRESNEL" and ref_level_bound != 3:
- # tab_write("aoi %.3g\n" % material.diffuse_fresnel_factor)
+ # tab_write(file, "aoi %.3g\n" % material.pov.diffuse_fresnel_factor)
pass # let's keep things simple for now
if material.pov.diffuse_shader == "LAMBERT" and ref_level_bound != 3:
# trying to best match lambert attenuation by that constant brilliance value
- tab_write("brilliance 1\n")
+ tab_write(file, "brilliance 1\n")
if ref_level_bound == 2:
# ------------------------------ Specular Shader ------------------------------ #
# No difference between phong and cook torrence in blender HaHa!
- if (
- material.pov.specular_shader == "COOKTORR"
- or material.pov.specular_shader == "PHONG"
- ):
- tab_write("phong %.3g\n" % material.pov.specular_intensity)
- tab_write("phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
+ if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
+ tab_write(file, "phong %.3g\n" % material.pov.specular_intensity)
+ tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
# POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
elif material.pov.specular_shader == "BLINN":
# Use blender Blinn's IOR just as some factor for spec intensity
tab_write(
+ file,
"specular %.3g\n"
- % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0))
+ % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
)
- tab_write("roughness %.3g\n" % roughness)
+ tab_write(file, "roughness %.3g\n" % roughness)
# Could use brilliance 2(or varying around 2 depending on ior or factor) too.
elif material.pov.specular_shader == "TOON":
- tab_write("phong %.3g\n" % (material.pov.specular_intensity * 2.0))
+ tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
# use extreme phong_size
- tab_write("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0))
+ tab_write(
+ file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
+ )
elif material.pov.specular_shader == "WARDISO":
# find best suited default constant for brilliance Use both phong and
# specular for some values.
tab_write(
+ file,
"specular %.3g\n"
- % (material.pov.specular_intensity / (material.pov.specular_slope + 0.0005))
+ % (
+ material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
+ ),
)
# find best suited default constant for brilliance Use both phong and
# specular for some values.
- tab_write("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0))
+ tab_write(
+ file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
+ )
# find best suited default constant for brilliance Use both phong and
# specular for some values.
- tab_write("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
+ tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
# -------------------------------------------------------------------------------- #
elif ref_level_bound == 1:
- if (
- material.pov.specular_shader == "COOKTORR"
- or material.pov.specular_shader == "PHONG"
- ):
- tab_write("phong 0\n") # %.3g\n" % (material.pov.specular_intensity/5))
- tab_write("phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
+ if material.pov.specular_shader in ["COOKTORR", "PHONG"]:
+ tab_write(file, "phong 0\n") # %.3g\n" % (material.pov.specular_intensity/5))
+ tab_write(file, "phong_size %.3g\n" % (material.pov.specular_hardness / 3.14))
# POV-Ray 'specular' keyword corresponds to a Blinn model, without the ior.
elif material.pov.specular_shader == "BLINN":
# Use blender Blinn's IOR just as some factor for spec intensity
tab_write(
+ file,
"specular %.3g\n"
- % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0))
+ % (material.pov.specular_intensity * (material.pov.specular_ior / 4.0)),
)
- tab_write("roughness %.3g\n" % roughness)
+ tab_write(file, "roughness %.3g\n" % roughness)
# Could use brilliance 2(or varying around 2 depending on ior or factor) too.
elif material.pov.specular_shader == "TOON":
- tab_write("phong %.3g\n" % (material.pov.specular_intensity * 2.0))
+ tab_write(file, "phong %.3g\n" % (material.pov.specular_intensity * 2.0))
# use extreme phong_size
- tab_write("phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0))
+ tab_write(
+ file, "phong_size %.3g\n" % (0.1 + material.pov.specular_toon_smooth / 2.0)
+ )
elif material.pov.specular_shader == "WARDISO":
# find best suited default constant for brilliance Use both phong and
# specular for some values.
tab_write(
+ file,
"specular %.3g\n"
- % (material.pov.specular_intensity / (material.pov.specular_slope + 0.0005))
+ % (
+ material.pov.specular_intensity / (material.pov.specular_slope + 0.0005)
+ ),
)
# find best suited default constant for brilliance Use both phong and
# specular for some values.
- tab_write("roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0))
+ tab_write(
+ file, "roughness %.4g\n" % (0.0005 + material.pov.specular_slope / 10.0)
+ )
# find best suited default constant for brilliance Use both phong and
# specular for some values.
- tab_write("brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
+ tab_write(file, "brilliance %.4g\n" % (1.8 - material.pov.specular_slope * 1.8))
elif ref_level_bound == 3:
# Spec must be Max at ref_level_bound 3 so that white of mixing texture always shows specularity
# That's why it's multiplied by 255. maybe replace by texture's brightest pixel value?
if material.pov_texture_slots:
max_spec_factor = (
- material.pov.specular_intensity
- * material.pov.specular_color.v
- * 255
- * slot.specular_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
+ 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))
+ tab_write(file, "specular %.3g\n" % max_spec_factor)
+ tab_write(file, "roughness %.3g\n" % (1 / material.pov.specular_hardness))
+ tab_write(file, "diffuse %.3g, %.3g\n" % (front_diffuse, back_diffuse))
- tab_write("ambient %.3g\n" % material.pov.ambient)
+ tab_write(file, "ambient %.3g\n" % material.pov.ambient)
# POV-Ray blends the global value
- # tab_write("ambient rgb <%.3g, %.3g, %.3g>\n" % \
+ # tab_write(file, "ambient rgb <%.3g, %.3g, %.3g>\n" % \
# tuple([c*material.pov.ambient for c in world.ambient_color]))
- tab_write("emission %.3g\n" % material.pov.emit) # New in POV-Ray 3.7
+ tab_write(file, "emission %.3g\n" % material.pov.emit) # New in POV-Ray 3.7
# POV-Ray just ignores roughness if there's no specular keyword
- # tab_write("roughness %.3g\n" % roughness)
+ # tab_write(file, "roughness %.3g\n" % roughness)
if material.pov.conserve_energy:
# added for more realistic shading. Needs some checking to see if it
# really works. --Maurice.
- tab_write("conserve_energy\n")
+ tab_write(file, "conserve_energy\n")
if colored_specular_found:
- tab_write("metallic\n")
+ tab_write(file, "metallic\n")
# 'phong 70.0 '
- if ref_level_bound != 1:
- if material.pov_raytrace_mirror.use:
- raytrace_mirror = material.pov_raytrace_mirror
- if raytrace_mirror.reflect_factor:
- 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)
- # 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")
- tab_write(
- "roughness %.6f\n" % (0.000001 / raytrace_mirror.gloss_factor)
- )
- # tab_write("#end #end #end\n") # This and previous comment for backward compatibility, messier pov code
- if material.pov.mirror_use_IOR: # WORKING ?
- # Removed from the line below: gives a more physically correct
- # material but needs proper IOR. --Maurice
- tab_write("fresnel 1 ")
+ if ref_level_bound != 1 and material.pov_raytrace_mirror.use:
+ raytrace_mirror = material.pov_raytrace_mirror
+ if raytrace_mirror.reflect_factor:
+ tab_write(file, "reflection {\n")
+ tab_write(file, "rgb <%.3g, %.3g, %.3g>\n" % material.pov.mirror_color[:])
+ if material.metallic:
+ tab_write(file, "metallic %.3g\n" % material.metallic)
+ # Blurry reflections for UberPOV
+ if using_uberpov and raytrace_mirror.gloss_factor < 1.0:
+ # tab_write(file, "#ifdef(unofficial) #if(unofficial = \"patch\") #if(patch(\"upov-reflection-roughness\") > 0)\n")
tab_write(
- "falloff %.3g exponent %.3g} "
- % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor)
+ file, "roughness %.6f\n" % (0.000001 / raytrace_mirror.gloss_factor)
)
+ # tab_write(file, "#end #end #end\n") # This and previous comment for backward compatibility, messier pov code
+ if material.pov.mirror_use_IOR: # WORKING ?
+ # Removed from the line below: gives a more physically correct
+ # material but needs proper IOR. --Maurice
+ tab_write(file, "fresnel 1 ")
+ tab_write(
+ file,
+ "falloff %.3g exponent %.3g} "
+ % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_factor),
+ )
if material.pov_subsurface_scattering.use:
subsurface_scattering = material.pov_subsurface_scattering
tab_write(
+ file,
"subsurface { translucency <%.3g, %.3g, %.3g> }\n"
% (
(subsurface_scattering.radius[0]),
(subsurface_scattering.radius[1]),
(subsurface_scattering.radius[2]),
- )
+ ),
)
if material.pov.irid_enable:
tab_write(
+ file,
"irid { %.4g thickness %.4g turbulence %.4g }"
% (
material.pov.irid_amount,
material.pov.irid_thickness,
material.pov.irid_turbulence,
- )
+ ),
)
else:
- tab_write("diffuse 0.8\n")
- tab_write("phong 70.0\n")
+ tab_write(file, "diffuse 0.8\n")
+ tab_write(file, "phong 70.0\n")
- # tab_write("specular 0.2\n")
+ # tab_write(file, "specular 0.2\n")
# This is written into the object
"""
@@ -369,16 +379,16 @@ def write_material(
'interior { ior %.3g} ' % material.raytrace_transparency.ior
"""
- # tab_write("crand 1.0\n") # Sand granyness
- # tab_write("metallic %.6f\n" % material.spec)
- # tab_write("phong %.6f\n" % material.spec)
- # tab_write("phong_size %.6f\n" % material.spec)
- # tab_write("brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness
+ # tab_write(file, "crand 1.0\n") # Sand granyness
+ # tab_write(file, "metallic %.6f\n" % material.metallic)
+ # tab_write(file, "phong %.6f\n" % material.spec)
+ # tab_write(file, "phong_size %.6f\n" % material.spec)
+ # tab_write(file, "brilliance %.6f " % (material.pov.specular_hardness/256.0) # Like hardness
- tab_write("}\n\n")
+ tab_write(file, "}\n\n")
# ref_level_bound=2 Means translation of spec and mir levels for when no map influences them
- pov_has_no_specular_maps(ref_level_bound=2)
+ pov_has_no_specular_maps(file, ref_level_bound=2)
if material:
special_texture_found = False
@@ -411,1330 +421,7 @@ def write_material(
if special_texture_found or colored_specular_found:
# ref_level_bound=1 Means No specular nor Mirror reflection
- pov_has_no_specular_maps(ref_level_bound=1)
+ pov_has_no_specular_maps(file, ref_level_bound=1)
# ref_level_bound=3 Means Maximum Spec and Mirror
- pov_has_no_specular_maps(ref_level_bound=3)
-
-
-def export_pattern(texture):
- """Translate Blender procedural textures to POV patterns and write to pov file.
-
- Function Patterns can be used to better access sub components of a pattern like
- grey values for influence mapping
- """
- tex = texture
- pat = tex.pov
- pat_name = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name))
- mapping_dif = "translate <%.4g,%.4g,%.4g> scale <%.4g,%.4g,%.4g>" % (
- pat.tex_mov_x,
- pat.tex_mov_y,
- pat.tex_mov_z,
- 1.0 / pat.tex_scale_x,
- 1.0 / pat.tex_scale_y,
- 1.0 / pat.tex_scale_z,
- )
- text_strg = ""
-
- def export_color_ramp(texture):
- tex = texture
- pat = tex.pov
- col_ramp_strg = "color_map {\n"
- num_color = 0
- for el in tex.color_ramp.elements:
- num_color += 1
- pos = el.position
- col = el.color
- col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3]
- if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}:
- col_ramp_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % (
- pos,
- col_r,
- col_g,
- col_b,
- col_a,
- )
- if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3:
- col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "hexagon" and num_color < 4:
- col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "square" and num_color < 5:
- col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "triangular" and num_color < 7:
- col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
-
- col_ramp_strg += "} \n"
- # end color map
- return col_ramp_strg
-
- # much work to be done here only defaults translated for now:
- # pov noise_generator 3 means perlin noise
- if tex.type not in {"NONE", "IMAGE"} and pat.tex_pattern_type == "emulator":
- text_strg += "pigment {\n"
- # ------------------------- EMULATE BLENDER VORONOI TEXTURE ------------------------- #
- if tex.type == "VORONOI":
- text_strg += "crackle\n"
- text_strg += " offset %.4g\n" % tex.nabla
- text_strg += " form <%.4g,%.4g,%.4g>\n" % (tex.weight_1, tex.weight_2, tex.weight_3)
- if tex.distance_metric == "DISTANCE":
- text_strg += " metric 2.5\n"
- if tex.distance_metric == "DISTANCE_SQUARED":
- text_strg += " metric 2.5\n"
- text_strg += " poly_wave 2\n"
- if tex.distance_metric == "MINKOVSKY":
- text_strg += " metric %s\n" % tex.minkovsky_exponent
- if tex.distance_metric == "MINKOVSKY_FOUR":
- text_strg += " metric 4\n"
- if tex.distance_metric == "MINKOVSKY_HALF":
- text_strg += " metric 0.5\n"
- if tex.distance_metric == "CHEBYCHEV":
- text_strg += " metric 10\n"
- if tex.distance_metric == "MANHATTAN":
- text_strg += " metric 1\n"
-
- if tex.color_mode == "POSITION":
- text_strg += "solid\n"
- text_strg += "scale 0.25\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<0,0,0,1>]\n"
- text_strg += "[1 color rgbt<1,1,1,0>]\n"
- text_strg += "}\n"
-
- # ------------------------- EMULATE BLENDER CLOUDS TEXTURE ------------------------- #
- if tex.type == "CLOUDS":
- if tex.noise_type == "SOFT_NOISE":
- text_strg += "wrinkles\n"
- text_strg += "scale 0.25\n"
- else:
- text_strg += "granite\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<0,0,0,1>]\n"
- text_strg += "[1 color rgbt<1,1,1,0>]\n"
- text_strg += "}\n"
-
- # ------------------------- EMULATE BLENDER WOOD TEXTURE ------------------------- #
- if tex.type == "WOOD":
- if tex.wood_type == "RINGS":
- text_strg += "wood\n"
- text_strg += "scale 0.25\n"
- if tex.wood_type == "RINGNOISE":
- text_strg += "wood\n"
- text_strg += "scale 0.25\n"
- text_strg += "turbulence %.4g\n" % (tex.turbulence / 100)
- if tex.wood_type == "BANDS":
- text_strg += "marble\n"
- text_strg += "scale 0.25\n"
- text_strg += "rotate <45,-45,45>\n"
- if tex.wood_type == "BANDNOISE":
- text_strg += "marble\n"
- text_strg += "scale 0.25\n"
- text_strg += "rotate <45,-45,45>\n"
- text_strg += "turbulence %.4g\n" % (tex.turbulence / 10)
-
- if tex.noise_basis_2 == "SIN":
- text_strg += "sine_wave\n"
- if tex.noise_basis_2 == "TRI":
- text_strg += "triangle_wave\n"
- if tex.noise_basis_2 == "SAW":
- text_strg += "ramp_wave\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<0,0,0,0>]\n"
- text_strg += "[1 color rgbt<1,1,1,0>]\n"
- text_strg += "}\n"
-
- # ------------------------- EMULATE BLENDER STUCCI TEXTURE ------------------------- #
- if tex.type == "STUCCI":
- text_strg += "bozo\n"
- text_strg += "scale 0.25\n"
- if tex.noise_type == "HARD_NOISE":
- text_strg += "triangle_wave\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbf<1,1,1,0>]\n"
- text_strg += "[1 color rgbt<0,0,0,1>]\n"
- text_strg += "}\n"
- else:
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbf<0,0,0,1>]\n"
- text_strg += "[1 color rgbt<1,1,1,0>]\n"
- text_strg += "}\n"
-
- # ------------------------- EMULATE BLENDER MAGIC TEXTURE ------------------------- #
- if tex.type == "MAGIC":
- text_strg += "leopard\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0.5>]\n"
- text_strg += "[0.25 color rgbf<0,1,0,0.75>]\n"
- text_strg += "[0.5 color rgbf<0,0,1,0.75>]\n"
- text_strg += "[0.75 color rgbf<1,0,1,0.75>]\n"
- text_strg += "[1 color rgbf<0,1,0,0.75>]\n"
- text_strg += "}\n"
- text_strg += "scale 0.1\n"
-
- # ------------------------- EMULATE BLENDER MARBLE TEXTURE ------------------------- #
- if tex.type == "MARBLE":
- text_strg += "marble\n"
- text_strg += "turbulence 0.5\n"
- text_strg += "noise_generator 3\n"
- text_strg += "scale 0.75\n"
- text_strg += "rotate <45,-45,45>\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- if tex.marble_type == "SOFT":
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<0,0,0,0>]\n"
- text_strg += "[0.05 color rgbt<0,0,0,0>]\n"
- text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n"
- text_strg += "}\n"
- elif tex.marble_type == "SHARP":
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<0,0,0,0>]\n"
- text_strg += "[0.025 color rgbt<0,0,0,0>]\n"
- text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n"
- text_strg += "}\n"
- else:
- text_strg += "[0 color rgbt<0,0,0,0>]\n"
- text_strg += "[1 color rgbt<1,1,1,0>]\n"
- text_strg += "}\n"
- if tex.noise_basis_2 == "SIN":
- text_strg += "sine_wave\n"
- if tex.noise_basis_2 == "TRI":
- text_strg += "triangle_wave\n"
- if tex.noise_basis_2 == "SAW":
- text_strg += "ramp_wave\n"
-
- # ------------------------- EMULATE BLENDER BLEND TEXTURE ------------------------- #
- if tex.type == "BLEND":
- if tex.progression == "RADIAL":
- text_strg += "radial\n"
- if tex.use_flip_axis == "HORIZONTAL":
- text_strg += "rotate x*90\n"
- else:
- text_strg += "rotate <-90,0,90>\n"
- text_strg += "ramp_wave\n"
- elif tex.progression == "SPHERICAL":
- text_strg += "spherical\n"
- text_strg += "scale 3\n"
- text_strg += "poly_wave 1\n"
- elif tex.progression == "QUADRATIC_SPHERE":
- text_strg += "spherical\n"
- text_strg += "scale 3\n"
- text_strg += " poly_wave 2\n"
- elif tex.progression == "DIAGONAL":
- text_strg += "gradient <1,1,0>\n"
- text_strg += "scale 3\n"
- elif tex.use_flip_axis == "HORIZONTAL":
- text_strg += "gradient x\n"
- text_strg += "scale 2.01\n"
- elif tex.use_flip_axis == "VERTICAL":
- text_strg += "gradient y\n"
- text_strg += "scale 2.01\n"
- # text_strg+="ramp_wave\n"
- # text_strg+="frequency 0.5\n"
- text_strg += "phase 0.5\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- if tex.progression == "LINEAR":
- text_strg += " poly_wave 1\n"
- if tex.progression == "QUADRATIC":
- text_strg += " poly_wave 2\n"
- if tex.progression == "EASING":
- text_strg += " poly_wave 1.5\n"
-
- # ------------------------- EMULATE BLENDER MUSGRAVE TEXTURE ------------------------- #
- # if tex.type == 'MUSGRAVE':
- # text_strg+="function{ f_ridged_mf( x, y, 0, 1, 2, 9, -0.5, 3,3 )*0.5}\n"
- # text_strg+="color_map {\n"
- # text_strg+="[0 color rgbf<0,0,0,1>]\n"
- # text_strg+="[1 color rgbf<1,1,1,0>]\n"
- # text_strg+="}\n"
- # simplified for now:
-
- if tex.type == "MUSGRAVE":
- text_strg += "bozo scale 0.25 \n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += (
- "color_map {[0.5 color rgbf<0,0,0,1>][1 color rgbt<1,1,1,0>]}ramp_wave \n"
- )
-
- # ------------------------- EMULATE BLENDER DISTORTED NOISE TEXTURE ------------------------- #
- if tex.type == "DISTORTED_NOISE":
- text_strg += "average\n"
- text_strg += " pigment_map {\n"
- text_strg += " [1 bozo scale 0.25 turbulence %.4g\n" % tex.distortion
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- text_strg += "]\n"
-
- if tex.noise_distortion == "CELL_NOISE":
- text_strg += " [1 cells scale 0.1\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- text_strg += "]\n"
- if tex.noise_distortion == "VORONOI_CRACKLE":
- text_strg += " [1 crackle scale 0.25\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- text_strg += "]\n"
- if tex.noise_distortion in [
- "VORONOI_F1",
- "VORONOI_F2",
- "VORONOI_F3",
- "VORONOI_F4",
- "VORONOI_F2_F1",
- ]:
- text_strg += " [1 crackle metric 2.5 scale 0.25 turbulence %.4g\n" % (
- tex.distortion / 2
- )
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- text_strg += "]\n"
- else:
- text_strg += " [1 wrinkles scale 0.25\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0 color rgbt<1,1,1,0>]\n"
- text_strg += "[1 color rgbf<0,0,0,1>]\n"
- text_strg += "}\n"
- text_strg += "]\n"
- text_strg += " }\n"
-
- # ------------------------- EMULATE BLENDER NOISE TEXTURE ------------------------- #
- if tex.type == "NOISE":
- text_strg += "cells\n"
- text_strg += "turbulence 3\n"
- text_strg += "omega 3\n"
- if tex.use_color_ramp:
- text_strg += export_color_ramp(tex)
- else:
- text_strg += "color_map {\n"
- text_strg += "[0.75 color rgb<0,0,0,>]\n"
- text_strg += "[1 color rgb<1,1,1,>]\n"
- text_strg += "}\n"
-
- # ------------------------- IGNORE OTHER BLENDER TEXTURE ------------------------- #
- else: # non translated textures
- pass
- text_strg += "}\n\n"
-
- text_strg += "#declare f%s=\n" % pat_name
- text_strg += "function{pigment{%s}}\n" % pat_name
- text_strg += "\n"
-
- elif pat.tex_pattern_type != "emulator":
- text_strg += "pigment {\n"
- text_strg += "%s\n" % pat.tex_pattern_type
- if pat.tex_pattern_type == "agate":
- text_strg += "agate_turb %.4g\n" % pat.modifier_turbulence
- if pat.tex_pattern_type in {"spiral1", "spiral2", "tiling"}:
- text_strg += "%s\n" % pat.modifier_numbers
- if pat.tex_pattern_type == "quilted":
- text_strg += "control0 %s control1 %s\n" % (
- pat.modifier_control0,
- pat.modifier_control1,
- )
- if pat.tex_pattern_type == "mandel":
- text_strg += "%s exponent %s \n" % (pat.f_iter, pat.f_exponent)
- if pat.tex_pattern_type == "julia":
- text_strg += "<%.4g, %.4g> %s exponent %s \n" % (
- pat.julia_complex_1,
- pat.julia_complex_2,
- pat.f_iter,
- pat.f_exponent,
- )
- if pat.tex_pattern_type == "magnet" and pat.magnet_style == "mandel":
- text_strg += "%s mandel %s \n" % (pat.magnet_type, pat.f_iter)
- if pat.tex_pattern_type == "magnet" and pat.magnet_style == "julia":
- text_strg += "%s julia <%.4g, %.4g> %s\n" % (
- pat.magnet_type,
- pat.julia_complex_1,
- pat.julia_complex_2,
- pat.f_iter,
- )
- if pat.tex_pattern_type in {"mandel", "julia", "magnet"}:
- text_strg += "interior %s, %.4g\n" % (pat.f_ior, pat.f_ior_fac)
- text_strg += "exterior %s, %.4g\n" % (pat.f_eor, pat.f_eor_fac)
- if pat.tex_pattern_type == "gradient":
- text_strg += "<%s, %s, %s> \n" % (
- pat.grad_orient_x,
- pat.grad_orient_y,
- pat.grad_orient_z,
- )
- if pat.tex_pattern_type == "pavement":
- num_tiles = pat.pave_tiles
- num_pattern = 1
- if pat.pave_sides == "4" and pat.pave_tiles == 3:
- num_pattern = pat.pave_pat_2
- if pat.pave_sides == "6" and pat.pave_tiles == 3:
- num_pattern = pat.pave_pat_3
- if pat.pave_sides == "3" and pat.pave_tiles == 4:
- num_pattern = pat.pave_pat_3
- if pat.pave_sides == "3" and pat.pave_tiles == 5:
- num_pattern = pat.pave_pat_4
- if pat.pave_sides == "4" and pat.pave_tiles == 4:
- num_pattern = pat.pave_pat_5
- if pat.pave_sides == "6" and pat.pave_tiles == 4:
- num_pattern = pat.pave_pat_7
- if pat.pave_sides == "4" and pat.pave_tiles == 5:
- num_pattern = pat.pave_pat_12
- if pat.pave_sides == "3" and pat.pave_tiles == 6:
- num_pattern = pat.pave_pat_12
- if pat.pave_sides == "6" and pat.pave_tiles == 5:
- num_pattern = pat.pave_pat_22
- if pat.pave_sides == "4" and pat.pave_tiles == 6:
- num_pattern = pat.pave_pat_35
- if pat.pave_sides == "6" and pat.pave_tiles == 6:
- num_tiles = 5
- text_strg += "number_of_sides %s number_of_tiles %s pattern %s form %s \n" % (
- pat.pave_sides,
- num_tiles,
- num_pattern,
- pat.pave_form,
- )
- # ------------------------- functions ------------------------- #
- if pat.tex_pattern_type == "function":
- text_strg += "{ %s" % pat.func_list
- text_strg += "(x"
- if pat.func_plus_x != "NONE":
- if pat.func_plus_x == "increase":
- text_strg += "*"
- if pat.func_plus_x == "plus":
- text_strg += "+"
- text_strg += "%.4g" % pat.func_x
- text_strg += ",y"
- if pat.func_plus_y != "NONE":
- if pat.func_plus_y == "increase":
- text_strg += "*"
- if pat.func_plus_y == "plus":
- text_strg += "+"
- text_strg += "%.4g" % pat.func_y
- text_strg += ",z"
- if pat.func_plus_z != "NONE":
- if pat.func_plus_z == "increase":
- text_strg += "*"
- if pat.func_plus_z == "plus":
- text_strg += "+"
- text_strg += "%.4g" % pat.func_z
- sort = -1
- if pat.func_list in {
- "f_comma",
- "f_crossed_trough",
- "f_cubic_saddle",
- "f_cushion",
- "f_devils_curve",
- "f_enneper",
- "f_glob",
- "f_heart",
- "f_hex_x",
- "f_hex_y",
- "f_hunt_surface",
- "f_klein_bottle",
- "f_kummer_surface_v1",
- "f_lemniscate_of_gerono",
- "f_mitre",
- "f_nodal_cubic",
- "f_noise_generator",
- "f_odd",
- "f_paraboloid",
- "f_pillow",
- "f_piriform",
- "f_quantum",
- "f_quartic_paraboloid",
- "f_quartic_saddle",
- "f_sphere",
- "f_steiners_roman",
- "f_torus_gumdrop",
- "f_umbrella",
- }:
- sort = 0
- if pat.func_list in {
- "f_bicorn",
- "f_bifolia",
- "f_boy_surface",
- "f_superellipsoid",
- "f_torus",
- }:
- sort = 1
- if pat.func_list in {
- "f_ellipsoid",
- "f_folium_surface",
- "f_hyperbolic_torus",
- "f_kampyle_of_eudoxus",
- "f_parabolic_torus",
- "f_quartic_cylinder",
- "f_torus2",
- }:
- sort = 2
- if pat.func_list in {
- "f_blob2",
- "f_cross_ellipsoids",
- "f_flange_cover",
- "f_isect_ellipsoids",
- "f_kummer_surface_v2",
- "f_ovals_of_cassini",
- "f_rounded_box",
- "f_spikes_2d",
- "f_strophoid",
- }:
- sort = 3
- if pat.func_list in {
- "f_algbr_cyl1",
- "f_algbr_cyl2",
- "f_algbr_cyl3",
- "f_algbr_cyl4",
- "f_blob",
- "f_mesh1",
- "f_poly4",
- "f_spikes",
- }:
- sort = 4
- if pat.func_list in {
- "f_devils_curve_2d",
- "f_dupin_cyclid",
- "f_folium_surface_2d",
- "f_hetero_mf",
- "f_kampyle_of_eudoxus_2d",
- "f_lemniscate_of_gerono_2d",
- "f_polytubes",
- "f_ridge",
- "f_ridged_mf",
- "f_spiral",
- "f_witch_of_agnesi",
- }:
- sort = 5
- if pat.func_list in {"f_helix1", "f_helix2", "f_piriform_2d", "f_strophoid_2d"}:
- sort = 6
- if pat.func_list == "f_helical_torus":
- sort = 7
- if sort > -1:
- text_strg += ",%.4g" % pat.func_P0
- if sort > 0:
- text_strg += ",%.4g" % pat.func_P1
- if sort > 1:
- text_strg += ",%.4g" % pat.func_P2
- if sort > 2:
- text_strg += ",%.4g" % pat.func_P3
- if sort > 3:
- text_strg += ",%.4g" % pat.func_P4
- if sort > 4:
- text_strg += ",%.4g" % pat.func_P5
- if sort > 5:
- text_strg += ",%.4g" % pat.func_P6
- if sort > 6:
- text_strg += ",%.4g" % pat.func_P7
- text_strg += ",%.4g" % pat.func_P8
- text_strg += ",%.4g" % pat.func_P9
- text_strg += ")}\n"
- # ------------------------- end functions ------------------------- #
- if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}:
- text_strg += "color_map {\n"
- num_color = 0
- if tex.use_color_ramp:
- for el in tex.color_ramp.elements:
- num_color += 1
- pos = el.position
- col = el.color
- col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3]
- if pat.tex_pattern_type not in {
- "checker",
- "hexagon",
- "square",
- "triangular",
- "brick",
- }:
- text_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % (
- pos,
- col_r,
- col_g,
- col_b,
- col_a,
- )
- if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3:
- text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "hexagon" and num_color < 4:
- text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "square" and num_color < 5:
- text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- if pat.tex_pattern_type == "triangular" and num_color < 7:
- text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (col_r, col_g, col_b, col_a)
- else:
- text_strg += "[0 color rgbf<0,0,0,1>]\n"
- text_strg += "[1 color rgbf<1,1,1,0>]\n"
- if pat.tex_pattern_type not in {"checker", "hexagon", "square", "triangular", "brick"}:
- text_strg += "} \n"
- if pat.tex_pattern_type == "brick":
- text_strg += "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n" % (
- pat.brick_size_x,
- pat.brick_size_y,
- pat.brick_size_z,
- pat.brick_mortar,
- )
- text_strg += "%s \n" % mapping_dif
- text_strg += "rotate <%.4g,%.4g,%.4g> \n" % (pat.tex_rot_x, pat.tex_rot_y, pat.tex_rot_z)
- text_strg += "turbulence <%.4g,%.4g,%.4g> \n" % (
- pat.warp_turbulence_x,
- pat.warp_turbulence_y,
- pat.warp_turbulence_z,
- )
- text_strg += "octaves %s \n" % pat.modifier_octaves
- text_strg += "lambda %.4g \n" % pat.modifier_lambda
- text_strg += "omega %.4g \n" % pat.modifier_omega
- text_strg += "frequency %.4g \n" % pat.modifier_frequency
- text_strg += "phase %.4g \n" % pat.modifier_phase
- text_strg += "}\n\n"
- text_strg += "#declare f%s=\n" % pat_name
- text_strg += "function{pigment{%s}}\n" % pat_name
- text_strg += "\n"
- return text_strg
-
-
-def string_strip_hyphen(name):
- """POV naming schemes like to conform to most restrictive charsets."""
- return name.replace("-", "")
-
-
-# WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-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
- from os import path
-
- declare_nodes = []
- scene = bpy.context.scene
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayFinishNode" and node.outputs["Finish"].is_linked:
- file.write("#declare %s = finish {\n" % pov_node_name)
- emission = node.inputs["Emission"].default_value
- if node.inputs["Emission"].is_linked:
- pass
- file.write(" emission %.4g\n" % emission)
- for link in ntree.links:
- if link.to_node == node:
-
- if link.from_node.bl_idname == "PovrayDiffuseNode":
- intensity = 0
- albedo = ""
- brilliance = 0
- crand = 0
- if link.from_node.inputs["Intensity"].is_linked:
- pass
- else:
- intensity = link.from_node.inputs["Intensity"].default_value
- if link.from_node.inputs["Albedo"].is_linked:
- pass
- else:
- if link.from_node.inputs["Albedo"].default_value:
- albedo = "albedo"
- file.write(" diffuse %s %.4g\n" % (albedo, intensity))
- if link.from_node.inputs["Brilliance"].is_linked:
- pass
- else:
- brilliance = link.from_node.inputs["Brilliance"].default_value
- file.write(" brilliance %.4g\n" % brilliance)
- if link.from_node.inputs["Crand"].is_linked:
- pass
- else:
- crand = link.from_node.inputs["Crand"].default_value
- if crand > 0:
- file.write(" crand %.4g\n" % crand)
-
- if link.from_node.bl_idname == "PovraySubsurfaceNode":
- if scene.povray.sslt_enable:
- energy = 0
- r = g = b = 0
- if link.from_node.inputs["Translucency"].is_linked:
- pass
- else:
- r, g, b, a = link.from_node.inputs["Translucency"].default_value[:]
- if link.from_node.inputs["Energy"].is_linked:
- pass
- else:
- energy = link.from_node.inputs["Energy"].default_value
- file.write(
- " subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n"
- % (r, g, b, energy)
- )
-
- if link.from_node.bl_idname in {"PovraySpecularNode", "PovrayPhongNode"}:
- intensity = 0
- albedo = ""
- roughness = 0
- metallic = 0
- phong_size = 0
- highlight = "specular"
- if link.from_node.inputs["Intensity"].is_linked:
- pass
- else:
- intensity = link.from_node.inputs["Intensity"].default_value
-
- if link.from_node.inputs["Albedo"].is_linked:
- pass
- else:
- if link.from_node.inputs["Albedo"].default_value:
- albedo = "albedo"
- if link.from_node.bl_idname in {"PovrayPhongNode"}:
- highlight = "phong"
- file.write(" %s %s %.4g\n" % (highlight, albedo, intensity))
-
- if link.from_node.bl_idname in {"PovraySpecularNode"}:
- if link.from_node.inputs["Roughness"].is_linked:
- pass
- else:
- roughness = link.from_node.inputs["Roughness"].default_value
- file.write(" roughness %.6g\n" % roughness)
-
- if link.from_node.bl_idname in {"PovrayPhongNode"}:
- if link.from_node.inputs["Size"].is_linked:
- pass
- else:
- phong_size = link.from_node.inputs["Size"].default_value
- file.write(" phong_size %s\n" % phong_size)
-
- if link.from_node.inputs["Metallic"].is_linked:
- pass
- else:
- metallic = link.from_node.inputs["Metallic"].default_value
- file.write(" metallic %.4g\n" % metallic)
-
- if link.from_node.bl_idname in {"PovrayMirrorNode"}:
- file.write(" reflection {\n")
- color = None
- exponent = 0
- metallic = 0
- falloff = 0
- fresnel = ""
- conserve = ""
- if link.from_node.inputs["Color"].is_linked:
- pass
- else:
- color = link.from_node.inputs["Color"].default_value[:]
- file.write(
- " <%.4g,%.4g,%.4g>\n"
- % (color[0], color[1], color[2])
- )
-
- if link.from_node.inputs["Exponent"].is_linked:
- pass
- else:
- exponent = link.from_node.inputs["Exponent"].default_value
- file.write(" exponent %.4g\n" % exponent)
-
- if link.from_node.inputs["Falloff"].is_linked:
- pass
- else:
- falloff = link.from_node.inputs["Falloff"].default_value
- file.write(" falloff %.4g\n" % falloff)
-
- if link.from_node.inputs["Metallic"].is_linked:
- pass
- else:
- metallic = link.from_node.inputs["Metallic"].default_value
- file.write(" metallic %.4g" % metallic)
-
- if link.from_node.inputs["Fresnel"].is_linked:
- pass
- else:
- if link.from_node.inputs["Fresnel"].default_value:
- fresnel = "fresnel"
-
- if link.from_node.inputs["Conserve energy"].is_linked:
- pass
- else:
- if link.from_node.inputs["Conserve energy"].default_value:
- conserve = "conserve_energy"
-
- file.write(" %s}\n %s\n" % (fresnel, conserve))
-
- if link.from_node.bl_idname == "PovrayAmbientNode":
- ambient = (0, 0, 0)
- if link.from_node.inputs["Ambient"].is_linked:
- pass
- else:
- ambient = link.from_node.inputs["Ambient"].default_value[:]
- file.write(" ambient <%.4g,%.4g,%.4g>\n" % ambient)
-
- if link.from_node.bl_idname in {"PovrayIridescenceNode"}:
- file.write(" irid {\n")
- amount = 0
- thickness = 0
- turbulence = 0
- if link.from_node.inputs["Amount"].is_linked:
- pass
- else:
- amount = link.from_node.inputs["Amount"].default_value
- file.write(" %.4g\n" % amount)
-
- if link.from_node.inputs["Thickness"].is_linked:
- pass
- else:
- exponent = link.from_node.inputs["Thickness"].default_value
- file.write(" thickness %.4g\n" % thickness)
-
- if link.from_node.inputs["Turbulence"].is_linked:
- pass
- else:
- falloff = link.from_node.inputs["Turbulence"].default_value
- file.write(" turbulence %.4g}\n" % turbulence)
-
- file.write("}\n")
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayTransformNode" and node.outputs["Transform"].is_linked:
- tx = node.inputs["Translate x"].default_value
- ty = node.inputs["Translate y"].default_value
- tz = node.inputs["Translate z"].default_value
- rx = node.inputs["Rotate x"].default_value
- ry = node.inputs["Rotate y"].default_value
- rz = node.inputs["Rotate z"].default_value
- sx = node.inputs["Scale x"].default_value
- sy = node.inputs["Scale y"].default_value
- sz = node.inputs["Scale z"].default_value
- file.write(
- "#declare %s = transform {\n"
- " translate<%.4g,%.4g,%.4g>\n"
- " rotate<%.4g,%.4g,%.4g>\n"
- " scale<%.4g,%.4g,%.4g>}\n" % (pov_node_name, tx, ty, tz, rx, ry, rz, sx, sy, sz)
- )
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayColorImageNode" and node.outputs["Pigment"].is_linked:
- declare_nodes.append(node.name)
- if node.image == "":
- 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)
- transform = ""
- for link in ntree.links:
- if (
- link.from_node.bl_idname == "PovrayTransformNode"
- and link.to_node == node
- ):
- pov_trans_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- transform = "transform {%s}" % pov_trans_name
- uv = ""
- if node.map_type == "uv_mapping":
- uv = "uv_mapping"
- filepath = bpy.path.abspath(im.filepath)
- file.write("#declare %s = pigment {%s image_map {\n" % (pov_node_name, uv))
- premul = "off"
- if node.premultiplied:
- premul = "on"
- once = ""
- if node.once:
- once = "once"
- file.write(
- ' "%s"\n gamma %.6g\n premultiplied %s\n'
- % (filepath, node.inputs["Gamma"].default_value, premul)
- )
- file.write(" %s\n" % once)
- if node.map_type != "uv_mapping":
- file.write(" map_type %s\n" % node.map_type)
- file.write(
- " interpolate %s\n filter all %.4g\n transmit all %.4g\n"
- % (
- node.interpolate,
- node.inputs["Filter"].default_value,
- node.inputs["Transmit"].default_value,
- )
- )
- file.write(" }\n")
- file.write(" %s\n" % transform)
- file.write(" }\n")
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayImagePatternNode" and node.outputs["Pattern"].is_linked:
- declare_nodes.append(node.name)
- if node.image != "":
- im = bpy.data.images[node.image]
- if im.filepath and path.exists(bpy.path.abspath(im.filepath)):
- transform = ""
- for link in ntree.links:
- if (
- link.from_node.bl_idname == "PovrayTransformNode"
- and link.to_node == node
- ):
- pov_trans_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- transform = "transform {%s}" % pov_trans_name
- uv = ""
- if node.map_type == "uv_mapping":
- uv = "uv_mapping"
- filepath = bpy.path.abspath(im.filepath)
- file.write("#macro %s() %s image_pattern {\n" % (pov_node_name, uv))
- premul = "off"
- if node.premultiplied:
- premul = "on"
- once = ""
- if node.once:
- once = "once"
- file.write(
- ' "%s"\n gamma %.6g\n premultiplied %s\n'
- % (filepath, node.inputs["Gamma"].default_value, premul)
- )
- file.write(" %s\n" % once)
- if node.map_type != "uv_mapping":
- file.write(" map_type %s\n" % node.map_type)
- file.write(" interpolate %s\n" % node.interpolate)
- file.write(" }\n")
- file.write(" %s\n" % transform)
- file.write("#end\n")
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayBumpMapNode" and node.outputs["Normal"].is_linked:
- if node.image != "":
- im = bpy.data.images[node.image]
- if im.filepath and path.exists(bpy.path.abspath(im.filepath)):
- transform = ""
- for link in ntree.links:
- if (
- link.from_node.bl_idname == "PovrayTransformNode"
- and link.to_node == node
- ):
- pov_trans_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- transform = "transform {%s}" % pov_trans_name
- uv = ""
- if node.map_type == "uv_mapping":
- uv = "uv_mapping"
- filepath = bpy.path.abspath(im.filepath)
- file.write("#declare %s = normal {%s bump_map {\n" % (pov_node_name, uv))
- once = ""
- if node.once:
- once = "once"
- file.write(' "%s"\n' % filepath)
- file.write(" %s\n" % once)
- if node.map_type != "uv_mapping":
- file.write(" map_type %s\n" % node.map_type)
- bump_size = node.inputs["Normal"].default_value
- if node.inputs["Normal"].is_linked:
- pass
- file.write(
- " interpolate %s\n bump_size %.4g\n" % (node.interpolate, bump_size)
- )
- file.write(" }\n")
- file.write(" %s\n" % transform)
- file.write(" }\n")
- declare_nodes.append(node.name)
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayPigmentNode" and node.outputs["Pigment"].is_linked:
- declare_nodes.append(node.name)
- r, g, b = node.inputs["Color"].default_value[:]
- f = node.inputs["Filter"].default_value
- t = node.inputs["Transmit"].default_value
- if node.inputs["Color"].is_linked:
- pass
- file.write(
- "#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n"
- % (pov_node_name, r, g, b, f, t)
- )
-
- for node in ntree.nodes:
- pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name
- if node.bl_idname == "PovrayTextureNode" and node.outputs["Texture"].is_linked:
- declare_nodes.append(node.name)
- r, g, b = node.inputs["Pigment"].default_value[:]
- pov_col_name = "color rgb <%.4g,%.4g,%.4g>" % (r, g, b)
- if node.inputs["Pigment"].is_linked:
- for link in ntree.links:
- if link.to_node == node and link.to_socket.name == "Pigment":
- pov_col_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- file.write("#declare %s = texture{\n pigment{%s}\n" % (pov_node_name, pov_col_name))
- if node.inputs["Normal"].is_linked:
- for link in ntree.links:
- if (
- link.to_node == node
- and link.to_socket.name == "Normal"
- and link.from_node.name in declare_nodes
- ):
- pov_nor_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- file.write(" normal{%s}\n" % pov_nor_name)
- if node.inputs["Finish"].is_linked:
- for link in ntree.links:
- if link.to_node == node and link.to_socket.name == "Finish":
- pov_fin_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name))
- + "_%s" % pov_mat_name
- )
- file.write(" finish{%s}\n" % pov_fin_name)
- file.write("}\n")
- declare_nodes.append(node.name)
-
- for i in range(0, len(ntree.nodes)):
- for node in ntree.nodes:
- if node.bl_idname in {"ShaderNodeGroup", "ShaderTextureMapNode"}:
- for output in node.outputs:
- if (
- output.name == "Texture"
- and output.is_linked
- and (node.name not in declare_nodes)
- ):
- declare = True
- for link in ntree.links:
- if link.to_node == node and link.to_socket.name not in {
- "",
- "Color ramp",
- "Mapping",
- "Transform",
- "Modifier",
- }:
- if link.from_node.name not in declare_nodes:
- declare = False
- if declare:
- pov_node_name = (
- string_strip_hyphen(bpy.path.clean_name(node.name))
- + "_%s" % pov_mat_name
- )
- uv = ""
- warp = ""
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "PovrayMappingNode"
- and link.from_node.warp_type != "NONE"
- ):
- w_type = link.from_node.warp_type
- if w_type == "uv_mapping":
- uv = "uv_mapping"
- else:
- tor = ""
- if w_type == "toroidal":
- tor = (
- "major_radius %.4g"
- % link.from_node.warp_tor_major_radius
- )
- orient = link.from_node.warp_orientation
- exp = link.from_node.warp_dist_exp
- warp = "warp{%s orientation %s dist_exp %.4g %s}" % (
- w_type,
- orient,
- exp,
- tor,
- )
- if link.from_node.warp_type == "planar":
- warp = "warp{%s %s %.4g}" % (w_type, orient, exp)
- if link.from_node.warp_type == "cubic":
- warp = "warp{%s}" % w_type
- file.write("#declare %s = texture {%s\n" % (pov_node_name, uv))
- pattern = node.inputs[0].default_value
- advanced = ""
- if node.inputs[0].is_linked:
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "ShaderPatternNode"
- ):
- # ------------ advanced ------------------------- #
- lfn = link.from_node
- pattern = lfn.pattern
- if pattern == "agate":
- advanced = "agate_turb %.4g" % lfn.agate_turb
- if pattern == "crackle":
- advanced = "form <%.4g,%.4g,%.4g>" % (
- lfn.crackle_form_x,
- lfn.crackle_form_y,
- lfn.crackle_form_z,
- )
- advanced += " metric %.4g" % lfn.crackle_metric
- if lfn.crackle_solid:
- advanced += " solid"
- if pattern in {"spiral1", "spiral2"}:
- advanced = "%.4g" % lfn.spiral_arms
- if pattern in {"tiling"}:
- advanced = "%.4g" % lfn.tiling_number
- if pattern in {"gradient"}:
- advanced = "%s" % lfn.gradient_orient
- if (
- link.to_node == node
- and link.from_node.bl_idname == "PovrayImagePatternNode"
- ):
- pov_macro_name = (
- string_strip_hyphen(
- bpy.path.clean_name(link.from_node.name)
- )
- + "_%s" % pov_mat_name
- )
- pattern = "%s()" % pov_macro_name
- file.write(" %s %s %s\n" % (pattern, advanced, warp))
-
- repeat = ""
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "PovrayMultiplyNode"
- ):
- if link.from_node.amount_x > 1:
- repeat += "warp{repeat %.4g * x}" % link.from_node.amount_x
- if link.from_node.amount_y > 1:
- repeat += " warp{repeat %.4g * y}" % link.from_node.amount_y
- if link.from_node.amount_z > 1:
- repeat += " warp{repeat %.4g * z}" % link.from_node.amount_z
-
- transform = ""
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "PovrayTransformNode"
- ):
- pov_trans_name = (
- string_strip_hyphen(
- bpy.path.clean_name(link.from_node.name)
- )
- + "_%s" % pov_mat_name
- )
- transform = "transform {%s}" % pov_trans_name
- x = 0
- y = 0
- z = 0
- d = 0
- e = 0
- f = 0
- g = 0
- h = 0
- modifier = False
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "PovrayModifierNode"
- ):
- modifier = True
- if link.from_node.inputs["Turb X"].is_linked:
- pass
- else:
- x = link.from_node.inputs["Turb X"].default_value
-
- if link.from_node.inputs["Turb Y"].is_linked:
- pass
- else:
- y = link.from_node.inputs["Turb Y"].default_value
-
- if link.from_node.inputs["Turb Z"].is_linked:
- pass
- else:
- z = link.from_node.inputs["Turb Z"].default_value
-
- if link.from_node.inputs["Octaves"].is_linked:
- pass
- else:
- d = link.from_node.inputs["Octaves"].default_value
-
- if link.from_node.inputs["Lambda"].is_linked:
- pass
- else:
- e = link.from_node.inputs["Lambda"].default_value
-
- if link.from_node.inputs["Omega"].is_linked:
- pass
- else:
- f = link.from_node.inputs["Omega"].default_value
-
- if link.from_node.inputs["Frequency"].is_linked:
- pass
- else:
- g = link.from_node.inputs["Frequency"].default_value
-
- if link.from_node.inputs["Phase"].is_linked:
- pass
- else:
- h = link.from_node.inputs["Phase"].default_value
-
- turb = "turbulence <%.4g,%.4g,%.4g>" % (x, y, z)
- octv = "octaves %s" % d
- lmbd = "lambda %.4g" % e
- omg = "omega %.4g" % f
- freq = "frequency %.4g" % g
- pha = "phase %.4g" % h
-
- file.write("\n")
- if pattern not in {
- "checker",
- "hexagon",
- "square",
- "triangular",
- "brick",
- }:
- file.write(" texture_map {\n")
- if node.inputs["Color ramp"].is_linked:
- for link in ntree.links:
- if (
- link.to_node == node
- and link.from_node.bl_idname == "ShaderNodeValToRGB"
- ):
- els = link.from_node.color_ramp.elements
- n = -1
- for el in els:
- n += 1
- pov_in_mat_name = string_strip_hyphen(
- bpy.path.clean_name(link.from_node.name)
- ) + "_%s_%s" % (n, pov_mat_name)
- default = True
- for ilink in ntree.links:
- if (
- ilink.to_node == node
- and ilink.to_socket.name == str(n)
- ):
- default = False
- pov_in_mat_name = (
- string_strip_hyphen(
- bpy.path.clean_name(
- ilink.from_node.name
- )
- )
- + "_%s" % pov_mat_name
- )
- if default:
- r, g, b, a = el.color[:]
- file.write(
- " #declare %s = texture{"
- "pigment{"
- "color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n"
- % (pov_in_mat_name, r, g, b, 1 - a)
- )
- file.write(
- " [%s %s]\n" % (el.position, pov_in_mat_name)
- )
- else:
- els = [[0, 0, 0, 0], [1, 1, 1, 1]]
- for t in range(0, 2):
- pov_in_mat_name = string_strip_hyphen(
- bpy.path.clean_name(link.from_node.name)
- ) + "_%s_%s" % (t, pov_mat_name)
- default = True
- for ilink in ntree.links:
- if ilink.to_node == node and ilink.to_socket.name == str(t):
- default = False
- pov_in_mat_name = (
- string_strip_hyphen(
- bpy.path.clean_name(ilink.from_node.name)
- )
- + "_%s" % pov_mat_name
- )
- if default:
- r, g, b = els[t][1], els[t][2], els[t][3]
- if pattern not in {
- "checker",
- "hexagon",
- "square",
- "triangular",
- "brick",
- }:
- file.write(
- " #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n"
- % (pov_in_mat_name, r, g, b)
- )
- else:
- file.write(
- " texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n"
- % (r, g, b)
- )
- if pattern not in {
- "checker",
- "hexagon",
- "square",
- "triangular",
- "brick",
- }:
- file.write(" [%s %s]\n" % (els[t][0], pov_in_mat_name))
- else:
- if not default:
- file.write(" texture{%s}\n" % pov_in_mat_name)
- if pattern not in {
- "checker",
- "hexagon",
- "square",
- "triangular",
- "brick",
- }:
- file.write("}\n")
- if pattern == "brick":
- file.write(
- "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n"
- % (
- node.brick_size_x,
- node.brick_size_y,
- node.brick_size_z,
- node.brick_mortar,
- )
- )
- file.write(" %s %s" % (repeat, transform))
- if modifier:
- file.write(
- " %s %s %s %s %s %s" % (turb, octv, lmbd, omg, freq, pha)
- )
- file.write("}\n")
- declare_nodes.append(node.name)
-
- for link in ntree.links:
- if link.to_node.bl_idname == "PovrayOutputNode" and link.from_node.name in declare_nodes:
- pov_mat_node_name = (
- string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + "_%s" % pov_mat_name
- )
- file.write("#declare %s = %s\n" % (pov_mat_name, pov_mat_node_name))
+ pov_has_no_specular_maps(file, ref_level_bound=3)
diff --git a/render_povray/shading_gui.py b/render_povray/shading_gui.py
index 0eb9e5f0..55e411e3 100755
--- a/render_povray/shading_gui.py
+++ b/render_povray/shading_gui.py
@@ -14,7 +14,7 @@ from bl_ui import properties_material
for member in dir(properties_material):
subclass = getattr(properties_material, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_material
from .shading_properties import check_material
@@ -28,10 +28,10 @@ def simple_material(mat):
class MaterialButtonsPanel:
"""Use this class to define buttons from the material tab of properties window."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "material"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -42,20 +42,24 @@ class MaterialButtonsPanel:
class MATERIAL_PT_POV_shading(MaterialButtonsPanel, Panel):
bl_label = "Shading"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ 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)
+ 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'}:
+ if mat.pov.type in {"SURFACE", "WIRE"}:
split = layout.split()
col = split.column()
@@ -107,8 +111,8 @@ class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel):
"""Use this class to define pov sss buttons panel."""
bl_label = "Subsurface Scattering"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -116,7 +120,7 @@ class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel):
engine = context.scene.render.engine
return (
check_material(mat)
- and (mat.pov.type in {'SURFACE', 'WIRE'})
+ and (mat.pov.type in {"SURFACE", "WIRE"})
and (engine in cls.COMPAT_ENGINES)
)
@@ -138,9 +142,9 @@ class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel):
row = layout.row().split()
sub = row.row(align=True).split(align=True, factor=0.75)
sub.menu(MATERIAL_MT_POV_sss_presets.__name__, text=MATERIAL_MT_POV_sss_presets.bl_label)
- sub.operator(MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='ADD')
+ sub.operator(MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon="ADD")
sub.operator(
- MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon='REMOVE'
+ MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon="REMOVE"
).remove_active = True
split = layout.split()
@@ -168,8 +172,8 @@ class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel):
bl_label = "Activate Node Settings"
bl_context = "material"
- bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"HIDE_HEADER"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -188,7 +192,7 @@ class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel):
# layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
# the above replaced with a context hook below:
layout.operator(
- "WM_OT_context_toggle", text="Use POV-Ray Nodes", icon='NODETREE'
+ "WM_OT_context_toggle", text="Use POV-Ray Nodes", icon="NODETREE"
).data_path = "material.pov.material_use_nodes"
@@ -197,8 +201,8 @@ class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel):
bl_label = "Active Node Settings"
bl_context = "material"
- bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"HIDE_HEADER"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -216,20 +220,16 @@ class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel):
node_tree = mat.node_tree
if node_tree and mat.use_nodes:
layout = self.layout
- node = node_tree.nodes.active
- if node:
+ if node := node_tree.nodes.active:
layout.prop(mat.pov, "material_active_node")
layout.context_pointer_set("node", node)
if hasattr(node, "draw_buttons_ext"):
node.draw_buttons_ext(context, layout)
elif hasattr(node, "draw_buttons"):
node.draw_buttons(context, layout)
- value_inputs = [
- socket
- for socket in node.inputs
- if socket.enabled and not socket.is_linked
- ]
- if value_inputs:
+ if value_inputs := [
+ socket for socket in node.inputs if socket.enabled and not socket.is_linked
+ ]:
layout.separator()
layout.label(text="Inputs:")
for socket in value_inputs:
@@ -243,7 +243,7 @@ class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel):
"""Use this class to define standard material specularity (highlights) buttons."""
bl_label = "Specular"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -251,16 +251,16 @@ class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel):
engine = context.scene.render.engine
return (
check_material(mat)
- and (mat.pov.type in {'SURFACE', 'WIRE'})
+ and (mat.pov.type in {"SURFACE", "WIRE"})
and (engine in cls.COMPAT_ENGINES)
)
def draw(self, context):
layout = self.layout
- mat = context.material.pov
+ mat = context.material
- layout.active = not mat.use_shadeless
+ layout.active = not mat.pov.use_shadeless
split = layout.split()
@@ -269,26 +269,26 @@ class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel):
col.prop(mat, "specular_intensity", text="Intensity")
col = split.column()
- col.prop(mat, "specular_shader", text="")
- col.prop(mat, "use_specular_ramp", text="Ramp")
+ col.prop(mat.pov, "specular_shader", text="")
+ col.prop(mat.pov, "use_specular_ramp", text="Ramp")
col = layout.column()
- if mat.specular_shader in {'COOKTORR', 'PHONG'}:
- col.prop(mat, "specular_hardness", text="Hardness")
- elif mat.specular_shader == 'BLINN':
+ if mat.pov.specular_shader in {"COOKTORR", "PHONG"}:
+ col.prop(mat.pov, "specular_hardness", text="Hardness")
+ elif mat.pov.specular_shader == "BLINN":
row = col.row()
- row.prop(mat, "specular_hardness", text="Hardness")
- row.prop(mat, "specular_ior", text="IOR")
- elif mat.specular_shader == 'WARDISO':
- col.prop(mat, "specular_slope", text="Slope")
- elif mat.specular_shader == 'TOON':
+ row.prop(mat.pov, "specular_hardness", text="Hardness")
+ row.prop(mat.pov, "specular_ior", text="IOR")
+ elif mat.pov.specular_shader == "WARDISO":
+ col.prop(mat.pov, "specular_slope", text="Slope")
+ elif mat.pov.specular_shader == "TOON":
row = col.row()
- row.prop(mat, "specular_toon_size", text="Size")
- row.prop(mat, "specular_toon_smooth", text="Smooth")
+ row.prop(mat.pov, "specular_toon_size", text="Size")
+ row.prop(mat.pov, "specular_toon_smooth", text="Smooth")
- if mat.use_specular_ramp:
+ if mat.pov.use_specular_ramp:
layout.separator()
- layout.template_color_ramp(mat, "specular_ramp", expand=True)
+ layout.template_color_ramp(mat.pov, "specular_ramp", expand=True)
layout.separator()
row = layout.row()
@@ -302,9 +302,9 @@ class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel):
"""Use this class to define standard material reflectivity (mirror) buttons."""
bl_label = "Mirror"
- bl_options = {'DEFAULT_CLOSED'}
+ bl_options = {"DEFAULT_CLOSED"}
bl_idname = "MATERIAL_PT_POV_raytrace_mirror"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -312,7 +312,7 @@ class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel):
engine = context.scene.render.engine
return (
check_material(mat)
- and (mat.pov.type in {'SURFACE', 'WIRE'})
+ and (mat.pov.type in {"SURFACE", "WIRE"})
and (engine in cls.COMPAT_ENGINES)
)
@@ -368,7 +368,7 @@ class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
"""Use this class to define pov material transparency (alpha) buttons."""
bl_label = "Transparency"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -376,7 +376,7 @@ class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
engine = context.scene.render.engine
return (
check_material(mat)
- and (mat.pov.type in {'SURFACE', 'WIRE'})
+ and (mat.pov.type in {"SURFACE", "WIRE"})
and (engine in cls.COMPAT_ENGINES)
)
@@ -404,7 +404,7 @@ class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
col = split.column()
col.prop(mat.pov, "alpha")
row = col.row()
- row.active = (base_mat.pov.transparency_method != 'MASK') and (not mat.pov.use_shadeless)
+ row.active = (base_mat.pov.transparency_method != "MASK") and (not mat.pov.use_shadeless)
row.prop(mat.pov, "specular_alpha", text="Specular")
col = split.column()
@@ -414,7 +414,7 @@ class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
sub.active = rayt.fresnel > 0.0
sub.prop(rayt, "fresnel_factor", text="Blend")
- if base_mat.pov.transparency_method == 'RAYTRACE':
+ if base_mat.pov.transparency_method == "RAYTRACE":
layout.separator()
split = layout.split()
split.active = base_mat.pov.use_transparency
@@ -440,7 +440,7 @@ class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel):
bl_label = "POV-Ray Reflection"
bl_parent_id = "MATERIAL_PT_POV_raytrace_mirror"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -472,13 +472,13 @@ class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel):
col2.active = mat.pov_raytrace_mirror.use
col2.prop(mat.pov, "mirror_use_IOR")
if mat.pov.mirror_use_IOR:
- col2.alignment = 'CENTER'
+ col2.alignment = "CENTER"
col2.label(text="The current Raytrace ")
col2.label(text="Transparency IOR is: " + str(mat.pov_raytrace_transparency.ior))
- col2.prop(mat.pov, "mirror_metallic")
-'''
+
+"""
#group some native Blender (SSS) and POV (Fade)settings under such a parent panel?
class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel):
bl_label = "POV-Ray Interior"
@@ -496,14 +496,14 @@ class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel):
def draw_header(self, context):
mat = context.material
-'''
+"""
class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel, Panel):
"""Use this class to define pov fading (absorption) color buttons."""
bl_label = "POV-Ray Absorption"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
# bl_parent_id = "material.pov_interior"
@classmethod
@@ -537,7 +537,7 @@ class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel):
"""Use this class to define pov caustics buttons."""
bl_label = "Caustics"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -580,7 +580,7 @@ class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel):
if not mat.pov.refraction_caustics and not mat.pov.photons_reflection:
col = layout.column()
- col.alignment = 'CENTER'
+ col.alignment = "CENTER"
col.label(text="Caustics override is on, ")
col.label(text="but you didn't chose any !")
@@ -589,15 +589,15 @@ class MATERIAL_PT_strand(MaterialButtonsPanel, Panel):
"""Use this class to define Blender strand antialiasing buttons."""
bl_label = "Strand"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return (
- mat and (mat.pov.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES)
+ mat and (mat.pov.type in {"SURFACE", "WIRE", "HALO"}) and (engine in cls.COMPAT_ENGINES)
)
def draw(self, context):
@@ -624,7 +624,7 @@ class MATERIAL_PT_strand(MaterialButtonsPanel, Panel):
col.label(text="Shading:")
col.prop(tan, "width_fade")
ob = context.object
- if ob and ob.type == 'MESH':
+ if ob and ob.type == "MESH":
col.prop_search(tan, "uv_layer", ob.data, "uv_layers", text="")
else:
col.prop(tan, "uv_layer", text="")
@@ -640,16 +640,16 @@ class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel):
"""Use this class to define pov custom code declared name field."""
bl_label = "Custom POV Code"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
mat = context.material
col = layout.column()
- col.alignment = 'RIGHT'
+ col.alignment = "RIGHT"
col.label(text="Override properties with this")
col.label(text="text editor {pov code} block")
- layout.prop(mat.pov, "replacement_text", text="#declare name", icon='SYNTAX_ON')
+ layout.prop(mat.pov, "replacement_text", text="#declare name", icon="SYNTAX_ON")
classes = (
diff --git a/render_povray/shading_nodes.py b/render_povray/shading_nodes.py
deleted file mode 100755
index fef5d381..00000000
--- a/render_povray/shading_nodes.py
+++ /dev/null
@@ -1,1992 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-# <pep8 compliant>
-""""Nodes based User interface for shaders exported to POV textures."""
-import bpy
-
-from bpy.utils import register_class, unregister_class
-from bpy.types import Menu, Node, NodeSocket, CompositorNodeTree, TextureNodeTree, Operator
-from bpy.props import (
- StringProperty,
- BoolProperty,
- IntProperty,
- FloatProperty,
- FloatVectorProperty,
- EnumProperty,
-)
-import nodeitems_utils
-from nodeitems_utils import NodeCategory, NodeItem
-
-
-# ---------------------------------------------------------------- #
-# Pov Nodes init
-# ---------------------------------------------------------------- #
-
-
-class PovraySocketUniversal(NodeSocket):
- bl_idname = "PovraySocketUniversal"
- bl_label = "Povray Socket"
- value_unlimited: bpy.props.FloatProperty(default=0.0)
- value_0_1: bpy.props.FloatProperty(min=0.0, max=1.0, default=0.0)
- value_0_10: bpy.props.FloatProperty(min=0.0, max=10.0, default=0.0)
- value_000001_10: bpy.props.FloatProperty(min=0.000001, max=10.0, default=0.0)
- value_1_9: bpy.props.IntProperty(min=1, max=9, default=1)
- value_0_255: bpy.props.IntProperty(min=0, max=255, default=0)
- percent: bpy.props.FloatProperty(min=0.0, max=100.0, default=0.0)
-
- def draw(self, context, layout, node, text):
- space = context.space_data
- tree = space.edit_tree
- links = tree.links
- if self.is_linked:
- value = []
- for link in links:
- if link.from_node == node:
- inps = link.to_node.inputs
- for inp in inps:
- if inp.bl_idname == "PovraySocketFloat_0_1" and inp.is_linked:
- prop = "value_0_1"
- if prop not in value:
- value.append(prop)
- if inp.bl_idname == "PovraySocketFloat_000001_10" and inp.is_linked:
- prop = "value_000001_10"
- if prop not in value:
- value.append(prop)
- if inp.bl_idname == "PovraySocketFloat_0_10" and inp.is_linked:
- prop = "value_0_10"
- if prop not in value:
- value.append(prop)
- if inp.bl_idname == "PovraySocketInt_1_9" and inp.is_linked:
- prop = "value_1_9"
- if prop not in value:
- value.append(prop)
- if inp.bl_idname == "PovraySocketInt_0_255" and inp.is_linked:
- prop = "value_0_255"
- if prop not in value:
- value.append(prop)
- if inp.bl_idname == "PovraySocketFloatUnlimited" and inp.is_linked:
- prop = "value_unlimited"
- if prop not in value:
- value.append(prop)
- if len(value) == 1:
- layout.prop(self, "%s" % value[0], text=text)
- else:
- layout.prop(self, "percent", text="Percent")
- else:
- layout.prop(self, "percent", text=text)
-
- def draw_color(self, context, node):
- return (1, 0, 0, 1)
-
-
-class PovraySocketFloat_0_1(NodeSocket):
- bl_idname = "PovraySocketFloat_0_1"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(
- description="Input node Value_0_1", min=0, max=1, default=0
- )
-
- def draw(self, context, layout, node, text):
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (0.5, 0.7, 0.7, 1)
-
-
-class PovraySocketFloat_0_10(NodeSocket):
- bl_idname = "PovraySocketFloat_0_10"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(
- description="Input node Value_0_10", min=0, max=10, default=0
- )
-
- def draw(self, context, layout, node, text):
- if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked:
- layout.label(text="")
- self.hide_value = True
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (0.65, 0.65, 0.65, 1)
-
-
-class PovraySocketFloat_10(NodeSocket):
- bl_idname = "PovraySocketFloat_10"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(
- description="Input node Value_10", min=-10, max=10, default=0
- )
-
- def draw(self, context, layout, node, text):
- if node.bl_idname == "ShaderNormalMapNode" and node.inputs[2].is_linked:
- layout.label(text="")
- self.hide_value = True
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (0.65, 0.65, 0.65, 1)
-
-
-class PovraySocketFloatPositive(NodeSocket):
- bl_idname = "PovraySocketFloatPositive"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(
- description="Input Node Value Positive", min=0.0, default=0
- )
-
- def draw(self, context, layout, node, text):
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (0.045, 0.005, 0.136, 1)
-
-
-class PovraySocketFloat_000001_10(NodeSocket):
- bl_idname = "PovraySocketFloat_000001_10"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(min=0.000001, max=10, default=0.000001)
-
- def draw(self, context, layout, node, text):
- if self.is_output or self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (1, 0, 0, 1)
-
-
-class PovraySocketFloatUnlimited(NodeSocket):
- bl_idname = "PovraySocketFloatUnlimited"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(default=0.0)
-
- def draw(self, context, layout, node, text):
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text, slider=True)
-
- def draw_color(self, context, node):
- return (0.7, 0.7, 1, 1)
-
-
-class PovraySocketInt_1_9(NodeSocket):
- bl_idname = "PovraySocketInt_1_9"
- bl_label = "Povray Socket"
- default_value: bpy.props.IntProperty(
- description="Input node Value_1_9", min=1, max=9, default=6
- )
-
- def draw(self, context, layout, node, text):
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text)
-
- def draw_color(self, context, node):
- return (1, 0.7, 0.7, 1)
-
-
-class PovraySocketInt_0_256(NodeSocket):
- bl_idname = "PovraySocketInt_0_256"
- bl_label = "Povray Socket"
- default_value: bpy.props.IntProperty(min=0, max=255, default=0)
-
- def draw(self, context, layout, node, text):
- if self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text)
-
- def draw_color(self, context, node):
- return (0.5, 0.5, 0.5, 1)
-
-
-class PovraySocketPattern(NodeSocket):
- bl_idname = "PovraySocketPattern"
- bl_label = "Povray Socket"
-
- default_value: bpy.props.EnumProperty(
- name="Pattern",
- description="Select the pattern",
- items=(
- ("boxed", "Boxed", ""),
- ("brick", "Brick", ""),
- ("cells", "Cells", ""),
- ("checker", "Checker", ""),
- ("granite", "Granite", ""),
- ("leopard", "Leopard", ""),
- ("marble", "Marble", ""),
- ("onion", "Onion", ""),
- ("planar", "Planar", ""),
- ("quilted", "Quilted", ""),
- ("ripples", "Ripples", ""),
- ("radial", "Radial", ""),
- ("spherical", "Spherical", ""),
- ("spotted", "Spotted", ""),
- ("waves", "Waves", ""),
- ("wood", "Wood", ""),
- ("wrinkles", "Wrinkles", ""),
- ),
- default="granite",
- )
-
- def draw(self, context, layout, node, text):
- if self.is_output or self.is_linked:
- layout.label(text="Pattern")
- else:
- layout.prop(self, "default_value", text=text)
-
- def draw_color(self, context, node):
- return (1, 1, 1, 1)
-
-
-class PovraySocketColor(NodeSocket):
- bl_idname = "PovraySocketColor"
- bl_label = "Povray Socket"
-
- default_value: FloatVectorProperty(
- precision=4,
- step=0.01,
- min=0,
- soft_max=1,
- default=(0.0, 0.0, 0.0),
- options={"ANIMATABLE"},
- subtype="COLOR",
- )
-
- def draw(self, context, layout, node, text):
- if self.is_output or self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text)
-
- def draw_color(self, context, node):
- return (1, 1, 0, 1)
-
-
-class PovraySocketColorRGBFT(NodeSocket):
- bl_idname = "PovraySocketColorRGBFT"
- bl_label = "Povray Socket"
-
- default_value: FloatVectorProperty(
- precision=4,
- step=0.01,
- min=0,
- soft_max=1,
- default=(0.0, 0.0, 0.0),
- options={"ANIMATABLE"},
- subtype="COLOR",
- )
- f: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0)
- t: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0)
-
- def draw(self, context, layout, node, text):
- if self.is_output or self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text=text)
-
- def draw_color(self, context, node):
- return (1, 1, 0, 1)
-
-
-class PovraySocketTexture(NodeSocket):
- bl_idname = "PovraySocketTexture"
- bl_label = "Povray Socket"
- default_value: bpy.props.IntProperty()
-
- def draw(self, context, layout, node, text):
- layout.label(text=text)
-
- def draw_color(self, context, node):
- return (0, 1, 0, 1)
-
-
-class PovraySocketTransform(NodeSocket):
- bl_idname = "PovraySocketTransform"
- bl_label = "Povray Socket"
- default_value: bpy.props.IntProperty(min=0, max=255, default=0)
-
- def draw(self, context, layout, node, text):
- layout.label(text=text)
-
- def draw_color(self, context, node):
- return (99 / 255, 99 / 255, 199 / 255, 1)
-
-
-class PovraySocketNormal(NodeSocket):
- bl_idname = "PovraySocketNormal"
- bl_label = "Povray Socket"
- default_value: bpy.props.IntProperty(min=0, max=255, default=0)
-
- def draw(self, context, layout, node, text):
- layout.label(text=text)
-
- def draw_color(self, context, node):
- return (0.65, 0.65, 0.65, 1)
-
-
-class PovraySocketSlope(NodeSocket):
- bl_idname = "PovraySocketSlope"
- bl_label = "Povray Socket"
- default_value: bpy.props.FloatProperty(min=0.0, max=1.0)
- height: bpy.props.FloatProperty(min=0.0, max=10.0)
- slope: bpy.props.FloatProperty(min=-10.0, max=10.0)
-
- def draw(self, context, layout, node, text):
- if self.is_output or self.is_linked:
- layout.label(text=text)
- else:
- layout.prop(self, "default_value", text="")
- layout.prop(self, "height", text="")
- layout.prop(self, "slope", text="")
-
- def draw_color(self, context, node):
- return (0, 0, 0, 1)
-
-
-class PovraySocketMap(NodeSocket):
- bl_idname = "PovraySocketMap"
- bl_label = "Povray Socket"
- default_value: bpy.props.StringProperty()
-
- def draw(self, context, layout, node, text):
- layout.label(text=text)
-
- def draw_color(self, context, node):
- return (0.2, 0, 0.2, 1)
-
-
-class PovrayShaderNodeCategory(NodeCategory):
- @classmethod
- def poll(cls, context):
- return context.space_data.tree_type == "ObjectNodeTree"
-
-
-class PovrayTextureNodeCategory(NodeCategory):
- @classmethod
- def poll(cls, context):
- return context.space_data.tree_type == "TextureNodeTree"
-
-
-class PovraySceneNodeCategory(NodeCategory):
- @classmethod
- def poll(cls, context):
- return context.space_data.tree_type == "CompositorNodeTree"
-
-
-node_categories = [
- PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items=[NodeItem("PovrayOutputNode")]),
- PovrayShaderNodeCategory("SIMPLE", "Simple texture", items=[NodeItem("PovrayTextureNode")]),
- PovrayShaderNodeCategory(
- "MAPS",
- "Maps",
- items=[
- NodeItem("PovrayBumpMapNode"),
- NodeItem("PovrayColorImageNode"),
- NodeItem("ShaderNormalMapNode"),
- NodeItem("PovraySlopeNode"),
- NodeItem("ShaderTextureMapNode"),
- NodeItem("ShaderNodeValToRGB"),
- ],
- ),
- PovrayShaderNodeCategory(
- "OTHER",
- "Other patterns",
- items=[NodeItem("PovrayImagePatternNode"), NodeItem("ShaderPatternNode")],
- ),
- PovrayShaderNodeCategory("COLOR", "Color", items=[NodeItem("PovrayPigmentNode")]),
- PovrayShaderNodeCategory(
- "TRANSFORM",
- "Transform",
- items=[
- NodeItem("PovrayMappingNode"),
- NodeItem("PovrayMultiplyNode"),
- NodeItem("PovrayModifierNode"),
- NodeItem("PovrayTransformNode"),
- NodeItem("PovrayValueNode"),
- ],
- ),
- PovrayShaderNodeCategory(
- "FINISH",
- "Finish",
- items=[
- NodeItem("PovrayFinishNode"),
- NodeItem("PovrayDiffuseNode"),
- NodeItem("PovraySpecularNode"),
- NodeItem("PovrayPhongNode"),
- NodeItem("PovrayAmbientNode"),
- NodeItem("PovrayMirrorNode"),
- NodeItem("PovrayIridescenceNode"),
- NodeItem("PovraySubsurfaceNode"),
- ],
- ),
- PovrayShaderNodeCategory(
- "CYCLES",
- "Cycles",
- items=[
- NodeItem("ShaderNodeAddShader"),
- NodeItem("ShaderNodeAmbientOcclusion"),
- NodeItem("ShaderNodeAttribute"),
- NodeItem("ShaderNodeBackground"),
- NodeItem("ShaderNodeBlackbody"),
- NodeItem("ShaderNodeBrightContrast"),
- NodeItem("ShaderNodeBsdfAnisotropic"),
- NodeItem("ShaderNodeBsdfDiffuse"),
- NodeItem("ShaderNodeBsdfGlass"),
- NodeItem("ShaderNodeBsdfGlossy"),
- NodeItem("ShaderNodeBsdfHair"),
- NodeItem("ShaderNodeBsdfRefraction"),
- NodeItem("ShaderNodeBsdfToon"),
- NodeItem("ShaderNodeBsdfTranslucent"),
- NodeItem("ShaderNodeBsdfTransparent"),
- NodeItem("ShaderNodeBsdfVelvet"),
- NodeItem("ShaderNodeBump"),
- NodeItem("ShaderNodeCameraData"),
- NodeItem("ShaderNodeCombineHSV"),
- NodeItem("ShaderNodeCombineRGB"),
- NodeItem("ShaderNodeCombineXYZ"),
- NodeItem("ShaderNodeEmission"),
- NodeItem("ShaderNodeExtendedMaterial"),
- NodeItem("ShaderNodeFresnel"),
- NodeItem("ShaderNodeGamma"),
- NodeItem("ShaderNodeGeometry"),
- NodeItem("ShaderNodeGroup"),
- NodeItem("ShaderNodeHairInfo"),
- NodeItem("ShaderNodeHoldout"),
- NodeItem("ShaderNodeHueSaturation"),
- NodeItem("ShaderNodeInvert"),
- NodeItem("ShaderNodeLampData"),
- NodeItem("ShaderNodeLayerWeight"),
- NodeItem("ShaderNodeLightFalloff"),
- NodeItem("ShaderNodeLightPath"),
- NodeItem("ShaderNodeMapping"),
- NodeItem("ShaderNodeMaterial"),
- NodeItem("ShaderNodeMath"),
- NodeItem("ShaderNodeMixRGB"),
- NodeItem("ShaderNodeMixShader"),
- NodeItem("ShaderNodeNewGeometry"),
- NodeItem("ShaderNodeNormal"),
- NodeItem("ShaderNodeNormalMap"),
- NodeItem("ShaderNodeObjectInfo"),
- NodeItem("ShaderNodeOutput"),
- NodeItem("ShaderNodeOutputLamp"),
- NodeItem("ShaderNodeOutputLineStyle"),
- NodeItem("ShaderNodeOutputMaterial"),
- NodeItem("ShaderNodeOutputWorld"),
- NodeItem("ShaderNodeParticleInfo"),
- NodeItem("ShaderNodeRGB"),
- NodeItem("ShaderNodeRGBCurve"),
- NodeItem("ShaderNodeRGBToBW"),
- NodeItem("ShaderNodeScript"),
- NodeItem("ShaderNodeSeparateHSV"),
- NodeItem("ShaderNodeSeparateRGB"),
- NodeItem("ShaderNodeSeparateXYZ"),
- NodeItem("ShaderNodeSqueeze"),
- NodeItem("ShaderNodeSubsurfaceScattering"),
- NodeItem("ShaderNodeTangent"),
- NodeItem("ShaderNodeTexBrick"),
- NodeItem("ShaderNodeTexChecker"),
- NodeItem("ShaderNodeTexCoord"),
- NodeItem("ShaderNodeTexEnvironment"),
- NodeItem("ShaderNodeTexGradient"),
- NodeItem("ShaderNodeTexImage"),
- NodeItem("ShaderNodeTexMagic"),
- NodeItem("ShaderNodeTexMusgrave"),
- NodeItem("ShaderNodeTexNoise"),
- NodeItem("ShaderNodeTexPointDensity"),
- NodeItem("ShaderNodeTexSky"),
- NodeItem("ShaderNodeTexVoronoi"),
- NodeItem("ShaderNodeTexWave"),
- NodeItem("ShaderNodeTexture"),
- NodeItem("ShaderNodeUVAlongStroke"),
- NodeItem("ShaderNodeUVMap"),
- NodeItem("ShaderNodeValToRGB"),
- NodeItem("ShaderNodeValue"),
- NodeItem("ShaderNodeVectorCurve"),
- NodeItem("ShaderNodeVectorMath"),
- NodeItem("ShaderNodeVectorTransform"),
- NodeItem("ShaderNodeVolumeAbsorption"),
- NodeItem("ShaderNodeVolumeScatter"),
- NodeItem("ShaderNodeWavelength"),
- NodeItem("ShaderNodeWireframe"),
- ],
- ),
- PovrayTextureNodeCategory(
- "TEXTUREOUTPUT",
- "Output",
- items=[NodeItem("TextureNodeValToRGB"), NodeItem("TextureOutputNode")],
- ),
- PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items=[NodeItem("IsoPropsNode")]),
- PovraySceneNodeCategory("FOG", "Fog", items=[NodeItem("PovrayFogNode")]),
-]
-# -------- end nodes init
-# -------- nodes ui
-# -------- Nodes
-
-# def find_node_input(node, name):
-# for input in node.inputs:
-# if input.name == name:
-# return input
-
-# def panel_node_draw(layout, id_data, output_type, input_name):
-# if not id_data.use_nodes:
-# #layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
-# #layout.operator("pov.use_shading_nodes", icon='NODETREE')
-# layout.operator("WM_OT_context_toggle", icon='NODETREE').data_path = \
-# "material.pov.material_use_nodes"
-# return False
-
-# ntree = id_data.node_tree
-
-# node = find_node(id_data, output_type)
-# if not node:
-# layout.label(text="No output node")
-# else:
-# input = find_node_input(node, input_name)
-# layout.template_node_view(ntree, node, input)
-
-# return True
-
-
-class NODE_MT_POV_map_create(Menu):
- """Create maps"""
-
- bl_idname = "POVRAY_MT_node_map_create"
- bl_label = "Create map"
-
- def draw(self, context):
- layout = self.layout
- layout.operator("node.map_create")
-
-
-def menu_func_nodes(self, context):
- ob = context.object
- if hasattr(ob, 'active_material'):
- mat = context.object.active_material
- if mat and context.space_data.tree_type == 'ObjectNodeTree':
- self.layout.prop(mat.pov, "material_use_nodes")
- self.layout.menu(NODE_MT_POV_map_create.bl_idname)
- self.layout.operator("wm.updatepreviewkey")
- if hasattr(mat, 'active_texture') and context.scene.render.engine == 'POVRAY_RENDER':
- tex = mat.active_texture
- if tex and context.space_data.tree_type == 'TextureNodeTree':
- self.layout.prop(tex.pov, "texture_use_nodes")
-
-
-# -------- object
-
-
-class ObjectNodeTree(bpy.types.NodeTree):
- """Povray Material Nodes"""
-
- bl_idname = 'ObjectNodeTree'
- bl_label = 'Povray Object Nodes'
- bl_icon = 'PLUGIN'
-
- @classmethod
- def poll(cls, context):
- return context.scene.render.engine == 'POVRAY_RENDER'
-
- @classmethod
- def get_from_context(cls, context):
- ob = context.active_object
- if ob and ob.type not in {'LIGHT'}:
- ma = ob.active_material
- if ma is not None:
- nt_name = ma.node_tree
- if nt_name != '':
- return nt_name, ma, ma
- return (None, None, None)
-
- def update(self):
- self.refresh = True
-
-
-# -------- output # ---------------------------------------------------------------- #
-
-
-class PovrayOutputNode(Node, ObjectNodeTree):
- """Output"""
-
- bl_idname = 'PovrayOutputNode'
- bl_label = 'Output'
- bl_icon = 'SHADING_TEXTURE'
-
- def init(self, context):
-
- self.inputs.new('PovraySocketTexture', "Texture")
-
- def draw_buttons(self, context, layout):
-
- ob = context.object
- layout.prop(ob.pov, "object_ior", slider=True)
-
- def draw_buttons_ext(self, context, layout):
-
- ob = context.object
- layout.prop(ob.pov, "object_ior", slider=True)
-
- def draw_label(self):
- return "Output"
-
-
-# -------- material # ---------------------------------------------------------------- #
-class PovrayTextureNode(Node, ObjectNodeTree):
- """Texture"""
-
- bl_idname = 'PovrayTextureNode'
- bl_label = 'Simple texture'
- bl_icon = 'SHADING_TEXTURE'
-
- def init(self, context):
-
- color = self.inputs.new('PovraySocketColor', "Pigment")
- color.default_value = (1, 1, 1)
- normal = self.inputs.new('NodeSocketFloat', "Normal")
- normal.hide_value = True
- finish = self.inputs.new('NodeSocketVector', "Finish")
- finish.hide_value = True
-
- self.outputs.new('PovraySocketTexture', "Texture")
-
- def draw_label(self):
- return "Simple texture"
-
-
-class PovrayFinishNode(Node, ObjectNodeTree):
- """Finish"""
-
- bl_idname = 'PovrayFinishNode'
- bl_label = 'Finish'
- bl_icon = 'SHADING_TEXTURE'
-
- def init(self, context):
-
- self.inputs.new('PovraySocketFloat_0_1', "Emission")
- ambient = self.inputs.new('NodeSocketVector', "Ambient")
- ambient.hide_value = True
- diffuse = self.inputs.new('NodeSocketVector', "Diffuse")
- diffuse.hide_value = True
- specular = self.inputs.new('NodeSocketVector', "Highlight")
- specular.hide_value = True
- mirror = self.inputs.new('NodeSocketVector', "Mirror")
- mirror.hide_value = True
- iridescence = self.inputs.new('NodeSocketVector', "Iridescence")
- iridescence.hide_value = True
- subsurface = self.inputs.new('NodeSocketVector', "Translucency")
- subsurface.hide_value = True
- self.outputs.new('NodeSocketVector', "Finish")
-
- def draw_label(self):
- return "Finish"
-
-
-class PovrayDiffuseNode(Node, ObjectNodeTree):
- """Diffuse"""
-
- bl_idname = 'PovrayDiffuseNode'
- bl_label = 'Diffuse'
- bl_icon = 'MATSPHERE'
-
- def init(self, context):
-
- intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity")
- intensity.default_value = 0.8
- albedo = self.inputs.new('NodeSocketBool', "Albedo")
- albedo.default_value = False
- brilliance = self.inputs.new('PovraySocketFloat_0_10', "Brilliance")
- brilliance.default_value = 1.8
- self.inputs.new('PovraySocketFloat_0_1', "Crand")
- self.outputs.new('NodeSocketVector', "Diffuse")
-
- def draw_label(self):
- return "Diffuse"
-
-
-class PovrayPhongNode(Node, ObjectNodeTree):
- """Phong"""
-
- bl_idname = 'PovrayPhongNode'
- bl_label = 'Phong'
- bl_icon = 'MESH_UVSPHERE'
-
- def init(self, context):
-
- albedo = self.inputs.new('NodeSocketBool', "Albedo")
- intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity")
- intensity.default_value = 0.8
- phong_size = self.inputs.new('PovraySocketInt_0_256', "Size")
- phong_size.default_value = 60
- metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic")
-
- self.outputs.new('NodeSocketVector', "Phong")
-
- def draw_label(self):
- return "Phong"
-
-
-class PovraySpecularNode(Node, ObjectNodeTree):
- """Specular"""
-
- bl_idname = 'PovraySpecularNode'
- bl_label = 'Specular'
- bl_icon = 'MESH_UVSPHERE'
-
- def init(self, context):
-
- albedo = self.inputs.new('NodeSocketBool', "Albedo")
- intensity = self.inputs.new('PovraySocketFloat_0_1', "Intensity")
- intensity.default_value = 0.8
- roughness = self.inputs.new('PovraySocketFloat_0_1', "Roughness")
- roughness.default_value = 0.02
- metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic")
-
- self.outputs.new('NodeSocketVector', "Specular")
-
- def draw_label(self):
- return "Specular"
-
-
-class PovrayMirrorNode(Node, ObjectNodeTree):
- """Mirror"""
-
- bl_idname = 'PovrayMirrorNode'
- bl_label = 'Mirror'
- bl_icon = 'SHADING_TEXTURE'
-
- def init(self, context):
-
- color = self.inputs.new('PovraySocketColor', "Color")
- color.default_value = (1, 1, 1)
- metallic = self.inputs.new('PovraySocketFloat_0_1', "Metallic")
- metallic.default_value = 1.0
- exponent = self.inputs.new('PovraySocketFloat_0_1', "Exponent")
- exponent.default_value = 1.0
- self.inputs.new('PovraySocketFloat_0_1', "Falloff")
- self.inputs.new('NodeSocketBool', "Fresnel")
- self.inputs.new('NodeSocketBool', "Conserve energy")
- self.outputs.new('NodeSocketVector', "Mirror")
-
- def draw_label(self):
- return "Mirror"
-
-
-class PovrayAmbientNode(Node, ObjectNodeTree):
- """Ambient"""
-
- bl_idname = 'PovrayAmbientNode'
- bl_label = 'Ambient'
- bl_icon = 'SHADING_SOLID'
-
- def init(self, context):
-
- self.inputs.new('PovraySocketColor', "Ambient")
-
- self.outputs.new('NodeSocketVector', "Ambient")
-
- def draw_label(self):
- return "Ambient"
-
-
-class PovrayIridescenceNode(Node, ObjectNodeTree):
- """Iridescence"""
-
- bl_idname = 'PovrayIridescenceNode'
- bl_label = 'Iridescence'
- bl_icon = 'MESH_UVSPHERE'
-
- def init(self, context):
-
- amount = self.inputs.new('NodeSocketFloat', "Amount")
- amount.default_value = 0.25
- thickness = self.inputs.new('NodeSocketFloat', "Thickness")
- thickness.default_value = 1
- self.inputs.new('NodeSocketFloat', "Turbulence")
-
- self.outputs.new('NodeSocketVector', "Iridescence")
-
- def draw_label(self):
- return "Iridescence"
-
-
-class PovraySubsurfaceNode(Node, ObjectNodeTree):
- """Subsurface"""
-
- bl_idname = 'PovraySubsurfaceNode'
- bl_label = 'Subsurface'
- bl_icon = 'MESH_UVSPHERE'
-
- def init(self, context):
-
- translucency = self.inputs.new('NodeSocketColor', "Translucency")
- translucency.default_value = (0, 0, 0, 1)
- energy = self.inputs.new('PovraySocketInt_0_256', "Energy")
- energy.default_value = 20
- self.outputs.new('NodeSocketVector', "Translucency")
-
- def draw_buttons(self, context, layout):
- scene = context.scene
- layout.prop(scene.pov, "sslt_enable", text="SSLT")
-
- def draw_buttons_ext(self, context, layout):
- scene = context.scene
- layout.prop(scene.pov, "sslt_enable", text="SSLT")
-
- def draw_label(self):
- return "Subsurface"
-
-
-# ---------------------------------------------------------------- #
-
-
-class PovrayMappingNode(Node, ObjectNodeTree):
- """Mapping"""
-
- bl_idname = 'PovrayMappingNode'
- bl_label = 'Mapping'
- bl_icon = 'NODE_TEXTURE'
-
- warp_type: EnumProperty(
- name="Warp Types",
- description="Select the type of warp",
- items=(
- ('cubic', "Cubic", ""),
- ('cylindrical', "Cylindrical", ""),
- ('planar', "Planar", ""),
- ('spherical', "Spherical", ""),
- ('toroidal', "Toroidal", ""),
- ('uv_mapping', "UV", ""),
- ('NONE', "None", "No indentation"),
- ),
- default='NONE',
- )
-
- warp_orientation: EnumProperty(
- name="Warp Orientation",
- description="Select the orientation of warp",
- items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")),
- default='y',
- )
-
- warp_dist_exp: FloatProperty(
- name="Distance exponent", description="Distance exponent", min=0.0, max=100.0, default=1.0
- )
-
- warp_tor_major_radius: FloatProperty(
- name="Major radius",
- description="Torus is distance from major radius",
- min=0.0,
- max=5.0,
- default=1.0,
- )
-
- def init(self, context):
- self.outputs.new('NodeSocketVector', "Mapping")
-
- def draw_buttons(self, context, layout):
-
- column = layout.column()
- column.prop(self, "warp_type", text="Warp type")
- if self.warp_type in {'toroidal', 'spherical', 'cylindrical', 'planar'}:
- column.prop(self, "warp_orientation", text="Orientation")
- column.prop(self, "warp_dist_exp", text="Exponent")
- if self.warp_type == 'toroidal':
- column.prop(self, "warp_tor_major_radius", text="Major R")
-
- def draw_buttons_ext(self, context, layout):
-
- column = layout.column()
- column.prop(self, "warp_type", text="Warp type")
- if self.warp_type in {'toroidal', 'spherical', 'cylindrical', 'planar'}:
- column.prop(self, "warp_orientation", text="Orientation")
- column.prop(self, "warp_dist_exp", text="Exponent")
- if self.warp_type == 'toroidal':
- column.prop(self, "warp_tor_major_radius", text="Major R")
-
- def draw_label(self):
- return "Mapping"
-
-
-class PovrayMultiplyNode(Node, ObjectNodeTree):
- """Multiply"""
-
- bl_idname = 'PovrayMultiplyNode'
- bl_label = 'Multiply'
- bl_icon = 'SHADING_SOLID'
-
- amount_x: FloatProperty(
- name="X", description="Number of repeats", min=1.0, max=10000.0, default=1.0
- )
-
- amount_y: FloatProperty(
- name="Y", description="Number of repeats", min=1.0, max=10000.0, default=1.0
- )
-
- amount_z: FloatProperty(
- name="Z", description="Number of repeats", min=1.0, max=10000.0, default=1.0
- )
-
- def init(self, context):
- self.outputs.new('NodeSocketVector', "Amount")
-
- def draw_buttons(self, context, layout):
-
- column = layout.column()
- column.label(text="Amount")
- row = column.row(align=True)
- row.prop(self, "amount_x")
- row.prop(self, "amount_y")
- row.prop(self, "amount_z")
-
- def draw_buttons_ext(self, context, layout):
-
- column = layout.column()
- column.label(text="Amount")
- row = column.row(align=True)
- row.prop(self, "amount_x")
- row.prop(self, "amount_y")
- row.prop(self, "amount_z")
-
- def draw_label(self):
- return "Multiply"
-
-
-class PovrayTransformNode(Node, ObjectNodeTree):
- """Transform"""
-
- bl_idname = 'PovrayTransformNode'
- bl_label = 'Transform'
- bl_icon = 'NODE_TEXTURE'
-
- def init(self, context):
-
- self.inputs.new('PovraySocketFloatUnlimited', "Translate x")
- self.inputs.new('PovraySocketFloatUnlimited', "Translate y")
- self.inputs.new('PovraySocketFloatUnlimited', "Translate z")
- self.inputs.new('PovraySocketFloatUnlimited', "Rotate x")
- self.inputs.new('PovraySocketFloatUnlimited', "Rotate y")
- self.inputs.new('PovraySocketFloatUnlimited', "Rotate z")
- sX = self.inputs.new('PovraySocketFloatUnlimited', "Scale x")
- sX.default_value = 1.0
- sY = self.inputs.new('PovraySocketFloatUnlimited', "Scale y")
- sY.default_value = 1.0
- sZ = self.inputs.new('PovraySocketFloatUnlimited', "Scale z")
- sZ.default_value = 1.0
-
- self.outputs.new('NodeSocketVector', "Transform")
-
- def draw_label(self):
- return "Transform"
-
-
-class PovrayValueNode(Node, ObjectNodeTree):
- """Value"""
-
- bl_idname = 'PovrayValueNode'
- bl_label = 'Value'
- bl_icon = 'SHADING_SOLID'
-
- def init(self, context):
-
- self.outputs.new('PovraySocketUniversal', "Value")
-
- def draw_label(self):
- return "Value"
-
-
-class PovrayModifierNode(Node, ObjectNodeTree):
- """Modifier"""
-
- bl_idname = 'PovrayModifierNode'
- bl_label = 'Modifier'
- bl_icon = 'NODE_TEXTURE'
-
- def init(self, context):
-
- turb_x = self.inputs.new('PovraySocketFloat_0_10', "Turb X")
- turb_x.default_value = 0.1
- turb_y = self.inputs.new('PovraySocketFloat_0_10', "Turb Y")
- turb_y.default_value = 0.1
- turb_z = self.inputs.new('PovraySocketFloat_0_10', "Turb Z")
- turb_z.default_value = 0.1
- octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves")
- octaves.default_value = 1
- lambat = self.inputs.new('PovraySocketFloat_0_10', "Lambda")
- lambat.default_value = 2.0
- omega = self.inputs.new('PovraySocketFloat_0_10', "Omega")
- omega.default_value = 0.5
- freq = self.inputs.new('PovraySocketFloat_0_10', "Frequency")
- freq.default_value = 2.0
- self.inputs.new('PovraySocketFloat_0_10', "Phase")
-
- self.outputs.new('NodeSocketVector', "Modifier")
-
- def draw_label(self):
- return "Modifier"
-
-
-class PovrayPigmentNode(Node, ObjectNodeTree):
- """Pigment"""
-
- bl_idname = 'PovrayPigmentNode'
- bl_label = 'Color'
- bl_icon = 'SHADING_SOLID'
-
- def init(self, context):
-
- color = self.inputs.new('PovraySocketColor', "Color")
- color.default_value = (1, 1, 1)
- pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter")
- transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit")
- self.outputs.new('NodeSocketColor', "Pigment")
-
- def draw_label(self):
- return "Color"
-
-
-class PovrayColorImageNode(Node, ObjectNodeTree):
- """ColorImage"""
-
- bl_idname = 'PovrayColorImageNode'
- bl_label = 'Image map'
-
- map_type: bpy.props.EnumProperty(
- name="Map type",
- description="",
- items=(
- ('uv_mapping', "UV", ""),
- ('0', "Planar", "Default planar mapping"),
- ('1', "Spherical", "Spherical mapping"),
- ('2', "Cylindrical", "Cylindrical mapping"),
- ('5', "Torroidal", "Torus or donut shaped mapping"),
- ),
- default='0',
- )
- image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
- interpolate: EnumProperty(
- name="Interpolate",
- description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
- items=(
- ('2', "Bilinear", "Gives bilinear interpolation"),
- ('4', "Normalized", "Gives normalized distance"),
- ),
- default='2',
- )
- premultiplied: BoolProperty(default=False)
- once: BoolProperty(description="Not to repeat", default=False)
-
- def init(self, context):
-
- gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
- gamma.default_value = 2.0
- transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit")
- pov_filter = self.inputs.new('PovraySocketFloat_0_1', "Filter")
- mapping = self.inputs.new('NodeSocketVector', "Mapping")
- mapping.hide_value = True
- transform = self.inputs.new('NodeSocketVector', "Transform")
- transform.hide_value = True
- modifier = self.inputs.new('NodeSocketVector', "Modifier")
- modifier.hide_value = True
-
- self.outputs.new('NodeSocketColor', "Pigment")
-
- def draw_buttons(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- row = column.row()
- row.prop(self, "premultiplied", text="Premul")
- row.prop(self, "once", text="Once")
-
- def draw_buttons_ext(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- row = column.row()
- row.prop(self, "premultiplied", text="Premul")
- row.prop(self, "once", text="Once")
-
- def draw_label(self):
- return "Image map"
-
-
-class PovrayBumpMapNode(Node, ObjectNodeTree):
- """BumpMap"""
-
- bl_idname = 'PovrayBumpMapNode'
- bl_label = 'Bump map'
- bl_icon = 'TEXTURE'
-
- map_type: bpy.props.EnumProperty(
- name="Map type",
- description="",
- items=(
- ('uv_mapping', "UV", ""),
- ('0', "Planar", "Default planar mapping"),
- ('1', "Spherical", "Spherical mapping"),
- ('2', "Cylindrical", "Cylindrical mapping"),
- ('5', "Torroidal", "Torus or donut shaped mapping"),
- ),
- default='0',
- )
- image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
- interpolate: EnumProperty(
- name="Interpolate",
- description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
- items=(
- ('2', "Bilinear", "Gives bilinear interpolation"),
- ('4', "Normalized", "Gives normalized distance"),
- ),
- default='2',
- )
- once: BoolProperty(description="Not to repeat", default=False)
-
- def init(self, context):
-
- self.inputs.new('PovraySocketFloat_0_10', "Normal")
- mapping = self.inputs.new('NodeSocketVector', "Mapping")
- mapping.hide_value = True
- transform = self.inputs.new('NodeSocketVector', "Transform")
- transform.hide_value = True
- modifier = self.inputs.new('NodeSocketVector', "Modifier")
- modifier.hide_value = True
-
- normal = self.outputs.new('NodeSocketFloat', "Normal")
- normal.hide_value = True
-
- def draw_buttons(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- column.prop(self, "once", text="Once")
-
- def draw_buttons_ext(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- column.prop(self, "once", text="Once")
-
- def draw_label(self):
- return "Bump Map"
-
-
-class PovrayImagePatternNode(Node, ObjectNodeTree):
- """ImagePattern"""
-
- bl_idname = 'PovrayImagePatternNode'
- bl_label = 'Image pattern'
- bl_icon = 'NODE_TEXTURE'
-
- map_type: bpy.props.EnumProperty(
- name="Map type",
- description="",
- items=(
- ('uv_mapping', "UV", ""),
- ('0', "Planar", "Default planar mapping"),
- ('1', "Spherical", "Spherical mapping"),
- ('2', "Cylindrical", "Cylindrical mapping"),
- ('5', "Torroidal", "Torus or donut shaped mapping"),
- ),
- default='0',
- )
- image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
- interpolate: EnumProperty(
- name="Interpolate",
- description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
- items=(
- ('2', "Bilinear", "Gives bilinear interpolation"),
- ('4', "Normalized", "Gives normalized distance"),
- ),
- default='2',
- )
- premultiplied: BoolProperty(default=False)
- once: BoolProperty(description="Not to repeat", default=False)
- use_alpha: BoolProperty(default=True)
-
- def init(self, context):
-
- gamma = self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
- gamma.default_value = 2.0
-
- self.outputs.new('PovraySocketPattern', "Pattern")
-
- def draw_buttons(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- row = column.row()
- row.prop(self, "premultiplied", text="Premul")
- row.prop(self, "once", text="Once")
- column.prop(self, "use_alpha", text="Use alpha")
-
- def draw_buttons_ext(self, context, layout):
-
- column = layout.column()
- im = None
- for image in bpy.data.images:
- if image.name == self.image:
- im = image
- split = column.split(factor=0.8, align=True)
- split.prop_search(self, "image", context.blend_data, "images", text="")
- split.operator("pov.imageopen", text="", icon="FILEBROWSER")
- if im is not None:
- column.prop(im, "source", text="")
- column.prop(self, "map_type", text="")
- column.prop(self, "interpolate", text="")
- row = column.row()
- row.prop(self, "premultiplied", text="Premul")
- row.prop(self, "once", text="Once")
-
- def draw_label(self):
- return "Image pattern"
-
-
-class ShaderPatternNode(Node, ObjectNodeTree):
- """Pattern"""
-
- bl_idname = 'ShaderPatternNode'
- bl_label = 'Other patterns'
-
- pattern: EnumProperty(
- name="Pattern",
- description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling",
- items=(
- ('agate', "Agate", ""),
- ('crackle', "Crackle", ""),
- ('gradient', "Gradient", ""),
- ('pavement', "Pavement", ""),
- ('spiral1', "Spiral 1", ""),
- ('spiral2', "Spiral 2", ""),
- ('tiling', "Tiling", ""),
- ),
- default='agate',
- )
-
- agate_turb: FloatProperty(
- name="Agate turb", description="Agate turbulence", min=0.0, max=100.0, default=0.5
- )
-
- crackle_form_x: FloatProperty(
- name="X", description="Form vector X", min=-150.0, max=150.0, default=-1
- )
-
- crackle_form_y: FloatProperty(
- name="Y", description="Form vector Y", min=-150.0, max=150.0, default=1
- )
-
- crackle_form_z: FloatProperty(
- name="Z", description="Form vector Z", min=-150.0, max=150.0, default=0
- )
-
- crackle_metric: FloatProperty(
- name="Metric", description="Crackle metric", min=0.0, max=150.0, default=1
- )
-
- crackle_solid: BoolProperty(name="Solid", description="Crackle solid", default=False)
-
- spiral_arms: FloatProperty(name="Number", description="", min=0.0, max=256.0, default=2.0)
-
- tiling_number: IntProperty(name="Number", description="", min=1, max=27, default=1)
-
- gradient_orient: EnumProperty(
- name="Orient",
- description="",
- items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")),
- default='x',
- )
-
- def init(self, context):
-
- pat = self.outputs.new('PovraySocketPattern', "Pattern")
-
- def draw_buttons(self, context, layout):
-
- layout.prop(self, "pattern", text="")
- if self.pattern == 'agate':
- layout.prop(self, "agate_turb")
- if self.pattern == 'crackle':
- layout.prop(self, "crackle_metric")
- layout.prop(self, "crackle_solid")
- layout.label(text="Form:")
- layout.prop(self, "crackle_form_x")
- layout.prop(self, "crackle_form_y")
- layout.prop(self, "crackle_form_z")
- if self.pattern in {"spiral1", "spiral2"}:
- layout.prop(self, "spiral_arms")
- if self.pattern in {'tiling'}:
- layout.prop(self, "tiling_number")
- if self.pattern in {'gradient'}:
- layout.prop(self, "gradient_orient")
-
- def draw_buttons_ext(self, context, layout):
- pass
-
- def draw_label(self):
- return "Other patterns"
-
-
-class ShaderTextureMapNode(Node, ObjectNodeTree):
- """Texture Map"""
-
- bl_idname = 'ShaderTextureMapNode'
- bl_label = 'Texture map'
-
- brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
-
- brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
-
- brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
-
- brick_mortar: FloatProperty(
- name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
- )
-
- def init(self, context):
- mat = bpy.context.object.active_material
- self.inputs.new('PovraySocketPattern', "")
- color = self.inputs.new('NodeSocketColor', "Color ramp")
- color.hide_value = True
- for i in range(0, 4):
- transform = self.inputs.new('PovraySocketTransform', "Transform")
- transform.hide_value = True
- number = mat.pov.inputs_number
- for i in range(number):
- self.inputs.new('PovraySocketTexture', "%s" % i)
-
- self.outputs.new('PovraySocketTexture', "Texture")
-
- def draw_buttons(self, context, layout):
-
- if self.inputs[0].default_value == 'brick':
- layout.prop(self, "brick_mortar")
- layout.label(text="Brick size:")
- layout.prop(self, "brick_size_x")
- layout.prop(self, "brick_size_y")
- layout.prop(self, "brick_size_z")
-
- def draw_buttons_ext(self, context, layout):
-
- if self.inputs[0].default_value == 'brick':
- layout.prop(self, "brick_mortar")
- layout.label(text="Brick size:")
- layout.prop(self, "brick_size_x")
- layout.prop(self, "brick_size_y")
- layout.prop(self, "brick_size_z")
-
- def draw_label(self):
- return "Texture map"
-
-
-class ShaderNormalMapNode(Node, ObjectNodeTree):
- """Normal Map"""
-
- bl_idname = 'ShaderNormalMapNode'
- bl_label = 'Normal map'
-
- brick_size_x: FloatProperty(name="X", description="", min=0.0000, max=1.0000, default=0.2500)
-
- brick_size_y: FloatProperty(name="Y", description="", min=0.0000, max=1.0000, default=0.0525)
-
- brick_size_z: FloatProperty(name="Z", description="", min=0.0000, max=1.0000, default=0.1250)
-
- brick_mortar: FloatProperty(
- name="Mortar", description="Mortar", min=0.000, max=1.500, default=0.01
- )
-
- def init(self, context):
- self.inputs.new('PovraySocketPattern', "")
- normal = self.inputs.new('PovraySocketFloat_10', "Normal")
- slope = self.inputs.new('PovraySocketMap', "Slope map")
- for i in range(0, 4):
- transform = self.inputs.new('PovraySocketTransform', "Transform")
- transform.hide_value = True
- self.outputs.new('PovraySocketNormal', "Normal")
-
- def draw_buttons(self, context, layout):
- # for i, inp in enumerate(self.inputs):
-
- if self.inputs[0].default_value == 'brick':
- layout.prop(self, "brick_mortar")
- layout.label(text="Brick size:")
- layout.prop(self, "brick_size_x")
- layout.prop(self, "brick_size_y")
- layout.prop(self, "brick_size_z")
-
- def draw_buttons_ext(self, context, layout):
-
- if self.inputs[0].default_value == 'brick':
- layout.prop(self, "brick_mortar")
- layout.label(text="Brick size:")
- layout.prop(self, "brick_size_x")
- layout.prop(self, "brick_size_y")
- layout.prop(self, "brick_size_z")
-
- def draw_label(self):
- return "Normal map"
-
-
-class ShaderNormalMapEntryNode(Node, ObjectNodeTree):
- """Normal Map Entry"""
-
- bl_idname = 'ShaderNormalMapEntryNode'
- bl_label = 'Normal map entry'
-
- def init(self, context):
- self.inputs.new('PovraySocketFloat_0_1', "Stop")
- self.inputs.new('PovraySocketFloat_0_1', "Gray")
-
- def draw_label(self):
- return "Normal map entry"
-
-
-class IsoPropsNode(Node, CompositorNodeTree):
- """ISO Props"""
-
- bl_idname = 'IsoPropsNode'
- bl_label = 'Iso'
- node_label: StringProperty(maxlen=1024)
-
- def init(self, context):
- ob = bpy.context.object
- self.node_label = ob.name
- text_name = ob.pov.function_text
- if text_name:
- text = bpy.data.texts[text_name]
- for line in text.lines:
- split = line.body.split()
- if split[0] == "#declare":
- socket = self.inputs.new('NodeSocketFloat', "%s" % split[1])
- value = split[3].split(";")
- value = value[0]
- socket.default_value = float(value)
-
- def draw_label(self):
- return self.node_label
-
-
-class PovrayFogNode(Node, CompositorNodeTree):
- """Fog settings"""
-
- bl_idname = 'PovrayFogNode'
- bl_label = 'Fog'
-
- def init(self, context):
- color = self.inputs.new('NodeSocketColor', "Color")
- color.default_value = (0.7, 0.7, 0.7, 0.25)
- self.inputs.new('PovraySocketFloat_0_1', "Filter")
- distance = self.inputs.new('NodeSocketInt', "Distance")
- distance.default_value = 150
- self.inputs.new('NodeSocketBool', "Ground")
- fog_offset = self.inputs.new('NodeSocketFloat', "Offset")
- fog_alt = self.inputs.new('NodeSocketFloat', "Altitude")
- turb = self.inputs.new('NodeSocketVector', "Turbulence")
- turb_depth = self.inputs.new('PovraySocketFloat_0_10', "Depth")
- turb_depth.default_value = 0.5
- octaves = self.inputs.new('PovraySocketInt_1_9', "Octaves")
- octaves.default_value = 5
- lambdat = self.inputs.new('PovraySocketFloat_0_10', "Lambda")
- lambdat.default_value = 1.25
- omega = self.inputs.new('PovraySocketFloat_0_10', "Omega")
- omega.default_value = 0.35
- translate = self.inputs.new('NodeSocketVector', "Translate")
- rotate = self.inputs.new('NodeSocketVector', "Rotate")
- scale = self.inputs.new('NodeSocketVector', "Scale")
- scale.default_value = (1, 1, 1)
-
- def draw_label(self):
- return "Fog"
-
-
-class PovraySlopeNode(Node, TextureNodeTree):
- """Output"""
-
- bl_idname = 'PovraySlopeNode'
- bl_label = 'Slope Map'
-
- def init(self, context):
- self.use_custom_color = True
- self.color = (0, 0.2, 0)
- slope = self.inputs.new('PovraySocketSlope', "0")
- slope = self.inputs.new('PovraySocketSlope', "1")
- slopemap = self.outputs.new('PovraySocketMap', "Slope map")
- output.hide_value = True
-
- def draw_buttons(self, context, layout):
-
- layout.operator("pov.nodeinputadd")
- row = layout.row()
- row.label(text='Value')
- row.label(text='Height')
- row.label(text='Slope')
-
- def draw_buttons_ext(self, context, layout):
-
- layout.operator("pov.nodeinputadd")
- row = layout.row()
- row.label(text='Value')
- row.label(text='Height')
- row.label(text='Slope')
-
- def draw_label(self):
- return "Slope Map"
-
-
-# -------- Texture nodes # ---------------------------------------------------------------- #
-class TextureOutputNode(Node, TextureNodeTree):
- """Output"""
-
- bl_idname = 'TextureOutputNode'
- bl_label = 'Color Map'
-
- def init(self, context):
- tex = bpy.context.object.active_material.active_texture
- num_sockets = int(tex.pov.density_lines / 32)
- for i in range(num_sockets):
- color = self.inputs.new('NodeSocketColor', "%s" % i)
- color.hide_value = True
-
- def draw_buttons(self, context, layout):
-
- layout.label(text="Color Ramps:")
-
- def draw_label(self):
- return "Color Map"
-
-
-# ------------------------------------------------------------------------------ #
-# --------------------------------- Operators ---------------------------------- #
-# ------------------------------------------------------------------------------ #
-
-
-class NODE_OT_iso_add(Operator):
- bl_idname = "pov.nodeisoadd"
- bl_label = "Create iso props"
-
- def execute(self, context):
- ob = bpy.context.object
- if not bpy.context.scene.use_nodes:
- bpy.context.scene.use_nodes = True
- tree = bpy.context.scene.node_tree
- for node in tree.nodes:
- if node.bl_idname == "IsoPropsNode" and node.label == ob.name:
- tree.nodes.remove(node)
- isonode = tree.nodes.new('IsoPropsNode')
- isonode.location = (0, 0)
- isonode.label = ob.name
- return {'FINISHED'}
-
-
-class NODE_OT_map_create(Operator):
- bl_idname = "node.map_create"
- bl_label = "Create map"
-
- def execute(self, context):
- x = y = 0
- space = context.space_data
- tree = space.edit_tree
- for node in tree.nodes:
- if node.select:
- x, y = node.location
- node.select = False
- tmap = tree.nodes.new('ShaderTextureMapNode')
- tmap.location = (x - 200, y)
- return {'FINISHED'}
-
- def invoke(self, context, event):
- wm = context.window_manager
- return wm.invoke_props_dialog(self)
-
- def draw(self, context):
- layout = self.layout
- mat = context.object.active_material
- layout.prop(mat.pov, "inputs_number")
-
-
-class NODE_OT_povray_node_texture_map_add(Operator):
- bl_idname = "pov.nodetexmapadd"
- bl_label = "Texture map"
-
- def execute(self, context):
- tree = bpy.context.object.active_material.node_tree
- tmap = tree.nodes.active
- bpy.context.object.active_material.node_tree.nodes.active = tmap
- el = tmap.color_ramp.elements.new(0.5)
- for el in tmap.color_ramp.elements:
- el.color = (0, 0, 0, 1)
- for inp in tmap.inputs:
- tmap.inputs.remove(inp)
- for outp in tmap.outputs:
- tmap.outputs.remove(outp)
- pattern = tmap.inputs.new('NodeSocketVector', "Pattern")
- pattern.hide_value = True
- for i in range(0, 3):
- tmap.inputs.new('NodeSocketColor', "Shader")
- tmap.outputs.new('NodeSocketShader', "BSDF")
- tmap.label = "Texture Map"
- return {'FINISHED'}
-
-
-class NODE_OT_povray_node_output_add(Operator):
- bl_idname = "pov.nodeoutputadd"
- bl_label = "Output"
-
- def execute(self, context):
- tree = bpy.context.object.active_material.node_tree
- tmap = tree.nodes.new('ShaderNodeOutputMaterial')
- bpy.context.object.active_material.node_tree.nodes.active = tmap
- for inp in tmap.inputs:
- tmap.inputs.remove(inp)
- tmap.inputs.new('NodeSocketShader', "Surface")
- tmap.label = "Output"
- return {'FINISHED'}
-
-
-class NODE_OT_povray_node_layered_add(Operator):
- bl_idname = "pov.nodelayeredadd"
- bl_label = "Layered material"
-
- def execute(self, context):
- tree = bpy.context.object.active_material.node_tree
- tmap = tree.nodes.new('ShaderNodeAddShader')
- bpy.context.object.active_material.node_tree.nodes.active = tmap
- tmap.label = "Layered material"
- return {'FINISHED'}
-
-
-class NODE_OT_povray_input_add(Operator):
- bl_idname = "pov.nodeinputadd"
- bl_label = "Add entry"
-
- def execute(self, context):
- node = bpy.context.object.active_material.node_tree.nodes.active
- if node.type in {'VALTORGB'}:
- number = 1
- for inp in node.inputs:
- if inp.type == 'SHADER':
- number += 1
- node.inputs.new('NodeSocketShader', "%s" % number)
- els = node.color_ramp.elements
- pos1 = els[len(els) - 1].position
- pos2 = els[len(els) - 2].position
- pos = (pos1 - pos2) / 2 + pos2
- el = els.new(pos)
-
- if node.bl_idname == 'PovraySlopeNode':
- number = len(node.inputs)
- node.inputs.new('PovraySocketSlope', "%s" % number)
-
- return {'FINISHED'}
-
-
-class NODE_OT_povray_input_remove(Operator):
- bl_idname = "pov.nodeinputremove"
- bl_label = "Remove input"
-
- def execute(self, context):
- node = bpy.context.object.active_material.node_tree.nodes.active
- if node.type in {'VALTORGB', 'ADD_SHADER'}:
- number = len(node.inputs) - 1
- if number > 5:
- inp = node.inputs[number]
- node.inputs.remove(inp)
- if node.type in {'VALTORGB'}:
- els = node.color_ramp.elements
- number = len(els) - 2
- el = els[number]
- els.remove(el)
- return {'FINISHED'}
-
-
-class NODE_OT_povray_image_open(Operator):
- bl_idname = "pov.imageopen"
- bl_label = "Open"
-
- filepath: StringProperty(
- name="File Path", description="Open image", maxlen=1024, subtype='FILE_PATH'
- )
-
- def invoke(self, context, event):
- context.window_manager.fileselect_add(self)
- return {'RUNNING_MODAL'}
-
- def execute(self, context):
- im = bpy.data.images.load(self.filepath)
- node = context.object.active_material.node_tree.nodes.active
- node.image = im.name
- return {'FINISHED'}
-
-
-# class TEXTURE_OT_povray_open_image(Operator):
-# bl_idname = "pov.openimage"
-# bl_label = "Open Image"
-
-# filepath = StringProperty(
-# name="File Path",
-# description="Open image",
-# maxlen=1024,
-# subtype='FILE_PATH',
-# )
-
-# def invoke(self, context, event):
-# context.window_manager.fileselect_add(self)
-# return {'RUNNING_MODAL'}
-
-# def execute(self, context):
-# im=bpy.data.images.load(self.filepath)
-# tex = context.texture
-# tex.pov.image = im.name
-# view_layer = context.view_layer
-# view_layer.update()
-# return {'FINISHED'}
-
-
-class PovrayPatternNode(Operator):
- bl_idname = "pov.patternnode"
- bl_label = "Pattern"
-
- add = True
-
- def execute(self, context):
- space = context.space_data
- tree = space.edit_tree
- for node in tree.nodes:
- node.select = False
- if self.add:
- tmap = tree.nodes.new('ShaderNodeValToRGB')
- tmap.label = "Pattern"
- for inp in tmap.inputs:
- tmap.inputs.remove(inp)
- for outp in tmap.outputs:
- tmap.outputs.remove(outp)
- pattern = tmap.inputs.new('PovraySocketPattern', "Pattern")
- pattern.hide_value = True
- mapping = tmap.inputs.new('NodeSocketVector', "Mapping")
- mapping.hide_value = True
- transform = tmap.inputs.new('NodeSocketVector', "Transform")
- transform.hide_value = True
- modifier = tmap.inputs.new('NodeSocketVector', "Modifier")
- modifier.hide_value = True
- for i in range(0, 2):
- tmap.inputs.new('NodeSocketShader', "%s" % (i + 1))
- tmap.outputs.new('NodeSocketShader', "Material")
- tmap.outputs.new('NodeSocketColor', "Color")
- tree.nodes.active = tmap
- self.add = False
- aNode = tree.nodes.active
- aNode.select = True
- v2d = context.region.view2d
- x, y = v2d.region_to_view(self.x, self.y)
- aNode.location = (x, y)
-
- def modal(self, context, event):
- if event.type == 'MOUSEMOVE':
- self.x = event.mouse_region_x
- self.y = event.mouse_region_y
- self.execute(context)
- return {'RUNNING_MODAL'}
- elif event.type == 'LEFTMOUSE':
- return {'FINISHED'}
- elif event.type in ('RIGHTMOUSE', 'ESC'):
- return {'CANCELLED'}
-
- return {'RUNNING_MODAL'}
-
- def invoke(self, context, event):
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
-
-
-class UpdatePreviewMaterial(Operator):
- """Operator update preview material"""
-
- bl_idname = "node.updatepreview"
- bl_label = "Update preview"
-
- def execute(self, context):
- scene = context.view_layer
- ob = context.object
- for obj in scene.objects:
- if obj != ob:
- scene.objects.active = ob
- break
- scene.objects.active = ob
-
- def modal(self, context, event):
- if event.type == 'RIGHTMOUSE':
- self.execute(context)
- return {'FINISHED'}
- return {'PASS_THROUGH'}
-
- def invoke(self, context, event):
- context.window_manager.modal_handler_add(self)
- return {'RUNNING_MODAL'}
-
-
-class UpdatePreviewKey(Operator):
- """Operator update preview keymap"""
-
- bl_idname = "wm.updatepreviewkey"
- bl_label = "Activate RMB"
-
- @classmethod
- def poll(cls, context):
- conf = context.window_manager.keyconfigs.active
- mapstr = "Node Editor"
- map = conf.keymaps[mapstr]
- try:
- map.keymap_items["node.updatepreview"]
- return False
- except BaseException as e:
- print(e.__doc__)
- print('An exception occurred: {}'.format(e))
- return True
-
- def execute(self, context):
- conf = context.window_manager.keyconfigs.active
- mapstr = "Node Editor"
- map = conf.keymaps[mapstr]
- map.keymap_items.new("node.updatepreview", type='RIGHTMOUSE', value="PRESS")
- return {'FINISHED'}
-
-
-classes = (
- PovraySocketUniversal,
- PovraySocketFloat_0_1,
- PovraySocketFloat_0_10,
- PovraySocketFloat_10,
- PovraySocketFloatPositive,
- PovraySocketFloat_000001_10,
- PovraySocketFloatUnlimited,
- PovraySocketInt_1_9,
- PovraySocketInt_0_256,
- PovraySocketPattern,
- PovraySocketColor,
- PovraySocketColorRGBFT,
- PovraySocketTexture,
- PovraySocketTransform,
- PovraySocketNormal,
- PovraySocketSlope,
- PovraySocketMap,
- # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
- # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
- # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
- NODE_MT_POV_map_create,
- ObjectNodeTree,
- PovrayOutputNode,
- PovrayTextureNode,
- PovrayFinishNode,
- PovrayDiffuseNode,
- PovrayPhongNode,
- PovraySpecularNode,
- PovrayMirrorNode,
- PovrayAmbientNode,
- PovrayIridescenceNode,
- PovraySubsurfaceNode,
- PovrayMappingNode,
- PovrayMultiplyNode,
- PovrayTransformNode,
- PovrayValueNode,
- PovrayModifierNode,
- PovrayPigmentNode,
- PovrayColorImageNode,
- PovrayBumpMapNode,
- PovrayImagePatternNode,
- ShaderPatternNode,
- ShaderTextureMapNode,
- ShaderNormalMapNode,
- ShaderNormalMapEntryNode,
- IsoPropsNode,
- PovrayFogNode,
- PovraySlopeNode,
- TextureOutputNode,
- NODE_OT_iso_add,
- NODE_OT_map_create,
- NODE_OT_povray_node_texture_map_add,
- NODE_OT_povray_node_output_add,
- NODE_OT_povray_node_layered_add,
- NODE_OT_povray_input_add,
- NODE_OT_povray_input_remove,
- NODE_OT_povray_image_open,
- PovrayPatternNode,
- UpdatePreviewMaterial,
- UpdatePreviewKey,
-)
-
-
-def register():
- bpy.types.NODE_HT_header.append(menu_func_nodes)
- nodeitems_utils.register_node_categories("POVRAYNODES", node_categories)
- for cls in classes:
- register_class(cls)
-
-
-def unregister():
- for cls in reversed(classes):
- unregister_class(cls)
- nodeitems_utils.unregister_node_categories("POVRAYNODES")
- bpy.types.NODE_HT_header.remove(menu_func_nodes)
diff --git a/render_povray/shading_properties.py b/render_povray/shading_properties.py
index bcc8944d..a1c358de 100755
--- a/render_povray/shading_properties.py
+++ b/render_povray/shading_properties.py
@@ -19,10 +19,7 @@ from bpy.props import (
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:
- # No active node material
- return not mat.node_tree
- return True
+ return not mat.node_tree if mat.use_nodes else True
return False
@@ -30,28 +27,27 @@ def pov_context_tex_datablock(context):
"""Texture context type recreated as deprecated in blender 2.8"""
idblock = context.brush
- if idblock and context.scene.texture_context == 'OTHER':
+ if idblock and context.scene.texture_context == "OTHER":
return idblock
# idblock = bpy.context.active_object.active_material
idblock = context.view_layer.objects.active.active_material
- if idblock and context.scene.texture_context == 'MATERIAL':
+ if idblock and context.scene.texture_context == "MATERIAL":
return idblock
idblock = context.scene.world
- if idblock and context.scene.texture_context == 'WORLD':
+ if idblock and context.scene.texture_context == "WORLD":
return idblock
idblock = context.light
- if idblock and context.scene.texture_context == 'LIGHT':
+ if idblock and context.scene.texture_context == "LIGHT":
return idblock
- if context.particle_system and context.scene.texture_context == 'PARTICLES':
+ if context.particle_system and context.scene.texture_context == "PARTICLES":
idblock = context.particle_system.settings
-
idblock = context.line_style
- if idblock and context.scene.texture_context == 'LINESTYLE':
+ if idblock and context.scene.texture_context == "LINESTYLE":
return idblock
return idblock
@@ -88,7 +84,7 @@ def active_texture_name_from_search(self, context):
# bpy.context.tool_settings.image_paint.brush.mask_texture = tex
except BaseException as e:
print(e.__doc__)
- print('An exception occurred: {}'.format(e))
+ print("An exception occurred: {}".format(e))
def brush_texture_update(self, context):
@@ -99,10 +95,9 @@ def brush_texture_update(self, context):
# mat = context.view_layer.objects.active.active_material
idblock = pov_context_tex_datablock(context)
slot = idblock.pov_texture_slots[idblock.pov.active_texture_index]
- tex = slot.texture
-
- if tex:
+ if tex := slot.texture:
# Switch paint brush to active texture so slot and settings remain contextual
+ # bpy.ops.pov.textureslotupdate()
bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex]
bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex]
@@ -200,7 +195,7 @@ class RenderPovSettingsMaterial(PropertyGroup):
default=1.0,
precision=3,
)
-
+ # TODO: replace by newer agnostic Material.diffuse_color and remove from pov panel
diffuse_color: FloatVectorProperty(
name="Diffuse color",
description="Diffuse color of the material",
@@ -414,7 +409,6 @@ class RenderPovSettingsMaterial(PropertyGroup):
options={"ANIMATABLE"},
subtype="COLOR",
)
-
specular_hardness: IntProperty(
name="Hardness",
description="How hard (sharp) the specular reflection is",
@@ -422,7 +416,7 @@ class RenderPovSettingsMaterial(PropertyGroup):
max=511,
default=30,
)
-
+ # TODO: replace by newer agnostic Material.specular_intensity and remove from pov panel
specular_intensity: FloatProperty(
name="Intensity",
description="How intense (bright) the specular reflection is",
@@ -642,6 +636,7 @@ class RenderPovSettingsMaterial(PropertyGroup):
default=False,
)
+ # TODO create interface:
use_vertex_color_paint: BoolProperty(
name="Vertex Color Paint",
description="Replace object base color with vertex "
@@ -713,12 +708,6 @@ class RenderPovSettingsMaterial(PropertyGroup):
default=False,
)
- mirror_metallic: BoolProperty(
- name="Metallic Reflection",
- description="mirror reflections get colored as diffuse (for metallic materials)",
- default=False,
- )
-
conserve_energy: BoolProperty(
name="Conserve Energy",
description="Light transmitted is more correctly reduced by mirror reflections, "
@@ -780,7 +769,9 @@ class RenderPovSettingsMaterial(PropertyGroup):
)
fake_caustics: BoolProperty(
- name="Fake Caustics", description="use only (Fast) fake refractive caustics", default=True
+ name="Fake Caustics",
+ description="use only (Fast) fake refractive caustics based on transparent shadows",
+ default=True
)
fake_caustics_power: FloatProperty(
@@ -872,7 +863,7 @@ class RenderPovSettingsMaterial(PropertyGroup):
for node in tree.nodes:
if node.bl_idname == "PovrayOutputNode":
o += 1
- if node.bl_idname == "PovrayTextureNode":
+ elif node.bl_idname == "PovrayTextureNode":
m += 1
if o == 1 and m == 1:
default = False
@@ -926,15 +917,16 @@ class RenderPovSettingsMaterial(PropertyGroup):
def pigment_normal_callback(self, context):
render = context.scene.pov.render # XXX comment out > remove?
- items = [("pigment", "Pigment", ""), ("normal", "Normal", "")]
- # XXX Find any other such traces of hgpovray experiment > remove or deploy ?
- if render == "hgpovray":
- items = [
+ return (
+ [("pigment", "Pigment", ""), ("normal", "Normal", "")]
+ # XXX Find any other such traces of hgpovray experiment > remove or deploy ?
+ if render != "hgpovray"
+ else [
("pigment", "Pigment", ""),
("normal", "Normal", ""),
("modulation", "Modulation", ""),
]
- return items
+ )
def glow_callback(self, context):
scene = context.scene
@@ -966,1282 +958,12 @@ class RenderPovSettingsMaterial(PropertyGroup):
object_preview_bgcontrast: FloatProperty(name="Contrast", min=0.0, max=1.0, default=0.5)
-class MaterialRaytraceTransparency(PropertyGroup):
- """Declare transparency panel properties controllable in UI and translated to POV."""
-
- depth: IntProperty(
- name="Depth",
- description="Maximum allowed number of light inter-refractions",
- min=0,
- max=32767,
- default=2,
- )
-
- depth_max: FloatProperty(
- name="Depth",
- description="Maximum depth for light to travel through the "
- "transparent material before becoming fully filtered (0.0 is disabled)",
- min=0,
- max=100,
- default=0.0,
- )
-
- falloff: FloatProperty(
- name="Falloff",
- description="Falloff power for transmissivity filter effect (1.0 is linear)",
- min=0.1,
- max=10.0,
- default=1.0,
- precision=3,
- )
-
- filter: FloatProperty(
- name="Filter",
- description="Amount to blend in the material’s diffuse color in raytraced "
- "transparency (simulating absorption)",
- min=0.0,
- max=1.0,
- default=0.0,
- precision=3,
- )
-
- fresnel: FloatProperty(
- name="Fresnel",
- description="Power of Fresnel for transparency (Ray or ZTransp)",
- min=0.0,
- max=5.0,
- soft_min=0.0,
- soft_max=5.0,
- default=0.0,
- precision=3,
- )
-
- fresnel_factor: FloatProperty(
- name="Blend",
- description="Blending factor for Fresnel",
- min=0.0,
- max=5.0,
- soft_min=0.0,
- soft_max=5.0,
- default=1.250,
- precision=3,
- )
-
- gloss_factor: FloatProperty(
- name="Amount",
- description="The clarity of the refraction. "
- "(values < 1.0 give diffuse, blurry refractions)",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- precision=3,
- )
-
- gloss_samples: IntProperty(
- name="Samples",
- description="frequency of the noise sample used for blurry refractions",
- min=0,
- max=1024,
- default=18,
- )
-
- gloss_threshold: FloatProperty(
- name="Threshold",
- description="Threshold for adaptive sampling (if a sample "
- "contributes less than this amount [as a percentage], "
- "sampling is stopped)",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=0.005,
- precision=3,
- )
-
- ior: FloatProperty(
- name="IOR",
- description="Sets angular index of refraction for raytraced refraction",
- min=-0.0,
- max=10.0,
- soft_min=0.25,
- soft_max=4.0,
- default=1.3,
- )
-
-
-class MaterialRaytraceMirror(PropertyGroup):
- """Declare reflection panel properties controllable in UI and translated to POV."""
-
- bl_description = ("Raytraced reflection settings for the Material",)
-
- use: BoolProperty(name="Mirror", description="Enable raytraced reflections", default=False)
-
- depth: IntProperty(
- name="Depth",
- description="Maximum allowed number of light inter-reflections",
- min=0,
- max=32767,
- default=2,
- )
-
- distance: FloatProperty(
- name="Max Dist",
- description="Maximum distance of reflected rays "
- "(reflections further than this range "
- "fade to sky color or material color)",
- min=0.0,
- max=100000.0,
- soft_min=0.0,
- soft_max=10000.0,
- default=0.0,
- precision=3,
- )
-
- fade_to: EnumProperty(
- items=[
- ("FADE_TO_SKY", "Fade to sky", ""),
- ("FADE_TO_MATERIAL", "Fade to material color", ""),
- ],
- name="Fade-out Color",
- description="The color that rays with no intersection within the "
- "Max Distance take (material color can be best for "
- "indoor scenes, sky color for outdoor)",
- default="FADE_TO_SKY",
- )
-
- fresnel: FloatProperty(
- name="Fresnel",
- description="Power of Fresnel for mirror reflection",
- min=0.0,
- max=5.0,
- soft_min=0.0,
- soft_max=5.0,
- default=0.0,
- precision=3,
- )
-
- fresnel_factor: FloatProperty(
- name="Blend",
- description="Blending factor for Fresnel",
- min=0.0,
- max=5.0,
- soft_min=0.0,
- soft_max=5.0,
- default=1.250,
- precision=3,
- )
-
- gloss_anisotropic: FloatProperty(
- name="Anisotropic",
- description="The shape of the reflection, from 0.0 (circular) "
- "to 1.0 (fully stretched along the tangent",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- precision=3,
- )
-
- gloss_factor: FloatProperty(
- name="Amount",
- description="The shininess of the reflection "
- "(values < 1.0 give diffuse, blurry reflections)",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- precision=3,
- )
-
- gloss_samples: IntProperty(
- name="Noise",
- description="Frequency of the noise pattern bumps averaged for blurry reflections",
- min=0,
- max=1024,
- default=18,
- )
-
- gloss_threshold: FloatProperty(
- name="Threshold",
- description="Threshold for adaptive sampling (if a sample "
- "contributes less than this amount [as a percentage], "
- "sampling is stopped)",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=0.005,
- precision=3,
- )
-
- mirror_color: FloatVectorProperty(
- name="Mirror color",
- description="Mirror color of the material",
- precision=4,
- step=0.01,
- default=(1.0, 1.0, 1.0),
- options={"ANIMATABLE"},
- subtype="COLOR",
- )
-
- reflect_factor: FloatProperty(
- name="Reflectivity",
- description="Amount of mirror reflection for raytrace",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- precision=3,
- )
-
-
-class MaterialSubsurfaceScattering(PropertyGroup):
- r"""Declare SSS/SSTL properties controllable in UI and translated to POV."""
-
- bl_description = ("Subsurface scattering settings for the material",)
-
- use: BoolProperty(
- name="Subsurface Scattering",
- description="Enable diffuse subsurface scatting " "effects in a material",
- default=False,
- )
-
- back: FloatProperty(
- name="Back",
- description="Back scattering weight",
- min=0.0,
- max=10.0,
- soft_min=0.0,
- soft_max=10.0,
- default=1.0,
- precision=3,
- )
-
- color: FloatVectorProperty(
- name="Scattering color",
- description="Scattering color",
- precision=4,
- step=0.01,
- default=(0.604, 0.604, 0.604),
- options={"ANIMATABLE"},
- subtype="COLOR",
- )
-
- color_factor: FloatProperty(
- name="Color",
- description="Blend factor for SSS colors",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- precision=3,
- )
-
- error_threshold: FloatProperty(
- name="Error",
- description="Error tolerance (low values are slower and higher quality)",
- default=0.050,
- precision=3,
- )
-
- front: FloatProperty(
- name="Front",
- description="Front scattering weight",
- min=0.0,
- max=2.0,
- soft_min=0.0,
- soft_max=2.0,
- default=1.0,
- precision=3,
- )
-
- ior: FloatProperty(
- name="IOR",
- description="Index of refraction (higher values are denser)",
- min=-0.0,
- max=10.0,
- soft_min=0.1,
- soft_max=2.0,
- default=1.3,
- )
-
- radius: FloatVectorProperty(
- name="RGB Radius",
- description="Mean red/green/blue scattering path length",
- precision=4,
- step=0.01,
- min=0.001,
- default=(1.0, 1.0, 1.0),
- options={"ANIMATABLE"},
- )
-
- scale: FloatProperty(
- name="Scale", description="Object scale factor", default=0.100, precision=3
- )
-
- texture_factor: FloatProperty(
- name="Texture",
- description="Texture scattering blend factor",
- min=0.0,
- max=1.0,
- soft_min=0.0,
- soft_max=1.0,
- default=0.0,
- precision=3,
- )
-
-
-class MaterialStrandSettings(PropertyGroup):
- """Declare strand properties controllable in UI and translated to POV."""
-
- bl_description = ("Strand settings for the material",)
-
- blend_distance: FloatProperty(
- name="Distance",
- description="Worldspace distance over which to blend in the surface normal",
- min=0.0,
- max=10.0,
- soft_min=0.0,
- soft_max=10.0,
- default=0.0,
- precision=3,
- )
-
- root_size: FloatProperty(
- name="Root",
- description="Start size of strands in pixels or Blender units",
- min=0.25,
- default=1.0,
- precision=5,
- )
-
- shape: FloatProperty(
- name="Shape",
- description="Positive values make strands rounder, negative ones make strands spiky",
- min=-0.9,
- max=0.9,
- default=0.0,
- precision=3,
- )
-
- size_min: FloatProperty(
- name="Minimum",
- description="Minimum size of strands in pixels",
- min=0.001,
- max=10.0,
- default=1.0,
- precision=3,
- )
-
- tip_size: FloatProperty(
- name="Tip",
- description="End size of strands in pixels or Blender units",
- min=0.0,
- default=1.0,
- precision=5,
- )
-
- use_blender_units: BoolProperty(
- name="Blender Units",
- description="Use Blender units for widths instead of pixels",
- default=False,
- )
-
- use_surface_diffuse: BoolProperty(
- name="Surface diffuse",
- description="Make diffuse shading more similar to shading the surface",
- default=False,
- )
-
- use_tangent_shading: BoolProperty(
- name="Tangent Shading",
- description="Use direction of strands as normal for tangent-shading",
- default=True,
- )
-
- uv_layer: StringProperty(
- name="UV Layer",
- # icon="GROUP_UVS",
- description="Name of UV map to override",
- default="",
- )
-
- width_fade: FloatProperty(
- name="Width Fade",
- description="Transparency along the width of the strand",
- min=0.0,
- max=2.0,
- default=0.0,
- precision=3,
- )
-
- # halo
-
- # Halo settings for the material
- # Type: MaterialHalo, (readonly, never None)
-
- # ambient
-
- # Amount of global ambient color the material receives
- # Type: float in [0, 1], default 0.0
-
- # darkness
-
- # Minnaert darkness
- # Type: float in [0, 2], default 0.0
-
- # diffuse_color
-
- # Diffuse color of the material
- # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
-
- # diffuse_fresnel
-
- # Power of Fresnel
- # Type: float in [0, 5], default 0.0
-
- # diffuse_fresnel_factor
-
- # Blending factor of Fresnel
- # Type: float in [0, 5], default 0.0
-
- # diffuse_intensity
-
- # Amount of diffuse reflection
- # Type: float in [0, 1], default 0.0
-
- # diffuse_ramp
-
- # Color ramp used to affect diffuse shading
- # Type: ColorRamp, (readonly)
-
- # diffuse_ramp_blend
-
- # Blending method of the ramp and the diffuse color
- # Type: enum in [‘MIX’, ‘ADD’, ‘MULTIPLY’, ‘SUBTRACT’, ‘SCREEN’, ‘DIVIDE’, ‘DIFFERENCE’, ‘DARKEN’, ‘LIGHTEN’, ‘OVERLAY’, ‘DODGE’, ‘BURN’, ‘HUE’, ‘SATURATION’, ‘VALUE’, ‘COLOR’, ‘SOFT_LIGHT’, ‘LINEAR_LIGHT’], default ‘MIX’
-
- # diffuse_ramp_factor
-
- # Blending factor (also uses alpha in Colorband)
- # Type: float in [0, 1], default 0.0
-
- # diffuse_ramp_input
-
- # How the ramp maps on the surface
- # Type: enum in [‘SHADER’, ‘ENERGY’, ‘NORMAL’, ‘RESULT’], default ‘SHADER’
-
- # diffuse_shader
-
- # LAMBERT Lambert, Use a Lambertian shader.
- # OREN_NAYAR Oren-Nayar, Use an Oren-Nayar shader.
- # TOON Toon, Use a toon shader.
- # MINNAERT Minnaert, Use a Minnaert shader.
- # FRESNEL Fresnel, Use a Fresnel shader.
-
- # Type: enum in [‘LAMBERT’, ‘OREN_NAYAR’, ‘TOON’, ‘MINNAERT’, ‘FRESNEL’], default ‘LAMBERT’
-
- # diffuse_toon_size
-
- # Size of diffuse toon area
- # Type: float in [0, 3.14], default 0.0
-
- # diffuse_toon_smooth
-
- # Smoothness of diffuse toon area
- # Type: float in [0, 1], default 0.0
-
- # emit
-
- # Amount of light to emit
- # Type: float in [0, inf], default 0.0
-
- # game_settings
-
- # Game material settings
- # Type: MaterialGameSettings, (readonly, never None)
-
- # halo
-
- # Halo settings for the material
- # Type: MaterialHalo, (readonly, never None)
-
- # invert_z
-
- # Render material’s faces with an inverted Z buffer (scanline only)
- # Type: boolean, default False
-
- # light_group
-
- # Limit lighting to lamps in this Group
- # Type: Group
-
- # line_color
-
- # Line color used for Freestyle line rendering
- # Type: float array of 4 items in [0, inf], default (0.0, 0.0, 0.0, 0.0)
-
- # line_priority
-
- # The line color of a higher priority is used at material boundaries
- # Type: int in [0, 32767], default 0
-
- # mirror_color
-
- # Mirror color of the material
- # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
-
- # node_tree
-
- # Node tree for node based materials
- # Type: NodeTree, (readonly)
-
- # offset_z
-
- # Give faces an artificial offset in the Z buffer for Z transparency
- # Type: float in [-inf, inf], default 0.0
-
- # paint_active_slot
-
- # Index of active texture paint slot
- # Type: int in [0, 32767], default 0
-
- # paint_clone_slot
-
- # Index of clone texture paint slot
- # Type: int in [0, 32767], default 0
-
- # pass_index
-
- # Index number for the “Material Index” render pass
- # Type: int in [0, 32767], default 0
-
- # physics
-
- # Game physics settings
- # Type: MaterialPhysics, (readonly, never None)
-
- # preview_render_type
-
- # Type of preview render
-
- # FLAT Flat, Flat XY plane.
- # SPHERE Sphere, Sphere.
- # CUBE Cube, Cube.
- # MONKEY Monkey, Monkey.
- # HAIR Hair, Hair strands.
- # SPHERE_A World Sphere, Large sphere with sky.
-
- # Type: enum in [‘FLAT’, ‘SPHERE’, ‘CUBE’, ‘MONKEY’, ‘HAIR’, ‘SPHERE_A’], default ‘FLAT’
-
- # raytrace_mirror
-
- # Raytraced reflection settings for the material
- # Type: MaterialRaytraceMirror, (readonly, never None)
-
- # raytrace_transparency
-
- # Raytraced transparency settings for the material
- # Type: MaterialRaytraceTransparency, (readonly, never None)
-
- # roughness
-
- # Oren-Nayar Roughness
- # Type: float in [0, 3.14], default 0.0
-
- # shadow_buffer_bias
-
- # Factor to multiply shadow buffer bias with (0 is ignore)
- # Type: float in [0, 10], default 0.0
-
- # shadow_cast_alpha
-
- # Shadow casting alpha, in use for Irregular and Deep shadow buffer
- # Type: float in [0.001, 1], default 0.0
-
- # shadow_only_type
-
- # How to draw shadows
-
- # SHADOW_ONLY_OLD Shadow and Distance, Old shadow only method.
- # SHADOW_ONLY Shadow Only, Improved shadow only method.
- # SHADOW_ONLY_SHADED Shadow and Shading, Improved shadow only method which also renders lightless areas as shadows.
-
- # Type: enum in [‘SHADOW_ONLY_OLD’, ‘SHADOW_ONLY’, ‘SHADOW_ONLY_SHADED’], default ‘SHADOW_ONLY_OLD’
-
- # shadow_ray_bias
-
- # Shadow raytracing bias to prevent terminator problems on shadow boundary
- # Type: float in [0, 0.25], default 0.0
-
- # specular_color
-
- # Specular color of the material
- # Type: float array of 3 items in [0, inf], default (0.0, 0.0, 0.0)
-
- # specular_hardness
-
- # How hard (sharp) the specular reflection is
- # Type: int in [1, 511], default 0
-
- # specular_intensity
-
- # How intense (bright) the specular reflection is
- # Type: float in [0, 1], default 0.0
-
- # specular_ior
-
- # Specular index of refraction
- # Type: float in [1, 10], default 0.0
-
- # specular_ramp
-
- # Color ramp used to affect specular shading
- # Type: ColorRamp, (readonly)
-
- # specular_ramp_blend
-
- # Blending method of the ramp and the specular color
- # Type: enum in [‘MIX’, ‘ADD’, ‘MULTIPLY’, ‘SUBTRACT’, ‘SCREEN’, ‘DIVIDE’, ‘DIFFERENCE’, ‘DARKEN’, ‘LIGHTEN’, ‘OVERLAY’, ‘DODGE’, ‘BURN’, ‘HUE’, ‘SATURATION’, ‘VALUE’, ‘COLOR’, ‘SOFT_LIGHT’, ‘LINEAR_LIGHT’], default ‘MIX’
-
- # specular_ramp_factor
-
- # Blending factor (also uses alpha in Colorband)
- # Type: float in [0, 1], default 0.0
-
- # specular_ramp_input
-
- # How the ramp maps on the surface
- # Type: enum in [‘SHADER’, ‘ENERGY’, ‘NORMAL’, ‘RESULT’], default ‘SHADER’
- # specular_shader
-
- # COOKTORR CookTorr, Use a Cook-Torrance shader.
- # PHONG Phong, Use a Phong shader.
- # BLINN Blinn, Use a Blinn shader.
- # TOON Toon, Use a toon shader.
- # WARDISO WardIso, Use a Ward anisotropic shader.
-
- # Type: enum in [‘COOKTORR’, ‘PHONG’, ‘BLINN’, ‘TOON’, ‘WARDISO’], default ‘COOKTORR’
-
- # specular_slope
-
- # The standard deviation of surface slope
- # Type: float in [0, 0.4], default 0.0
-
- # specular_toon_size
-
- # Size of specular toon area
- # Type: float in [0, 1.53], default 0.0
-
- # specular_toon_smooth
-
- # Smoothness of specular toon area
- # Type: float in [0, 1], default 0.0
-
- # strand
-
- # Strand settings for the material
- # Type: MaterialStrand, (readonly, never None)
-
- # subsurface_scattering
-
- # Subsurface scattering settings for the material
- # Type: MaterialSubsurfaceScattering, (readonly, never None)
-
- # texture_paint_images
-
- # Texture images used for texture painting
- # Type: bpy_prop_collection of Image, (readonly)
-
- # texture_paint_slots
-
- # Texture slots defining the mapping and influence of textures
- # Type: bpy_prop_collection of TexPaintSlot, (readonly)
-
- # texture_slots
-
- # Texture slots defining the mapping and influence of textures
- # Type: MaterialTextureSlots bpy_prop_collection of MaterialTextureSlot, (readonly)
-
- # translucency
-
- # Amount of diffuse shading on the back side
- # Type: float in [0, 1], default 0.0
-
- # transparency_method
-
- # Method to use for rendering transparency
-
- # MASK Mask, Mask the background.
- # Z_TRANSPARENCY Z Transparency, Use alpha buffer for transparent faces.
- # RAYTRACE Raytrace, Use raytracing for transparent refraction rendering.
-
- # Type: enum in [‘MASK’, ‘Z_TRANSPARENCY’, ‘RAYTRACE’], default ‘MASK’
-
- # type
-
- # Material type defining how the object is rendered
-
- # SURFACE Surface, Render object as a surface.
- # WIRE Wire, Render the edges of faces as wires (not supported in raytracing).
- # VOLUME Volume, Render object as a volume.
- # HALO Halo, Render object as halo particles.
-
- # Type: enum in [‘SURFACE’, ‘WIRE’, ‘VOLUME’, ‘HALO’], default ‘SURFACE’
-
- # use_cast_approximate
-
- # Allow this material to cast shadows when using approximate ambient occlusion
- # Type: boolean, default False
-
- # use_cast_buffer_shadows
-
- # Allow this material to cast shadows from shadow buffer lamps
- # Type: boolean, default False
-
- # use_cast_shadows
-
- # Allow this material to cast shadows
- # Type: boolean, default False
-
- # use_cast_shadows_only
-
- # Make objects with this material appear invisible (not rendered), only casting shadows
- # Type: boolean, default False
-
- # use_cubic
-
- # Use cubic interpolation for diffuse values, for smoother transitions
- # Type: boolean, default False
-
- # use_diffuse_ramp
-
- # Toggle diffuse ramp operations
- # Type: boolean, default False
-
- # use_face_texture
-
- # Replace the object’s base color with color from UV map image textures
- # Type: boolean, default False
-
- # use_face_texture_alpha
-
- # Replace the object’s base alpha value with alpha from UV map image textures
- # Type: boolean, default False
-
- # use_full_oversampling
-
- # Force this material to render full shading/textures for all anti-aliasing samples
- # Type: boolean, default False
-
- # use_light_group_exclusive
-
- # Material uses the light group exclusively - these lamps are excluded from other scene lighting
- # Type: boolean, default False
-
- # use_light_group_local
-
- # When linked in, material uses local light group with the same name
- # Type: boolean, default False
-
- # use_mist
-
- # Use mist with this material (in world settings)
- # Type: boolean, default False
-
- # use_nodes
-
- # Use shader nodes to render the material
- # Type: boolean, default False
-
- # use_object_color
-
- # Modulate the result with a per-object color
- # Type: boolean, default False
-
- # use_only_shadow
-
- # Render shadows as the material’s alpha value, making the material transparent except for shadowed areas
- # Type: boolean, default False
-
- # use_ray_shadow_bias
-
- # Prevent raytraced shadow errors on surfaces with smooth shaded normals (terminator problem)
- # Type: boolean, default False
-
- # use_raytrace
-
- # Include this material and geometry that uses it in raytracing calculations
- # Type: boolean, default False
-
- # use_shadeless
-
- # Make this material insensitive to light or shadow
- # Type: boolean, default False
-
- # use_shadows
-
- # Allow this material to receive shadows
- # Type: boolean, default False
-
- # use_sky
-
- # Render this material with zero alpha, with sky background in place (scanline only)
- # Type: boolean, default False
-
- # use_specular_ramp
-
- # Toggle specular ramp operations
- # Type: boolean, default False
-
- # use_tangent_shading
-
- # Use the material’s tangent vector instead of the normal for shading - for anisotropic shading effects
- # Type: boolean, default False
-
- # use_textures
-
- # Enable/Disable each texture
- # Type: boolean array of 18 items, default (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
-
- # use_transparency
-
- # Render material as transparent
- # Type: boolean, default False
-
- # use_transparent_shadows
-
- # Allow this object to receive transparent shadows cast through other objects
- # Type: boolean, default False
-
- # use_uv_project
-
- # Use to ensure UV interpolation is correct for camera projections (use with UV project modifier)
- # Type: boolean, default False
-
- # use_vertex_color_light
-
- # Add vertex colors as additional lighting
- # Type: boolean, default False
-
- # use_vertex_color_paint
-
- # Replace object base color with vertex colors (multiply with ‘texture face’ face assigned textures)
- # Type: boolean, default False
-
- # volume
-
- # Volume settings for the material
- # Type: MaterialVolume, (readonly, never None)
- """
- (mat.type in {'SURFACE', 'WIRE', 'VOLUME'})
- "use_transparency")
-
-
-
- mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY'
-
-
-
-
- col.prop(mat, "use_raytrace")
- col.prop(mat, "use_full_oversampling")
-
- sub.prop(mat, "use_sky")
-
-
- col.prop(mat, "use_cast_shadows", text="Cast")
- col.prop(mat, "use_cast_shadows_only", text="Cast Only")
- col.prop(mat, "use_cast_buffer_shadows")
-
- sub.active = mat.use_cast_buffer_shadows
- sub.prop(mat, "shadow_cast_alpha", text="Casting Alpha")
- col.prop(mat, "use_cast_approximate")
-
-
-
- col.prop(mat, "diffuse_color", text="")
-
- sub.active = (not mat.use_shadeless)
-
- sub.prop(mat, "diffuse_intensity", text="Intensity")
-
-
- col.prop(mat, "diffuse_shader", text="")
- col.prop(mat, "use_diffuse_ramp", text="Ramp")
-
-
- if mat.diffuse_shader == 'OREN_NAYAR':
- col.prop(mat, "roughness")
- elif mat.diffuse_shader == 'MINNAERT':
- col.prop(mat, "darkness")
- elif mat.diffuse_shader == 'TOON':
-
- row.prop(mat, "diffuse_toon_size", text="Size")
- row.prop(mat, "diffuse_toon_smooth", text="Smooth")
- elif mat.diffuse_shader == 'FRESNEL':
-
- row.prop(mat, "diffuse_fresnel", text="Fresnel")
- row.prop(mat, "diffuse_fresnel_factor", text="Factor")
-
- if mat.use_diffuse_ramp:
-
- col.template_color_ramp(mat, "diffuse_ramp", expand=True)
-
-
-
- row.prop(mat, "diffuse_ramp_input", text="Input")
- row.prop(mat, "diffuse_ramp_blend", text="Blend")
-
- col.prop(mat, "diffuse_ramp_factor", text="Factor")
-
-
-
-
- col.prop(mat, "specular_color", text="")
- col.prop(mat, "specular_intensity", text="Intensity")
-
- col.prop(mat, "specular_shader", text="")
- col.prop(mat, "use_specular_ramp", text="Ramp")
-
- if mat.specular_shader in {'COOKTORR', 'PHONG'}:
- col.prop(mat, "specular_hardness", text="Hardness")
- elif mat.specular_shader == 'BLINN':
-
- row.prop(mat, "specular_hardness", text="Hardness")
- row.prop(mat, "specular_ior", text="IOR")
- elif mat.specular_shader == 'WARDISO':
- col.prop(mat, "specular_slope", text="Slope")
- elif mat.specular_shader == 'TOON':
-
- row.prop(mat, "specular_toon_size", text="Size")
- row.prop(mat, "specular_toon_smooth", text="Smooth")
-
- if mat.use_specular_ramp:
- layout.separator()
- layout.template_color_ramp(mat, "specular_ramp", expand=True)
- layout.separator()
-
- row = layout.row()
- row.prop(mat, "specular_ramp_input", text="Input")
- row.prop(mat, "specular_ramp_blend", text="Blend")
-
- layout.prop(mat, "specular_ramp_factor", text="Factor")
-
-
- class MATERIAL_PT_shading(MaterialButtonsPanel, Panel):
- bl_label = "Shading"
- COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
-
- mat = active_node_mat(context.material)
-
- if mat.type in {'SURFACE', 'WIRE'}:
- split = layout.split()
-
- col = split.column()
- sub = col.column()
- sub.active = not mat.use_shadeless
- sub.prop(mat, "emit")
- sub.prop(mat, "ambient")
- sub = col.column()
- sub.prop(mat, "translucency")
-
- col = split.column()
- col.prop(mat, "use_shadeless")
- sub = col.column()
- sub.active = not mat.use_shadeless
- sub.prop(mat, "use_tangent_shading")
- sub.prop(mat, "use_cubic")
-
-
- class MATERIAL_PT_transp(MaterialButtonsPanel, Panel):
- bl_label = "Transparency"
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- mat = context.material
-
- if simple_material(mat):
- self.layout.prop(mat, "use_transparency", text="")
-
- def draw(self, context):
- layout = self.layout
-
- base_mat = context.material
- mat = active_node_mat(context.material)
- rayt = mat.raytrace_transparency
-
- if simple_material(base_mat):
- row = layout.row()
- row.active = mat.use_transparency
- row.prop(mat, "transparency_method", expand=True)
-
- split = layout.split()
- split.active = base_mat.use_transparency
-
- col = split.column()
- col.prop(mat, "alpha")
- row = col.row()
- row.active = (base_mat.transparency_method != 'MASK') and (not mat.use_shadeless)
- row.prop(mat, "specular_alpha", text="Specular")
-
- col = split.column()
- col.active = (not mat.use_shadeless)
- col.prop(rayt, "fresnel")
- sub = col.column()
- sub.active = (rayt.fresnel > 0.0)
- sub.prop(rayt, "fresnel_factor", text="Blend")
-
- if base_mat.transparency_method == 'RAYTRACE':
- layout.separator()
- split = layout.split()
- split.active = base_mat.use_transparency
-
- col = split.column()
- col.prop(rayt, "ior")
- col.prop(rayt, "filter")
- col.prop(rayt, "falloff")
- col.prop(rayt, "depth_max")
- col.prop(rayt, "depth")
-
- col = split.column()
- col.label(text="Gloss:")
- col.prop(rayt, "gloss_factor", text="Amount")
- sub = col.column()
- sub.active = rayt.gloss_factor < 1.0
- sub.prop(rayt, "gloss_threshold", text="Threshold")
- sub.prop(rayt, "gloss_samples", text="Samples")
-
-
- class MATERIAL_PT_mirror(MaterialButtonsPanel, Panel):
- bl_label = "Mirror"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- raym = active_node_mat(context.material).raytrace_mirror
-
- self.layout.prop(raym, "use", text="")
-
- def draw(self, context):
- layout = self.layout
-
- mat = active_node_mat(context.material)
- raym = mat.raytrace_mirror
-
- layout.active = raym.use
-
- split = layout.split()
-
- col = split.column()
- col.prop(raym, "reflect_factor")
- col.prop(mat, "mirror_color", text="")
-
- col = split.column()
- col.prop(raym, "fresnel")
- sub = col.column()
- sub.active = (raym.fresnel > 0.0)
- sub.prop(raym, "fresnel_factor", text="Blend")
-
- split = layout.split()
-
- col = split.column()
- col.separator()
- col.prop(raym, "depth")
- col.prop(raym, "distance", text="Max Dist")
- col.separator()
- sub = col.split(percentage=0.4)
- sub.active = (raym.distance > 0.0)
- sub.label(text="Fade To:")
- sub.prop(raym, "fade_to", text="")
-
- col = split.column()
- col.label(text="Gloss:")
- col.prop(raym, "gloss_factor", text="Amount")
- sub = col.column()
- sub.active = (raym.gloss_factor < 1.0)
- sub.prop(raym, "gloss_threshold", text="Threshold")
- sub.prop(raym, "gloss_samples", text="Samples")
- sub.prop(raym, "gloss_anisotropic", text="Anisotropic")
-
-
- class MATERIAL_PT_sss(MaterialButtonsPanel, Panel):
- bl_label = "Subsurface Scattering"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- mat = active_node_mat(context.material)
- sss = mat.subsurface_scattering
-
- self.layout.active = (not mat.use_shadeless)
- self.layout.prop(sss, "use", text="")
-
- def draw(self, context):
- layout = self.layout
-
- mat = active_node_mat(context.material)
- sss = mat.subsurface_scattering
-
- layout.active = (sss.use) and (not mat.use_shadeless)
-
- row = layout.row().split()
- sub = row.row(align=True).split(align=True, percentage=0.75)
- sub.menu("MATERIAL_MT_sss_presets", text=bpy.types.MATERIAL_MT_sss_presets.bl_label)
- sub.operator("material.sss_preset_add", text="", icon='ZOOMIN')
- sub.operator("material.sss_preset_add", text="", icon='ZOOMOUT').remove_active = True
-
- split = layout.split()
-
- col = split.column()
- col.prop(sss, "ior")
- col.prop(sss, "scale")
- col.prop(sss, "color", text="")
- col.prop(sss, "radius", text="RGB Radius", expand=True)
-
- col = split.column()
- sub = col.column(align=True)
- sub.label(text="Blend:")
- sub.prop(sss, "color_factor", text="Color")
- sub.prop(sss, "texture_factor", text="Texture")
- sub.label(text="Scattering Weight:")
- sub.prop(sss, "front")
- sub.prop(sss, "back")
- col.separator()
- col.prop(sss, "error_threshold", text="Error")
-
-
- class MATERIAL_PT_halo(MaterialButtonsPanel, Panel):
- bl_label = "Halo"
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES)
-
- def draw(self, context):
- layout = self.layout
-
- mat = context.material # don't use node material
- halo = mat.halo
-
- def number_but(layout, toggle, number, name, color):
- row = layout.row(align=True)
- row.prop(halo, toggle, text="")
- sub = row.column(align=True)
- sub.active = getattr(halo, toggle)
- sub.prop(halo, number, text=name, translate=False)
- if not color == "":
- sub.prop(mat, color, text="")
-
- split = layout.split()
-
- col = split.column()
- col.prop(mat, "alpha")
- col.prop(mat, "diffuse_color", text="")
- col.prop(halo, "seed")
-
- col = split.column()
- col.prop(halo, "size")
- col.prop(halo, "hardness")
- col.prop(halo, "add")
-
- layout.label(text="Options:")
-
- split = layout.split()
- col = split.column()
- col.prop(halo, "use_texture")
- col.prop(halo, "use_vertex_normal")
- col.prop(halo, "use_extreme_alpha")
- col.prop(halo, "use_shaded")
- col.prop(halo, "use_soft")
-
- col = split.column()
- number_but(col, "use_ring", "ring_count", iface_("Rings"), "mirror_color")
- number_but(col, "use_lines", "line_count", iface_("Lines"), "specular_color")
- number_but(col, "use_star", "star_tip_count", iface_("Star Tips"), "")
-
-
- class MATERIAL_PT_flare(MaterialButtonsPanel, Panel):
- bl_label = "Flare"
- COMPAT_ENGINES = {'BLENDER_RENDER'}
-
- @classmethod
- def poll(cls, context):
- mat = context.material
- engine = context.scene.render.engine
- return mat and (mat.type == 'HALO') and (engine in cls.COMPAT_ENGINES)
-
- def draw_header(self, context):
- halo = context.material.halo
-
- self.layout.prop(halo, "use_flare_mode", text="")
-
- def draw(self, context):
- layout = self.layout
-
- mat = context.material # don't use node material
- halo = mat.halo
-
- layout.active = halo.use_flare_mode
-
- split = layout.split()
-
- col = split.column()
- col.prop(halo, "flare_size", text="Size")
- col.prop(halo, "flare_boost", text="Boost")
- col.prop(halo, "flare_seed", text="Seed")
-
- col = split.column()
- col.prop(halo, "flare_subflare_count", text="Subflares")
- col.prop(halo, "flare_subflare_size", text="Subsize")
-
- """
-
# ------------------------------ End Old Blender Internal Props ------------------------------ #
classes = (
RenderPovSettingsMaterial,
- MaterialRaytraceTransparency,
- MaterialRaytraceMirror,
- MaterialSubsurfaceScattering,
- MaterialStrandSettings,
)
@@ -2250,21 +972,9 @@ def register():
register_class(cls)
bpy.types.Material.pov = PointerProperty(type=RenderPovSettingsMaterial)
- bpy.types.Material.pov_raytrace_transparency = PointerProperty(
- type=MaterialRaytraceTransparency
- )
- bpy.types.Material.pov_subsurface_scattering = PointerProperty(
- type=MaterialSubsurfaceScattering
- )
- bpy.types.Material.strand = PointerProperty(type=MaterialStrandSettings)
- bpy.types.Material.pov_raytrace_mirror = PointerProperty(type=MaterialRaytraceMirror)
def unregister():
- del bpy.types.Material.pov_subsurface_scattering
- del bpy.types.Material.strand
- del bpy.types.Material.pov_raytrace_mirror
- del bpy.types.Material.pov_raytrace_transparency
del bpy.types.Material.pov
for cls in reversed(classes):
diff --git a/render_povray/shading_ray_properties.py b/render_povray/shading_ray_properties.py
new file mode 100644
index 00000000..76e26f0e
--- /dev/null
+++ b/render_povray/shading_ray_properties.py
@@ -0,0 +1,374 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+"""Declare shading properties exported to POV textures."""
+import bpy
+from bpy.utils import register_class, unregister_class
+from bpy.types import PropertyGroup
+from bpy.props import (
+ FloatVectorProperty,
+ StringProperty,
+ BoolProperty,
+ IntProperty,
+ FloatProperty,
+ EnumProperty,
+ PointerProperty,
+)
+
+
+class MaterialRaytraceTransparency(PropertyGroup):
+ """Declare transparency panel properties controllable in UI and translated to POV."""
+
+ depth: IntProperty(
+ name="Depth",
+ description="Maximum allowed number of light inter-refractions",
+ min=0,
+ max=32767,
+ default=2,
+ )
+
+ depth_max: FloatProperty(
+ name="Depth",
+ description="Maximum depth for light to travel through the "
+ "transparent material before becoming fully filtered (0.0 is disabled)",
+ min=0,
+ max=100,
+ default=0.0,
+ )
+
+ falloff: FloatProperty(
+ name="Falloff",
+ description="Falloff power for transmissivity filter effect (1.0 is linear)",
+ min=0.1,
+ max=10.0,
+ default=1.0,
+ precision=3,
+ )
+
+ filter: FloatProperty(
+ name="Filter",
+ description="Amount to blend in the material’s diffuse color in raytraced "
+ "transparency (simulating absorption)",
+ min=0.0,
+ max=1.0,
+ default=0.0,
+ precision=3,
+ )
+
+ fresnel: FloatProperty(
+ name="Fresnel",
+ description="Power of Fresnel for transparency (Ray or ZTransp)",
+ min=0.0,
+ max=5.0,
+ soft_min=0.0,
+ soft_max=5.0,
+ default=0.0,
+ precision=3,
+ )
+
+ fresnel_factor: FloatProperty(
+ name="Blend",
+ description="Blending factor for Fresnel",
+ min=0.0,
+ max=5.0,
+ soft_min=0.0,
+ soft_max=5.0,
+ default=1.250,
+ precision=3,
+ )
+
+ gloss_factor: FloatProperty(
+ name="Amount",
+ description="The clarity of the refraction. "
+ "(values < 1.0 give diffuse, blurry refractions)",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=1.0,
+ precision=3,
+ )
+
+ gloss_samples: IntProperty(
+ name="Samples",
+ description="frequency of the noise sample used for blurry refractions",
+ min=0,
+ max=1024,
+ default=18,
+ )
+
+ gloss_threshold: FloatProperty(
+ name="Threshold",
+ description="Threshold for adaptive sampling (if a sample "
+ "contributes less than this amount [as a percentage], "
+ "sampling is stopped)",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=0.005,
+ precision=3,
+ )
+
+ ior: FloatProperty(
+ name="IOR",
+ description="Sets angular index of refraction for raytraced refraction",
+ min=-0.0,
+ max=10.0,
+ soft_min=0.25,
+ soft_max=4.0,
+ default=1.3,
+ )
+
+
+class MaterialRaytraceMirror(PropertyGroup):
+ """Declare reflection panel properties controllable in UI and translated to POV."""
+
+ bl_description = ("Raytraced reflection settings for the Material",)
+
+ use: BoolProperty(name="Mirror", description="Enable raytraced reflections", default=False)
+
+ depth: IntProperty(
+ name="Depth",
+ description="Maximum allowed number of light inter-reflections",
+ min=0,
+ max=32767,
+ default=2,
+ )
+
+ distance: FloatProperty(
+ name="Max Dist",
+ description="Maximum distance of reflected rays "
+ "(reflections further than this range "
+ "fade to sky color or material color)",
+ min=0.0,
+ max=100000.0,
+ soft_min=0.0,
+ soft_max=10000.0,
+ default=0.0,
+ precision=3,
+ )
+
+ fade_to: EnumProperty(
+ items=[
+ ("FADE_TO_SKY", "Fade to sky", ""),
+ ("FADE_TO_MATERIAL", "Fade to material color", ""),
+ ],
+ name="Fade-out Color",
+ description="The color that rays with no intersection within the "
+ "Max Distance take (material color can be best for "
+ "indoor scenes, sky color for outdoor)",
+ default="FADE_TO_SKY",
+ )
+
+ fresnel: FloatProperty(
+ name="Fresnel",
+ description="Power of Fresnel for mirror reflection",
+ min=0.0,
+ max=5.0,
+ soft_min=0.0,
+ soft_max=5.0,
+ default=0.0,
+ precision=3,
+ )
+
+ fresnel_factor: FloatProperty(
+ name="Blend",
+ description="Blending factor for Fresnel",
+ min=0.0,
+ max=5.0,
+ soft_min=0.0,
+ soft_max=5.0,
+ default=1.250,
+ precision=3,
+ )
+
+ gloss_anisotropic: FloatProperty(
+ name="Anisotropic",
+ description="The shape of the reflection, from 0.0 (circular) "
+ "to 1.0 (fully stretched along the tangent",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=1.0,
+ precision=3,
+ )
+
+ gloss_factor: FloatProperty(
+ name="Amount",
+ description="The shininess of the reflection "
+ "(values < 1.0 give diffuse, blurry reflections)",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=1.0,
+ precision=3,
+ )
+
+ gloss_samples: IntProperty(
+ name="Noise",
+ description="Frequency of the noise pattern bumps averaged for blurry reflections",
+ min=0,
+ max=1024,
+ default=18,
+ )
+
+ gloss_threshold: FloatProperty(
+ name="Threshold",
+ description="Threshold for adaptive sampling (if a sample "
+ "contributes less than this amount [as a percentage], "
+ "sampling is stopped)",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=0.005,
+ precision=3,
+ )
+
+ mirror_color: FloatVectorProperty(
+ name="Mirror color",
+ description="Mirror color of the material",
+ precision=4,
+ step=0.01,
+ default=(1.0, 1.0, 1.0),
+ options={"ANIMATABLE"},
+ subtype="COLOR",
+ )
+
+ reflect_factor: FloatProperty(
+ name="Reflectivity",
+ description="Amount of mirror reflection for raytrace",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=1.0,
+ precision=3,
+ )
+
+
+class MaterialSubsurfaceScattering(PropertyGroup):
+ """Declare SSS/SSTL properties controllable in UI and translated to POV."""
+
+ bl_description = ("Subsurface scattering settings for the material",)
+
+ use: BoolProperty(
+ name="Subsurface Scattering",
+ description="Enable diffuse subsurface scatting " "effects in a material",
+ default=False,
+ )
+
+ back: FloatProperty(
+ name="Back",
+ description="Back scattering weight",
+ min=0.0,
+ max=10.0,
+ soft_min=0.0,
+ soft_max=10.0,
+ default=1.0,
+ precision=3,
+ )
+
+ color: FloatVectorProperty(
+ name="Scattering color",
+ description="Scattering color",
+ precision=4,
+ step=0.01,
+ default=(0.604, 0.604, 0.604),
+ options={"ANIMATABLE"},
+ subtype="COLOR",
+ )
+
+ color_factor: FloatProperty(
+ name="Color",
+ description="Blend factor for SSS colors",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=1.0,
+ precision=3,
+ )
+
+ error_threshold: FloatProperty(
+ name="Error",
+ description="Error tolerance (low values are slower and higher quality)",
+ default=0.050,
+ precision=3,
+ )
+
+ front: FloatProperty(
+ name="Front",
+ description="Front scattering weight",
+ min=0.0,
+ max=2.0,
+ soft_min=0.0,
+ soft_max=2.0,
+ default=1.0,
+ precision=3,
+ )
+
+ ior: FloatProperty(
+ name="IOR",
+ description="Index of refraction (higher values are denser)",
+ min=-0.0,
+ max=10.0,
+ soft_min=0.1,
+ soft_max=2.0,
+ default=1.3,
+ )
+
+ radius: FloatVectorProperty(
+ name="RGB Radius",
+ description="Mean red/green/blue scattering path length",
+ precision=4,
+ step=0.01,
+ min=0.001,
+ default=(1.0, 1.0, 1.0),
+ options={"ANIMATABLE"},
+ )
+
+ scale: FloatProperty(
+ name="Scale", description="Object scale factor", default=0.100, precision=3
+ )
+
+ texture_factor: FloatProperty(
+ name="Texture",
+ description="Texture scattering blend factor",
+ min=0.0,
+ max=1.0,
+ soft_min=0.0,
+ soft_max=1.0,
+ default=0.0,
+ precision=3,
+ )
+
+classes = (
+ MaterialRaytraceTransparency,
+ MaterialRaytraceMirror,
+ MaterialSubsurfaceScattering,
+)
+
+def register():
+ for cls in classes:
+ register_class(cls)
+
+ bpy.types.Material.pov_raytrace_transparency = PointerProperty(
+ type=MaterialRaytraceTransparency
+ )
+ bpy.types.Material.pov_subsurface_scattering = PointerProperty(
+ type=MaterialSubsurfaceScattering
+ )
+ bpy.types.Material.pov_raytrace_mirror = PointerProperty(type=MaterialRaytraceMirror)
+
+
+def unregister():
+ del bpy.types.Material.pov_subsurface_scattering
+ del bpy.types.Material.pov_raytrace_mirror
+ del bpy.types.Material.pov_raytrace_transparency
+
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/render_povray/texturing.py b/render_povray/texturing.py
index dadc5e5c..55e72565 100755
--- a/render_povray/texturing.py
+++ b/render_povray/texturing.py
@@ -6,26 +6,28 @@
import os
import bpy
+local_material_names = []
+material_finish = None
+
def write_texture_influence(
- using_uberpov,
+ file,
mater,
material_names_dictionary,
- local_material_names,
- path_image,
- exported_lights_count,
image_format,
img_map,
img_map_transforms,
tab_write,
comments,
- string_strip_hyphen,
- safety,
col,
preview_dir,
unpacked_images,
):
"""Translate Blender texture influences to various POV texture tricks and write to pov file."""
+
+ from .scenography import path_image, exported_lights_count
+ from .render import string_strip_hyphen, safety, using_uberpov
+
material_finish = material_names_dictionary[mater.name]
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"):
@@ -46,126 +48,129 @@ def write_texture_influence(
texture_alpha = ""
# procedural_flag=False
tmpidx = -1
- for t in mater.pov_texture_slots:
+ used_texture_slots = (tesl for tesl in mater.pov_texture_slots if tesl.use)
+ # and (bpy.data.textures[mater.pov_texture_slots[tmpidx-1].texture] is not None)
+ for t in used_texture_slots:
tmpidx += 1
# index = mater.pov.active_texture_index
slot = mater.pov_texture_slots[tmpidx] # [index]
povtex = slot.texture # slot.name
tex = bpy.data.textures[povtex]
-
- if t and (t.use and (tex is not None)):
- # 'NONE' ('NONE' type texture is different from no texture covered above)
- if tex.type == "NONE" and tex.pov.tex_pattern_type == "emulator":
- continue # move to next slot
-
- # Implicit else-if (as not skipped by previous "continue")
- if tex.type != "IMAGE" and tex.type != "NONE":
- # PROCEDURAL TEXTURE
- image_filename = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name))
- if image_filename:
- if t.use_map_color_diffuse:
- texture_dif = image_filename
- # colvalue = t.default_value # UNUSED
- t_dif = t
- if t_dif.texture.pov.tex_gamma_enable:
- img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
- if t.use_map_specular or t.use_map_raymir:
- texture_spec = image_filename
- # colvalue = t.default_value # UNUSED
- t_spec = t
- if t.use_map_normal:
- texture_norm = image_filename
- # colvalue = t.normal_factor/10 # UNUSED
- # textNormName=tex.image.name + ".normal"
- # was the above used? --MR
- t_nor = t
- if t.use_map_alpha:
- texture_alpha = image_filename
- # colvalue = t.alpha_factor * 10.0 # UNUSED
- # textDispName=tex.image.name + ".displ"
- # was the above used? --MR
- t_alpha = t
- # RASTER IMAGE
- elif tex.type == "IMAGE" and tex.image and tex.pov.tex_pattern_type == "emulator":
- # NOT A PROCEDURAL TEXTURE
- # PACKED
- if tex.image.packed_file:
- orig_image_filename = tex.image.filepath_raw
- unpackedfilename = os.path.join(
- preview_dir,
- ("unpacked_img_" + (string_strip_hyphen(bpy.path.clean_name(tex.name)))),
- )
- if not os.path.exists(unpackedfilename):
- # record which images that were newly copied and can be safely
- # cleaned up
- unpacked_images.append(unpackedfilename)
- tex.image.filepath_raw = unpackedfilename
- tex.image.save()
- image_filename = unpackedfilename.replace("\\", "/")
- # .replace("\\","/") to get only forward slashes as it's what POV prefers,
- # even on windows
- tex.image.filepath_raw = orig_image_filename
- # FILE
- else:
- image_filename = path_image(tex.image)
- # IMAGE SEQUENCE BEGINS
- if (
- image_filename
- 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:
- texdata = bpy.data.textures[t.texture]
- if t.use_map_color_diffuse:
- texture_dif = image_filename
- # colvalue = t.default_value # UNUSED
- t_dif = t
- print(texdata)
- if texdata.pov.tex_gamma_enable:
- img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
- if t.use_map_specular or t.use_map_raymir:
- texture_spec = image_filename
- # colvalue = t.default_value # UNUSED
- t_spec = t
- if t.use_map_normal:
- texture_norm = image_filename
- # colvalue = t.normal_factor/10 # UNUSED
- # textNormName=tex.image.name + ".normal"
- # was the above used? --MR
- t_nor = t
- if t.use_map_alpha:
- texture_alpha = image_filename
- # colvalue = t.alpha_factor * 10.0 # UNUSED
- # textDispName=tex.image.name + ".displ"
- # was the above used? --MR
- t_alpha = t
+ if tex is None:
+ continue # move to next slot
+ # 'NONE' ('NONE' type texture is different from no texture covered above)
+ if tex.type == "NONE" and tex.pov.tex_pattern_type == "emulator":
+ continue # move to next slot
+
+ # Implicit else-if (as not skipped by previous "continue")
+ if tex.type not in ("IMAGE","NONE"):
+ # PROCEDURAL TEXTURE
+ image_filename = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name))
+ if image_filename:
+ if t.use_map_color_diffuse:
+ texture_dif = image_filename
+ # colvalue = t.default_value # UNUSED
+ t_dif = t
+ if t_dif.texture.pov.tex_gamma_enable:
+ img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
+ if t.use_map_specular or t.use_map_raymir:
+ texture_spec = image_filename
+ # colvalue = t.default_value # UNUSED
+ t_spec = t
+ if t.use_map_normal:
+ texture_norm = image_filename
+ # colvalue = t.normal_factor/10 # UNUSED
+ # textNormName=tex.image.name + ".normal"
+ # was the above used? --MR
+ t_nor = t
+ if t.use_map_alpha:
+ texture_alpha = image_filename
+ # colvalue = t.alpha_factor * 10.0 # UNUSED
+ # textDispName=tex.image.name + ".displ"
+ # was the above used? --MR
+ t_alpha = t
+ # RASTER IMAGE
+ elif tex.type == "IMAGE" and tex.image and tex.pov.tex_pattern_type == "emulator":
+ # NOT A PROCEDURAL TEXTURE
+ # PACKED
+ if tex.image.packed_file:
+ orig_image_filename = tex.image.filepath_raw
+ unpackedfilename = os.path.join(
+ preview_dir,
+ ("unpacked_img_" + (string_strip_hyphen(bpy.path.clean_name(tex.name)))),
+ )
+ if not os.path.exists(unpackedfilename):
+ # record which images that were newly copied and can be safely
+ # cleaned up
+ unpacked_images.append(unpackedfilename)
+ tex.image.filepath_raw = unpackedfilename
+ tex.image.save()
+ image_filename = unpackedfilename.replace("\\", "/")
+ # .replace("\\","/") to get only forward slashes as it's what POV prefers,
+ # even on windows
+ tex.image.filepath_raw = orig_image_filename
+ # FILE
+ else:
+ image_filename = path_image(tex.image)
+ # IMAGE SEQUENCE BEGINS
+ if image_filename 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:
+ texdata = bpy.data.textures[t.texture]
+ if t.use_map_color_diffuse:
+ texture_dif = image_filename
+ # colvalue = t.default_value # UNUSED
+ t_dif = t
+ print(texdata)
+ if texdata.pov.tex_gamma_enable:
+ img_gamma = " gamma %.3g " % t_dif.texture.pov.tex_gamma_value
+ if t.use_map_specular or t.use_map_raymir:
+ texture_spec = image_filename
+ # colvalue = t.default_value # UNUSED
+ t_spec = t
+ if t.use_map_normal:
+ texture_norm = image_filename
+ # colvalue = t.normal_factor/10 # UNUSED
+ # textNormName=tex.image.name + ".normal"
+ # was the above used? --MR
+ t_nor = t
+ if t.use_map_alpha:
+ texture_alpha = image_filename
+ # colvalue = t.alpha_factor * 10.0 # UNUSED
+ # textDispName=tex.image.name + ".displ"
+ # was the above used? --MR
+ t_alpha = t
# -----------------------------------------------------------------------------
- tab_write("\n")
+ tab_write(file, "\n")
# THIS AREA NEEDS TO LEAVE THE TEXTURE OPEN UNTIL ALL MAPS ARE WRITTEN DOWN.
current_material_name = string_strip_hyphen(material_names_dictionary[mater.name])
+ global local_material_names
local_material_names.append(current_material_name)
- tab_write("\n#declare MAT_%s = \ntexture{\n" % current_material_name)
+ tab_write(file, "\n#declare MAT_%s = \ntexture{\n" % current_material_name)
# -----------------------------------------------------------------------------
- if mater.pov.replacement_text != "":
- tab_write("%s\n" % mater.pov.replacement_text)
+ if mater.pov.replacement_text:
+ tab_write(file, "%s\n" % mater.pov.replacement_text)
# -----------------------------------------------------------------------------
# XXX TODO: replace by new POV MINNAERT rather than aoi
if mater.pov.diffuse_shader == "MINNAERT":
- tab_write("\n")
- tab_write("aoi\n")
- tab_write("texture_map {\n")
- tab_write("[%.3g finish {diffuse %.3g}]\n" % (mater.darkness / 2.0, 2.0 - mater.darkness))
- tab_write("[%.3g\n" % (1.0 - (mater.darkness / 2.0)))
+ tab_write(file, "\n")
+ tab_write(file, "aoi\n")
+ tab_write(file, "texture_map {\n")
+ tab_write(
+ file, "[%.3g finish {diffuse %.3g}]\n" % (mater.pov.darkness / 2.0,
+ 2.0 - mater.pov.darkness)
+ )
+ tab_write(file, "[%.3g\n" % (1.0 - (mater.pov.darkness / 2.0)))
if mater.pov.diffuse_shader == "FRESNEL":
# For FRESNEL diffuse in POV, we'll layer slope patterned textures
@@ -174,55 +179,61 @@ def write_texture_influence(
c = 1
while c <= exported_lights_count:
- tab_write("slope { lampTarget%s }\n" % c)
- tab_write("texture_map {\n")
+ tab_write(file, "slope { lampTarget%s }\n" % c)
+ tab_write(file, "texture_map {\n")
# Diffuse Fresnel value and factor go up to five,
# other kind of values needed: used the number 5 below to remap
tab_write(
+ file,
"[%.3g finish {diffuse %.3g}]\n"
% (
- (5.0 - mater.diffuse_fresnel) / 5,
- (mater.diffuse_intensity * ((5.0 - mater.diffuse_fresnel_factor) / 5)),
- )
+ (5.0 - mater.pov.diffuse_fresnel) / 5,
+ (mater.pov.diffuse_intensity * ((5.0 - mater.pov.diffuse_fresnel_factor) / 5)),
+ ),
)
tab_write(
- "[%.3g\n" % ((mater.diffuse_fresnel_factor / 5) * (mater.diffuse_fresnel / 5.0))
+ file,
+ "[%.3g\n" % ((mater.pov.diffuse_fresnel_factor / 5) * (mater.pov.diffuse_fresnel / 5.0)),
)
c += 1
# if shader is a 'FRESNEL' or 'MINNAERT': slope pigment pattern or aoi
# and texture map above, the rest below as one of its entry
- if texture_spec != "" or texture_alpha != "":
- if texture_spec != "":
- # tab_write("\n")
- tab_write("pigment_pattern {\n")
+ if texture_spec or texture_alpha:
+ if texture_spec:
+ # tab_write(file, "\n")
+ tab_write(file, "pigment_pattern {\n")
mapping_spec = img_map_transforms(t_spec)
if texture_spec and texture_spec.startswith("PAT_"):
- tab_write("function{f%s(x,y,z).grey}\n" % texture_spec)
+ tab_write(file, "function{f%s(x,y,z).grey}\n" % texture_spec)
else:
tab_write(
+ file,
'uv_mapping image_map{%s "%s" %s}\n'
- % (image_format(texture_spec), texture_spec, img_map(t_spec))
+ % (image_format(texture_spec), texture_spec, img_map(t_spec)),
)
- tab_write("%s\n" % mapping_spec)
- tab_write("}\n")
- tab_write("texture_map {\n")
- tab_write("[0 \n")
+ tab_write(file, "%s\n" % mapping_spec)
+ tab_write(file, "}\n")
+ tab_write(file, "texture_map {\n")
+ tab_write(file, "[0 \n")
- if texture_dif == "":
- if texture_alpha != "":
- tab_write("\n")
+ if not texture_dif:
+ if texture_alpha:
+ tab_write(file, "\n")
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
- tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha))
+ tab_write(
+ file, "function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha)
+ )
else:
tab_write(
+ file,
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s'
% (
@@ -230,110 +241,118 @@ def write_texture_influence(
texture_alpha,
img_map(t_alpha),
mapping_alpha,
- )
+ ),
)
- tab_write("}\n")
- tab_write("pigment_map {\n")
- tab_write("[0 color rgbft<0,0,0,1,1>]\n")
+ tab_write(file, "}\n")
+ tab_write(file, "pigment_map {\n")
+ tab_write(file, "[0 color rgbft<0,0,0,1,1>]\n")
tab_write(
+ file,
"[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n"
- % (col[0], col[1], col[2], pov_filter, trans)
+ % (col[0], col[1], col[2], pov_filter, trans),
)
- tab_write("}\n")
- tab_write("}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
else:
tab_write(
+ file,
"pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n"
- % (col[0], col[1], col[2], pov_filter, trans)
+ % (col[0], col[1], col[2], pov_filter, trans),
)
else:
mapping_dif = img_map_transforms(t_dif)
- if texture_alpha != "":
+ if texture_alpha:
mapping_alpha = img_map_transforms(t_alpha)
- tab_write("pigment {\n")
- tab_write("pigment_pattern {\n")
+ tab_write(file, "pigment {\n")
+ tab_write(file, "pigment_pattern {\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
- tab_write("function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha))
+ tab_write(
+ file, "function{f%s(x,y,z).transmit}%s\n" % (texture_alpha, mapping_alpha)
+ )
else:
tab_write(
+ file,
'uv_mapping image_map{%s "%s" %s}%s}\n'
% (
image_format(texture_alpha),
texture_alpha,
img_map(t_alpha),
mapping_alpha,
- )
+ ),
)
- tab_write("pigment_map {\n")
- tab_write("[0 color rgbft<0,0,0,1,1>]\n")
+ tab_write(file, "pigment_map {\n")
+ tab_write(file, "[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)
+ # tab_write(file, "[1 pigment{%s}]\n" %texture_dif)
if texture_dif:
if not texture_dif.startswith("PAT_"):
tab_write(
+ file,
'[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.startswith("PAT_"):
- tab_write("[1 %s]\n" % texture_dif)
- tab_write("}\n")
- tab_write("}\n")
+ tab_write(file, "[1 %s]\n" % texture_dif)
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
- tab_write("}\n")
+ tab_write(file, "}\n")
else:
if texture_dif and texture_dif.startswith("PAT_"):
- tab_write("pigment{%s}\n" % texture_dif)
+ tab_write(file, "pigment{%s}\n" % texture_dif)
else:
tab_write(
+ file,
'pigment {uv_mapping image_map {%s "%s" %s}%s}\n'
% (
image_format(texture_dif),
texture_dif,
(img_gamma + img_map(t_dif)),
mapping_dif,
- )
+ ),
)
# scale 1 rotate y*0
# imageMap = ("{image_map {%s \"%s\" %s }\n" % \
# (image_format(textures),textures,img_map(t_dif)))
- # tab_write("uv_mapping pigment %s} %s finish {%s}\n" % \
+ # tab_write(file, "uv_mapping pigment %s} %s finish {%s}\n" % \
# (imageMap,mapping,safety(material_finish)))
- # tab_write("pigment {uv_mapping image_map {%s \"%s\" %s}%s} " \
+ # tab_write(file, "pigment {uv_mapping image_map {%s \"%s\" %s}%s} " \
# "finish {%s}\n" % \
# (image_format(texture_dif), texture_dif, img_map(t_dif),
# mapping_dif, safety(material_finish)))
- if texture_spec != "":
+ if texture_spec:
# ref_level_bound 1 is no specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
else:
# ref_level_bound 2 is translated spec
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
- if texture_norm != "":
+ if texture_norm:
# scale 1 rotate y*0
mapping_normal = img_map_transforms(t_nor)
if texture_norm and texture_norm.startswith("PAT_"):
tab_write(
+ file,
"normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n"
- % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal)
+ % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal),
)
else:
- tab_write("normal {\n")
+ tab_write(file, "normal {\n")
# XXX TODO: fix and propagate the micro normals reflection blur below
# to non textured materials
if (
@@ -341,83 +360,94 @@ def write_texture_influence(
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
- tab_write("average\n")
- tab_write("normal_map{\n")
+ tab_write(file, "average\n")
+ tab_write(file, "normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
- tab_write("[1.0 ") # Proceed with user bump...
+ tab_write(file, "[1.0 ") # Proceed with user bump...
tab_write(
+ file,
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s'
% (
@@ -426,7 +456,7 @@ def write_texture_influence(
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
- )
+ ),
)
# ...Then close its last entry and the the normal_map itself
if (
@@ -434,132 +464,148 @@ def write_texture_influence(
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
- tab_write(r"]}}"+"\n")
+ tab_write(file, r"]}}" + "\n")
else:
- tab_write(r"}"+"\n")
- if texture_spec != "":
- tab_write("]\n")
+ tab_write(file, r"}" + "\n")
+ if texture_spec:
+ tab_write(file, "]\n")
# -------- Second index for mapping specular max value -------- #
- tab_write("[1 \n")
+ tab_write(file, "[1 \n")
- if texture_dif == "" and mater.pov.replacement_text == "":
- if texture_alpha != "":
+ if not texture_dif and not mater.pov.replacement_text:
+ if texture_alpha:
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
- tab_write("function{f%s(x,y,z).transmit %s}\n" % (texture_alpha, mapping_alpha))
+ tab_write(
+ file, "function{f%s(x,y,z).transmit %s}\n" % (texture_alpha, mapping_alpha)
+ )
else:
tab_write(
+ file,
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s}\n'
- % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha)
+ % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha),
)
- tab_write("pigment_map {\n")
- tab_write("[0 color rgbft<0,0,0,1,1>]\n")
+ tab_write(file, "pigment_map {\n")
+ tab_write(file, "[0 color rgbft<0,0,0,1,1>]\n")
tab_write(
+ file,
"[1 color rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>]\n"
- % (col[0], col[1], col[2], pov_filter, trans)
+ % (col[0], col[1], col[2], pov_filter, trans),
)
- tab_write("}\n")
- tab_write("}\n")
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
else:
tab_write(
+ file,
"pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>}\n"
- % (col[0], col[1], col[2], pov_filter, trans)
+ % (col[0], col[1], col[2], pov_filter, trans),
)
- if texture_spec != "":
+ if texture_spec:
# ref_level_bound 3 is full specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
if (
mater.pov_raytrace_mirror.use
- and mater.pov_raytrace_mirror.gloss_factor < 1.0
+ and mater.pov_raytrace_mirror.gloss_factor < 1
and not using_uberpov
):
- tab_write("normal {\n")
- tab_write("average\n")
- tab_write("normal_map{\n")
+ tab_write(file, "normal {\n")
+ tab_write(file, "average\n")
+ tab_write(file, "normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
# XXX IF USER BUMP_MAP
- if texture_norm != "":
+ if texture_norm:
tab_write(
- "[1.0 "
+ file, "[1.0 "
) # Blurry reflection or not Proceed with user bump in either case...
tab_write(
+ file,
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s\n'
% (
@@ -568,104 +614,110 @@ def write_texture_influence(
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
- )
+ ),
)
# ...Then close the normal_map itself if blurry reflection
if (
mater.pov_raytrace_mirror.use
- and mater.pov_raytrace_mirror.gloss_factor < 1.0
+ and mater.pov_raytrace_mirror.gloss_factor < 1
and not using_uberpov
):
- tab_write("]\n}}\n")
+ tab_write(file, "]\n}}\n")
else:
- tab_write("}\n")
+ tab_write(file, "}\n")
elif colored_specular_found:
# ref_level_bound 1 is no specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=1)))
else:
# ref_level_bound 2 is translated specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
- elif mater.pov.replacement_text == "":
+ elif not mater.pov.replacement_text:
mapping_dif = img_map_transforms(t_dif)
- if texture_alpha != "":
+ if texture_alpha:
mapping_alpha = img_map_transforms(t_alpha)
if texture_alpha and texture_alpha.startswith("PAT_"):
tab_write(
+ file,
"pigment{pigment_pattern {function{f%s(x,y,z).transmit}%s}\n"
- % (texture_alpha, mapping_alpha)
+ % (texture_alpha, mapping_alpha),
)
else:
tab_write(
+ file,
"pigment {pigment_pattern {uv_mapping image_map"
'{%s "%s" %s}%s}\n'
- % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha)
+ % (image_format(texture_alpha), texture_alpha, img_map(t_alpha), mapping_alpha),
)
- tab_write("pigment_map {\n")
- tab_write("[0 color rgbft<0,0,0,1,1>]\n")
+ tab_write(file, "pigment_map {\n")
+ tab_write(file, "[0 color rgbft<0,0,0,1,1>]\n")
if texture_alpha and texture_alpha.startswith("PAT_"):
- tab_write("[1 function{f%s(x,y,z).transmit}%s]\n" % (texture_alpha, mapping_alpha))
+ tab_write(
+ file, "[1 function{f%s(x,y,z).transmit}%s]\n" % (texture_alpha, mapping_alpha)
+ )
elif texture_dif and not texture_dif.startswith("PAT_"):
tab_write(
+ file,
'[1 uv_mapping image_map {%s "%s" %s} %s]\n'
% (
image_format(texture_dif),
texture_dif,
(img_map(t_dif) + img_gamma),
mapping_dif,
- )
+ ),
)
elif texture_dif and texture_dif.startswith("PAT_"):
- tab_write("[1 %s %s]\n" % (texture_dif, mapping_dif))
- tab_write("}\n")
- tab_write("}\n")
+ tab_write(file, "[1 %s %s]\n" % (texture_dif, mapping_dif))
+ tab_write(file, "}\n")
+ tab_write(file, "}\n")
else:
if texture_dif and texture_dif.startswith("PAT_"):
- tab_write("pigment{%s %s}\n" % (texture_dif, mapping_dif))
+ tab_write(file, "pigment{%s %s}\n" % (texture_dif, mapping_dif))
else:
- tab_write("pigment {\n")
- tab_write("uv_mapping image_map {\n")
- # tab_write("%s \"%s\" %s}%s\n" % \
+ tab_write(file, "pigment {\n")
+ tab_write(file, "uv_mapping image_map {\n")
+ # tab_write(file, "%s \"%s\" %s}%s\n" % \
# (image_format(texture_dif), texture_dif,
# (img_gamma + img_map(t_dif)),mapping_dif))
- tab_write('%s "%s" \n' % (image_format(texture_dif), texture_dif))
- tab_write("%s\n" % (img_gamma + img_map(t_dif)))
- tab_write("}\n")
- tab_write("%s\n" % mapping_dif)
- tab_write("}\n")
+ tab_write(file, '%s "%s" \n' % (image_format(texture_dif), texture_dif))
+ tab_write(file, "%s\n" % (img_gamma + img_map(t_dif)))
+ tab_write(file, "}\n")
+ tab_write(file, "%s\n" % mapping_dif)
+ tab_write(file, "}\n")
- if texture_spec != "":
+ if texture_spec:
# ref_level_bound 3 is full specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=3)))
else:
# ref_level_bound 2 is translated specular
- tab_write("finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
+ tab_write(file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=2)))
# scale 1 rotate y*0
# imageMap = ("{image_map {%s \"%s\" %s }" % \
# (image_format(textures), textures,img_map(t_dif)))
- # tab_write("\n\t\t\tuv_mapping pigment %s} %s finish {%s}" % \
+ # tab_write(file, "\n\t\t\tuv_mapping pigment %s} %s finish {%s}" % \
# (imageMap, mapping, safety(material_finish)))
- # tab_write("\n\t\t\tpigment {uv_mapping image_map " \
+ # tab_write(file, "\n\t\t\tpigment {uv_mapping image_map " \
# "{%s \"%s\" %s}%s} finish {%s}" % \
# (image_format(texture_dif), texture_dif,img_map(t_dif),
# mapping_dif, safety(material_finish)))
- if texture_norm != "" and mater.pov.replacement_text == "":
+ if texture_norm and not mater.pov.replacement_text:
mapping_normal = img_map_transforms(t_nor)
if texture_norm and texture_norm.startswith("PAT_"):
tab_write(
+ file,
"normal{function{f%s(x,y,z).grey} bump_size %.4g %s}\n"
- % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal)
+ % (texture_norm, (-t_nor.normal_factor * 9.5), mapping_normal),
)
else:
- tab_write("normal {\n")
+ tab_write(file, "normal {\n")
# XXX TODO: fix and propagate the micro normals reflection blur below
# to non textured materials
if (
@@ -673,85 +725,96 @@ def write_texture_influence(
and mater.pov_raytrace_mirror.gloss_factor < 1.0
and not using_uberpov
):
- tab_write("average\n")
- tab_write("normal_map{\n")
+ tab_write(file, "average\n")
+ tab_write(file, "normal_map{\n")
# 0.5 for entries below means a 50 percent mix
# between the micro normal and user bump map
# order seems indifferent as commutative
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(10 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.1]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.15]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.2]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.25]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.3]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.35]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.4]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.45]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
+ file,
"[0.025 bumps %.4g scale 0.1*%.4g phase 0.5]\n"
% (
(10 / (mater.pov_raytrace_mirror.gloss_factor + 0.01)),
(1 / (mater.pov_raytrace_mirror.gloss_samples + 0.001)),
- )
+ ),
) # micronormals blurring
tab_write(
- "[1.0 "
+ file, "[1.0 "
) # Blurry reflection or not Proceed with user bump in either case...
tab_write(
+ file,
"uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s\n'
% (
@@ -760,38 +823,38 @@ def write_texture_influence(
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
- )
+ ),
)
# ...Then close the normal_map itself if blurry reflection
if (
mater.pov_raytrace_mirror.use
- and mater.pov_raytrace_mirror.gloss_factor < 1.0
+ and mater.pov_raytrace_mirror.gloss_factor < 1
and not using_uberpov
):
- tab_write("]}}\n")
+ tab_write(file, "]}}\n")
else:
- tab_write("}\n")
- if texture_spec != "" and mater.pov.replacement_text == "":
- tab_write("]\n")
+ tab_write(file, "}\n")
+ if texture_spec and not mater.pov.replacement_text:
+ tab_write(file, "]\n")
- tab_write("}\n")
+ tab_write(file, "}\n")
# End of slope/ior texture_map
- if mater.pov.diffuse_shader == "MINNAERT" and mater.pov.replacement_text == "":
- tab_write("]\n")
- tab_write("}\n")
- if mater.pov.diffuse_shader == "FRESNEL" and mater.pov.replacement_text == "":
+ if mater.pov.diffuse_shader == "MINNAERT" and not mater.pov.replacement_text:
+ tab_write(file, "]\n")
+ tab_write(file, "}\n")
+ if mater.pov.diffuse_shader == "FRESNEL" and not mater.pov.replacement_text:
c = 1
while c <= exported_lights_count:
- tab_write("]\n")
- tab_write("}\n")
+ tab_write(file, "]\n")
+ tab_write(file, "}\n")
c += 1
# Close first layer of POV "texture" (Blender material)
- tab_write("}\n")
+ tab_write(file, "}\n")
colored_specular_found = bool(
- (mater.pov.specular_color.s > 0.0) and (mater.pov.diffuse_shader != "MINNAERT")
+ (mater.pov.specular_color.s > 0) and (mater.pov.diffuse_shader != "MINNAERT")
)
# Write another layered texture using invisible diffuse and metallic trick
@@ -814,32 +877,32 @@ def write_texture_influence(
)
if colored_specular_found and not special_texture_found:
if comments:
- tab_write(" // colored highlights with a stransparent metallic layer\n")
+ tab_write(file, " // colored highlights with a stransparent metallic layer\n")
else:
- tab_write("\n")
+ tab_write(file, "\n")
- tab_write("texture {\n")
+ tab_write(file, "texture {\n")
tab_write(
+ file,
"pigment {rgbft<%.3g, %.3g, %.3g, 0, 1>}\n"
% (
mater.pov.specular_color[0],
mater.pov.specular_color[1],
mater.pov.specular_color[2],
- )
+ ),
)
tab_write(
- "finish {%s}\n" % (safety(material_finish, ref_level_bound=2))
+ file, "finish {%s}\n" % (safety(material_finish, ref_level_bound=2))
) # ref_level_bound 2 is translated spec
texture_norm = ""
for t in mater.pov_texture_slots:
- if t and tex.pov.tex_pattern_type != "emulator":
+ if tex.pov.tex_pattern_type != "emulator":
# PROCEDURAL TEXTURE
image_filename = string_strip_hyphen(bpy.path.clean_name(tex.name))
if (
- t
- and tex.type == "IMAGE"
+ tex.type == "IMAGE"
and t.use
and tex.image
and tex.pov.tex_pattern_type == "emulator"
@@ -855,12 +918,14 @@ def write_texture_influence(
t_nor = t
if procedural_flag:
tab_write(
+ file,
"normal{function"
"{f%s(x,y,z).grey} bump_size %.4g}\n"
- % (texture_norm, (-t_nor.normal_factor * 9.5))
+ % (texture_norm, (-t_nor.normal_factor * 9.5)),
)
else:
tab_write(
+ file,
"normal {uv_mapping bump_map "
'{%s "%s" %s bump_size %.4g }%s}\n'
% (
@@ -869,7 +934,7 @@ def write_texture_influence(
img_map(t_nor),
(-t_nor.normal_factor * 9.5),
mapping_normal,
- )
+ ),
)
- tab_write("}\n") # THEN IT CAN CLOSE LAST LAYER OF TEXTURE
+ tab_write(file, "}\n") # THEN IT CAN CLOSE LAST LAYER OF TEXTURE
diff --git a/render_povray/texturing_gui.py b/render_povray/texturing_gui.py
index 007eda24..5346eaff 100755
--- a/render_povray/texturing_gui.py
+++ b/render_povray/texturing_gui.py
@@ -34,17 +34,17 @@ from bl_ui import properties_texture
for member in dir(properties_texture):
subclass = getattr(properties_texture, member)
if hasattr(subclass, "COMPAT_ENGINES"):
- subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
+ subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
del properties_texture
class TextureButtonsPanel:
"""Use this class to define buttons from the texture tab properties."""
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
bl_context = "texture"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -57,19 +57,19 @@ class TEXTURE_MT_POV_specials(Menu):
"""Use this class to define pov texture slot operations buttons."""
bl_label = "Texture Specials"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
- layout.operator("texture.slot_copy", icon='COPYDOWN')
- layout.operator("texture.slot_paste", icon='PASTEDOWN')
+ layout.operator("texture.slot_copy", icon="COPYDOWN")
+ layout.operator("texture.slot_paste", icon="PASTEDOWN")
class WORLD_TEXTURE_SLOTS_UL_POV_layerlist(UIList):
"""Use this class to show pov texture slots list."""
- index: bpy.props.IntProperty(name='index')
+ index: bpy.props.IntProperty(name="index")
# should active_propname be index or..?
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
@@ -84,18 +84,18 @@ class WORLD_TEXTURE_SLOTS_UL_POV_layerlist(UIList):
slot = item
# ma = slot.name
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
- if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ if self.layout_type in {"DEFAULT", "COMPACT"}:
# You should always start your row layout by a label (icon + text), or a non-embossed text field,
# this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
# Note "data" names should never be translated!
if slot:
- layout.prop(slot, "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!).
- elif self.layout_type in {'GRID'}:
- layout.alignment = 'CENTER'
+ elif self.layout_type in {"GRID"}:
+ layout.alignment = "CENTER"
layout.label(text="", icon_value=icon)
@@ -103,7 +103,7 @@ class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(UIList):
"""Use this class to show pov texture slots list."""
# texture_slots:
- index: bpy.props.IntProperty(name='index')
+ index: bpy.props.IntProperty(name="index")
# foo = random prop
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
@@ -111,18 +111,18 @@ class MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist(UIList):
slot = item
# ma = slot.name
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
- if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ if self.layout_type in {"DEFAULT", "COMPACT"}:
# You should always start your row layout by a label (icon + text), or a non-embossed text field,
# this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
# Note "data" names should never be translated!
if slot:
- layout.prop(slot, "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!).
- elif self.layout_type in {'GRID'}:
- layout.alignment = 'CENTER'
+ elif self.layout_type in {"GRID"}:
+ layout.alignment = "CENTER"
layout.label(text="", icon_value=icon)
@@ -131,8 +131,8 @@ class TEXTURE_PT_context(TextureButtonsPanel, Panel):
bl_label = ""
bl_context = "texture"
- bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'POVRAY_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ bl_options = {"HIDE_HEADER"}
+ COMPAT_ENGINES = {"POVRAY_RENDER", "BLENDER_EEVEE", "BLENDER_WORKBENCH"}
# register but not unregistered because
# the modified parts concern only POVRAY_RENDER
@@ -140,8 +140,8 @@ class TEXTURE_PT_context(TextureButtonsPanel, Panel):
def poll(cls, context):
return (
context.scene.texture_context
- not in ('MATERIAL', 'WORLD', 'LIGHT', 'PARTICLES', 'LINESTYLE')
- or context.scene.render.engine != 'POVRAY_RENDER'
+ not in ("MATERIAL", "WORLD", "LIGHT", "PARTICLES", "LINESTYLE")
+ or context.scene.render.engine != "POVRAY_RENDER"
)
def draw(self, context):
@@ -181,8 +181,8 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
"""Use this class to show pov texture context buttons."""
bl_label = ""
- bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"HIDE_HEADER"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -208,7 +208,7 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
wld = context.scene.world
layout.prop(scene, "texture_context", expand=True)
- if scene.texture_context == 'MATERIAL' and mat is not None:
+ if scene.texture_context == "MATERIAL" and mat is not None:
row = layout.row()
row.template_list(
@@ -223,8 +223,8 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
type="DEFAULT",
)
col = row.column(align=True)
- col.operator("pov.textureslotadd", icon='ADD', text='')
- col.operator("pov.textureslotremove", icon='REMOVE', text='')
+ col.operator("pov.textureslotadd", icon="ADD", text="")
+ col.operator("pov.textureslotremove", icon="REMOVE", text="")
# XXX todo: recreate for pov_texture_slots?
# col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP'
# col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN'
@@ -236,26 +236,29 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
try:
povtex = slot.texture # slot.name
tex = bpy.data.textures[povtex]
- col.prop(tex, 'use_fake_user', text='')
+ col.prop(tex, "use_fake_user", text="")
# layout.label(text='Linked Texture data browser:')
# propname = slot.texture_search
# if slot.texture was a pointer to texture data rather than just a name string:
# layout.template_ID(povtex, "texture", new="texture.new")
except KeyError:
tex = None
- layout.prop_search(
- slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE'
+ row = layout.row(align=True)
+ row.prop_search(
+ slot, "texture_search", bpy.data, "textures", text="", icon="TEXTURE"
)
- try:
- bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[
- slot.texture_search
- ]
- bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[
- slot.texture_search
- ]
- except KeyError:
- # texture not hand-linked by user
- pass
+
+ row.operator("pov.textureslotupdate", icon="FILE_REFRESH", text="")
+ # try:
+ # bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[
+ # slot.texture_search
+ # ]
+ # bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[
+ # slot.texture_search
+ # ]
+ # except KeyError:
+ # # texture not hand-linked by user
+ # pass
if tex:
layout.separator()
@@ -266,7 +269,7 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
# else:
# for i in range(18): # length of material texture slots
# mat.pov_texture_slots.add()
- elif scene.texture_context == 'WORLD' and wld is not None:
+ elif scene.texture_context == "WORLD" and wld is not None:
row = layout.row()
row.template_list(
@@ -281,8 +284,8 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
type="DEFAULT",
)
col = row.column(align=True)
- col.operator("pov.textureslotadd", icon='ADD', text='')
- col.operator("pov.textureslotremove", icon='REMOVE', text='')
+ col.operator("pov.textureslotadd", icon="ADD", text="")
+ col.operator("pov.textureslotremove", icon="REMOVE", text="")
# todo: recreate for pov_texture_slots?
# col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP'
@@ -294,14 +297,14 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
slot = wld.pov_texture_slots[index]
povtex = slot.texture # slot.name
tex = bpy.data.textures[povtex]
- col.prop(tex, 'use_fake_user', text='')
+ col.prop(tex, "use_fake_user", text="")
# layout.label(text='Linked Texture data browser:')
# propname = slot.texture_search # NOT USED
# if slot.texture was a pointer to texture data rather than just a name string:
# layout.template_ID(povtex, "texture", new="texture.new")
layout.prop_search(
- slot, 'texture_search', bpy.data, 'textures', text='', icon='TEXTURE'
+ slot, "texture_search", bpy.data, "textures", text="", icon="TEXTURE"
)
try:
bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[
@@ -321,9 +324,42 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
split.prop(tex, "type", text="")
+class TEXTURE_OT_POV_context_texture_update(Operator):
+ """Use this class for the texture slot update button."""
+
+ bl_idname = "pov.textureslotupdate"
+ bl_label = "Update"
+ bl_description = "Update texture_slot"
+ bl_options = {"REGISTER", "UNDO"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+
+ @classmethod
+ def poll(cls, context):
+ engine = context.scene.render.engine
+ mate = context.view_layer.objects.active.active_material
+ return mate and engine in cls.COMPAT_ENGINES
+
+ def execute(self, context):
+ # tex.use_fake_user = True
+ mat = context.view_layer.objects.active.active_material
+ index = mat.pov.active_texture_index
+ slot = mat.pov_texture_slots[index]
+ povtex = slot.texture # slot.name
+ tex = bpy.data.textures[povtex]
+
+ # Switch paint brush and paint brush mask
+ # to this texture so settings remain contextual
+ bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[slot.texture_search]
+ bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[
+ slot.texture_search
+ ]
+
+ return {"FINISHED"}
+
+
# Commented out below is a reminder of what existed in Blender Internal
# attributes need to be recreated
-'''
+"""
slot = getattr(context, "texture_slot", None)
node = getattr(context, "texture_node", None)
space = context.space_data
@@ -428,15 +464,15 @@ class TEXTURE_PT_POV_context_texture(TextureButtonsPanel, Panel):
split.prop(slot, "output_node", text="")
else:
split.label(text="Type:")
-'''
+"""
class TEXTURE_PT_colors(TextureButtonsPanel, Panel):
"""Use this class to show pov color ramps."""
bl_label = "Colors"
- bl_options = {'DEFAULT_CLOSED'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"DEFAULT_CLOSED"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
layout = self.layout
@@ -475,12 +511,14 @@ class TEXTURE_OT_POV_texture_slot_add(Operator):
bl_idname = "pov.textureslotadd"
bl_label = "Add"
bl_description = "Add texture_slot"
- bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"REGISTER", "UNDO"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
idblock = pov_context_tex_datablock(context)
- tex = bpy.data.textures.new(name='Texture', type='IMAGE')
+ slot_brush = bpy.data.brushes.new("POVtextureSlot")
+ context.tool_settings.image_paint.brush = slot_brush
+ tex = bpy.data.textures.new(name="Texture", type="IMAGE")
# tex.use_fake_user = True
# mat = context.view_layer.objects.active.active_material
slot = idblock.pov_texture_slots.add()
@@ -497,7 +535,7 @@ class TEXTURE_OT_POV_texture_slot_add(Operator):
# if area.type in ['PROPERTIES']:
# area.tag_redraw()
- return {'FINISHED'}
+ return {"FINISHED"}
class TEXTURE_OT_POV_texture_slot_remove(Operator):
@@ -506,32 +544,35 @@ class TEXTURE_OT_POV_texture_slot_remove(Operator):
bl_idname = "pov.textureslotremove"
bl_label = "Remove"
bl_description = "Remove texture_slot"
- bl_options = {'REGISTER', 'UNDO'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"REGISTER", "UNDO"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def execute(self, context):
idblock = pov_context_tex_datablock(context)
# mat = context.view_layer.objects.active.active_material
# tex_slot = idblock.pov_texture_slots.remove(idblock.pov.active_texture_index) # not used
+ # tex_to_delete = context.tool_settings.image_paint.brush.texture
+ # bpy.data.textures.remove(tex_to_delete, do_unlink=True, do_id_user=True, do_ui_user=True)
+ idblock.pov_texture_slots.remove(idblock.pov.active_texture_index)
if idblock.pov.active_texture_index > 0:
idblock.pov.active_texture_index -= 1
- try:
- tex = idblock.pov_texture_slots[idblock.pov.active_texture_index].texture
- except IndexError:
+ # try:
+ # tex = idblock.pov_texture_slots[idblock.pov.active_texture_index].texture
+ # except IndexError:
# No more slots
- return {'FINISHED'}
+ return {"FINISHED"}
# Switch paint brush to previous texture so settings remain contextual
# if 'tex' in locals(): # Would test is the tex variable is assigned / exists
- bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex]
- bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex]
+ # bpy.context.tool_settings.image_paint.brush.texture = bpy.data.textures[tex]
+ # bpy.context.tool_settings.image_paint.brush.mask_texture = bpy.data.textures[tex]
- return {'FINISHED'}
+ return {"FINISHED"}
class TextureSlotPanel(TextureButtonsPanel):
"""Use this class to show pov texture slots panel."""
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
@classmethod
def poll(cls, context):
@@ -547,8 +588,8 @@ class TEXTURE_PT_POV_type(TextureButtonsPanel, Panel):
"""Use this class to define pov texture type buttons."""
bl_label = "POV Textures"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
- bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+ bl_options = {"HIDE_HEADER"}
def draw(self, context):
layout = self.layout
@@ -568,8 +609,8 @@ class TEXTURE_PT_POV_preview(TextureButtonsPanel, Panel):
"""Use this class to define pov texture preview panel."""
bl_label = "Preview"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
- bl_options = {'HIDE_HEADER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+ bl_options = {"HIDE_HEADER"}
@classmethod
def poll(cls, context):
@@ -578,7 +619,7 @@ class TEXTURE_PT_POV_preview(TextureButtonsPanel, Panel):
return False
tex = context.texture
# mat = bpy.context.active_object.active_material #unused
- return tex and (tex.pov.tex_pattern_type != 'emulator') and (engine in cls.COMPAT_ENGINES)
+ return tex and (tex.pov.tex_pattern_type != "emulator") and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
tex = context.texture
@@ -587,7 +628,7 @@ class TEXTURE_PT_POV_preview(TextureButtonsPanel, Panel):
layout = self.layout
# if idblock:
# layout.template_preview(tex, parent=idblock, slot=slot)
- if tex.pov.tex_pattern_type != 'emulator':
+ if tex.pov.tex_pattern_type != "emulator":
layout.operator("tex.preview_update")
else:
layout.template_preview(tex, slot=slot)
@@ -597,28 +638,28 @@ class TEXTURE_PT_POV_parameters(TextureButtonsPanel, Panel):
"""Use this class to define pov texture pattern buttons."""
bl_label = "POV Pattern Options"
- bl_options = {'HIDE_HEADER'}
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ bl_options = {"HIDE_HEADER"}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw(self, context):
# mat = bpy.context.active_object.active_material # Unused
tex = context.texture
- if tex is not None and tex.pov.tex_pattern_type != 'emulator':
+ if tex is not None and tex.pov.tex_pattern_type != "emulator":
layout = self.layout
- if tex.pov.tex_pattern_type == 'agate':
+ if tex.pov.tex_pattern_type == "agate":
layout.prop(tex.pov, "modifier_turbulence", text="Agate Turbulence")
- if tex.pov.tex_pattern_type in {'spiral1', 'spiral2'}:
+ if tex.pov.tex_pattern_type in {"spiral1", "spiral2"}:
layout.prop(tex.pov, "modifier_numbers", text="Number of arms")
- if tex.pov.tex_pattern_type == 'tiling':
+ if tex.pov.tex_pattern_type == "tiling":
layout.prop(tex.pov, "modifier_numbers", text="Pattern number")
- if tex.pov.tex_pattern_type == 'magnet':
+ if tex.pov.tex_pattern_type == "magnet":
layout.prop(tex.pov, "magnet_style", text="Magnet style")
align = True
- if tex.pov.tex_pattern_type == 'quilted':
+ if tex.pov.tex_pattern_type == "quilted":
row = layout.row(align=align)
row.prop(tex.pov, "modifier_control0", text="Control0")
row.prop(tex.pov, "modifier_control1", text="Control1")
- if tex.pov.tex_pattern_type == 'brick':
+ if tex.pov.tex_pattern_type == "brick":
col = layout.column(align=align)
row = col.row()
row.prop(tex.pov, "brick_size_x", text="Brick size X")
@@ -626,20 +667,20 @@ class TEXTURE_PT_POV_parameters(TextureButtonsPanel, Panel):
row = col.row()
row.prop(tex.pov, "brick_size_z", text="Brick size Z")
row.prop(tex.pov, "brick_mortar", text="Brick mortar")
- if tex.pov.tex_pattern_type in {'julia', 'mandel', 'magnet'}:
+ if tex.pov.tex_pattern_type in {"julia", "mandel", "magnet"}:
col = layout.column(align=align)
- if tex.pov.tex_pattern_type == 'julia':
+ if tex.pov.tex_pattern_type == "julia":
row = col.row()
row.prop(tex.pov, "julia_complex_1", text="Complex 1")
row.prop(tex.pov, "julia_complex_2", text="Complex 2")
- if tex.pov.tex_pattern_type == 'magnet' and tex.pov.magnet_style == 'julia':
+ if tex.pov.tex_pattern_type == "magnet" and tex.pov.magnet_style == "julia":
row = col.row()
row.prop(tex.pov, "julia_complex_1", text="Complex 1")
row.prop(tex.pov, "julia_complex_2", text="Complex 2")
row = col.row()
- if tex.pov.tex_pattern_type in {'julia', 'mandel'}:
+ if tex.pov.tex_pattern_type in {"julia", "mandel"}:
row.prop(tex.pov, "f_exponent", text="Exponent")
- if tex.pov.tex_pattern_type == 'magnet':
+ if tex.pov.tex_pattern_type == "magnet":
row.prop(tex.pov, "magnet_type", text="Type")
row.prop(tex.pov, "f_iter", text="Iterations")
row = col.row()
@@ -648,43 +689,43 @@ class TEXTURE_PT_POV_parameters(TextureButtonsPanel, Panel):
row = col.row()
row.prop(tex.pov, "f_eor", text="Exterior")
row.prop(tex.pov, "f_eor_fac", text="Factor E")
- if tex.pov.tex_pattern_type == 'gradient':
+ if tex.pov.tex_pattern_type == "gradient":
layout.label(text="Gradient orientation:")
column_flow = layout.column_flow(columns=3, align=True)
column_flow.prop(tex.pov, "grad_orient_x", text="X")
column_flow.prop(tex.pov, "grad_orient_y", text="Y")
column_flow.prop(tex.pov, "grad_orient_z", text="Z")
- if tex.pov.tex_pattern_type == 'pavement':
+ if tex.pov.tex_pattern_type == "pavement":
layout.prop(tex.pov, "pave_sides", text="Pavement:number of sides")
col = layout.column(align=align)
column_flow = col.column_flow(columns=3, align=True)
column_flow.prop(tex.pov, "pave_tiles", text="Tiles")
- if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 6:
+ if tex.pov.pave_sides == "4" and tex.pov.pave_tiles == 6:
column_flow.prop(tex.pov, "pave_pat_35", text="Pattern")
- if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 5:
+ if tex.pov.pave_sides == "6" and tex.pov.pave_tiles == 5:
column_flow.prop(tex.pov, "pave_pat_22", text="Pattern")
- if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 5:
+ if tex.pov.pave_sides == "4" and tex.pov.pave_tiles == 5:
column_flow.prop(tex.pov, "pave_pat_12", text="Pattern")
- if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 6:
+ if tex.pov.pave_sides == "3" and tex.pov.pave_tiles == 6:
column_flow.prop(tex.pov, "pave_pat_12", text="Pattern")
- if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 4:
+ if tex.pov.pave_sides == "6" and tex.pov.pave_tiles == 4:
column_flow.prop(tex.pov, "pave_pat_7", text="Pattern")
- if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 4:
+ if tex.pov.pave_sides == "4" and tex.pov.pave_tiles == 4:
column_flow.prop(tex.pov, "pave_pat_5", text="Pattern")
- if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 5:
+ if tex.pov.pave_sides == "3" and tex.pov.pave_tiles == 5:
column_flow.prop(tex.pov, "pave_pat_4", text="Pattern")
- if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 3:
+ if tex.pov.pave_sides == "6" and tex.pov.pave_tiles == 3:
column_flow.prop(tex.pov, "pave_pat_3", text="Pattern")
- if tex.pov.pave_sides == '3' and tex.pov.pave_tiles == 4:
+ if tex.pov.pave_sides == "3" and tex.pov.pave_tiles == 4:
column_flow.prop(tex.pov, "pave_pat_3", text="Pattern")
- if tex.pov.pave_sides == '4' and tex.pov.pave_tiles == 3:
+ if tex.pov.pave_sides == "4" and tex.pov.pave_tiles == 3:
column_flow.prop(tex.pov, "pave_pat_2", text="Pattern")
- if tex.pov.pave_sides == '6' and tex.pov.pave_tiles == 6:
+ if tex.pov.pave_sides == "6" and tex.pov.pave_tiles == 6:
column_flow.label(text="!!! 5 tiles!")
column_flow.prop(tex.pov, "pave_form", text="Form")
- if tex.pov.tex_pattern_type == 'function':
+ if tex.pov.tex_pattern_type == "function":
layout.prop(tex.pov, "func_list", text="Functions")
- if tex.pov.tex_pattern_type == 'function' and tex.pov.func_list != "NONE":
+ if tex.pov.tex_pattern_type == "function" and tex.pov.func_list != "NONE":
func = None
if tex.pov.func_list in {"f_noise3d", "f_ph", "f_r", "f_th"}:
func = 0
@@ -864,9 +905,9 @@ class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel):
"""Use this class to define POV texture mapping buttons."""
bl_label = "Mapping"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
@classmethod
def poll(cls, context):
@@ -894,7 +935,7 @@ class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel):
col = split.column()
col.prop(tex, "texture_coords", text="")
- if tex.texture_coords == 'ORCO':
+ if tex.texture_coords == "ORCO":
"""
ob = context.object
if ob and ob.type == 'MESH':
@@ -902,21 +943,21 @@ class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel):
split.label(text="Mesh:")
split.prop(ob.data, "texco_mesh", text="")
"""
- elif tex.texture_coords == 'UV':
+ elif tex.texture_coords == "UV":
split = layout.split(percentage=0.3)
split.label(text="Map:")
ob = context.object
- if ob and ob.type == 'MESH':
+ if ob and ob.type == "MESH":
split.prop_search(tex, "uv_layer", ob.data, "uv_textures", text="")
else:
split.prop(tex, "uv_layer", text="")
- elif tex.texture_coords == 'OBJECT':
+ elif tex.texture_coords == "OBJECT":
split = layout.split(percentage=0.3)
split.label(text="Object:")
split.prop(tex, "object", text="")
- elif tex.texture_coords == 'ALONG_STROKE':
+ elif tex.texture_coords == "ALONG_STROKE":
split = layout.split(percentage=0.3)
split.label(text="Use Tips:")
split.prop(tex, "use_tips", text="")
@@ -945,13 +986,13 @@ class TEXTURE_PT_POV_mapping(TextureSlotPanel, Panel):
split = layout.split()
col = split.column()
- if tex.texture_coords in {'ORCO', 'UV'}:
+ if tex.texture_coords in {"ORCO", "UV"}:
col.prop(tex, "use_from_dupli")
- if idblock.type == 'VOLUME' and tex.texture_coords == 'ORCO':
+ if idblock.type == "VOLUME" and tex.texture_coords == "ORCO":
col.prop(tex, "use_map_to_bounds")
- elif tex.texture_coords == 'OBJECT':
+ elif tex.texture_coords == "OBJECT":
col.prop(tex, "use_from_original")
- if idblock.type == 'VOLUME':
+ if idblock.type == "VOLUME":
col.prop(tex, "use_map_to_bounds")
else:
col.label()
@@ -971,9 +1012,9 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
"""Use this class to define pov texture influence buttons."""
bl_label = "Influence"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
# bl_context = 'texture'
@classmethod
@@ -982,7 +1023,7 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
if (
# isinstance(idblock, Brush) and # Brush used for everything since 2.8
context.scene.texture_context
- == 'OTHER'
+ == "OTHER"
): # XXX replace by isinstance(idblock, bpy.types.Brush) and ...
return False
@@ -1006,7 +1047,9 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
] # bpy.data.textures[mat.active_texture_index]
# below tex unused yet ...maybe for particles?
try:
- tex = bpy.data.textures[idblock.pov_texture_slots[idblock.pov.active_texture_index].texture] # NOT USED
+ tex = bpy.data.textures[
+ idblock.pov_texture_slots[idblock.pov.active_texture_index].texture
+ ] # NOT USED
except KeyError:
tex = None # NOT USED
@@ -1022,7 +1065,7 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
split = layout.split()
col = split.column()
- if idblock.pov.type in {'SURFACE', 'WIRE'}:
+ if idblock.pov.type in {"SURFACE", "WIRE"}:
split = layout.split()
@@ -1054,7 +1097,7 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
factor_but(col, "use_map_warp", "warp_factor", "Warp")
factor_but(col, "use_map_displacement", "displacement_factor", "Displace")
- elif idblock.pov.type == 'HALO':
+ elif idblock.pov.type == "HALO":
layout.label(text="Halo:")
split = layout.split()
@@ -1067,7 +1110,7 @@ class TEXTURE_PT_POV_influence(TextureSlotPanel, Panel):
factor_but(col, "use_map_raymir", "raymir_factor", "Size")
factor_but(col, "use_map_hardness", "hardness_factor", "Hardness")
factor_but(col, "use_map_translucency", "translucency_factor", "Add")
- elif idblock.pov.type == 'VOLUME':
+ elif idblock.pov.type == "VOLUME":
layout.label(text="Volume:")
split = layout.split()
@@ -1182,12 +1225,12 @@ class TEXTURE_PT_POV_tex_gamma(TextureButtonsPanel, Panel):
"""Use this class to define pov texture gamma buttons."""
bl_label = "Image Gamma"
- COMPAT_ENGINES = {'POVRAY_RENDER'}
+ COMPAT_ENGINES = {"POVRAY_RENDER"}
def draw_header(self, context):
tex = context.texture
- self.layout.prop(tex.pov, "tex_gamma_enable", text="", icon='SEQ_LUMA_WAVEFORM')
+ self.layout.prop(tex.pov, "tex_gamma_enable", text="", icon="SEQ_LUMA_WAVEFORM")
def draw(self, context):
layout = self.layout
@@ -1226,6 +1269,7 @@ classes = (
MATERIAL_TEXTURE_SLOTS_UL_POV_layerlist,
TEXTURE_OT_POV_texture_slot_add,
TEXTURE_OT_POV_texture_slot_remove,
+ TEXTURE_OT_POV_context_texture_update,
TEXTURE_PT_POV_influence,
TEXTURE_PT_POV_mapping,
)
diff --git a/render_povray/texturing_procedural.py b/render_povray/texturing_procedural.py
new file mode 100644
index 00000000..df707630
--- /dev/null
+++ b/render_povray/texturing_procedural.py
@@ -0,0 +1,694 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""Use Blender procedural textures exported to POV patterns."""
+
+import bpy
+
+
+def export_pattern(texture):
+ """Translate Blender procedural textures to POV patterns and write to pov file.
+
+ Function Patterns can be used to better access sub components of a pattern like
+ grey values for influence mapping
+ """
+ from .render import string_strip_hyphen
+
+ tex = texture
+ pat = tex.pov
+ pat_name = "PAT_%s" % string_strip_hyphen(bpy.path.clean_name(tex.name))
+ mapping_dif = "translate <%.4g,%.4g,%.4g> scale <%.4g,%.4g,%.4g>" % (
+ pat.tex_mov_x,
+ pat.tex_mov_y,
+ pat.tex_mov_z,
+ 1.0 / pat.tex_scale_x,
+ 1.0 / pat.tex_scale_y,
+ 1.0 / pat.tex_scale_z,
+ )
+ text_strg = ""
+
+ def export_color_ramp(texture):
+ tex = texture
+ pat = tex.pov
+ col_ramp_strg = "color_map {\n"
+ for num_color, el in enumerate(tex.color_ramp.elements, start=1):
+ pos = el.position
+ col = el.color
+ col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3]
+ if pat.tex_pattern_type not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ col_ramp_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % (
+ pos,
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3:
+ col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "hexagon" and num_color < 4:
+ col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "square" and num_color < 5:
+ col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "triangular" and num_color < 7:
+ col_ramp_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+
+ col_ramp_strg += "} \n"
+ # end color map
+ return col_ramp_strg
+
+ # much work to be done here only defaults translated for now:
+ # pov noise_generator 3 means perlin noise
+ if tex.type not in {"NONE", "IMAGE"} and pat.tex_pattern_type == "emulator":
+ text_strg += "pigment {\n"
+ # ------------------------- EMULATE BLENDER VORONOI TEXTURE ------------------------- #
+ if tex.type == "VORONOI":
+ text_strg += "crackle\n"
+ text_strg += " offset %.4g\n" % tex.nabla
+ text_strg += " form <%.4g,%.4g,%.4g>\n" % (
+ tex.weight_1,
+ tex.weight_2,
+ tex.weight_3,
+ )
+ if tex.distance_metric == "DISTANCE":
+ text_strg += " metric 2.5\n"
+ if tex.distance_metric == "DISTANCE_SQUARED":
+ text_strg += " metric 2.5\n"
+ text_strg += " poly_wave 2\n"
+ if tex.distance_metric == "MINKOVSKY":
+ text_strg += " metric %s\n" % tex.minkovsky_exponent
+ if tex.distance_metric == "MINKOVSKY_FOUR":
+ text_strg += " metric 4\n"
+ if tex.distance_metric == "MINKOVSKY_HALF":
+ text_strg += " metric 0.5\n"
+ if tex.distance_metric == "CHEBYCHEV":
+ text_strg += " metric 10\n"
+ if tex.distance_metric == "MANHATTAN":
+ text_strg += " metric 1\n"
+
+ if tex.color_mode == "POSITION":
+ text_strg += "solid\n"
+ text_strg += "scale 0.25\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<0,0,0,1>]\n"
+ text_strg += "[1 color rgbt<1,1,1,0>]\n"
+ text_strg += "}\n"
+
+ # ------------------------- EMULATE BLENDER CLOUDS TEXTURE ------------------------- #
+ if tex.type == "CLOUDS":
+ if tex.noise_type == "SOFT_NOISE":
+ text_strg += "wrinkles\n"
+ text_strg += "scale 0.25\n"
+ else:
+ text_strg += "granite\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<0,0,0,1>]\n"
+ text_strg += "[1 color rgbt<1,1,1,0>]\n"
+ text_strg += "}\n"
+
+ # ------------------------- EMULATE BLENDER WOOD TEXTURE ------------------------- #
+ if tex.type == "WOOD":
+ if tex.wood_type == "RINGS":
+ text_strg += "wood\n"
+ text_strg += "scale 0.25\n"
+ if tex.wood_type == "RINGNOISE":
+ text_strg += "wood\n"
+ text_strg += "scale 0.25\n"
+ text_strg += "turbulence %.4g\n" % (tex.turbulence / 100)
+ if tex.wood_type == "BANDS":
+ text_strg += "marble\n"
+ text_strg += "scale 0.25\n"
+ text_strg += "rotate <45,-45,45>\n"
+ if tex.wood_type == "BANDNOISE":
+ text_strg += "marble\n"
+ text_strg += "scale 0.25\n"
+ text_strg += "rotate <45,-45,45>\n"
+ text_strg += "turbulence %.4g\n" % (tex.turbulence / 10)
+
+ if tex.noise_basis_2 == "SIN":
+ text_strg += "sine_wave\n"
+ if tex.noise_basis_2 == "TRI":
+ text_strg += "triangle_wave\n"
+ if tex.noise_basis_2 == "SAW":
+ text_strg += "ramp_wave\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<0,0,0,0>]\n"
+ text_strg += "[1 color rgbt<1,1,1,0>]\n"
+ text_strg += "}\n"
+
+ # ------------------------- EMULATE BLENDER STUCCI TEXTURE ------------------------- #
+ if tex.type == "STUCCI":
+ text_strg += "bozo\n"
+ text_strg += "scale 0.25\n"
+ if tex.noise_type == "HARD_NOISE":
+ text_strg += "triangle_wave\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbf<1,1,1,0>]\n"
+ text_strg += "[1 color rgbt<0,0,0,1>]\n"
+ text_strg += "}\n"
+ else:
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbf<0,0,0,1>]\n"
+ text_strg += "[1 color rgbt<1,1,1,0>]\n"
+ text_strg += "}\n"
+
+ # ------------------------- EMULATE BLENDER MAGIC TEXTURE ------------------------- #
+ if tex.type == "MAGIC":
+ text_strg += "leopard\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0.5>]\n"
+ text_strg += "[0.25 color rgbf<0,1,0,0.75>]\n"
+ text_strg += "[0.5 color rgbf<0,0,1,0.75>]\n"
+ text_strg += "[0.75 color rgbf<1,0,1,0.75>]\n"
+ text_strg += "[1 color rgbf<0,1,0,0.75>]\n"
+ text_strg += "}\n"
+ text_strg += "scale 0.1\n"
+
+ # ------------------------- EMULATE BLENDER MARBLE TEXTURE ------------------------- #
+ if tex.type == "MARBLE":
+ text_strg += "marble\n"
+ text_strg += "turbulence 0.5\n"
+ text_strg += "noise_generator 3\n"
+ text_strg += "scale 0.75\n"
+ text_strg += "rotate <45,-45,45>\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ if tex.marble_type == "SOFT":
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<0,0,0,0>]\n"
+ text_strg += "[0.05 color rgbt<0,0,0,0>]\n"
+ text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n"
+ text_strg += "}\n"
+ elif tex.marble_type == "SHARP":
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<0,0,0,0>]\n"
+ text_strg += "[0.025 color rgbt<0,0,0,0>]\n"
+ text_strg += "[1 color rgbt<0.9,0.9,0.9,0>]\n"
+ text_strg += "}\n"
+ else:
+ text_strg += "[0 color rgbt<0,0,0,0>]\n"
+ text_strg += "[1 color rgbt<1,1,1,0>]\n"
+ text_strg += "}\n"
+ if tex.noise_basis_2 == "SIN":
+ text_strg += "sine_wave\n"
+ if tex.noise_basis_2 == "TRI":
+ text_strg += "triangle_wave\n"
+ if tex.noise_basis_2 == "SAW":
+ text_strg += "ramp_wave\n"
+
+ # ------------------------- EMULATE BLENDER BLEND TEXTURE ------------------------- #
+ if tex.type == "BLEND":
+ if tex.progression == "RADIAL":
+ text_strg += "radial\n"
+ if tex.use_flip_axis == "HORIZONTAL":
+ text_strg += "rotate x*90\n"
+ else:
+ text_strg += "rotate <-90,0,90>\n"
+ text_strg += "ramp_wave\n"
+ elif tex.progression == "SPHERICAL":
+ text_strg += "spherical\n"
+ text_strg += "scale 3\n"
+ text_strg += "poly_wave 1\n"
+ elif tex.progression == "QUADRATIC_SPHERE":
+ text_strg += "spherical\n"
+ text_strg += "scale 3\n"
+ text_strg += " poly_wave 2\n"
+ elif tex.progression == "DIAGONAL":
+ text_strg += "gradient <1,1,0>\n"
+ text_strg += "scale 3\n"
+ elif tex.use_flip_axis == "HORIZONTAL":
+ text_strg += "gradient x\n"
+ text_strg += "scale 2.01\n"
+ elif tex.use_flip_axis == "VERTICAL":
+ text_strg += "gradient y\n"
+ text_strg += "scale 2.01\n"
+ # text_strg+="ramp_wave\n"
+ # text_strg+="frequency 0.5\n"
+ text_strg += "phase 0.5\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ if tex.progression == "LINEAR":
+ text_strg += " poly_wave 1\n"
+ if tex.progression == "QUADRATIC":
+ text_strg += " poly_wave 2\n"
+ if tex.progression == "EASING":
+ text_strg += " poly_wave 1.5\n"
+
+ # ------------------------- EMULATE BLENDER MUSGRAVE TEXTURE ------------------------- #
+ # if tex.type == 'MUSGRAVE':
+ # text_strg+="function{ f_ridged_mf( x, y, 0, 1, 2, 9, -0.5, 3,3 )*0.5}\n"
+ # text_strg+="color_map {\n"
+ # text_strg+="[0 color rgbf<0,0,0,1>]\n"
+ # text_strg+="[1 color rgbf<1,1,1,0>]\n"
+ # text_strg+="}\n"
+ # simplified for now:
+
+ if tex.type == "MUSGRAVE":
+ text_strg += "bozo scale 0.25 \n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += (
+ "color_map {[0.5 color rgbf<0,0,0,1>][1 color rgbt<1,1,1,0>]}ramp_wave \n"
+ )
+
+ # ------------------------- EMULATE BLENDER DISTORTED NOISE TEXTURE ------------------------- #
+ if tex.type == "DISTORTED_NOISE":
+ text_strg += "average\n"
+ text_strg += " pigment_map {\n"
+ text_strg += " [1 bozo scale 0.25 turbulence %.4g\n" % tex.distortion
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ text_strg += "]\n"
+
+ if tex.noise_distortion == "CELL_NOISE":
+ text_strg += " [1 cells scale 0.1\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ text_strg += "]\n"
+ if tex.noise_distortion == "VORONOI_CRACKLE":
+ text_strg += " [1 crackle scale 0.25\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ text_strg += "]\n"
+ if tex.noise_distortion in [
+ "VORONOI_F1",
+ "VORONOI_F2",
+ "VORONOI_F3",
+ "VORONOI_F4",
+ "VORONOI_F2_F1",
+ ]:
+ text_strg += " [1 crackle metric 2.5 scale 0.25 turbulence %.4g\n" % (
+ tex.distortion / 2
+ )
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ text_strg += "]\n"
+ else:
+ text_strg += " [1 wrinkles scale 0.25\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0 color rgbt<1,1,1,0>]\n"
+ text_strg += "[1 color rgbf<0,0,0,1>]\n"
+ text_strg += "}\n"
+ text_strg += "]\n"
+ text_strg += " }\n"
+
+ # ------------------------- EMULATE BLENDER NOISE TEXTURE ------------------------- #
+ if tex.type == "NOISE":
+ text_strg += "cells\n"
+ text_strg += "turbulence 3\n"
+ text_strg += "omega 3\n"
+ if tex.use_color_ramp:
+ text_strg += export_color_ramp(tex)
+ else:
+ text_strg += "color_map {\n"
+ text_strg += "[0.75 color rgb<0,0,0,>]\n"
+ text_strg += "[1 color rgb<1,1,1,>]\n"
+ text_strg += "}\n"
+
+ # ------------------------- IGNORE OTHER BLENDER TEXTURE ------------------------- #
+ else: # non translated textures
+ pass
+ text_strg += "}\n\n"
+
+ text_strg += "#declare f%s=\n" % pat_name
+ text_strg += "function{pigment{%s}}\n" % pat_name
+ text_strg += "\n"
+
+ elif pat.tex_pattern_type != "emulator":
+ text_strg += "pigment {\n"
+ text_strg += "%s\n" % pat.tex_pattern_type
+ if pat.tex_pattern_type == "agate":
+ text_strg += "agate_turb %.4g\n" % pat.modifier_turbulence
+ if pat.tex_pattern_type in {"spiral1", "spiral2", "tiling"}:
+ text_strg += "%s\n" % pat.modifier_numbers
+ if pat.tex_pattern_type == "quilted":
+ text_strg += "control0 %s control1 %s\n" % (
+ pat.modifier_control0,
+ pat.modifier_control1,
+ )
+ if pat.tex_pattern_type == "mandel":
+ text_strg += "%s exponent %s \n" % (pat.f_iter, pat.f_exponent)
+ if pat.tex_pattern_type == "julia":
+ text_strg += "<%.4g, %.4g> %s exponent %s \n" % (
+ pat.julia_complex_1,
+ pat.julia_complex_2,
+ pat.f_iter,
+ pat.f_exponent,
+ )
+ if pat.tex_pattern_type == "magnet" and pat.magnet_style == "mandel":
+ text_strg += "%s mandel %s \n" % (pat.magnet_type, pat.f_iter)
+ if pat.tex_pattern_type == "magnet" and pat.magnet_style == "julia":
+ text_strg += "%s julia <%.4g, %.4g> %s\n" % (
+ pat.magnet_type,
+ pat.julia_complex_1,
+ pat.julia_complex_2,
+ pat.f_iter,
+ )
+ if pat.tex_pattern_type in {"mandel", "julia", "magnet"}:
+ text_strg += "interior %s, %.4g\n" % (pat.f_ior, pat.f_ior_fac)
+ text_strg += "exterior %s, %.4g\n" % (pat.f_eor, pat.f_eor_fac)
+ if pat.tex_pattern_type == "gradient":
+ text_strg += "<%s, %s, %s> \n" % (
+ pat.grad_orient_x,
+ pat.grad_orient_y,
+ pat.grad_orient_z,
+ )
+ if pat.tex_pattern_type == "pavement":
+ num_tiles = pat.pave_tiles
+ num_pattern = 1
+ if pat.pave_sides == "4" and pat.pave_tiles == 3:
+ num_pattern = pat.pave_pat_2
+ if pat.pave_sides == "6" and pat.pave_tiles == 3:
+ num_pattern = pat.pave_pat_3
+ if pat.pave_sides == "3" and pat.pave_tiles == 4:
+ num_pattern = pat.pave_pat_3
+ if pat.pave_sides == "3" and pat.pave_tiles == 5:
+ num_pattern = pat.pave_pat_4
+ if pat.pave_sides == "4" and pat.pave_tiles == 4:
+ num_pattern = pat.pave_pat_5
+ if pat.pave_sides == "6" and pat.pave_tiles == 4:
+ num_pattern = pat.pave_pat_7
+ if pat.pave_sides == "4" and pat.pave_tiles == 5:
+ num_pattern = pat.pave_pat_12
+ if pat.pave_sides == "3" and pat.pave_tiles == 6:
+ num_pattern = pat.pave_pat_12
+ if pat.pave_sides == "6" and pat.pave_tiles == 5:
+ num_pattern = pat.pave_pat_22
+ if pat.pave_sides == "4" and pat.pave_tiles == 6:
+ num_pattern = pat.pave_pat_35
+ if pat.pave_sides == "6" and pat.pave_tiles == 6:
+ num_tiles = 5
+ text_strg += "number_of_sides %s number_of_tiles %s pattern %s form %s \n" % (
+ pat.pave_sides,
+ num_tiles,
+ num_pattern,
+ pat.pave_form,
+ )
+ # ------------------------- functions ------------------------- #
+ if pat.tex_pattern_type == "function":
+ text_strg += "{ %s" % pat.func_list
+ text_strg += "(x"
+ if pat.func_plus_x != "NONE":
+ if pat.func_plus_x == "increase":
+ text_strg += "*"
+ if pat.func_plus_x == "plus":
+ text_strg += "+"
+ text_strg += "%.4g" % pat.func_x
+ text_strg += ",y"
+ if pat.func_plus_y != "NONE":
+ if pat.func_plus_y == "increase":
+ text_strg += "*"
+ if pat.func_plus_y == "plus":
+ text_strg += "+"
+ text_strg += "%.4g" % pat.func_y
+ text_strg += ",z"
+ if pat.func_plus_z != "NONE":
+ if pat.func_plus_z == "increase":
+ text_strg += "*"
+ if pat.func_plus_z == "plus":
+ text_strg += "+"
+ text_strg += "%.4g" % pat.func_z
+ sort = -1
+ if pat.func_list in {
+ "f_comma",
+ "f_crossed_trough",
+ "f_cubic_saddle",
+ "f_cushion",
+ "f_devils_curve",
+ "f_enneper",
+ "f_glob",
+ "f_heart",
+ "f_hex_x",
+ "f_hex_y",
+ "f_hunt_surface",
+ "f_klein_bottle",
+ "f_kummer_surface_v1",
+ "f_lemniscate_of_gerono",
+ "f_mitre",
+ "f_nodal_cubic",
+ "f_noise_generator",
+ "f_odd",
+ "f_paraboloid",
+ "f_pillow",
+ "f_piriform",
+ "f_quantum",
+ "f_quartic_paraboloid",
+ "f_quartic_saddle",
+ "f_sphere",
+ "f_steiners_roman",
+ "f_torus_gumdrop",
+ "f_umbrella",
+ }:
+ sort = 0
+ if pat.func_list in {
+ "f_bicorn",
+ "f_bifolia",
+ "f_boy_surface",
+ "f_superellipsoid",
+ "f_torus",
+ }:
+ sort = 1
+ if pat.func_list in {
+ "f_ellipsoid",
+ "f_folium_surface",
+ "f_hyperbolic_torus",
+ "f_kampyle_of_eudoxus",
+ "f_parabolic_torus",
+ "f_quartic_cylinder",
+ "f_torus2",
+ }:
+ sort = 2
+ if pat.func_list in {
+ "f_blob2",
+ "f_cross_ellipsoids",
+ "f_flange_cover",
+ "f_isect_ellipsoids",
+ "f_kummer_surface_v2",
+ "f_ovals_of_cassini",
+ "f_rounded_box",
+ "f_spikes_2d",
+ "f_strophoid",
+ }:
+ sort = 3
+ if pat.func_list in {
+ "f_algbr_cyl1",
+ "f_algbr_cyl2",
+ "f_algbr_cyl3",
+ "f_algbr_cyl4",
+ "f_blob",
+ "f_mesh1",
+ "f_poly4",
+ "f_spikes",
+ }:
+ sort = 4
+ if pat.func_list in {
+ "f_devils_curve_2d",
+ "f_dupin_cyclid",
+ "f_folium_surface_2d",
+ "f_hetero_mf",
+ "f_kampyle_of_eudoxus_2d",
+ "f_lemniscate_of_gerono_2d",
+ "f_polytubes",
+ "f_ridge",
+ "f_ridged_mf",
+ "f_spiral",
+ "f_witch_of_agnesi",
+ }:
+ sort = 5
+ if pat.func_list in {
+ "f_helix1",
+ "f_helix2",
+ "f_piriform_2d",
+ "f_strophoid_2d",
+ }:
+ sort = 6
+ if pat.func_list == "f_helical_torus":
+ sort = 7
+ if sort > -1:
+ text_strg += ",%.4g" % pat.func_P0
+ if sort > 0:
+ text_strg += ",%.4g" % pat.func_P1
+ if sort > 1:
+ text_strg += ",%.4g" % pat.func_P2
+ if sort > 2:
+ text_strg += ",%.4g" % pat.func_P3
+ if sort > 3:
+ text_strg += ",%.4g" % pat.func_P4
+ if sort > 4:
+ text_strg += ",%.4g" % pat.func_P5
+ if sort > 5:
+ text_strg += ",%.4g" % pat.func_P6
+ if sort > 6:
+ text_strg += ",%.4g" % pat.func_P7
+ text_strg += ",%.4g" % pat.func_P8
+ text_strg += ",%.4g" % pat.func_P9
+ text_strg += ")}\n"
+ # ------------------------- end functions ------------------------- #
+ if pat.tex_pattern_type not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ text_strg += "color_map {\n"
+ if tex.use_color_ramp:
+ for num_color, el in enumerate(tex.color_ramp.elements, start=1):
+ pos = el.position
+ col = el.color
+ col_r, col_g, col_b, col_a = col[0], col[1], col[2], 1 - col[3]
+ if pat.tex_pattern_type not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ text_strg += "[%.4g color rgbf<%.4g,%.4g,%.4g,%.4g>] \n" % (
+ pos,
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type in {"brick", "checker"} and num_color < 3:
+ text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "hexagon" and num_color < 4:
+ text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "square" and num_color < 5:
+ text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ if pat.tex_pattern_type == "triangular" and num_color < 7:
+ text_strg += "color rgbf<%.4g,%.4g,%.4g,%.4g> \n" % (
+ col_r,
+ col_g,
+ col_b,
+ col_a,
+ )
+ else:
+ text_strg += "[0 color rgbf<0,0,0,1>]\n"
+ text_strg += "[1 color rgbf<1,1,1,0>]\n"
+ if pat.tex_pattern_type not in {
+ "checker",
+ "hexagon",
+ "square",
+ "triangular",
+ "brick",
+ }:
+ text_strg += "} \n"
+ if pat.tex_pattern_type == "brick":
+ text_strg += "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n" % (
+ pat.brick_size_x,
+ pat.brick_size_y,
+ pat.brick_size_z,
+ pat.brick_mortar,
+ )
+ text_strg += "%s \n" % mapping_dif
+ text_strg += "rotate <%.4g,%.4g,%.4g> \n" % (
+ pat.tex_rot_x,
+ pat.tex_rot_y,
+ pat.tex_rot_z,
+ )
+ text_strg += "turbulence <%.4g,%.4g,%.4g> \n" % (
+ pat.warp_turbulence_x,
+ pat.warp_turbulence_y,
+ pat.warp_turbulence_z,
+ )
+ text_strg += "octaves %s \n" % pat.modifier_octaves
+ text_strg += "lambda %.4g \n" % pat.modifier_lambda
+ text_strg += "omega %.4g \n" % pat.modifier_omega
+ text_strg += "frequency %.4g \n" % pat.modifier_frequency
+ text_strg += "phase %.4g \n" % pat.modifier_phase
+ text_strg += "}\n\n"
+ text_strg += "#declare f%s=\n" % pat_name
+ text_strg += "function{pigment{%s}}\n" % pat_name
+ text_strg += "\n"
+ return text_strg
diff --git a/render_povray/texturing_properties.py b/render_povray/texturing_properties.py
index fbfb8125..8dc2038d 100755
--- a/render_povray/texturing_properties.py
+++ b/render_povray/texturing_properties.py
@@ -29,7 +29,7 @@ class MaterialTextureSlot(PropertyGroup):
"""Declare material texture slot level properties for UI and translated to POV."""
bl_idname = ("pov_texture_slots",)
- bl_description = ("Texture_slots from Blender-2.79",)
+ bl_description = ("Nodeless texture slots",)
# Adding a "real" texture datablock as property is not possible
# (or at least not easy through a dynamically populated EnumProperty).
@@ -367,86 +367,17 @@ class MaterialTextureSlot(PropertyGroup):
description="Amount texture affects texture coordinates of next channels",
default=0.0,
)
-
# ---------------------------------------------------------------- #
-
- blend_factor: FloatProperty(
- name="Blend",
- description="Amount texture affects color progression of the " "background",
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- )
-
- horizon_factor: FloatProperty(
- name="Horizon",
- description="Amount texture affects color of the horizon" "",
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- )
-
- object: StringProperty(
- name="Object",
- description="Object to use for mapping with Object texture coordinates",
- default="",
- )
-
- texture_coords: EnumProperty(
- name="Coordinates",
- description="Texture coordinates used to map the texture onto the background",
- items=(
- ("VIEW", "View", "Use view vector for the texture coordinates"),
- (
- "GLOBAL",
- "Global",
- "Use global coordinates for the texture coordinates (interior mist)",
- ),
- (
- "ANGMAP",
- "AngMap",
- "Use 360 degree angular coordinates, e.g. for spherical light probes",
- ),
- ("SPHERE", "Sphere", "For 360 degree panorama sky, spherical mapped, only top half"),
- ("EQUIRECT", "Equirectangular", "For 360 degree panorama sky, equirectangular mapping"),
- ("TUBE", "Tube", "For 360 degree panorama sky, cylindrical mapped, only top half"),
- ("OBJECT", "Object", "Use linked object’s coordinates for texture coordinates"),
- ),
- default="VIEW",
- )
-
- use_map_blend: BoolProperty(
- name="Blend Map", description="Affect the color progression of the background", default=True
- )
-
- use_map_horizon: BoolProperty(
- name="Horizon Map", description="Affect the color of the horizon", default=False
- )
-
- use_map_zenith_down: BoolProperty(
- name="", description="Affect the color of the zenith below", default=False
- )
-
- use_map_zenith_up: BoolProperty(
- name="Zenith Up Map", description="Affect the color of the zenith above", default=False
- )
-
- zenith_down_factor: FloatProperty(
- name="Zenith Down",
- description="Amount texture affects color of the zenith below",
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- )
-
- zenith_up_factor: FloatProperty(
- name="Zenith Up",
- description="Amount texture affects color of the zenith above",
- soft_min=0.0,
- soft_max=1.0,
- default=1.0,
- )
-
+ # so that its for loop stays one level less nested
+ # used_texture_slots generator expression requires :
+ def __iter__(self):
+ return self
+ def __next__(self):
+ tex = bpy.data.textures[self.texture]
+ while tex.pov.active_texture_index < len(bpy.data.textures): # XXX should use slots count
+ tex.pov.active_texture_index += 1
+ raise StopIteration
+ # ---------------------------------------------------------------- #
# ---------------------------------------------------------------- #
# Texture slots (World context) exported as POV texture properties.
diff --git a/render_povray/ui_core.py b/render_povray/ui_core.py
new file mode 100644
index 00000000..2768bea9
--- /dev/null
+++ b/render_povray/ui_core.py
@@ -0,0 +1,280 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# <pep8 compliant>
+
+"""User interface imports and preferences for the addon."""
+
+# 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 (
+# Operator,
+# Menu,
+# UIList,
+# Panel,
+# Brush,
+# Material,
+# Light,
+# World,
+# ParticleSettings,
+# FreestyleLineStyle,
+# )
+
+# from bl_operators.presets import AddPresetBase
+
+from . import (
+ render_gui,
+ scenography_gui,
+ model_gui,
+ shading_gui,
+ texturing_gui,
+ nodes, # for POV specific nodes
+ scripting_gui,
+ update_files,
+)
+
+
+# ------------ POV-Centric WORKSPACE ------------ #
+@persistent
+def pov_centric_moray_like_workspace(dummy):
+ """Set up a POV centric Workspace if addon was activated and saved as default renderer.
+
+ This would bring a ’_RestrictData’ error because UI needs to be fully loaded before
+ workspace changes so registering this function in bpy.app.handlers is needed.
+ By default handlers are freed when loading new files, but here we want the handler
+ to stay running across multiple files as part of this add-on. That is why the
+ bpy.app.handlers.persistent decorator is used (@persistent) above.
+ """
+ # Scripting workspace may have been altered from factory though, so should
+ # we put all within a Try... Except AttributeErrors ? Any better solution ?
+ # Should it simply not run when opening existing file? be a preferences operator to create
+ # Moray like workspace
+
+
+ # -----------------------------------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
+ try:
+ bpy.ops.wm.blend_strings_utf8_validate()
+ except BaseException as e:
+ print(e.__doc__)
+ print("An exception occurred: {}".format(e))
+ pass
+ # --------------------------------Workspaces------------------------------- #
+
+ # If this file is not the default do nothing so as to not mess up project dependant workspaces
+ if bpy.data.filepath:
+ return
+
+ 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"
+ )
+ return
+ if "POV-Ed" not in available_workspaces:
+ print(
+ "\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="Geometry Nodes", 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="Rendering", 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()
+
+
+# class TextureTypePanel(TextureButtonsPanel):
+
+# @classmethod
+# def poll(cls, context):
+# tex = context.texture
+# engine = context.scene.render.engine
+# return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES))
+
+
+def register():
+ update_files.register()
+ render_gui.register()
+ scenography_gui.register()
+ model_gui.register()
+ shading_gui.register()
+ texturing_gui.register()
+ nodes.register()
+ scripting_gui.register()
+
+ if pov_centric_moray_like_workspace not in bpy.app.handlers.load_post:
+ bpy.app.handlers.load_post.append(pov_centric_moray_like_workspace)
+
+
+def unregister():
+ if pov_centric_moray_like_workspace in bpy.app.handlers.load_post:
+ bpy.app.handlers.load_post.remove(pov_centric_moray_like_workspace)
+
+ scripting_gui.unregister()
+ nodes.unregister()
+ texturing_gui.unregister()
+ shading_gui.unregister()
+ model_gui.unregister()
+ scenography_gui.unregister()
+ render_gui.unregister()
+ update_files.unregister()
diff --git a/render_povray/update_files.py b/render_povray/update_files.py
index cc736a98..3c0aa28a 100755
--- a/render_povray/update_files.py
+++ b/render_povray/update_files.py
@@ -40,7 +40,7 @@ def update2_0_0_9():
# XXX We could also store the new name, but as it is just the same without leading pov_ ...
# Get default values of pov scene props.
old_sce_props = {
- k: getattr(bpy.types.Scene, k)[1].get('default', None)
+ k: getattr(bpy.types.Scene, k)[1].get("default", None)
for k in [
"pov_tempfiles_enable",
"pov_deletefiles_enable",
@@ -90,7 +90,7 @@ def update2_0_0_9():
# Get default values of pov material props.
old_mat_props = {
- k: getattr(bpy.types.Material, k)[1].get('default', None)
+ k: getattr(bpy.types.Material, k)[1].get("default", None)
for k in [
"pov_irid_enable",
"pov_mirror_use_IOR",
@@ -113,7 +113,7 @@ def update2_0_0_9():
# Get default values of pov texture props.
old_tex_props = {
- k: getattr(bpy.types.Texture, k)[1].get('default', None)
+ k: getattr(bpy.types.Texture, k)[1].get("default", None)
for k in [
"pov_tex_gamma_enable",
"pov_tex_gamma_value",
@@ -123,7 +123,7 @@ def update2_0_0_9():
# Get default values of pov object props.
old_obj_props = {
- k: getattr(bpy.types.Object, k)[1].get('default', None)
+ k: getattr(bpy.types.Object, k)[1].get("default", None)
for k in [
"pov_importance_value",
"pov_collect_photons",
@@ -133,7 +133,7 @@ def update2_0_0_9():
# Get default values of pov camera props.
old_cam_props = {
- k: getattr(bpy.types.Camera, k)[1].get('default', None)
+ k: getattr(bpy.types.Camera, k)[1].get("default", None)
for k in [
"pov_dof_enable",
"pov_dof_aperture",
@@ -147,8 +147,7 @@ def update2_0_0_9():
# Get default values of pov text props.
old_txt_props = {
- k: getattr(bpy.types.Text, k)[1].get('default', None)
- for k in ["pov_custom_code"]
+ k: getattr(bpy.types.Text, k)[1].get("default", None) for k in ["pov_custom_code"]
}
# -----------------------------------------------------------------------------
@@ -195,7 +194,7 @@ class RenderCopySettings(bpy.types.Operator):
bl_idname = "scene.pov_update_properties"
bl_label = "PovRay render: Update to script v0.0.9"
- bl_option = {'REGISTER'}
+ bl_option = {"REGISTER"}
@classmethod
def poll(cls, context):
@@ -203,7 +202,7 @@ class RenderCopySettings(bpy.types.Operator):
def execute(self, context):
update2_0_0_9()
- return {'FINISHED'}
+ return {"FINISHED"}
def register():
@@ -274,13 +273,13 @@ def register():
Scene.pov_media_color = FloatVectorProperty(
name="Media Color",
description="The atmospheric media color",
- subtype='COLOR',
+ subtype="COLOR",
precision=4,
step=0.01,
min=0,
soft_max=1,
default=(0.001, 0.001, 0.001),
- options={'ANIMATABLE'},
+ options={"ANIMATABLE"},
)
Scene.pov_baking_enable = BoolProperty(
@@ -561,12 +560,6 @@ def register():
default=False,
)
- Mat.pov_mirror_metallic = BoolProperty(
- name="Metallic Reflection",
- description="mirror reflections get colored as diffuse (for metallic materials)",
- default=False,
- )
-
Mat.pov_conserve_energy = BoolProperty(
name="Conserve Energy",
description="Light transmitted is more correctly reduced by mirror reflections, also the sum of diffuse and translucency gets reduced below one ",
@@ -606,13 +599,13 @@ def register():
Mat.pov_interior_fade_color = FloatVectorProperty(
name="Fade Color",
description="Color of filtered attenuation for transparent materials",
- subtype='COLOR',
+ subtype="COLOR",
precision=4,
step=0.01,
min=0.0,
soft_max=1.0,
default=(0, 0, 0),
- options={'ANIMATABLE'},
+ options={"ANIMATABLE"},
)
Mat.pov_caustics_enable = BoolProperty(
@@ -839,7 +832,6 @@ def unregister():
del Scene.pov_comments_enable
del Mat.pov_irid_enable
del Mat.pov_mirror_use_IOR
- del Mat.pov_mirror_metallic
del Mat.pov_conserve_energy
del Mat.pov_irid_amount
del Mat.pov_irid_thickness
diff --git a/render_povray/df3_library.py b/render_povray/voxel_lib.py
index 02497595..02497595 100755..100644
--- a/render_povray/df3_library.py
+++ b/render_povray/voxel_lib.py