810 lines
32 KiB
Python
Executable File
810 lines
32 KiB
Python
Executable File
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
"""Write the POV file using this file's functions and some from other modules then render it."""
|
|
|
|
import bpy
|
|
import subprocess
|
|
import os
|
|
from sys import platform
|
|
#import time
|
|
from math import (
|
|
pi,
|
|
) # maybe move to scenography.py and topology_*****_data.py respectively with smoke and matrix
|
|
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
|
|
|
|
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 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 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 .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):
|
|
|
|
"""Remove hyphen characters from a string to avoid POV errors."""
|
|
|
|
return name.replace("-", "")
|
|
|
|
|
|
def safety(name, ref_level_bound):
|
|
"""append suffix characters to names of various material declinations.
|
|
|
|
Material declinations are necessary to POV syntax and used in shading.py
|
|
by the pov_has_no_specular_maps function to create the finish map trick and
|
|
the suffixes avoid name collisions.
|
|
Keyword arguments:
|
|
name -- the initial material name as a string
|
|
ref_level_bound -- the enum number of the ref_level_bound being written:
|
|
ref_level_bound=1 is for texture with No specular nor Mirror reflection
|
|
ref_level_bound=2 is for texture with translation of spec and mir levels
|
|
for when no map influences them
|
|
ref_level_bound=3 is for texture with Maximum Spec and Mirror
|
|
"""
|
|
# All the try except clause below seems useless as each time
|
|
# prefix rewritten even after and outside of it what was the point?
|
|
# It may not even be any longer possible to feed no arg from Blender UI
|
|
# try:
|
|
# if name: # if int(name) > 0: # could be zero if no argument provided
|
|
# # and always triggered exception so is this similar ?
|
|
# prefix = "shader"
|
|
# except BaseException as e:
|
|
# print(e.__doc__)
|
|
# print('An exception occurred: {}'.format(e))
|
|
# prefix = "" # rewritten below...
|
|
prefix = "shader_"
|
|
name = string_strip_hyphen(name)
|
|
if ref_level_bound == 2:
|
|
return prefix + name
|
|
# implicit else-if (no return yet)
|
|
if ref_level_bound == 1:
|
|
return prefix + name + "0" # used for 0 of specular map
|
|
# implicit else-if (no return yet)
|
|
if ref_level_bound == 3:
|
|
return prefix + name + "1" # used for 1 of specular map
|
|
|
|
|
|
# -------- end safety string name material
|
|
|
|
|
|
csg_list = []
|
|
|
|
|
|
def is_renderable(ob):
|
|
"""test for objects flagged as hidden or boolean operands not to render"""
|
|
return not ob.hide_render and ob not in csg_list
|
|
|
|
|
|
def renderable_objects():
|
|
"""test for non hidden, non boolean operands objects to render"""
|
|
return [ob for ob in bpy.data.objects if is_renderable(ob)]
|
|
|
|
|
|
def non_renderable_objects():
|
|
"""Boolean operands only. Not to render"""
|
|
return list(csg_list)
|
|
|
|
|
|
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 = ""
|
|
if tabtype == 'SPACE':
|
|
tab_str = spaces * " "
|
|
elif tabtype == 'NONE':
|
|
tab_str = ""
|
|
elif tabtype == 'TAB':
|
|
tab_str = "\t"
|
|
return tab_str
|
|
|
|
|
|
|
|
'''
|
|
|
|
# below properties not added to __init__ yet to avoid conflicts with material sss scale
|
|
# unless it would override then should be interfaced also in scene units property tab
|
|
|
|
# if scene.pov.sslt_enable:
|
|
# file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit)
|
|
# file.write(" subsurface {\n")
|
|
# file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
|
|
# if scene.pov.sslt_radiosity:
|
|
# file.write(" radiosity on\n")
|
|
# file.write("}\n")
|
|
|
|
'''
|
|
|
|
|
|
# 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 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 """
|
|
|
|
with open(filename, "w") as file:
|
|
# Only for testing
|
|
if not scene:
|
|
scene = bpy.data.scenes[0]
|
|
|
|
render = scene.render
|
|
world = scene.world
|
|
global comments
|
|
comments = scene.pov.comments_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 = PovRender._locate_binary()
|
|
|
|
if using_uberpov:
|
|
print("Unofficial UberPOV feature set chosen in preferences")
|
|
else:
|
|
print("Official POV-Ray 3.7 feature set chosen in preferences")
|
|
if 'uber' in pov_binary:
|
|
print("The name of the binary suggests you are probably rendering with Uber POV engine")
|
|
else:
|
|
print("The name of the binary suggests you are probably rendering with standard POV engine")
|
|
|
|
|
|
def unique_name(name, name_seq):
|
|
"""Increment any generated POV name that could get identical to avoid collisions"""
|
|
|
|
if name not in name_seq:
|
|
name = string_strip_hyphen(name)
|
|
return name
|
|
|
|
name_orig = name
|
|
i = 1
|
|
while name in name_seq:
|
|
name = "%s_%.3d" % (name_orig, i)
|
|
i += 1
|
|
name = string_strip_hyphen(name)
|
|
return name
|
|
|
|
material_names_dictionary = {}
|
|
DEF_MAT_NAME = "" # or "Default"?
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
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(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:
|
|
file.write(" adc_bailout %.6f\n" % scene.pov.adc_bailout)
|
|
file.write(" ambient_light <%.6f,%.6f,%.6f>\n" % scene.pov.ambient_light[:])
|
|
file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n" % scene.pov.irid_wavelength[:])
|
|
file.write(" number_of_waves %s\n" % scene.pov.number_of_waves)
|
|
file.write(" noise_generator %s\n" % scene.pov.noise_generator)
|
|
if scene.pov.radio_enable:
|
|
tab_write(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
|
|
for material in bpy.data.materials:
|
|
if material.pov_subsurface_scattering.use and once_sss:
|
|
# In pov, the scale has reversed influence compared to blender. these number
|
|
# should correct that
|
|
tab_write(file,
|
|
"mm_per_unit %.6f\n" % (material.pov_subsurface_scattering.scale * 1000.0)
|
|
)
|
|
# 1000 rather than scale * (-100.0) + 15.0))
|
|
|
|
# In POV-Ray, the scale factor for all subsurface shaders needs to be the same
|
|
|
|
# formerly sslt_samples were multiplied by 100 instead of 10
|
|
sslt_samples = (11 - material.pov_subsurface_scattering.error_threshold) * 10
|
|
|
|
tab_write(file, "subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10))
|
|
once_sss = 0
|
|
|
|
if world and once_ambient:
|
|
tab_write(file, "ambient_light rgb<%.3g, %.3g, %.3g>\n" % world.pov.ambient_color[:])
|
|
once_ambient = 0
|
|
|
|
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:
|
|
file.write(
|
|
"//----------------------------------------------\n"
|
|
"//--Exported with POV-Ray exporter for Blender--\n"
|
|
"//----------------------------------------------\n\n"
|
|
)
|
|
file.write("#version 3.7;\n") # Switch below as soon as 3.8 beta gets easy linked
|
|
# file.write("#version 3.8;\n")
|
|
file.write(
|
|
"#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n"
|
|
)
|
|
if comments:
|
|
file.write("\n//--Global settings--\n\n")
|
|
|
|
export_global_settings(scene)
|
|
|
|
if comments:
|
|
file.write("\n//--Custom Code--\n\n")
|
|
scripting.export_custom_code(file)
|
|
|
|
if comments:
|
|
file.write("\n//--Patterns Definitions--\n\n")
|
|
local_pattern_names = []
|
|
for texture in bpy.data.textures: # ok?
|
|
if texture.users > 0:
|
|
current_pat_name = string_strip_hyphen(bpy.path.clean_name(texture.name))
|
|
# string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above
|
|
local_pattern_names.append(current_pat_name)
|
|
# use above list to prevent writing texture instances several times and assign in mats?
|
|
if (
|
|
texture.type not in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type == 'emulator'
|
|
) or (texture.type in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type != 'emulator'):
|
|
file.write("\n#declare PAT_%s = \n" % current_pat_name)
|
|
file.write(texturing_procedural.export_pattern(texture))
|
|
file.write("\n")
|
|
if comments:
|
|
file.write("\n//--Background--\n\n")
|
|
|
|
scenography.export_world(file, scene.world, scene, global_matrix, tab_write)
|
|
|
|
if comments:
|
|
file.write("\n//--Cameras--\n\n")
|
|
|
|
scenography.export_camera(file, scene, global_matrix, render, tab_write)
|
|
|
|
if comments:
|
|
file.write("\n//--Lamps--\n\n")
|
|
|
|
for ob in bpy.data.objects:
|
|
if ob.type == 'MESH':
|
|
for mod in ob.modifiers:
|
|
if mod.type == 'BOOLEAN' and mod.object not in csg_list:
|
|
csg_list.append(mod.object)
|
|
if csg_list:
|
|
csg = False
|
|
sel = non_renderable_objects()
|
|
# export non rendered boolean objects operands
|
|
model_all.objects_loop(
|
|
file,
|
|
scene,
|
|
sel,
|
|
csg,
|
|
material_names_dictionary,
|
|
unpacked_images,
|
|
tab_level,
|
|
tab_write,
|
|
info_callback,
|
|
)
|
|
|
|
csg = True
|
|
sel = renderable_objects()
|
|
|
|
scenography.export_lights(
|
|
[L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')],
|
|
file,
|
|
scene,
|
|
global_matrix,
|
|
tab_write,
|
|
)
|
|
|
|
if comments:
|
|
file.write("\n//--Rainbows--\n\n")
|
|
scenography.export_rainbows(
|
|
[L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')],
|
|
file,
|
|
scene,
|
|
global_matrix,
|
|
tab_write,
|
|
)
|
|
|
|
if comments:
|
|
file.write("\n//--Special Curves--\n\n")
|
|
for c in sel:
|
|
if c.is_modified(scene, 'RENDER'):
|
|
continue # don't export as pov curves objects with modifiers, but as mesh
|
|
# Implicit else-if (as not skipped by previous "continue")
|
|
if c.type == 'CURVE' and (c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'}):
|
|
model_curve_topology.export_curves(file, c, tab_write)
|
|
|
|
if comments:
|
|
file.write("\n//--Material Definitions--\n\n")
|
|
# write a default pigment for objects with no material (comment out to show black)
|
|
file.write("#default{ pigment{ color srgb 0.8 }}\n")
|
|
# Convert all materials to strings we can access directly per vertex.
|
|
# exportMaterials()
|
|
shading.write_material(
|
|
file,
|
|
using_uberpov,
|
|
DEF_MAT_NAME,
|
|
tab_write,
|
|
comments,
|
|
unique_name,
|
|
material_names_dictionary,
|
|
None,
|
|
) # default material
|
|
for material in bpy.data.materials:
|
|
if material.users > 0:
|
|
r, g, b, a = material.diffuse_color[:]
|
|
pigment_color = "pigment {rgbt <%.4g,%.4g,%.4g,%.4g>}" % (r, g, b, 1 - a)
|
|
if material.pov.material_use_nodes:
|
|
# Also make here other pigment_color fallback using BSDF node main color ?
|
|
ntree = material.node_tree
|
|
pov_mat_name = string_strip_hyphen(bpy.path.clean_name(material.name))
|
|
if len(ntree.nodes) == 0:
|
|
file.write('#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color))
|
|
else:
|
|
nodes_fn.write_nodes(pov_mat_name, ntree, file)
|
|
|
|
for node in ntree.nodes:
|
|
if node:
|
|
if node.bl_idname == "PovrayOutputNode":
|
|
if node.inputs["Texture"].is_linked:
|
|
for link in ntree.links:
|
|
if link.to_node.bl_idname == "PovrayOutputNode":
|
|
pov_mat_name = (
|
|
string_strip_hyphen(
|
|
bpy.path.clean_name(link.from_node.name)
|
|
)
|
|
+ "_%s" % pov_mat_name
|
|
)
|
|
else:
|
|
file.write(
|
|
'#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color)
|
|
)
|
|
else:
|
|
shading.write_material(
|
|
file,
|
|
using_uberpov,
|
|
DEF_MAT_NAME,
|
|
tab_write,
|
|
comments,
|
|
unique_name,
|
|
material_names_dictionary,
|
|
material,
|
|
)
|
|
# attributes are all the variables needed by the other python file...
|
|
if comments:
|
|
file.write("\n")
|
|
|
|
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()
|
|
model_all.objects_loop(
|
|
file,
|
|
scene,
|
|
sel,
|
|
csg,
|
|
material_names_dictionary,
|
|
unpacked_images,
|
|
tab_level,
|
|
tab_write,
|
|
info_callback,
|
|
)
|
|
# totime = time.time() - tbefore
|
|
# print("objects_loop took" + str(totime))
|
|
|
|
# What follow used to happen here:
|
|
# export_camera()
|
|
# 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
|
|
# CR: Baking should be a special case than. If "baking", than we could change the order.
|
|
|
|
if not file.closed:
|
|
file.close()
|
|
|
|
def write_pov_ini(filename_ini, filename_log, filename_pov, filename_image):
|
|
"""Write ini file."""
|
|
feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray
|
|
global using_uberpov
|
|
using_uberpov = feature_set == 'uberpov'
|
|
# scene = bpy.data.scenes[0]
|
|
scene = bpy.context.scene
|
|
render = scene.render
|
|
|
|
x = int(render.resolution_x * render.resolution_percentage * 0.01)
|
|
y = int(render.resolution_y * render.resolution_percentage * 0.01)
|
|
|
|
with open(filename_ini, "w") as file:
|
|
file.write("Version=3.7\n")
|
|
# write povray text stream to temporary file of same name with _log suffix
|
|
# file.write("All_File='%s'\n" % filename_log)
|
|
# DEBUG.OUT log if none specified:
|
|
file.write("All_File=1\n")
|
|
|
|
file.write("Input_File_Name='%s'\n" % filename_pov)
|
|
file.write("Output_File_Name='%s'\n" % filename_image)
|
|
|
|
file.write("Width=%d\n" % x)
|
|
file.write("Height=%d\n" % y)
|
|
|
|
# Border render.
|
|
if render.use_border:
|
|
file.write("Start_Column=%4g\n" % render.border_min_x)
|
|
file.write("End_Column=%4g\n" % render.border_max_x)
|
|
|
|
file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y))
|
|
file.write("End_Row=%4g\n" % (1.0 - render.border_min_y))
|
|
|
|
file.write("Bounding_Method=2\n") # The new automatic BSP is faster in most scenes
|
|
|
|
# Activated (turn this back off when better live exchange is done between the two programs
|
|
# (see next comment)
|
|
file.write("Display=1\n")
|
|
file.write("Pause_When_Done=0\n")
|
|
# PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the
|
|
# POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
|
|
file.write("Output_File_Type=N\n")
|
|
# file.write("Output_File_Type=T\n") # TGA, best progressive loading
|
|
file.write("Output_Alpha=1\n")
|
|
|
|
if scene.pov.antialias_enable:
|
|
# method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray
|
|
# needs higher sampling.
|
|
# aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5}
|
|
if using_uberpov:
|
|
method = {"0": 1, "1": 2, "2": 3}
|
|
else:
|
|
method = {"0": 1, "1": 2, "2": 2}
|
|
file.write("Antialias=on\n")
|
|
file.write("Antialias_Depth=%d\n" % scene.pov.antialias_depth)
|
|
file.write("Antialias_Threshold=%.3g\n" % scene.pov.antialias_threshold)
|
|
if using_uberpov and scene.pov.antialias_method == '2':
|
|
file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method])
|
|
file.write("Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence)
|
|
else:
|
|
file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method])
|
|
file.write("Antialias_Gamma=%.3g\n" % scene.pov.antialias_gamma)
|
|
if scene.pov.jitter_enable:
|
|
file.write("Jitter=on\n")
|
|
file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount)
|
|
else:
|
|
file.write("Jitter=off\n") # prevent animation flicker
|
|
|
|
else:
|
|
file.write("Antialias=off\n")
|
|
if not file.closed:
|
|
file.close()
|
|
|
|
|
|
# --------------------------------------------------------------------------------- #
|
|
# ----------------------------------- Operators ----------------------------------- #
|
|
# --------------------------------------------------------------------------------- #
|
|
class RenderPovTexturePreview(Operator):
|
|
"""Export only files necessary to texture preview and render image"""
|
|
|
|
bl_idname = "tex.preview_update"
|
|
bl_label = "Update preview"
|
|
|
|
def execute(self, context):
|
|
tex = bpy.context.object.active_material.active_texture # context.texture
|
|
tex_prev_name = string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev"
|
|
|
|
# Make sure Preview directory exists and is empty
|
|
if not os.path.isdir(preview_dir):
|
|
os.mkdir(preview_dir)
|
|
|
|
ini_prev_file = os.path.join(preview_dir, "Preview.ini")
|
|
input_prev_file = os.path.join(preview_dir, "Preview.pov")
|
|
output_prev_file = os.path.join(preview_dir, tex_prev_name)
|
|
# ---------------------------------- ini ---------------------------------- #
|
|
with open(ini_prev_file, "w") as file_ini:
|
|
file_ini.write('Version=3.8\n')
|
|
file_ini.write('Input_File_Name="%s"\n' % input_prev_file)
|
|
file_ini.write('Output_File_Name="%s.png"\n' % output_prev_file)
|
|
file_ini.write('Library_Path="%s"\n' % preview_dir)
|
|
file_ini.write('Width=256\n')
|
|
file_ini.write('Height=256\n')
|
|
file_ini.write('Pause_When_Done=0\n')
|
|
file_ini.write('Output_File_Type=N\n')
|
|
file_ini.write('Output_Alpha=1\n')
|
|
file_ini.write('Antialias=on\n')
|
|
file_ini.write('Sampling_Method=2\n')
|
|
file_ini.write('Antialias_Depth=3\n')
|
|
file_ini.write('-d\n')
|
|
if not file_ini.closed:
|
|
file_ini.close()
|
|
# ---------------------------------- pov ---------------------------------- #
|
|
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(texturing_procedural.export_pattern(tex))
|
|
|
|
file_pov.write("#declare Plane =\n")
|
|
file_pov.write("mesh {\n")
|
|
file_pov.write(
|
|
" triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
|
|
)
|
|
file_pov.write(
|
|
" triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
|
|
)
|
|
file_pov.write(" texture{%s}\n" % pat_name)
|
|
file_pov.write("}\n")
|
|
file_pov.write("object {Plane}\n")
|
|
file_pov.write("light_source {\n")
|
|
file_pov.write(" <0,4.38,-1.92e-07>\n")
|
|
file_pov.write(" color rgb<4, 4, 4>\n")
|
|
file_pov.write(" parallel\n")
|
|
file_pov.write(" point_at <0, 0, -1>\n")
|
|
file_pov.write("}\n")
|
|
file_pov.write("camera {\n")
|
|
file_pov.write(" location <0, 0, 0>\n")
|
|
file_pov.write(" look_at <0, 0, -1>\n")
|
|
file_pov.write(" right <-1.0, 0, 0>\n")
|
|
file_pov.write(" up <0, 1, 0>\n")
|
|
file_pov.write(" angle 96.805211\n")
|
|
file_pov.write(" rotate <-90.000003, -0.000000, 0.000000>\n")
|
|
file_pov.write(" translate <0.000000, 0.000000, 0.000000>\n")
|
|
file_pov.write("}\n")
|
|
if not file_pov.closed:
|
|
file_pov.close()
|
|
# ------------------------------- end write ------------------------------- #
|
|
|
|
pov_binary = PovRender._locate_binary()
|
|
|
|
if platform.startswith('win'):
|
|
with subprocess.Popen(
|
|
["%s" % pov_binary, "/EXIT", "%s" % ini_prev_file],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
) as p1:
|
|
p1.wait()
|
|
else:
|
|
with subprocess.Popen(
|
|
["%s" % pov_binary, "-d", "%s" % ini_prev_file],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
) as p1:
|
|
p1.wait()
|
|
|
|
tex.use_nodes = True
|
|
tree = tex.node_tree
|
|
links = tree.links
|
|
for n in tree.nodes:
|
|
tree.nodes.remove(n)
|
|
im = tree.nodes.new("TextureNodeImage")
|
|
path_prev = "%s.png" % output_prev_file
|
|
im.image = bpy.data.images.load(path_prev)
|
|
name = path_prev
|
|
name = name.split("/")
|
|
name = name[len(name) - 1]
|
|
im.name = name
|
|
im.location = 200, 200
|
|
previewer = tree.nodes.new('TextureNodeOutput')
|
|
previewer.label = "Preview"
|
|
previewer.location = 400, 400
|
|
links.new(im.outputs[0], previewer.inputs[0])
|
|
# tex.type="IMAGE" # makes clip extend possible
|
|
# tex.extension="CLIP"
|
|
return {'FINISHED'}
|
|
|
|
|
|
class RunPovTextRender(Operator):
|
|
"""Export files depending on text editor options and render image."""
|
|
|
|
bl_idname = "text.run"
|
|
bl_label = "Run"
|
|
bl_context = "text"
|
|
bl_description = "Run a render with this text only"
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
scene.pov.text_block = context.space_data.text.name
|
|
|
|
bpy.ops.render.render()
|
|
|
|
# empty text name property again
|
|
scene.pov.text_block = ""
|
|
return {'FINISHED'}
|
|
|
|
|
|
classes = (
|
|
#PovRender,
|
|
RenderPovTexturePreview,
|
|
RunPovTextRender,
|
|
)
|
|
|
|
|
|
def register():
|
|
for cls in classes:
|
|
register_class(cls)
|
|
scripting.register()
|
|
|
|
|
|
def unregister():
|
|
scripting.unregister()
|
|
for cls in reversed(classes):
|
|
unregister_class(cls)
|