Skip to content
Snippets Groups Projects
render.py 277 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ***** BEGIN GPL LICENSE BLOCK *****
    #
    # This program is free software; you can redistribute it and/or
    # modify it under the terms of the GNU General Public License
    # as published by the Free Software Foundation; either version 2
    # of the License, or (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software Foundation,
    # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # #**** END GPL LICENSE BLOCK #****
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    import bpy
    
    import subprocess
    import os
    import sys
    import time
    
    from math import atan, pi, degrees, sqrt, cos, sin
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    import random
    
    import platform  #
    import subprocess  #
    import tempfile  # generate temporary files with random names
    from bpy.types import Operator
    from imghdr import what  # imghdr is a python lib to identify image file types
    
    from bpy.utils import register_class
    
    from . import df3  # for smoke rendering
    from . import shading  # for BI POV shaders emulation
    from . import primitives  # for import and export of POV specific primitives
    from . import nodes  # for POV specific nodes
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    ##############################SF###########################
    
    ##############find image texture
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def imageFormat(imgF):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Identify input image filetypes to transmit to POV."""
        # First use the below explicit extensions to identify image file prospects
    
        ext = {
            'JPG': "jpeg",
            'JPEG': "jpeg",
            'GIF': "gif",
            'TGA': "tga",
            'IFF': "iff",
            'PPM': "ppm",
            'PNG': "png",
            'SYS': "sys",
            'TIFF': "tiff",
            'TIF': "tiff",
    
            'EXR': "exr",
            'HDR': "hdr",
    
        }.get(os.path.splitext(imgF)[-1].upper(), "")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Then, use imghdr to really identify the filetype as it can be different
    
            # maybe add a check for if path exists here?
            print(" WARNING: texture image has no extension")  # too verbose
    
            ext = what(imgF)  # imghdr is a python lib to identify image file types
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        return ext
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def imgMap(ts):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate mapping type from Blender UI to POV syntax and return that string."""
    
        image_map = ""
        if ts.mapping == 'FLAT':
            image_map = "map_type 0 "
        elif ts.mapping == 'SPHERE':
    
            image_map = "map_type 1 "
    
        elif ts.mapping == 'TUBE':
            image_map = "map_type 2 "
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        ## map_type 3 and 4 in development (?) (ENV in pov 3.8)
    
        ## for POV-Ray, currently they just seem to default back to Flat (type 0)
    
        # elif ts.mapping=="?":
    
        #    image_map = " map_type 3 "
    
        # elif ts.mapping=="?":
    
        #    image_map = " map_type 4 "
    
        if ts.texture.use_interpolation:
            image_map += " interpolate 2 "
        if ts.texture.extension == 'CLIP':
            image_map += " once "
    
        # image_map += "}"
        # if ts.mapping=='CUBE':
    
        #    image_map+= "warp { cubic } rotate <-90,0,180>"
        # no direct cube type mapping. Though this should work in POV 3.7
        # it doesn't give that good results(best suited to environment maps?)
    
        # if image_map == "":
    
        #    print(" No texture image  found ")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        return image_map
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate mapping transformations from Blender UI to POV syntax and return that string."""
    
        # XXX TODO: unchecked textures give error of variable referenced before assignment XXX
        # POV-Ray "scale" is not a number of repetitions factor, but ,its
        # inverse, a standard scale factor.
        # 0.5 Offset is needed relatively to scale because center of the
        # scale is 0.5,0.5 in blender and 0,0 in POV
    
        # Strange that the translation factor for scale is not the same as for
        # translate.
        # TODO: verify both matches with blender internal.
    
        image_map_transforms = (
            "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>"
            % (
                1.0 / ts.scale.x,
                1.0 / ts.scale.y,
                1.0 / ts.scale.z,
                0.5 - (0.5 / ts.scale.x) - (ts.offset.x),
                0.5 - (0.5 / ts.scale.y) - (ts.offset.y),
                ts.offset.z,
            )
        )
    
        # image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \
    
        # ( 1.0 / ts.scale.x,
        # 1.0 / ts.scale.y,
        # 1.0 / ts.scale.z,
        # (0.5 / ts.scale.x) + ts.offset.x,
        # (0.5 / ts.scale.y) + ts.offset.y,
        # ts.offset.z))
    
        # image_map_transforms = ("translate <-0.5,-0.5,0> scale <-1,-1,1> * <%.4g,%.4g,%.4g> translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \
    
        # (1.0 / ts.scale.x,
        # 1.0 / ts.scale.y,
        # 1.0 / ts.scale.z,
        # ts.offset.x,
        # ts.offset.y,
        # ts.offset.z))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def imgMapBG(wts):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate world mapping from Blender UI to POV syntax and return that string."""
    
        image_mapBG = ""
    
        # texture_coords refers to the mapping of world textures:
    
        if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL':
    
            image_mapBG = " map_type 0 "
    
        elif wts.texture_coords == 'ANGMAP':
            image_mapBG = " map_type 1 "
        elif wts.texture_coords == 'TUBE':
            image_mapBG = " map_type 2 "
    
        if wts.texture.use_interpolation:
            image_mapBG += " interpolate 2 "
        if wts.texture.extension == 'CLIP':
            image_mapBG += " once "
    
        # image_mapBG += "}"
        # if wts.mapping == 'CUBE':
    
        #   image_mapBG += "warp { cubic } rotate <-90,0,180>"
        # no direct cube type mapping. Though this should work in POV 3.7
        # it doesn't give that good results(best suited to environment maps?)
    
        # if image_mapBG == "":
    
        #    print(" No background texture image  found ")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        return image_mapBG
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def path_image(image):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Conform a path string to POV syntax to avoid POV errors."""
    
        return bpy.path.abspath(image.filepath, library=image.library).replace(
            "\\", "/"
        )
    
        # .replace("\\","/") to get only forward slashes as it's what POV prefers,
    
    Campbell Barton's avatar
    Campbell Barton committed
    # end find image texture
    # -----------------------------------------------------------------------------
    
    Campbell Barton's avatar
    Campbell Barton committed
    def string_strip_hyphen(name):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Remove hyphen characters from a string to avoid POV errors."""
    
    Campbell Barton's avatar
    Campbell Barton committed
        return name.replace("-", "")
    
    def safety(name, Level):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """append suffix characters to names of various material declinations.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        Material declinations are necessary to POV syntax and used in shading.py
        by the povHasnoSpecularMaps function to create the finish map trick and
        the suffixes avoid name collisions.
        Keyword arguments:
        name -- the initial material name as a string
        Level -- the enum number of the Level being written:
            Level=1 is for texture with No specular nor Mirror reflection
            Level=2 is for texture with translation of spec and mir levels
            for when no map influences them
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            Level=3 is for texture with Maximum Spec and Mirror
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        try:
    
    Campbell Barton's avatar
    Campbell Barton committed
            if int(name) > 0:
    
                prefix = "shader"
    
    Campbell Barton's avatar
    Campbell Barton committed
        except:
    
        prefix = "shader_"
    
    Campbell Barton's avatar
    Campbell Barton committed
        name = string_strip_hyphen(name)
    
            return prefix + name
    
            return prefix + name + "0"  # used for 0 of specular map
    
            return prefix + name + "1"  # used for 1 of specular map
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    ##############end safety string name material
    ##############################EndSF###########################
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    def is_renderable(scene, ob):
    
        return not ob.hide_render and ob not in csg_list
    
    def renderable_objects(scene):
        return [ob for ob in bpy.data.objects if is_renderable(scene, ob)]
    
    def no_renderable_objects(scene):
        return [ob for ob in csg_list]
    
    user_dir = bpy.utils.resource_path('USER')
    preview_dir = os.path.join(user_dir, "preview")
    
    
    ## Make sure Preview directory exists and is empty
    
    smokePath = os.path.join(preview_dir, "smoke.df3")
    
    def write_global_setting(scene,file):
        file.write("global_settings {\n")
        file.write("    assumed_gamma %.6f\n"%scene.pov.assumed_gamma)
    
        if scene.pov.global_settings_advanced:
            if scene.pov.radio_enable == False:
    
                file.write("    adc_bailout %.6f\n"%scene.pov.adc_bailout)
    
            file.write("    ambient_light <%.6f,%.6f,%.6f>\n"%scene.pov.ambient_light[:])
            file.write("    irid_wavelength <%.6f,%.6f,%.6f>\n"%scene.pov.irid_wavelength[:])
            file.write("    charset %s\n"%scene.pov.charset)
    
            file.write("    max_trace_level %s\n"%scene.pov.max_trace_level)
    
            file.write("    max_intersections %s\n"%scene.pov.max_intersections)
            file.write("    number_of_waves %s\n"%scene.pov.number_of_waves)
    
            file.write("    noise_generator %s\n"%scene.pov.noise_generator)
    
        # below properties not added to __init__ yet to avoid conflicts with material sss scale
    
        # unless it would override then should be interfaced also in scene units property tab
    
        # if scene.pov.sslt_enable:
    
            # file.write("    mm_per_unit %s\n"%scene.pov.mm_per_unit)
    
            # file.write("    subsurface {\n")
            # file.write("        samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
            # if scene.pov.sslt_radiosity:
                # file.write("        radiosity on\n")
            # file.write("}\n")
    
    
        if scene.pov.radio_enable:
            file.write("    radiosity {\n")
    
            file.write("        pretrace_start %.6f\n"%scene.pov.radio_pretrace_start)
            file.write("        pretrace_end %.6f\n"%scene.pov.radio_pretrace_end)
            file.write("        count %s\n"%scene.pov.radio_count)
            file.write("        nearest_count %s\n"%scene.pov.radio_nearest_count)
            file.write("        error_bound %.6f\n"%scene.pov.radio_error_bound)
            file.write("        recursion_limit %s\n"%scene.pov.radio_recursion_limit)
            file.write("        low_error_factor %.6f\n"%scene.pov.radio_low_error_factor)
            file.write("        gray_threshold %.6f\n"%scene.pov.radio_gray_threshold)
            file.write("        maximum_reuse %.6f\n"%scene.pov.radio_maximum_reuse)
            file.write("        minimum_reuse %.6f\n"%scene.pov.radio_minimum_reuse)
            file.write("        brightness %.6f\n"%scene.pov.radio_brightness)
    
            file.write("        adc_bailout %.6f\n"%scene.pov.radio_adc_bailout)
            if scene.pov.radio_normal:
    
                file.write("        normal on\n")
    
            if scene.pov.radio_always_sample:
    
                file.write("        always_sample on\n")
    
            if scene.pov.radio_media:
    
                file.write("        media on\n")
    
            if scene.pov.radio_subsurface:
                file.write("        subsurface on\n")
            file.write("    }\n")
    
        if scene.pov.photon_enable:
            file.write("    photons {\n")
            if scene.pov.photon_enable_count:
                file.write("        count %s\n"%scene.pov.photon_count)
            else:
                file.write("        spacing %.6g\n"%scene.pov.photon_spacing)
            if scene.pov.photon_gather:
                file.write("        gather %s, %s\n"%(scene.pov.photon_gather_min,scene.pov.photon_gather_max))
            if scene.pov.photon_autostop:
                file.write("        autostop %.4g\n"%scene.pov.photon_autostop_value)
            if scene.pov.photon_jitter_enable:
                file.write("        jitter %.4g\n"%scene.pov.photon_jitter)
            file.write("        max_trace_level %s\n"%scene.pov.photon_max_trace_level)
            if scene.pov.photon_adc:
                file.write("        adc_bailout %.6f\n"%scene.pov.photon_adc_bailout)
            if scene.pov.photon_media_enable:
                file.write("        media %s, %s\n"%(scene.pov.photon_media_steps,scene.pov.photon_media_factor))
    
            if scene.pov.photon_map_file_save_load in {'save'}:
                filePhName = 'Photon_map_file.ph'
                if scene.pov.photon_map_file != '':
                    filePhName = scene.pov.photon_map_file+'.ph'
                filePhDir = tempfile.gettempdir()
                path = bpy.path.abspath(scene.pov.photon_map_dir)
                if os.path.exists(path):
                    filePhDir = path
                fullFileName = os.path.join(filePhDir,filePhName)
                file.write('        save_file "%s"\n'%fullFileName)
                scene.pov.photon_map_file = fullFileName
            if scene.pov.photon_map_file_save_load in {'load'}:
                fullFileName = bpy.path.abspath(scene.pov.photon_map_file)
                if os.path.exists(fullFileName):
                    file.write('        load_file "%s"\n'%fullFileName)
    
            file.write("}\n")
        file.write("}\n")
    
    
    
    def write_object_modifiers(scene, ob, File):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Translate some object level POV statements from Blender UI
        to POV syntax and write to exported file """
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # Maybe return that string to be added instead of directly written.
    
    
        '''XXX WIP
        onceCSG = 0
        for mod in ob.modifiers:
            if onceCSG == 0:
                if mod :
                    if mod.type == 'BOOLEAN':
                        if ob.pov.boolean_mod == "POV":
                            File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" %
                                       (ob.pov.inside_vector[0],
                                        ob.pov.inside_vector[1],
                                        ob.pov.inside_vector[2]))
                            onceCSG = 1
        '''
    
    
        if ob.pov.double_illuminate:
    
            File.write("\tdouble_illuminate\n")
    
            File.write("\tno_shadow\n")
    
            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")
    
            File.write("\tinverse\n")
    
            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)
        '''
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    def write_pov(filename, scene=None, info_callback=None):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Main export process from Blender UI to POV syntax and write to exported file """
    
        file = open(filename, "w")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
        # Only for testing
        if not scene:
            scene = bpy.data.scenes[0]
    
        render = scene.render
        world = scene.world
    
        global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
    
        comments = scene.pov.comments_enable and not scene.pov.tempfiles_enable
    
        linebreaksinlists = (
            scene.pov.list_lf_enable and not scene.pov.tempfiles_enable
        )
        feature_set = bpy.context.preferences.addons[
            __package__
        ].preferences.branch_feature_set_povray
        using_uberpov = feature_set == 'uberpov'
    
        pov_binary = PovrayRender._locate_binary()
    
        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"
            )
    
            print(
                "The name of the binary suggests you are probably rendering with standard POV engine"
            )
    
    
        tab = setTab(scene.pov.indentation_character, scene.pov.indentation_spaces)
    
        if not scene.pov.tempfiles_enable:
    
            def tabWrite(str_o):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                """Indent POV syntax from brackets levels and write to exported file """
    
                global tabLevel
    
                brackets = (
                    str_o.count("{")
                    - str_o.count("}")
                    + str_o.count("[")
                    - str_o.count("]")
                )
    
                if brackets < 0:
                    tabLevel = tabLevel + brackets
                if tabLevel < 0:
                    print("Indentation Warning: tabLevel = %s" % tabLevel)
                    tabLevel = 0
                if tabLevel >= 1:
                    file.write("%s" % tab * tabLevel)
                file.write(str_o)
                if brackets > 0:
                    tabLevel = tabLevel + brackets
    
            def tabWrite(str_o):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                """write directly to exported file if user checked autonamed temp files (faster)."""
    
                file.write(str_o)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def uniqueName(name, nameSeq):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Increment any generated POV name that could get identical to avoid collisions"""
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
            if name not in nameSeq:
    
    Campbell Barton's avatar
    Campbell Barton committed
                name = string_strip_hyphen(name)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                return name
    
            name_orig = name
            i = 1
            while name in nameSeq:
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                i += 1
    
    Campbell Barton's avatar
    Campbell Barton committed
            name = string_strip_hyphen(name)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            return name
    
        def writeMatrix(matrix):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate some tranform matrix from Blender UI
            to POV syntax and write to exported file """
    
            tabWrite(
                "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],
                )
            )
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate some tranform matrix from Blender UI
            to POV syntax and return that string """
    
            sMatrix = (
                "matrix <%.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f>\n"
                % (
                    matrix[0][0],
                    matrix[1][0],
                    matrix[2][0],
                    matrix[0][1],
                    matrix[1][1],
                    matrix[2][1],
                    matrix[0][2],
                    matrix[1][2],
                    matrix[2][2],
                    matrix[0][3],
                    matrix[1][3],
                    matrix[2][3],
                )
            )
    
        def writeObjectMaterial(material, ob):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate some object level material from Blender UI (VS data level)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            to POV interior{} syntax and write it to exported file """
    
    
            # DH - modified some variables to be function local, avoiding RNA write
            # this should be checked to see if it is functionally correct
    
            # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
    
            # if material and material.transparency_method == 'RAYTRACE':
    
                # But there can be only one!
    
                if (
                    material.pov_subsurface_scattering.use
                ):  # SSS IOR get highest priority
    
                    tabWrite("ior %.6f\n" % material.pov_subsurface_scattering.ior)
    
                # Then the raytrace IOR taken from raytrace transparency properties and used for
                # reflections if IOR Mirror option is checked.
                elif material.pov.mirror_use_IOR:
    
                    tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)
    
                elif material.pov.transparency_method == 'Z_TRANSPARENCY':
    
                    tabWrite("interior {\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    tabWrite("ior 1.0\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                else:
    
                    tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)
    
                pov_fake_caustics = False
                pov_photons_refraction = False
                pov_photons_reflection = False
    
                    pov_photons_reflection = True
    
                    pov_fake_caustics = False
                    pov_photons_refraction = False
    
                    pov_fake_caustics = True
                    pov_photons_refraction = False
    
                    pov_fake_caustics = False
                    pov_photons_refraction = True
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                # 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:
    
                        tabWrite(
                            "caustics %.3g\n" % material.pov.fake_caustics_power
                        )
    
                        tabWrite(
                            "dispersion %.6f\n" % material.pov.photons_dispersion
                        )
                        tabWrite(
                            "dispersion_samples %.d\n"
                            % material.pov.photons_dispersion_samples
                        )
                # TODO
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                # Other interior args
    
                if (
                    material.pov.use_transparency
                    and material.pov.transparency_method == 'RAYTRACE'
                ):
    
                    # fade_distance
    
                    # In Blender this value has always been reversed compared to what tooltip says.
                    # 100.001 rather than 100 so that it does not get to 0
    
                    # which deactivates the feature in POV
    
                    tabWrite(
                        "fade_distance %.3g\n"
                        % (100.001 - material.pov_raytrace_transparency.depth_max)
                    )
    
                    tabWrite(
                        "fade_power %.3g\n"
                        % material.pov_raytrace_transparency.falloff
                    )
    
                    tabWrite(
                        "fade_color <%.3g, %.3g, %.3g>\n"
                        % material.pov.interior_fade_color[:]
                    )
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # (variable) dispersion_samples (constant count for now)
    
                if (
                    material.pov.photons_reflection
                    or material.pov.refraction_type == "2"
                ):
    
                    tabWrite("target %.3g\n" % ob.pov.spacing_multiplier)
    
                    if not ob.pov.collect_photons:
                        tabWrite("collect off\n")
                    if pov_photons_refraction:
                        tabWrite("refraction on\n")
                    if pov_photons_reflection:
                        tabWrite("reflection on\n")
                    tabWrite("}\n")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        materialNames = {}
    
        DEF_MAT_NAME = ""  # or "Default"?
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def exportCamera():
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate camera from Blender UI to POV syntax and write to exported file."""
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            camera = scene.camera
    
            # DH disabled for now, this isn't the correct context
    
            active_object = (
                None
            )  # bpy.context.active_object # does not always work  MR
    
            matrix = global_matrix @ camera.matrix_world
    
            focal_point = camera.data.dof.focus_distance
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
            # compute resolution
    
            Qsize = render.resolution_x / render.resolution_y
    
            tabWrite(
                "#declare camLocation  = <%.6f, %.6f, %.6f>;\n"
                % matrix.translation[:]
            )
            tabWrite(
                "#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
                % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
            )
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
            if (
                scene.pov.baking_enable
                and active_object
                and active_object.type == 'MESH'
            ):
                tabWrite(
                    "mesh_camera{ 1 3\n"
                )  # distribution 3 is what we want here
    
                tabWrite("mesh{%s}\n" % active_object.name)
                tabWrite("}\n")
                tabWrite("location <0,0,.01>")
                tabWrite("direction <0,0,-1>")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            # Using standard camera otherwise
            else:
    
                tabWrite("location  <0, 0, 0>\n")
                tabWrite("look_at  <0, 0, -1>\n")
    
                tabWrite("right <%s, 0, 0>\n" % -Qsize)
    
                tabWrite(
                    "angle  %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi)
                )
    
                tabWrite(
                    "rotate  <%.6f, %.6f, %.6f>\n"
                    % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
                )
    
                tabWrite("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
    
                if camera.data.dof.use_dof and (
                    focal_point != 0 or camera.data.dof.focus_object
                ):
                    tabWrite(
                        "aperture %.3g\n"
                        % (1 / camera.data.dof.aperture_fstop * 1000)
                    )
                    tabWrite(
                        "blur_samples %d %d\n"
                        % (
                            camera.data.pov.dof_samples_min,
                            camera.data.pov.dof_samples_max,
                        )
                    )
    
                    tabWrite("variance 1/%d\n" % camera.data.pov.dof_variance)
                    tabWrite("confidence %.3g\n" % camera.data.pov.dof_confidence)
    
                    if camera.data.dof.focus_object:
                        focalOb = scene.objects[camera.data.dof.focus_object.name]
    
                        matrixBlur = global_matrix @ focalOb.matrix_world
    
                        tabWrite(
                            "focal_point <%.4f,%.4f,%.4f>\n"
                            % matrixBlur.translation[:]
                        )
    
                    else:
                        tabWrite("focal_point <0, 0, %f>\n" % focal_point)
            if camera.data.pov.normal_enable:
    
                tabWrite(
                    "normal {%s %.4f turbulence %.4f scale %.4f}\n"
                    % (
                        camera.data.pov.normal_patterns,
    
                        camera.data.pov.cam_normal,
                        camera.data.pov.turbulence,
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def exportLamps(lamps):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Translate lights from Blender UI to POV syntax and write to exported file."""
    
            # Incremented after each lamp export to declare its target
            # currently used for Fresnel diffuse shader as their slope vector:
            global lampCount
            lampCount = 0
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            # Get all lamps
            for ob in lamps:
                lamp = ob.data
    
    
                matrix = global_matrix @ ob.matrix_world
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                # Color is no longer modified by energy
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                tabWrite("light_source {\n")
                tabWrite("< 0,0,0 >\n")
    
                tabWrite("color srgb<%.3g, %.3g, %.3g>\n" % color)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                if lamp.type == 'POINT':
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                    pass
    
                elif lamp.type == 'SPOT':
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
                    # Falloff is the main radius from the centre line
    
                    tabWrite(
                        "falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)
                    )  # 1 TO 179 FOR BOTH
                    tabWrite(
                        "radius %.6f\n"
                        % (
                            (degrees(lamp.spot_size) / 2.0)
                            * (1.0 - lamp.spot_blend)
                        )
                    )
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    # Blender does not have a tightness equivalent, 0 is most like blender default.
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    if lamp.pov.use_halo:
    
                        tabWrite("sphere{<0,0,0>,%.6f\n" % lamp.distance)
    
                        tabWrite("hollow\n")
                        tabWrite("material{\n")
                        tabWrite("texture{\n")
    
                        tabWrite(
                            "pigment{rgbf<1,1,1,%.4f>}\n"
                            % (lamp.pov.halo_intensity * 5.0)
                        )
    
                        tabWrite("}\n")
                        tabWrite("interior{\n")
                        tabWrite("media{\n")
                        tabWrite("emission 1\n")
                        tabWrite("scattering {1, 0.5}\n")
                        tabWrite("density{\n")
                        tabWrite("spherical\n")
                        tabWrite("color_map{\n")
                        tabWrite("[0.0 rgb <0,0,0>]\n")
                        tabWrite("[0.5 rgb <1,1,1>]\n")
                        tabWrite("[1.0 rgb <1,1,1>]\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
                        tabWrite("}\n")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                elif lamp.type == 'SUN':
    
                    tabWrite("parallel\n")
                    tabWrite("point_at  <0, 0, -1>\n")  # *must* be after 'parallel'
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
                elif lamp.type == 'AREA':
    
                    tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
    
                    # Area lights have no falloff type, so always use blenders lamp quad equivalent
                    # for those?
    
                    tabWrite("fade_power %d\n" % 2)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                    size_x = lamp.size
    
                    samples_x = lamp.pov.shadow_ray_samples_x
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                    if lamp.shape == 'SQUARE':
                        size_y = size_x
                        samples_y = samples_x
                    else:
                        size_y = lamp.size_y
    
                        samples_y = lamp.pov.shadow_ray_samples_y
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    tabWrite(
                        "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n"
                        % (size_x, size_y, samples_x, samples_y)
                    )
    
                    tabWrite("area_illumination\n")
    
                    if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED':
                        if lamp.pov.use_jitter:
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                    else:
    
                        tabWrite("adaptive 1\n")
                        tabWrite("jitter\n")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                # No shadow checked either at global or light level:
    
                if not scene.pov.use_shadows or (
                    lamp.pov.shadow_method == 'NOSHADOW'
                ):
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                # 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'}:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    if lamp.falloff_type == 'INVERSE_SQUARE':
    
                        tabWrite(
                            "fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0))
                        )
                        tabWrite(
                            "fade_power %d\n" % 2
                        )  # Use blenders lamp quad equivalent
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    elif lamp.falloff_type == 'INVERSE_LINEAR':
    
                        tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
    
                        tabWrite("fade_power %d\n" % 1)  # Use blenders lamp linear
    
                        tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
    
                        tabWrite("fade_power %d\n" % 3)
    
                        # Use blenders lamp constant equivalent no attenuation.
    
                    # Using Custom curve for fade power 3 for now.
                    elif lamp.falloff_type == 'CUSTOM_CURVE':
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                writeMatrix(matrix)
    
    
    
                lampCount += 1
    
                # v(A,B) rotates vector A about origin by vector B.
    
                file.write(
                    "#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n"
                    % (
                        lampCount,
                        -(ob.location.x),
                        -(ob.location.y),
                        -(ob.location.z),
                        ob.rotation_euler.x,
                        ob.rotation_euler.y,
                        ob.rotation_euler.z,
                    )
                )
    
        ####################################################################################################
    
        def exportRainbows(rainbows):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """write all POV rainbows primitives to exported file """
    
                povdataname = ob.data.name  # enough?
                angle = degrees(ob.data.spot_size / 2.5)  # radians in blender (2
                width = ob.data.spot_blend * 10
    
                distance = ob.data.shadow_buffer_clip_start
    
                # eps=0.0000001
                # angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero
                # width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height
    
                # cz-bz # let's say width of the rainbow is height of the cone (interfacing choice
    
                # v(A,B) rotates vector A about origin by vector B.
                # and avoid a 0 length vector by adding 1
    
                # file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \
    
                # (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1),
                # ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z))
    
                direction = (
                    ob.location.x,
                    ob.location.y,
                    ob.location.z,
                )  # not taking matrix into account
    
                rmatrix = global_matrix @ ob.matrix_world
    
                # ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1))
    
                # XXX Is result of the below offset by 90 degrees?
    
                up = ob.matrix_world.to_3x3()[1].xyz  # * global_matrix
    
                # XXX TO CHANGE:
    
                # formerly:
                # tabWrite("#declare %s = rainbow {\n"%povdataname)
    
                # clumsy for now but remove the rainbow from instancing
                # system because not an object. use lamps later instead of meshes
    
                # del data_ref[dataname]
    
                tabWrite("angle %.4f\n" % angle)
                tabWrite("width %.4f\n" % width)
                tabWrite("distance %.4f\n" % distance)
                tabWrite("arc_angle %.4f\n" % ob.pov.arc_angle)
                tabWrite("falloff_angle %.4f\n" % ob.pov.falloff_angle)
                tabWrite("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
                tabWrite("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
    
                tabWrite("color_map {\n")
    
                tabWrite("[0.000  color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
                tabWrite("[0.130  color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
                tabWrite("[0.298  color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
                tabWrite("[0.412  color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
                tabWrite("[0.526  color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
                tabWrite("[0.640  color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
                tabWrite("[0.754  color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
                tabWrite("[0.900  color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
                tabWrite("[1.000  color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
    
                povMatName = "Default_texture"
    
                # tabWrite("texture {%s}\n"%povMatName)
                write_object_modifiers(scene, ob, file)
                # tabWrite("rotate x*90\n")
                # matrix = global_matrix @ ob.matrix_world
                # writeMatrix(matrix)
    
                # continue #Don't render proxy mesh, skip to next object
    
        ################################XXX LOFT, ETC.
    
        def exportCurves(scene, ob):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """write all curves based POV primitives to exported file """
    
            name_orig = "OB" + ob.name
            dataname_orig = "DATA" + ob.data.name
    
            name = string_strip_hyphen(bpy.path.clean_name(name_orig))
            dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
    
            global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
    
            matrix = global_matrix @ ob.matrix_world
    
            bezier_sweep = False
            if ob.pov.curveshape == 'sphere_sweep':
    
                # inlined spheresweep macro, which itself calls Shapes.inc:
    
                file.write('        #include "shapes.inc"\n')
    
    
                file.write(
                    '        #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n'
                )
    
                file.write('        //input adjusting and inspection\n')
                file.write('        #if(_resolution <= 1)\n')
                file.write('            #local res = 1;\n')
                file.write('        #else\n')
                file.write('            #local res = int(_resolution);\n')
                file.write('        #end\n')
    
                file.write(
                    '        #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n'
                )
    
                file.write(
                    '        #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\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')