# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# #**** END GPL LICENSE BLOCK #****

# <pep8 compliant>

import bpy
import subprocess
import os
import sys
import time
from math import atan, pi, degrees, sqrt, cos, sin
####################
## Faster mesh export
import numpy as np
####################
import re
import random
import platform  #
import subprocess  #
import tempfile  # generate temporary files with random names
from bpy.types import Operator
from imghdr import what  # imghdr is a python lib to identify image file types
from bpy.utils import register_class

from . 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

##############################SF###########################
##############find image texture
def imageFormat(imgF):
    """Identify input image filetypes to transmit to POV."""
    # First use the below explicit extensions to identify image file prospects
    ext = {
        'JPG': "jpeg",
        'JPEG': "jpeg",
        'GIF': "gif",
        'TGA': "tga",
        'IFF': "iff",
        'PPM': "ppm",
        'PNG': "png",
        'SYS': "sys",
        'TIFF': "tiff",
        'TIF': "tiff",
        'EXR': "exr",
        'HDR': "hdr",
    }.get(os.path.splitext(imgF)[-1].upper(), "")
    # Then, use imghdr to really identify the filetype as it can be different
    if not ext:
        # maybe add a check for if path exists here?
        print(" WARNING: texture image has no extension")  # too verbose

        ext = what(imgF)  # imghdr is a python lib to identify image file types
    return ext


def imgMap(ts):
    """Translate mapping type from Blender UI to POV syntax and return that string."""
    image_map = ""
    texdata = bpy.data.textures[ts.texture]
    if ts.mapping == 'FLAT':
        image_map = "map_type 0 "
    elif ts.mapping == 'SPHERE':
        image_map = "map_type 1 "
    elif ts.mapping == 'TUBE':
        image_map = "map_type 2 "

    ## map_type 3 and 4 in development (?) (ENV in pov 3.8)
    ## for POV-Ray, currently they just seem to default back to Flat (type 0)
    # elif ts.mapping=="?":
    #    image_map = " map_type 3 "
    # elif ts.mapping=="?":
    #    image_map = " map_type 4 "
    if ts.use_interpolation: # Available if image sampling class reactivated?
        image_map += " interpolate 2 "
    if texdata.extension == 'CLIP':
        image_map += " once "
    # image_map += "}"
    # if ts.mapping=='CUBE':
    #    image_map+= "warp { cubic } rotate <-90,0,180>"
    # no direct cube type mapping. Though this should work in POV 3.7
    # it doesn't give that good results(best suited to environment maps?)
    # if image_map == "":
    #    print(" No texture image  found ")
    return image_map


def imgMapTransforms(ts):
    """Translate mapping transformations from Blender UI to POV syntax and return that string."""
    # XXX TODO: unchecked textures give error of variable referenced before assignment XXX
    # POV-Ray "scale" is not a number of repetitions factor, but ,its
    # inverse, a standard scale factor.
    # 0.5 Offset is needed relatively to scale because center of the
    # scale is 0.5,0.5 in blender and 0,0 in POV
    # Strange that the translation factor for scale is not the same as for
    # translate.
    # TODO: verify both matches with blender internal.
    image_map_transforms = ""
    image_map_transforms = (
        "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>"
        % (
            ts.scale[0],
            ts.scale[1],
            ts.scale[2],
            ts.offset[0],
            ts.offset[1],
            ts.offset[2],
        )
    )
    # image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \
    # ( 1.0 / ts.scale.x,
    # 1.0 / ts.scale.y,
    # 1.0 / ts.scale.z,
    # (0.5 / ts.scale.x) + ts.offset.x,
    # (0.5 / ts.scale.y) + ts.offset.y,
    # ts.offset.z))
    # image_map_transforms = ("translate <-0.5,-0.5,0> scale <-1,-1,1> * <%.4g,%.4g,%.4g> translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \
    # (1.0 / ts.scale.x,
    # 1.0 / ts.scale.y,
    # 1.0 / ts.scale.z,
    # ts.offset.x,
    # ts.offset.y,
    # ts.offset.z))
    return image_map_transforms


def imgMapBG(wts):
    """Translate world mapping from Blender UI to POV syntax and return that string."""
    tex = bpy.data.textures[wts.texture]
    image_mapBG = ""
    # texture_coords refers to the mapping of world textures:
    if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL':
        image_mapBG = " map_type 0 "
    elif wts.texture_coords == 'ANGMAP':
        image_mapBG = " map_type 1 "
    elif wts.texture_coords == 'TUBE':
        image_mapBG = " map_type 2 "

    if tex.use_interpolation:
        image_mapBG += " interpolate 2 "
    if tex.extension == 'CLIP':
        image_mapBG += " once "
    # image_mapBG += "}"
    # if wts.mapping == 'CUBE':
    #   image_mapBG += "warp { cubic } rotate <-90,0,180>"
    # no direct cube type mapping. Though this should work in POV 3.7
    # it doesn't give that good results(best suited to environment maps?)
    # if image_mapBG == "":
    #    print(" No background texture image  found ")
    return image_mapBG


def path_image(image):
    """Conform a path string to POV syntax to avoid POV errors."""
    return bpy.path.abspath(image.filepath, library=image.library).replace(
        "\\", "/"
    )
    # .replace("\\","/") to get only forward slashes as it's what POV prefers,
    # even on windows


# end find image texture
# -----------------------------------------------------------------------------


def string_strip_hyphen(name):
    """Remove hyphen characters from a string to avoid POV errors."""
    return name.replace("-", "")


def safety(name, Level):
    """append suffix characters to names of various material declinations.

    Material declinations are necessary to POV syntax and used in shading.py
    by the povHasnoSpecularMaps function to create the finish map trick and
    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
        Level=3 is for texture with Maximum Spec and Mirror
    """

    try:
        if int(name) > 0:
            prefix = "shader"
    except:
        prefix = ""
    prefix = "shader_"
    name = string_strip_hyphen(name)
    if Level == 2:
        return prefix + name
    elif Level == 1:
        return prefix + name + "0"  # used for 0 of specular map
    elif Level == 3:
        return prefix + name + "1"  # used for 1 of specular map


##############end safety string name material
##############################EndSF###########################

csg_list = []


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]


tabLevel = 0
unpacked_images = []

user_dir = bpy.utils.resource_path('USER')
preview_dir = os.path.join(user_dir, "preview")

## Make sure Preview directory exists and is empty
smokePath = os.path.join(preview_dir, "smoke.df3")
'''
def write_global_setting(scene,file):
    file.write("global_settings {\n")
    file.write("    assumed_gamma %.6f\n"%scene.pov.assumed_gamma)
    if scene.pov.global_settings_advanced:
        if scene.pov.radio_enable == False:
            file.write("    adc_bailout %.6f\n"%scene.pov.adc_bailout)
        file.write("    ambient_light <%.6f,%.6f,%.6f>\n"%scene.pov.ambient_light[:])
        file.write("    irid_wavelength <%.6f,%.6f,%.6f>\n"%scene.pov.irid_wavelength[:])
        file.write("    charset %s\n"%scene.pov.charset)
        file.write("    max_trace_level %s\n"%scene.pov.max_trace_level)
        file.write("    max_intersections %s\n"%scene.pov.max_intersections)
        file.write("    number_of_waves %s\n"%scene.pov.number_of_waves)
        file.write("    noise_generator %s\n"%scene.pov.noise_generator)

    # below properties not added to __init__ yet to avoid conflicts with material sss scale
    # unless it would override then should be interfaced also in scene units property tab

    # if scene.pov.sslt_enable:
        # file.write("    mm_per_unit %s\n"%scene.pov.mm_per_unit)
        # file.write("    subsurface {\n")
        # file.write("        samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
        # if scene.pov.sslt_radiosity:
            # file.write("        radiosity on\n")
        # file.write("}\n")

    if scene.pov.radio_enable:
        file.write("    radiosity {\n")
        file.write("        pretrace_start %.6f\n"%scene.pov.radio_pretrace_start)
        file.write("        pretrace_end %.6f\n"%scene.pov.radio_pretrace_end)
        file.write("        count %s\n"%scene.pov.radio_count)
        file.write("        nearest_count %s\n"%scene.pov.radio_nearest_count)
        file.write("        error_bound %.6f\n"%scene.pov.radio_error_bound)
        file.write("        recursion_limit %s\n"%scene.pov.radio_recursion_limit)
        file.write("        low_error_factor %.6f\n"%scene.pov.radio_low_error_factor)
        file.write("        gray_threshold %.6f\n"%scene.pov.radio_gray_threshold)
        file.write("        maximum_reuse %.6f\n"%scene.pov.radio_maximum_reuse)
        file.write("        minimum_reuse %.6f\n"%scene.pov.radio_minimum_reuse)
        file.write("        brightness %.6f\n"%scene.pov.radio_brightness)
        file.write("        adc_bailout %.6f\n"%scene.pov.radio_adc_bailout)
        if scene.pov.radio_normal:
            file.write("        normal on\n")
        if scene.pov.radio_always_sample:
            file.write("        always_sample on\n")
        if scene.pov.radio_media:
            file.write("        media on\n")
        if scene.pov.radio_subsurface:
            file.write("        subsurface on\n")
        file.write("    }\n")

    if scene.pov.photon_enable:
        file.write("    photons {\n")
        if scene.pov.photon_enable_count:
            file.write("        count %s\n"%scene.pov.photon_count)
        else:
            file.write("        spacing %.6g\n"%scene.pov.photon_spacing)
        if scene.pov.photon_gather:
            file.write("        gather %s, %s\n"%(scene.pov.photon_gather_min,scene.pov.photon_gather_max))
        if scene.pov.photon_autostop:
            file.write("        autostop %.4g\n"%scene.pov.photon_autostop_value)
        if scene.pov.photon_jitter_enable:
            file.write("        jitter %.4g\n"%scene.pov.photon_jitter)
        file.write("        max_trace_level %s\n"%scene.pov.photon_max_trace_level)
        if scene.pov.photon_adc:
            file.write("        adc_bailout %.6f\n"%scene.pov.photon_adc_bailout)
        if scene.pov.photon_media_enable:
            file.write("        media %s, %s\n"%(scene.pov.photon_media_steps,scene.pov.photon_media_factor))
        if scene.pov.photon_map_file_save_load in {'save'}:
            filePhName = 'Photon_map_file.ph'
            if scene.pov.photon_map_file != '':
                filePhName = scene.pov.photon_map_file+'.ph'
            filePhDir = tempfile.gettempdir()
            path = bpy.path.abspath(scene.pov.photon_map_dir)
            if os.path.exists(path):
                filePhDir = path
            fullFileName = os.path.join(filePhDir,filePhName)
            file.write('        save_file "%s"\n'%fullFileName)
            scene.pov.photon_map_file = fullFileName
        if scene.pov.photon_map_file_save_load in {'load'}:
            fullFileName = bpy.path.abspath(scene.pov.photon_map_file)
            if os.path.exists(fullFileName):
                file.write('        load_file "%s"\n'%fullFileName)
        file.write("}\n")
    file.write("}\n")
'''


def write_object_modifiers(scene, ob, File):
    """Translate some object level POV statements from Blender UI
    to POV syntax and write to exported file """

    # Maybe return that string to be added instead of directly written.

    '''XXX WIP
    onceCSG = 0
    for mod in ob.modifiers:
        if onceCSG == 0:
            if mod :
                if mod.type == 'BOOLEAN':
                    if ob.pov.boolean_mod == "POV":
                        File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" %
                                   (ob.pov.inside_vector[0],
                                    ob.pov.inside_vector[1],
                                    ob.pov.inside_vector[2]))
                        onceCSG = 1
    '''

    if ob.pov.hollow:
        File.write("\thollow\n")
    if ob.pov.double_illuminate:
        File.write("\tdouble_illuminate\n")
    if ob.pov.sturm:
        File.write("\tsturm\n")
    if ob.pov.no_shadow:
        File.write("\tno_shadow\n")
    if ob.pov.no_image:
        File.write("\tno_image\n")
    if ob.pov.no_reflection:
        File.write("\tno_reflection\n")
    if ob.pov.no_radiosity:
        File.write("\tno_radiosity\n")
    if ob.pov.inverse:
        File.write("\tinverse\n")
    if ob.pov.hierarchy:
        File.write("\thierarchy\n")

    # XXX, Commented definitions
    '''
    if scene.pov.photon_enable:
        File.write("photons {\n")
        if ob.pov.target:
            File.write("target %.4g\n"%ob.pov.target_value)
        if ob.pov.refraction:
            File.write("refraction on\n")
        if ob.pov.reflection:
            File.write("reflection on\n")
        if ob.pov.pass_through:
            File.write("pass_through\n")
        File.write("}\n")
    if ob.pov.object_ior > 1:
        File.write("interior {\n")
        File.write("ior %.4g\n"%ob.pov.object_ior)
        if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
            File.write("ior %.4g\n"%ob.pov.dispersion_value)
            File.write("ior %s\n"%ob.pov.dispersion_samples)
        if scene.pov.photon_enable == False:
            File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
    '''


def write_pov(filename, scene=None, info_callback=None):
    """Main export process from Blender UI to POV syntax and write to exported file """

    import mathutils

    # file = filename
    file = open(filename, "w")

    # 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"
        )
    else:
        print(
            "The name of the binary suggests you are probably rendering with standard POV engine"
        )

    def setTab(tabtype, spaces):
        TabStr = ""
        if tabtype == 'NONE':
            TabStr = ""
        elif tabtype == 'TAB':
            TabStr = "\t"
        elif tabtype == 'SPACE':
            TabStr = spaces * " "
        return TabStr

    tab = setTab(scene.pov.indentation_character, scene.pov.indentation_spaces)
    if not scene.pov.tempfiles_enable:

        def tabWrite(str_o):
            """Indent POV syntax from brackets levels and write to exported file """
            global tabLevel
            brackets = (
                str_o.count("{")
                - str_o.count("}")
                + str_o.count("[")
                - str_o.count("]")
            )
            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

    else:

        def tabWrite(str_o):
            """write directly to exported file if user checked autonamed temp files (faster)."""

            file.write(str_o)

    def uniqueName(name, nameSeq):
        """Increment any generated POV name that could get identical to avoid collisions"""

        if name not in nameSeq:
            name = string_strip_hyphen(name)
            return name

        name_orig = name
        i = 1
        while name in nameSeq:
            name = "%s_%.3d" % (name_orig, i)
            i += 1
        name = string_strip_hyphen(name)
        return name

    def writeMatrix(matrix):
        """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],
            )
        )

    def MatrixAsPovString(matrix):
        """Translate some tranform matrix from Blender UI
        to POV syntax and return that string """
        sMatrix = (
            "matrix <%.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f>\n"
            % (
                matrix[0][0],
                matrix[1][0],
                matrix[2][0],
                matrix[0][1],
                matrix[1][1],
                matrix[2][1],
                matrix[0][2],
                matrix[1][2],
                matrix[2][2],
                matrix[0][3],
                matrix[1][3],
                matrix[2][3],
            )
        )
        return sMatrix

    def writeObjectMaterial(material, ob):
        """Translate some object level material from Blender UI (VS data level)
        to POV interior{} syntax and write it to exported file """

        # DH - modified some variables to be function local, avoiding RNA write
        # this should be checked to see if it is functionally correct

        # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
        # if material and material.transparency_method == 'RAYTRACE':
        if material:
            # But there can be only one!
            if (
                material.pov_subsurface_scattering.use
            ):  # SSS IOR get highest priority
                tabWrite("interior {\n")
                tabWrite("ior %.6f\n" % material.pov_subsurface_scattering.ior)
            # Then the raytrace IOR taken from raytrace transparency properties and used for
            # reflections if IOR Mirror option is checked.
            elif material.pov.mirror_use_IOR:
                tabWrite("interior {\n")
                tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)
            elif material.pov.transparency_method == 'Z_TRANSPARENCY':
                tabWrite("interior {\n")
                tabWrite("ior 1.0\n")
            else:
                tabWrite("interior {\n")
                tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)

            pov_fake_caustics = False
            pov_photons_refraction = False
            pov_photons_reflection = False

            if material.pov.photons_reflection:
                pov_photons_reflection = True
            if not material.pov.refraction_caustics:
                pov_fake_caustics = False
                pov_photons_refraction = False
            elif material.pov.refraction_type == "1":
                pov_fake_caustics = True
                pov_photons_refraction = False
            elif material.pov.refraction_type == "2":
                pov_fake_caustics = False
                pov_photons_refraction = True

            # If only Raytrace transparency is set, its IOR will be used for refraction, but user
            # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
            # Last, if none of the above is specified, user can set up 'un-physical' fresnel
            # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
            if material.pov.caustics_enable:
                if pov_fake_caustics:
                    tabWrite(
                        "caustics %.3g\n" % material.pov.fake_caustics_power
                    )
                if pov_photons_refraction:
                    # Default of 1 means no dispersion
                    tabWrite(
                        "dispersion %.6f\n" % material.pov.photons_dispersion
                    )
                    tabWrite(
                        "dispersion_samples %.d\n"
                        % material.pov.photons_dispersion_samples
                    )
            # TODO
            # Other interior args
            if (
                material.pov.use_transparency
                and material.pov.transparency_method == 'RAYTRACE'
            ):
                # fade_distance
                # In Blender this value has always been reversed compared to what tooltip says.
                # 100.001 rather than 100 so that it does not get to 0
                # which deactivates the feature in POV
                tabWrite(
                    "fade_distance %.3g\n"
                    % (100.001 - material.pov_raytrace_transparency.depth_max)
                )
                # fade_power
                tabWrite(
                    "fade_power %.3g\n"
                    % material.pov_raytrace_transparency.falloff
                )
                # fade_color
                tabWrite(
                    "fade_color <%.3g, %.3g, %.3g>\n"
                    % material.pov.interior_fade_color[:]
                )

            # (variable) dispersion_samples (constant count for now)
            tabWrite("}\n")
            if (
                material.pov.photons_reflection
                or material.pov.refraction_type == "2"
            ):
                tabWrite("photons{")
                tabWrite("target %.3g\n" % ob.pov.spacing_multiplier)
                if not ob.pov.collect_photons:
                    tabWrite("collect off\n")
                if pov_photons_refraction:
                    tabWrite("refraction on\n")
                if pov_photons_reflection:
                    tabWrite("reflection on\n")
                tabWrite("}\n")

    materialNames = {}
    DEF_MAT_NAME = ""  # or "Default"?

    def exportCamera():
        """Translate camera from Blender UI to POV syntax and write to exported file."""
        camera = scene.camera

        # DH disabled for now, this isn't the correct context
        active_object = (
            None
        )  # bpy.context.active_object # does not always work  MR
        matrix = global_matrix @ camera.matrix_world
        focal_point = camera.data.dof.focus_distance

        # compute resolution
        Qsize = render.resolution_x / render.resolution_y
        tabWrite(
            "#declare camLocation  = <%.6f, %.6f, %.6f>;\n"
            % matrix.translation[:]
        )
        tabWrite(
            "#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
            % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
        )

        tabWrite("camera {\n")
        if (
            scene.pov.baking_enable
            and active_object
            and active_object.type == 'MESH'
        ):
            tabWrite(
                "mesh_camera{ 1 3\n"
            )  # distribution 3 is what we want here
            tabWrite("mesh{%s}\n" % active_object.name)
            tabWrite("}\n")
            tabWrite("location <0,0,.01>")
            tabWrite("direction <0,0,-1>")

        else:
            if camera.data.type == 'ORTHO':
                SensorHeightRatio = render.resolution_x * camera.data.ortho_scale / render.resolution_y
                tabWrite("orthographic\n")
                # Blender angle is radian so should be converted to degrees:
                # % (camera.data.angle * (180.0 / pi) )
                # but actually argument is not compulsory after angle in pov ortho mode
                tabWrite("angle\n")
                tabWrite("right <%6f, 0, 0>\n" % -camera.data.ortho_scale)
                tabWrite("location  <0, 0, 0>\n")
                tabWrite("look_at  <0, 0, -1>\n")
                tabWrite("up <0, %6f, 0>\n" % (camera.data.ortho_scale / Qsize))

            elif camera.data.type == 'PANO':
                tabWrite("panoramic\n")
                tabWrite("location  <0, 0, 0>\n")
                tabWrite("look_at  <0, 0, -1>\n")
                tabWrite("right <%s, 0, 0>\n" % -Qsize)
                tabWrite("up <0, 1, 0>\n")
                tabWrite(
                    "angle  %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi)
                )
            elif camera.data.type == 'PERSP':
                # Standard camera otherwise would be default in pov
                tabWrite("location  <0, 0, 0>\n")
                tabWrite("look_at  <0, 0, -1>\n")
                tabWrite("right <%s, 0, 0>\n" % -Qsize)
                tabWrite("up <0, 1, 0>\n")
                tabWrite(
                    "angle  %f\n" % ( 2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi )
                )

            tabWrite(
                "rotate  <%.6f, %.6f, %.6f>\n"
                % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
            )
            tabWrite("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
            if camera.data.dof.use_dof and (
                focal_point != 0 or camera.data.dof.focus_object
            ):
                tabWrite(
                    "aperture %.3g\n"
                    % (1 / camera.data.dof.aperture_fstop * 1000)
                )
                tabWrite(
                    "blur_samples %d %d\n"
                    % (
                        camera.data.pov.dof_samples_min,
                        camera.data.pov.dof_samples_max,
                    )
                )
                tabWrite("variance 1/%d\n" % camera.data.pov.dof_variance)
                tabWrite("confidence %.3g\n" % camera.data.pov.dof_confidence)
                if camera.data.dof.focus_object:
                    focalOb = scene.objects[camera.data.dof.focus_object.name]
                    matrixBlur = global_matrix @ focalOb.matrix_world
                    tabWrite(
                        "focal_point <%.4f,%.4f,%.4f>\n"
                        % matrixBlur.translation[:]
                    )
                else:
                    tabWrite("focal_point <0, 0, %f>\n" % focal_point)
        if camera.data.pov.normal_enable:
            tabWrite(
                "normal {%s %.4f turbulence %.4f scale %.4f}\n"
                % (
                    camera.data.pov.normal_patterns,
                    camera.data.pov.cam_normal,
                    camera.data.pov.turbulence,
                    camera.data.pov.scale,
                )
            )
        tabWrite("}\n")

    def exportLamps(lamps):
        """Translate lights from Blender UI to POV syntax and write to exported file."""

        # Incremented after each lamp export to declare its target
        # currently used for Fresnel diffuse shader as their slope vector:
        global lampCount
        lampCount = 0
        # Get all lamps
        for ob in lamps:
            lamp = ob.data

            matrix = global_matrix @ ob.matrix_world

            # Color is no longer modified by energy
            color = tuple([c for c in lamp.color])

            tabWrite("light_source {\n")
            tabWrite("< 0,0,0 >\n")
            tabWrite("color srgb<%.3g, %.3g, %.3g>\n" % color)

            if lamp.type == 'POINT':
                pass
            elif lamp.type == 'SPOT':
                tabWrite("spotlight\n")

                # Falloff is the main radius from the centre line
                tabWrite(
                    "falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)
                )  # 1 TO 179 FOR BOTH
                tabWrite(
                    "radius %.6f\n"
                    % (
                        (degrees(lamp.spot_size) / 2.0)
                        * (1.0 - lamp.spot_blend)
                    )
                )

                # Blender does not have a tightness equivalent, 0 is most like blender default.
                tabWrite("tightness 0\n")  # 0:10f

                tabWrite("point_at  <0, 0, -1>\n")
                if lamp.pov.use_halo:
                    tabWrite("looks_like{\n")
                    tabWrite("sphere{<0,0,0>,%.6f\n" % lamp.distance)
                    tabWrite("hollow\n")
                    tabWrite("material{\n")
                    tabWrite("texture{\n")
                    tabWrite(
                        "pigment{rgbf<1,1,1,%.4f>}\n"
                        % (lamp.pov.halo_intensity * 5.0)
                    )
                    tabWrite("}\n")
                    tabWrite("interior{\n")
                    tabWrite("media{\n")
                    tabWrite("emission 1\n")
                    tabWrite("scattering {1, 0.5}\n")
                    tabWrite("density{\n")
                    tabWrite("spherical\n")
                    tabWrite("color_map{\n")
                    tabWrite("[0.0 rgb <0,0,0>]\n")
                    tabWrite("[0.5 rgb <1,1,1>]\n")
                    tabWrite("[1.0 rgb <1,1,1>]\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
            elif lamp.type == 'SUN':
                tabWrite("parallel\n")
                tabWrite("point_at  <0, 0, -1>\n")  # *must* be after 'parallel'

            elif lamp.type == 'AREA':
                tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
                # Area lights have no falloff type, so always use blenders lamp quad equivalent
                # for those?
                tabWrite("fade_power %d\n" % 2)
                size_x = lamp.size
                samples_x = lamp.pov.shadow_ray_samples_x
                if lamp.shape == 'SQUARE':
                    size_y = size_x
                    samples_y = samples_x
                else:
                    size_y = lamp.size_y
                    samples_y = lamp.pov.shadow_ray_samples_y

                tabWrite(
                    "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n"
                    % (size_x, size_y, samples_x, samples_y)
                )
                tabWrite("area_illumination\n")
                if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED':
                    if lamp.pov.use_jitter:
                        tabWrite("jitter\n")
                else:
                    tabWrite("adaptive 1\n")
                    tabWrite("jitter\n")

            # No shadow checked either at global or light level:
            if not scene.pov.use_shadows or (
                lamp.pov.shadow_method == 'NOSHADOW'
            ):
                tabWrite("shadowless\n")

            # Sun shouldn't be attenuated. Area lights have no falloff attribute so they
            # are put to type 2 attenuation a little higher above.
            if lamp.type not in {'SUN', 'AREA'}:
                if lamp.falloff_type == 'INVERSE_SQUARE':
                    tabWrite(
                        "fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0))
                    )
                    tabWrite(
                        "fade_power %d\n" % 2
                    )  # Use blenders lamp quad equivalent
                elif lamp.falloff_type == 'INVERSE_LINEAR':
                    tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
                    tabWrite("fade_power %d\n" % 1)  # Use blenders lamp linear
                elif lamp.falloff_type == 'CONSTANT':
                    tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
                    tabWrite("fade_power %d\n" % 3)
                    # Use blenders lamp constant equivalent no attenuation.
                # Using Custom curve for fade power 3 for now.
                elif lamp.falloff_type == 'CUSTOM_CURVE':
                    tabWrite("fade_power %d\n" % 4)

            writeMatrix(matrix)

            tabWrite("}\n")

            lampCount += 1

            # v(A,B) rotates vector A about origin by vector B.
            file.write(
                "#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n"
                % (
                    lampCount,
                    -(ob.location.x),
                    -(ob.location.y),
                    -(ob.location.z),
                    ob.rotation_euler.x,
                    ob.rotation_euler.y,
                    ob.rotation_euler.z,
                )
            )

    ####################################################################################################
    def exportRainbows(rainbows):
        """write all POV rainbows primitives to exported file """
        for ob in rainbows:
            povdataname = ob.data.name  # enough?
            angle = degrees(ob.data.spot_size / 2.5)  # radians in blender (2
            width = ob.data.spot_blend * 10
            distance = ob.data.shadow_buffer_clip_start
            # eps=0.0000001
            # angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero
            # width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height
            # formerly:
            # cz-bz # let's say width of the rainbow is height of the cone (interfacing choice

            # v(A,B) rotates vector A about origin by vector B.
            # and avoid a 0 length vector by adding 1

            # file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \
            # (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1),
            # ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z))

            direction = (
                ob.location.x,
                ob.location.y,
                ob.location.z,
            )  # not taking matrix into account
            rmatrix = global_matrix @ ob.matrix_world

            # ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1))
            # XXX Is result of the below offset by 90 degrees?
            up = ob.matrix_world.to_3x3()[1].xyz  # * global_matrix

            # XXX TO CHANGE:
            # formerly:
            # tabWrite("#declare %s = rainbow {\n"%povdataname)

            # clumsy for now but remove the rainbow from instancing
            # system because not an object. use lamps later instead of meshes

            # del data_ref[dataname]
            tabWrite("rainbow {\n")

            tabWrite("angle %.4f\n" % angle)
            tabWrite("width %.4f\n" % width)
            tabWrite("distance %.4f\n" % distance)
            tabWrite("arc_angle %.4f\n" % ob.pov.arc_angle)
            tabWrite("falloff_angle %.4f\n" % ob.pov.falloff_angle)
            tabWrite("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
            tabWrite("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
            tabWrite("color_map {\n")
            tabWrite("[0.000  color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
            tabWrite("[0.130  color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
            tabWrite("[0.298  color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
            tabWrite("[0.412  color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
            tabWrite("[0.526  color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
            tabWrite("[0.640  color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
            tabWrite("[0.754  color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
            tabWrite("[0.900  color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
            tabWrite("[1.000  color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
            tabWrite("}\n")

            povMatName = "Default_texture"
            # tabWrite("texture {%s}\n"%povMatName)
            write_object_modifiers(scene, ob, file)
            # tabWrite("rotate x*90\n")
            # matrix = global_matrix @ ob.matrix_world
            # writeMatrix(matrix)
            tabWrite("}\n")
            # continue #Don't render proxy mesh, skip to next object

    ################################XXX LOFT, ETC.
    def exportCurves(scene, ob):
        """write all curves based POV primitives to exported file """
        name_orig = "OB" + ob.name
        dataname_orig = "DATA" + ob.data.name

        name = string_strip_hyphen(bpy.path.clean_name(name_orig))
        dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))

        global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
        matrix = global_matrix @ ob.matrix_world
        bezier_sweep = False
        if ob.pov.curveshape == 'sphere_sweep':
            # inlined spheresweep macro, which itself calls Shapes.inc:
            file.write('        #include "shapes.inc"\n')

            file.write(
                '        #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n'
            )
            file.write('        //input adjusting and inspection\n')
            file.write('        #if(_resolution <= 1)\n')
            file.write('            #local res = 1;\n')
            file.write('        #else\n')
            file.write('            #local res = int(_resolution);\n')
            file.write('        #end\n')
            file.write(
                '        #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n'
            )
            file.write('            #error ""\n')
            file.write(
                '        #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n'
            )
            file.write('            #error ""\n')
            file.write(
                '        #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n'
            )
            file.write('            #error ""\n')
            file.write('        #else\n')
            file.write(
                '            #local n_of_seg = div(dimension_size(_points_array,1), 4);\n'
            )
            file.write('            #local ctrl_pts_array = array[n_of_seg]\n')
            file.write('            #local ctrl_rs_array = array[n_of_seg]\n')
            file.write('            #for(i, 0, n_of_seg-1)\n')
            file.write(
                '                #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n'
            )
            file.write(
                '                #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n'
            )
            file.write('            #end\n')
            file.write('        #end\n')

            file.write('        //drawing\n')
            file.write('        #local mockup1 =\n')
            file.write('        #if(_merge_shape) merge{ #else union{ #end\n')
            file.write('            #for(i, 0, n_of_seg-1)\n')
            file.write('                #local has_head = true;\n')
            file.write('                #if(i = 0)\n')
            file.write(
                '                    #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n'
            )
            file.write('                        #local has_head = false;\n')
            file.write('                    #end\n')
            file.write('                #else\n')
            file.write(
                '                    #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n'
            )
            file.write('                        #local has_head = false;\n')
            file.write('                    #end\n')
            file.write('                #end\n')
            file.write('                #if(has_head = true)\n')
            file.write('                    sphere{\n')
            file.write(
                '                    ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n'
            )
            file.write('                    }\n')
            file.write('                #end\n')
            file.write('                #local para_t = (1/2)/res;\n')
            file.write(
                '                #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
            )
            file.write(
                '                #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
            )
            file.write(
                '                #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n'
            )
            file.write('                    object{\n')
            file.write(
                '                    Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n'
            )
            file.write('                    }\n')
            file.write('                #end\n')
            file.write('                sphere{\n')
            file.write('                this_point, this_radius\n')
            file.write('                }\n')
            file.write('                #for(j, 1, res-1)\n')
            file.write('                    #local last_point = this_point;\n')
            file.write(
                '                    #local last_radius = this_radius;\n'
            )
            file.write('                    #local para_t = (1/2+j)/res;\n')
            file.write(
                '                    #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
            )
            file.write(
                '                    #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
            )
            file.write(
                '                    #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
            )
            file.write('                        object{\n')
            file.write(
                '                        Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
            )
            file.write('                        }\n')
            file.write('                    #end\n')
            file.write('                    sphere{\n')
            file.write('                    this_point, this_radius\n')
            file.write('                    }\n')
            file.write('                #end\n')
            file.write('                #local last_point = this_point;\n')
            file.write('                #local last_radius = this_radius;\n')
            file.write(
                '                #local this_point = ctrl_pts_array[i][3];\n'
            )
            file.write(
                '                #local this_radius = ctrl_rs_array[i][3];\n'
            )
            file.write(
                '                #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
            )
            file.write('                    object{\n')
            file.write(
                '                    Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
            )
            file.write('                    }\n')
            file.write('                #end\n')
            file.write('                sphere{\n')
            file.write('                this_point, this_radius\n')
            file.write('                }\n')
            file.write('            #end\n')
            file.write('        }\n')
            file.write('        mockup1\n')
            file.write('        #end\n')

            for spl in ob.data.splines:
                if spl.type == "BEZIER":
                    bezier_sweep = True
        if ob.pov.curveshape in {'loft', 'birail'}:
            n = 0
            for spline in ob.data.splines:
                n += 1
                tabWrite('#declare %s%s=spline {\n' % (dataname, n))
                tabWrite('cubic_spline\n')
                lp = len(spline.points)
                delta = 1 / (lp)
                d = -delta
                point = spline.points[lp - 1]
                x, y, z, w = point.co[:]
                tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
                d += delta
                for point in spline.points:
                    x, y, z, w = point.co[:]
                    tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
                    d += delta
                for i in range(2):
                    point = spline.points[i]
                    x, y, z, w = point.co[:]
                    tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
                    d += delta
                tabWrite('}\n')
            if ob.pov.curveshape in {'loft'}:
                n = len(ob.data.splines)
                tabWrite('#declare %s = array[%s]{\n' % (dataname, (n + 3)))
                tabWrite('spline{%s%s},\n' % (dataname, n))
                for i in range(n):
                    tabWrite('spline{%s%s},\n' % (dataname, (i + 1)))
                tabWrite('spline{%s1},\n' % (dataname))
                tabWrite('spline{%s2}\n' % (dataname))
                tabWrite('}\n')
            # Use some of the Meshmaker.inc macro, here inlined
            file.write('#macro CheckFileName(FileName)\n')
            file.write('   #local Len=strlen(FileName);\n')
            file.write('   #if(Len>0)\n')
            file.write('      #if(file_exists(FileName))\n')
            file.write('         #if(Len>=4)\n')
            file.write(
                '            #local Ext=strlwr(substr(FileName,Len-3,4))\n'
            )
            file.write(
                '            #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
            )
            file.write('               #local Return=99;\n')
            file.write('            #else\n')
            file.write('               #local Return=0;\n')
            file.write('            #end\n')
            file.write('         #else\n')
            file.write('            #local Return=0;\n')
            file.write('         #end\n')
            file.write('      #else\n')
            file.write('         #if(Len>=4)\n')
            file.write(
                '            #local Ext=strlwr(substr(FileName,Len-3,4))\n'
            )
            file.write(
                '            #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
            )
            file.write('               #if (strcmp(Ext,".obj")=0)\n')
            file.write('                  #local Return=2;\n')
            file.write('               #end\n')
            file.write('               #if (strcmp(Ext,".pcm")=0)\n')
            file.write('                  #local Return=3;\n')
            file.write('               #end\n')
            file.write('               #if (strcmp(Ext,".arr")=0)\n')
            file.write('                  #local Return=4;\n')
            file.write('               #end\n')
            file.write('            #else\n')
            file.write('               #local Return=1;\n')
            file.write('            #end\n')
            file.write('         #else\n')
            file.write('            #local Return=1;\n')
            file.write('         #end\n')
            file.write('      #end\n')
            file.write('   #else\n')
            file.write('      #local Return=1;\n')
            file.write('   #end\n')
            file.write('   (Return)\n')
            file.write('#end\n')

            file.write('#macro BuildSpline(Arr, SplType)\n')
            file.write('   #local Ds=dimension_size(Arr,1);\n')
            file.write('   #local Asc=asc(strupr(SplType));\n')
            file.write('   #if(Asc!=67 & Asc!=76 & Asc!=81) \n')
            file.write('      #local Asc=76;\n')
            file.write(
                '      #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n'
            )
            file.write('   #end\n')
            file.write('   spline {\n')
            file.write('      #switch (Asc)\n')
            file.write('         #case (67) //C  cubic_spline\n')
            file.write('            cubic_spline\n')
            file.write('         #break\n')
            file.write('         #case (76) //L  linear_spline\n')
            file.write('            linear_spline\n')
            file.write('         #break\n')
            file.write('         #case (78) //N  linear_spline\n')
            file.write('            natural_spline\n')
            file.write('         #break\n')
            file.write('         #case (81) //Q  Quadratic_spline\n')
            file.write('            quadratic_spline\n')
            file.write('         #break\n')
            file.write('      #end\n')
            file.write('      #local Add=1/((Ds-2)-1);\n')
            file.write('      #local J=0-Add;\n')
            file.write('      #local I=0;\n')
            file.write('      #while (I<Ds)\n')
            file.write('         J\n')
            file.write('         Arr[I]\n')
            file.write('         #local I=I+1;\n')
            file.write('         #local J=J+Add;\n')
            file.write('      #end\n')
            file.write('   }\n')
            file.write('#end\n')

            file.write(
                '#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n'
            )
            # suppressed some file checking from original macro because no more separate files
            file.write(' #local Write=0;\n')
            file.write(
                ' #debug concat("\\n\\n Building mesh2: \\n   - vertex_vectors\\n")\n'
            )
            file.write('  #local NumVertices=dimension_size(VecArr,1);\n')
            file.write('  #switch (Write)\n')
            file.write('     #case(1)\n')
            file.write('        #write(\n')
            file.write('           MeshFile,\n')
            file.write('           "  vertex_vectors {\\n",\n')
            file.write('           "    ", str(NumVertices,0,0),"\\n    "\n')
            file.write('        )\n')
            file.write('     #break\n')
            file.write('     #case(2)\n')
            file.write('        #write(\n')
            file.write('           MeshFile,\n')
            file.write('           "# Vertices: ",str(NumVertices,0,0),"\\n"\n')
            file.write('        )\n')
            file.write('     #break\n')
            file.write('     #case(3)\n')
            file.write('        #write(\n')
            file.write('           MeshFile,\n')
            file.write('           str(2*NumVertices,0,0),",\\n"\n')
            file.write('        )\n')
            file.write('     #break\n')
            file.write('     #case(4)\n')
            file.write('        #write(\n')
            file.write('           MeshFile,\n')
            file.write(
                '           "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n  "\n'
            )
            file.write('        )\n')
            file.write('     #break\n')
            file.write('  #end\n')
            file.write('  mesh2 {\n')
            file.write('     vertex_vectors {\n')
            file.write('        NumVertices\n')
            file.write('        #local I=0;\n')
            file.write('        #while (I<NumVertices)\n')
            file.write('           VecArr[I]\n')
            file.write('           #switch(Write)\n')
            file.write('              #case(1)\n')
            file.write('                 #write(MeshFile, VecArr[I])\n')
            file.write('              #break\n')
            file.write('              #case(2)\n')
            file.write('                 #write(\n')
            file.write('                    MeshFile,\n')
            file.write(
                '                    "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n'
            )
            file.write('                 )\n')
            file.write('              #break\n')
            file.write('              #case(3)\n')
            file.write('                 #write(\n')
            file.write('                    MeshFile,\n')
            file.write(
                '                    VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n'
            )
            file.write('                 )\n')
            file.write('              #break\n')
            file.write('              #case(4)\n')
            file.write('                 #write(MeshFile, VecArr[I])\n')
            file.write('              #break\n')
            file.write('           #end\n')
            file.write('           #local I=I+1;\n')
            file.write('           #if(Write=1 | Write=4)\n')
            file.write('              #if(mod(I,3)=0)\n')
            file.write('                 #write(MeshFile,"\\n    ")\n')
            file.write('              #end\n')
            file.write('           #end \n')
            file.write('        #end\n')
            file.write('        #switch(Write)\n')
            file.write('           #case(1)\n')
            file.write('              #write(MeshFile,"\\n  }\\n")\n')
            file.write('           #break\n')
            file.write('           #case(2)\n')
            file.write('              #write(MeshFile,"\\n")\n')
            file.write('           #break\n')
            file.write('           #case(3)\n')
            file.write('              // do nothing\n')
            file.write('           #break\n')
            file.write('           #case(4) \n')
            file.write('              #write(MeshFile,"\\n}\\n")\n')
            file.write('           #break\n')
            file.write('        #end\n')
            file.write('     }\n')

            file.write('     #debug concat("   - normal_vectors\\n")    \n')
            file.write('     #local NumVertices=dimension_size(NormArr,1);\n')
            file.write('     #switch(Write)\n')
            file.write('        #case(1)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write('              "  normal_vectors {\\n",\n')
            file.write('              "    ", str(NumVertices,0,0),"\\n    "\n')
            file.write('           )\n')
            file.write('        #break\n')
            file.write('        #case(2)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write(
                '              "# Normals: ",str(NumVertices,0,0),"\\n"\n'
            )
            file.write('           )\n')
            file.write('        #break\n')
            file.write('        #case(3)\n')
            file.write('           // do nothing\n')
            file.write('        #break\n')
            file.write('        #case(4)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write(
                '              "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n  "\n'
            )
            file.write('           )\n')
            file.write('        #break\n')
            file.write('     #end\n')
            file.write('     normal_vectors {\n')
            file.write('        NumVertices\n')
            file.write('        #local I=0;\n')
            file.write('        #while (I<NumVertices)\n')
            file.write('           NormArr[I]\n')
            file.write('           #switch(Write)\n')
            file.write('              #case(1)\n')
            file.write('                 #write(MeshFile NormArr[I])\n')
            file.write('              #break\n')
            file.write('              #case(2)\n')
            file.write('                 #write(\n')
            file.write('                    MeshFile,\n')
            file.write(
                '                    "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n'
            )
            file.write('                 )\n')
            file.write('              #break\n')
            file.write('              #case(3)\n')
            file.write('                 #write(\n')
            file.write('                    MeshFile,\n')
            file.write(
                '                    NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n'
            )
            file.write('                 )\n')
            file.write('              #break\n')
            file.write('              #case(4)\n')
            file.write('                 #write(MeshFile NormArr[I])\n')
            file.write('              #break\n')
            file.write('           #end\n')
            file.write('           #local I=I+1;\n')
            file.write('           #if(Write=1 | Write=4) \n')
            file.write('              #if(mod(I,3)=0)\n')
            file.write('                 #write(MeshFile,"\\n    ")\n')
            file.write('              #end\n')
            file.write('           #end\n')
            file.write('        #end\n')
            file.write('        #switch(Write)\n')
            file.write('           #case(1)\n')
            file.write('              #write(MeshFile,"\\n  }\\n")\n')
            file.write('           #break\n')
            file.write('           #case(2)\n')
            file.write('              #write(MeshFile,"\\n")\n')
            file.write('           #break\n')
            file.write('           #case(3)\n')
            file.write('              //do nothing\n')
            file.write('           #break\n')
            file.write('           #case(4)\n')
            file.write('              #write(MeshFile,"\\n}\\n")\n')
            file.write('           #break\n')
            file.write('        #end\n')
            file.write('     }\n')

            file.write('     #debug concat("   - uv_vectors\\n")   \n')
            file.write('     #local NumVertices=dimension_size(UVArr,1);\n')
            file.write('     #switch(Write)\n')
            file.write('        #case(1)\n')
            file.write('           #write(\n')
            file.write('              MeshFile, \n')
            file.write('              "  uv_vectors {\\n",\n')
            file.write('              "    ", str(NumVertices,0,0),"\\n    "\n')
            file.write('           )\n')
            file.write('         #break\n')
            file.write('         #case(2)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write(
                '              "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n'
            )
            file.write('           )\n')
            file.write('         #break\n')
            file.write('         #case(3)\n')
            file.write(
                '           // do nothing, *.pcm does not support uv-vectors\n'
            )
            file.write('         #break\n')
            file.write('         #case(4)\n')
            file.write('            #write(\n')
            file.write('               MeshFile,\n')
            file.write(
                '               "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n  "\n'
            )
            file.write('            )\n')
            file.write('         #break\n')
            file.write('     #end\n')
            file.write('     uv_vectors {\n')
            file.write('        NumVertices\n')
            file.write('        #local I=0;\n')
            file.write('        #while (I<NumVertices)\n')
            file.write('           UVArr[I]\n')
            file.write('           #switch(Write)\n')
            file.write('              #case(1)\n')
            file.write('                 #write(MeshFile UVArr[I])\n')
            file.write('              #break\n')
            file.write('              #case(2)\n')
            file.write('                 #write(\n')
            file.write('                    MeshFile,\n')
            file.write(
                '                    "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n'
            )
            file.write('                 )\n')
            file.write('              #break\n')
            file.write('              #case(3)\n')
            file.write('                 //do nothing\n')
            file.write('              #break\n')
            file.write('              #case(4)\n')
            file.write('                 #write(MeshFile UVArr[I])\n')
            file.write('              #break\n')
            file.write('           #end\n')
            file.write('           #local I=I+1; \n')
            file.write('           #if(Write=1 | Write=4)\n')
            file.write('              #if(mod(I,3)=0)\n')
            file.write('                 #write(MeshFile,"\\n    ")\n')
            file.write('              #end \n')
            file.write('           #end\n')
            file.write('        #end \n')
            file.write('        #switch(Write)\n')
            file.write('           #case(1)\n')
            file.write('              #write(MeshFile,"\\n  }\\n")\n')
            file.write('           #break\n')
            file.write('           #case(2)\n')
            file.write('              #write(MeshFile,"\\n")\n')
            file.write('           #break\n')
            file.write('           #case(3)\n')
            file.write('              //do nothing\n')
            file.write('           #break\n')
            file.write('           #case(4)\n')
            file.write('              #write(MeshFile,"\\n}\\n")\n')
            file.write('           #break\n')
            file.write('        #end\n')
            file.write('     }\n')
            file.write('\n')
            file.write('     #debug concat("   - face_indices\\n")   \n')
            file.write('     #declare NumFaces=U*V*2;\n')
            file.write('     #switch(Write)\n')
            file.write('        #case(1)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write('              "  face_indices {\\n"\n')
            file.write('              "    ", str(NumFaces,0,0),"\\n    "\n')
            file.write('           )\n')
            file.write('        #break\n')
            file.write('        #case(2)\n')
            file.write('           #write (\n')
            file.write('              MeshFile,\n')
            file.write('              "# faces: ",str(NumFaces,0,0),"\\n"\n')
            file.write('           )\n')
            file.write('        #break\n')
            file.write('        #case(3)\n')
            file.write('           #write (\n')
            file.write('              MeshFile,\n')
            file.write('              "0,",str(NumFaces,0,0),",\\n"\n')
            file.write('           )\n')
            file.write('        #break\n')
            file.write('        #case(4)\n')
            file.write('           #write(\n')
            file.write('              MeshFile,\n')
            file.write(
                '              "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n  "\n'
            )
            file.write('           )\n')
            file.write('        #break\n')
            file.write('     #end\n')
            file.write('     face_indices {\n')
            file.write('        NumFaces\n')
            file.write('        #local I=0;\n')
            file.write('        #local H=0;\n')
            file.write('        #local NumVertices=dimension_size(VecArr,1);\n')
            file.write('        #while (I<V)\n')
            file.write('           #local J=0;\n')
            file.write('           #while (J<U)\n')
            file.write('              #local Ind=(I*U)+I+J;\n')
            file.write(
                '              <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
            )
            file.write('              #switch(Write)\n')
            file.write('                 #case(1)\n')
            file.write('                    #write(\n')
            file.write('                       MeshFile,\n')
            file.write(
                '                       <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
            )
            file.write('                    )\n')
            file.write('                 #break\n')
            file.write('                 #case(2)\n')
            file.write('                    #write(\n')
            file.write('                       MeshFile,\n')
            file.write(
                '                       "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n'
            )
            file.write(
                '                       "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n'
            )
            file.write('                    )\n')
            file.write('                 #break\n')
            file.write('                 #case(3)\n')
            file.write('                    #write(\n')
            file.write('                       MeshFile,\n')
            file.write(
                '                       Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
            )
            file.write(
                '                       Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
            )
            file.write('                    )\n')
            file.write('                 #break\n')
            file.write('                 #case(4)\n')
            file.write('                    #write(\n')
            file.write('                       MeshFile,\n')
            file.write(
                '                       <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
            )
            file.write('                    )\n')
            file.write('                 #break\n')
            file.write('              #end\n')
            file.write('              #local J=J+1;\n')
            file.write('              #local H=H+1;\n')
            file.write('              #if(Write=1 | Write=4)\n')
            file.write('                 #if(mod(H,3)=0)\n')
            file.write('                    #write(MeshFile,"\\n    ")\n')
            file.write('                 #end \n')
            file.write('              #end\n')
            file.write('           #end\n')
            file.write('           #local I=I+1;\n')
            file.write('        #end\n')
            file.write('     }\n')
            file.write('     #switch(Write)\n')
            file.write('        #case(1)\n')
            file.write('           #write(MeshFile, "\\n  }\\n}")\n')
            file.write('           #fclose MeshFile\n')
            file.write('           #debug concat(" Done writing\\n")\n')
            file.write('        #break\n')
            file.write('        #case(2)\n')
            file.write('           #fclose MeshFile\n')
            file.write('           #debug concat(" Done writing\\n")\n')
            file.write('        #break\n')
            file.write('        #case(3)\n')
            file.write('           #fclose MeshFile\n')
            file.write('           #debug concat(" Done writing\\n")\n')
            file.write('        #break\n')
            file.write('        #case(4)\n')
            file.write('           #write(MeshFile, "\\n}\\n}")\n')
            file.write('           #fclose MeshFile\n')
            file.write('           #debug concat(" Done writing\\n")\n')
            file.write('        #break\n')
            file.write('     #end\n')
            file.write('  }\n')
            file.write('#end\n')

            file.write(
                '#macro MSM(SplineArray, SplRes, Interp_type,  InterpRes, FileName)\n'
            )
            file.write('    #declare Build=CheckFileName(FileName);\n')
            file.write('    #if(Build=0)\n')
            file.write(
                '        #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n'
            )
            file.write('        #include FileName\n')
            file.write('        object{Surface}\n')
            file.write('    #else\n')
            file.write('        #local NumVertices=(SplRes+1)*(InterpRes+1);\n')
            file.write('        #local NumFaces=SplRes*InterpRes*2;\n')
            file.write(
                '        #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n'
            )
            file.write('        #local VecArr=array[NumVertices]\n')
            file.write('        #local NormArr=array[NumVertices]\n')
            file.write('        #local UVArr=array[NumVertices]\n')
            file.write('        #local N=dimension_size(SplineArray,1);\n')
            file.write('        #local TempSplArr0=array[N];\n')
            file.write('        #local TempSplArr1=array[N];\n')
            file.write('        #local TempSplArr2=array[N];\n')
            file.write('        #local PosStep=1/SplRes;\n')
            file.write('        #local InterpStep=1/InterpRes;\n')
            file.write('        #local Count=0;\n')
            file.write('        #local Pos=0;\n')
            file.write('        #while(Pos<=1)\n')
            file.write('            #local I=0;\n')
            file.write('            #if (Pos=0)\n')
            file.write('                #while (I<N)\n')
            file.write(
                '                    #local Spl=spline{SplineArray[I]}\n'
            )
            file.write(
                '                    #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n'
            )
            file.write(
                '                    #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n'
            )
            file.write(
                '                    #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n'
            )
            file.write('                    #local I=I+1;\n')
            file.write('                #end\n')
            file.write(
                '                #local S0=BuildSpline(TempSplArr0, Interp_type)\n'
            )
            file.write(
                '                #local S1=BuildSpline(TempSplArr1, Interp_type)\n'
            )
            file.write(
                '                #local S2=BuildSpline(TempSplArr2, Interp_type)\n'
            )
            file.write('            #else\n')
            file.write('                #while (I<N)\n')
            file.write(
                '                    #local Spl=spline{SplineArray[I]}\n'
            )
            file.write(
                '                    #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n'
            )
            file.write('                    #local I=I+1;\n')
            file.write('                #end\n')
            file.write(
                '                #local S1=BuildSpline(TempSplArr1, Interp_type)\n'
            )
            file.write('            #end\n')
            file.write('            #local J=0;\n')
            file.write('            #while (J<=1)\n')
            file.write('                #local P0=<0,0,0>+S0(J);\n')
            file.write('                #local P1=<0,0,0>+S1(J);\n')
            file.write('                #local P2=<0,0,0>+S2(J);\n')
            file.write('                #local P3=<0,0,0>+S0(J+InterpStep);\n')
            file.write('                #local P4=<0,0,0>+S0(J-InterpStep);\n')
            file.write('                #local B1=P4-P0;\n')
            file.write('                #local B2=P2-P0;\n')
            file.write('                #local B3=P3-P0;\n')
            file.write('                #local B4=P1-P0;\n')
            file.write('                #local N1=vcross(B1,B2);\n')
            file.write('                #local N2=vcross(B2,B3);\n')
            file.write('                #local N3=vcross(B3,B4);\n')
            file.write('                #local N4=vcross(B4,B1);\n')
            file.write(
                '                #local Norm=vnormalize((N1+N2+N3+N4));\n'
            )
            file.write('                #local VecArr[Count]=P0;\n')
            file.write('                #local NormArr[Count]=Norm;\n')
            file.write('                #local UVArr[Count]=<J,Pos>;\n')
            file.write('                #local J=J+InterpStep;\n')
            file.write('                #local Count=Count+1;\n')
            file.write('            #end\n')
            file.write('            #local S2=spline{S0}\n')
            file.write('            #local S0=spline{S1}\n')
            file.write(
                '            #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n'
            )
            file.write('            #local Pos=Pos+PosStep;\n')
            file.write('        #end\n')
            file.write(
                '        BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n'
            )
            file.write('    #end\n')
            file.write('#end\n\n')

            file.write(
                '#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n'
            )
            file.write('   #declare Build=CheckFileName(FileName);\n')
            file.write('   #if(Build=0)\n')
            file.write(
                '      #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n'
            )
            file.write('      #include FileName\n')
            file.write('      object{Surface}\n')
            file.write('   #else\n')
            file.write('      #local NumVertices=(Iter_U+1)*(Iter_V+1);\n')
            file.write('      #local NumFaces=Iter_U*Iter_V*2;\n')
            file.write(
                '      #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n'
            )
            file.write('      #declare VecArr=array[NumVertices]   \n')
            file.write('      #declare NormArr=array[NumVertices]   \n')
            file.write('      #local UVArr=array[NumVertices]      \n')
            file.write('      #local Spl1_0=Spl1(0);\n')
            file.write('      #local Spl2_0=Spl2(0);\n')
            file.write('      #local Spl3_0=Spl3(0);\n')
            file.write('      #local Spl4_0=Spl4(0);\n')
            file.write('      #local UStep=1/Iter_U;\n')
            file.write('      #local VStep=1/Iter_V;\n')
            file.write('      #local Count=0;\n')
            file.write('      #local I=0;\n')
            file.write('      #while (I<=1)\n')
            file.write('         #local Im=1-I;\n')
            file.write('         #local J=0;\n')
            file.write('         #while (J<=1)\n')
            file.write('            #local Jm=1-J;\n')
            file.write(
                '            #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n'
            )
            file.write(
                '            #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n'
            )
            file.write(
                '               LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n'
            )
            file.write('            #declare VecArr[Count]=P0;\n')
            file.write('            #local UVArr[Count]=<J,I>;\n')
            file.write('            #local J=J+UStep;\n')
            file.write('            #local Count=Count+1;\n')
            file.write('         #end\n')
            file.write('         #debug concat(\n')
            file.write(
                '            "\r Done ", str(Count,0,0)," vertices :         ",\n'
            )
            file.write('            str(100*Count/NumVertices,0,2)," %"\n')
            file.write('         )\n')
            file.write('         #local I=I+VStep;\n')
            file.write('      #end\n')
            file.write(
                '      #debug "\r Normals                                  "\n'
            )
            file.write('      #local Count=0;\n')
            file.write('      #local I=0;\n')
            file.write('      #while (I<=Iter_V)\n')
            file.write('         #local J=0;\n')
            file.write('         #while (J<=Iter_U)\n')
            file.write('            #local Ind=(I*Iter_U)+I+J;\n')
            file.write('            #local P0=VecArr[Ind];\n')
            file.write('            #if(J=0)\n')
            file.write('               #local P1=P0+(P0-VecArr[Ind+1]);\n')
            file.write('            #else\n')
            file.write('               #local P1=VecArr[Ind-1];\n')
            file.write('            #end\n')
            file.write('            #if (J=Iter_U)\n')
            file.write('               #local P2=P0+(P0-VecArr[Ind-1]);\n')
            file.write('            #else\n')
            file.write('               #local P2=VecArr[Ind+1];\n')
            file.write('            #end\n')
            file.write('            #if (I=0)\n')
            file.write(
                '               #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n'
            )
            file.write('            #else\n')
            file.write('               #local P3=VecArr[Ind-Iter_U-1];\n')
            file.write('            #end\n')
            file.write('            #if (I=Iter_V)\n')
            file.write(
                '               #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n'
            )
            file.write('            #else\n')
            file.write('               #local P4=VecArr[Ind+Iter_U+1];\n')
            file.write('            #end\n')
            file.write('            #local B1=P4-P0;\n')
            file.write('            #local B2=P2-P0;\n')
            file.write('            #local B3=P3-P0;\n')
            file.write('            #local B4=P1-P0;\n')
            file.write('            #local N1=vcross(B1,B2);\n')
            file.write('            #local N2=vcross(B2,B3);\n')
            file.write('            #local N3=vcross(B3,B4);\n')
            file.write('            #local N4=vcross(B4,B1);\n')
            file.write('            #local Norm=vnormalize((N1+N2+N3+N4));\n')
            file.write('            #declare NormArr[Count]=Norm;\n')
            file.write('            #local J=J+1;\n')
            file.write('            #local Count=Count+1;\n')
            file.write('         #end\n')
            file.write(
                '         #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n'
            )
            file.write('         #local I=I+1;\n')
            file.write('      #end\n')
            file.write(
                '      BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n'
            )
            file.write('   #end\n')
            file.write('#end\n\n')
        # Empty curves
        if len(ob.data.splines) == 0:
            tabWrite("\n//dummy sphere to represent empty curve location\n")
            tabWrite("#declare %s =\n" % dataname)
            tabWrite(
                "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
                % (ob.location.x, ob.location.y, ob.location.z)
            )  # ob.name > povdataname)
        # And non empty curves
        else:
            if bezier_sweep == False:
                tabWrite("#declare %s =\n" % dataname)
            if ob.pov.curveshape == 'sphere_sweep' and bezier_sweep == False:
                tabWrite("union {\n")
                for spl in ob.data.splines:
                    if spl.type != "BEZIER":
                        spl_type = "linear"
                        if spl.type == "NURBS":
                            spl_type = "cubic"
                        points = spl.points
                        numPoints = len(points)
                        if spl.use_cyclic_u:
                            numPoints += 3

                        tabWrite(
                            "sphere_sweep { %s_spline %s,\n"
                            % (spl_type, numPoints)
                        )
                        if spl.use_cyclic_u:
                            pt1 = points[len(points) - 1]
                            wpt1 = pt1.co
                            tabWrite(
                                "<%.4g,%.4g,%.4g>,%.4g\n"
                                % (
                                    wpt1[0],
                                    wpt1[1],
                                    wpt1[2],
                                    pt1.radius * ob.data.bevel_depth,
                                )
                            )
                        for pt in points:
                            wpt = pt.co
                            tabWrite(
                                "<%.4g,%.4g,%.4g>,%.4g\n"
                                % (
                                    wpt[0],
                                    wpt[1],
                                    wpt[2],
                                    pt.radius * ob.data.bevel_depth,
                                )
                            )
                        if spl.use_cyclic_u:
                            for i in range(0, 2):
                                endPt = points[i]
                                wpt = endPt.co
                                tabWrite(
                                    "<%.4g,%.4g,%.4g>,%.4g\n"
                                    % (
                                        wpt[0],
                                        wpt[1],
                                        wpt[2],
                                        endPt.radius * ob.data.bevel_depth,
                                    )
                                )

                    tabWrite("}\n")
            # below not used yet?
            if ob.pov.curveshape == 'sor':
                for spl in ob.data.splines:
                    if spl.type in {'POLY', 'NURBS'}:
                        points = spl.points
                        numPoints = len(points)
                        tabWrite("sor { %s,\n" % numPoints)
                        for pt in points:
                            wpt = pt.co
                            tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
                    else:
                        tabWrite("box { 0,0\n")
            if ob.pov.curveshape in {'lathe', 'prism'}:
                spl = ob.data.splines[0]
                if spl.type == "BEZIER":
                    points = spl.bezier_points
                    lenCur = len(points) - 1
                    lenPts = lenCur * 4
                    ifprism = ''
                    if ob.pov.curveshape in {'prism'}:
                        height = ob.data.extrude
                        ifprism = '-%s, %s,' % (height, height)
                        lenCur += 1
                        lenPts += 4
                    tabWrite(
                        "%s { bezier_spline %s %s,\n"
                        % (ob.pov.curveshape, ifprism, lenPts)
                    )
                    for i in range(0, lenCur):
                        p1 = points[i].co
                        pR = points[i].handle_right
                        end = i + 1
                        if i == lenCur - 1 and ob.pov.curveshape in {'prism'}:
                            end = 0
                        pL = points[end].handle_left
                        p2 = points[end].co
                        line = "<%.4g,%.4g>" % (p1[0], p1[1])
                        line += "<%.4g,%.4g>" % (pR[0], pR[1])
                        line += "<%.4g,%.4g>" % (pL[0], pL[1])
                        line += "<%.4g,%.4g>" % (p2[0], p2[1])
                        tabWrite("%s\n" % line)
                else:
                    points = spl.points
                    lenCur = len(points)
                    lenPts = lenCur
                    ifprism = ''
                    if ob.pov.curveshape in {'prism'}:
                        height = ob.data.extrude
                        ifprism = '-%s, %s,' % (height, height)
                        lenPts += 3
                    spl_type = 'quadratic'
                    if spl.type == 'POLY':
                        spl_type = 'linear'
                    tabWrite(
                        "%s { %s_spline %s %s,\n"
                        % (ob.pov.curveshape, spl_type, ifprism, lenPts)
                    )
                    if ob.pov.curveshape in {'prism'}:
                        pt = points[len(points) - 1]
                        wpt = pt.co
                        tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
                    for pt in points:
                        wpt = pt.co
                        tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
                    if ob.pov.curveshape in {'prism'}:
                        for i in range(2):
                            pt = points[i]
                            wpt = pt.co
                            tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
            if bezier_sweep:
                for p in range(len(ob.data.splines)):
                    br = []
                    depth = ob.data.bevel_depth
                    spl = ob.data.splines[p]
                    points = spl.bezier_points
                    lenCur = len(points) - 1
                    numPoints = lenCur * 4
                    if spl.use_cyclic_u:
                        lenCur += 1
                        numPoints += 4
                    tabWrite(
                        "#declare %s_points_%s = array[%s]{\n"
                        % (dataname, p, numPoints)
                    )
                    for i in range(lenCur):
                        p1 = points[i].co
                        pR = points[i].handle_right
                        end = i + 1
                        if spl.use_cyclic_u and i == (lenCur - 1):
                            end = 0
                        pL = points[end].handle_left
                        p2 = points[end].co
                        r3 = points[end].radius * depth
                        r0 = points[i].radius * depth
                        r1 = 2 / 3 * r0 + 1 / 3 * r3
                        r2 = 1 / 3 * r0 + 2 / 3 * r3
                        br.append((r0, r1, r2, r3))
                        line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2])
                        line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2])
                        line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2])
                        line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2])
                        tabWrite("%s\n" % line)
                    tabWrite("}\n")
                    tabWrite(
                        "#declare %s_radii_%s = array[%s]{\n"
                        % (dataname, p, len(br) * 4)
                    )
                    for Tuple in br:
                        tabWrite(
                            '%.4f,%.4f,%.4f,%.4f\n'
                            % (Tuple[0], Tuple[1], Tuple[2], Tuple[3])
                        )
                    tabWrite("}\n")
                if len(ob.data.splines) == 1:
                    tabWrite('#declare %s = object{\n' % dataname)
                    tabWrite(
                        '    Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n'
                        % (ob.data.resolution_u, dataname, p, dataname, p)
                    )
                else:
                    tabWrite('#declare %s = union{\n' % dataname)
                    for p in range(len(ob.data.splines)):
                        tabWrite(
                            '    object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n'
                            % (ob.data.resolution_u, dataname, p, dataname, p)
                        )
                    # tabWrite('#include "bezier_spheresweep.inc"\n') #now inlined
                # tabWrite('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth))
            if ob.pov.curveshape in {'loft'}:
                tabWrite(
                    'object {MSM(%s,%s,"c",%s,"")\n'
                    % (dataname, ob.pov.res_u, ob.pov.res_v)
                )
            if ob.pov.curveshape in {'birail'}:
                splines = '%s1,%s2,%s3,%s4' % (
                    dataname,
                    dataname,
                    dataname,
                    dataname,
                )
                tabWrite(
                    'object {Coons(%s, %s, %s, "")\n'
                    % (splines, ob.pov.res_u, ob.pov.res_v)
                )
            povMatName = "Default_texture"
            if ob.active_material:
                # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                try:
                    material = ob.active_material
                    writeObjectMaterial(material, ob)
                except IndexError:
                    print(me)
            # tabWrite("texture {%s}\n"%povMatName)
            if ob.pov.curveshape in {'prism'}:
                tabWrite("rotate <90,0,0>\n")
                tabWrite("scale y*-1\n")
            tabWrite("}\n")

    #################################################################

    def exportMeta(metas):
        """write all POV blob primitives and Blender Metas to exported file """
        # TODO - blenders 'motherball' naming is not supported.

        if comments and len(metas) >= 1:
            file.write("//--Blob objects--\n\n")
        # Get groups of metaballs by blender name prefix.
        meta_group = {}
        meta_elems = {}
        for ob in metas:
            prefix = ob.name.split(".")[0]
            if not prefix in meta_group:
                meta_group[prefix] = ob  # .data.threshold
            elems = [
                (elem, ob)
                for elem in ob.data.elements
                if elem.type
                in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'}
            ]
            if prefix in meta_elems:
                meta_elems[prefix].extend(elems)
            else:
                meta_elems[prefix] = elems

            # empty metaball
            if len(elems) == 0:
                tabWrite("\n//dummy sphere to represent empty meta location\n")
                tabWrite(
                    "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
                    % (ob.location.x, ob.location.y, ob.location.z)
                )  # ob.name > povdataname)
            # other metaballs
            else:
                for mg, ob in meta_group.items():
                    if len(meta_elems[mg]) != 0:
                        tabWrite(
                            "blob{threshold %.4g // %s \n"
                            % (ob.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':
                                tabWrite(
                                    "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g "
                                    % (
                                        loc.x,
                                        loc.y,
                                        loc.z,
                                        elem.radius,
                                        stiffness,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")
                            elif elem.type == 'ELLIPSOID':
                                tabWrite(
                                    "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g "
                                    % (
                                        loc.x / elem.size_x,
                                        loc.y / elem.size_y,
                                        loc.z / elem.size_z,
                                        elem.radius,
                                        stiffness,
                                    )
                                )
                                tabWrite(
                                    "scale <%.6g, %.6g, %.6g>"
                                    % (elem.size_x, elem.size_y, elem.size_z)
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")
                            elif elem.type == 'CAPSULE':
                                tabWrite(
                                    "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,
                                    )
                                )
                                # tabWrite("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z))
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")

                            elif elem.type == 'CUBE':
                                tabWrite(
                                    "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,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")
                                tabWrite(
                                    "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,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")
                                tabWrite(
                                    "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,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")

                            elif elem.type == 'PLANE':
                                tabWrite(
                                    "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,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")
                                tabWrite(
                                    "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,
                                    )
                                )
                                writeMatrix(
                                    global_matrix @ elems[1].matrix_world
                                )
                                tabWrite("}\n")

                        try:
                            material = elems[1].data.materials[
                                0
                            ]  # lame! - blender cant do enything else.
                        except:
                            material = None
                        if material:
                            diffuse_color = material.diffuse_color
                            trans = 1.0 - material.pov.alpha
                            if (
                                material.use_transparency
                                and material.transparency_method == 'RAYTRACE'
                            ):
                                povFilter = (
                                    material.pov_raytrace_transparency.filter
                                    * (1.0 - material.alpha)
                                )
                                trans = (1.0 - material.pov.alpha) - povFilter
                            else:
                                povFilter = 0.0
                            material_finish = materialNames[material.name]
                            tabWrite(
                                "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
                                % (
                                    diffuse_color[0],
                                    diffuse_color[1],
                                    diffuse_color[2],
                                    povFilter,
                                    trans,
                                )
                            )
                            tabWrite(
                                "finish{%s} " % safety(material_finish, Level=2)
                            )
                        else:
                            tabWrite(
                                "pigment{srgb 1} finish{%s} "
                                % (safety(DEF_MAT_NAME, Level=2))
                            )

                            writeObjectMaterial(material, ob)
                            # writeObjectMaterial(material, elems[1])
                            tabWrite(
                                "radiosity{importance %3g}\n"
                                % ob.pov.importance_value
                            )
                            tabWrite("}\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:
                tabWrite("blob {\n")
                tabWrite("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':

                        tabWrite("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
                        tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
                                 (loc.x / elem.size_x,
                                  loc.y / elem.size_y,
                                  loc.z / elem.size_z,
                                  elem.radius, stiffness))
                        tabWrite("scale <%.6g, %.6g, %.6g> \n" %
                                 (elem.size_x, elem.size_y, elem.size_z))

                if material:
                    diffuse_color = material.diffuse_color
                    trans = 1.0 - material.pov.alpha
                    if material.use_transparency and material.transparency_method == 'RAYTRACE':
                        povFilter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha)
                        trans = (1.0 - material.pov.alpha) - povFilter
                    else:
                        povFilter = 0.0

                    material_finish = materialNames[material.name]

                    tabWrite("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" %
                             (diffuse_color[0], diffuse_color[1], diffuse_color[2],
                              povFilter, trans))
                    tabWrite("finish {%s}\n" % safety(material_finish, Level=2))

                else:
                    tabWrite("pigment {srgb 1} \n")
                    # Write the finish last.
                    tabWrite("finish {%s}\n" % (safety(DEF_MAT_NAME, Level=2)))

                writeObjectMaterial(material, elems[1])

                writeMatrix(global_matrix @ ob.matrix_world)
                # Importance for radiosity sampling added here
                tabWrite("radiosity { \n")
                # importance > ob.pov.importance_value
                tabWrite("importance %3g \n" % importance)
                tabWrite("}\n")

                tabWrite("}\n")  # End of Metaball block

                if comments and len(metas) >= 1:
                    file.write("\n")
    '''
    #    objectNames = {}
    DEF_OBJ_NAME = "Default"

    def exportMeshes(scene, sel, csg):
        """write all meshes as POV mesh2{} syntax to exported file """
        #some numpy functions to speed up mesh export

        # TODO: also write a numpy function to read matrices at object level?
        # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
        def read_verts_co(self, mesh):
            #'float64' would be a slower 64-bit floating-point number numpy datatype
            # using 'float32' vert coordinates for now until any issue is reported
            mverts_co = np.zeros((len(mesh.vertices)*3), dtype=np.float32)
            mesh.vertices.foreach_get("co", mverts_co)
            return np.reshape(mverts_co, (len(mesh.vertices), 3))

        def read_verts_idx(self, mesh):
            mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
            mesh.vertices.foreach_get("index", mverts_idx)
            return np.reshape(mverts_idx, (len(mesh.vertices), 1))

        def read_verts_norms(self, mesh):
            #'float64' would be a slower 64-bit floating-point number numpy datatype
            # using less accurate 'float16' normals for now until any issue is reported
            mverts_no = np.zeros((len(mesh.vertices)*3), dtype=np.float16)
            mesh.vertices.foreach_get("normal", mverts_no)
            return np.reshape(mverts_no, (len(mesh.vertices), 3))

        def read_faces_idx(self, mesh):
            mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
            mesh.loop_triangles.foreach_get("index", mfaces_idx)
            return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))

        def read_faces_verts_indices(self, mesh):
            mfaces_verts_idx = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64)
            mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
            return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))

        #Why is below different from verex indices?
        def read_faces_verts_loops(self, mesh):
            mfaces_verts_loops = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64)
            mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
            return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))

        def read_faces_norms(self, mesh):
            #'float64' would be a slower 64-bit floating-point number numpy datatype
            # using less accurate 'float16' normals for now until any issue is reported
            mfaces_no = np.zeros((len(mesh.loop_triangles)*3), dtype=np.float16)
            mesh.loop_triangles.foreach_get("normal", mfaces_no)
            return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))

        def read_faces_smooth(self, mesh):
            mfaces_smth = np.zeros((len(mesh.loop_triangles)*1), dtype=np.bool)
            mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
            return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))

        def read_faces_material_indices(self, mesh):
            mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
            mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
            return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))



        #        obmatslist = []
        #        def hasUniqueMaterial():
        #            # Grab materials attached to object instances ...
        #            if hasattr(ob, 'material_slots'):
        #                for ms in ob.material_slots:
        #                    if ms.material is not None and ms.link == 'OBJECT':
        #                        if ms.material in obmatslist:
        #                            return False
        #                        else:
        #                            obmatslist.append(ms.material)
        #                            return True
        #        def hasObjectMaterial(ob):
        #            # Grab materials attached to object instances ...
        #            if hasattr(ob, 'material_slots'):
        #                for ms in ob.material_slots:
        #                    if ms.material is not None and ms.link == 'OBJECT':
        #                        # If there is at least one material slot linked to the object
        #                        # and not the data (mesh), always create a new, "private" data instance.
        #                        return True
        #            return False
        # For objects using local material(s) only!
        # This is a mapping between a tuple (dataname, materialnames, ...), and the POV dataname.
        # As only objects using:
        #     * The same data.
        #     * EXACTLY the same materials, in EXACTLY the same sockets.
        # ... can share a same instance in POV export.
        obmats2data = {}

        def checkObjectMaterials(ob, name, dataname):
            if hasattr(ob, 'material_slots'):
                has_local_mats = False
                key = [dataname]
                for ms in ob.material_slots:
                    if ms.material is not None:
                        key.append(ms.material.name)
                        if ms.link == 'OBJECT' and not has_local_mats:
                            has_local_mats = True
                    else:
                        # Even if the slot is empty, it is important to grab it...
                        key.append("")
                if has_local_mats:
                    # If this object uses local material(s), lets find if another object
                    # using the same data and exactly the same list of materials
                    # (in the same slots) has already been processed...
                    # Note that here also, we use object name as new, unique dataname for Pov.
                    key = tuple(key)  # Lists are not hashable...
                    if key not in obmats2data:
                        obmats2data[key] = name
                    return obmats2data[key]
            return None

        data_ref = {}

        def store(scene, ob, name, dataname, matrix):
            # The Object needs to be written at least once but if its data is
            # already in data_ref this has already been done.
            # This func returns the "povray" name of the data, or None
            # if no writing is needed.
            if ob.is_modified(scene, 'RENDER'):
                # Data modified.
                # Create unique entry in data_ref by using object name
                # (always unique in Blender) as data name.
                data_ref[name] = [(name, MatrixAsPovString(matrix))]
                return name
            # Here, we replace dataname by the value returned by checkObjectMaterials, only if
            # it is not evaluated to False (i.e. only if the object uses some local material(s)).
            dataname = checkObjectMaterials(ob, name, dataname) or dataname
            if dataname in data_ref:
                # Data already known, just add the object instance.
                data_ref[dataname].append((name, MatrixAsPovString(matrix)))
                # No need to write data
                return None
            else:
                # Data not yet processed, create a new entry in data_ref.
                data_ref[dataname] = [(name, MatrixAsPovString(matrix))]
                return dataname

        def exportSmoke(smoke_obj_name):
            # if LuxManager.CurrentScene.name == 'preview':
            # return 1, 1, 1, 1.0
            # else:
            flowtype = -1
            smoke_obj = bpy.data.objects[smoke_obj_name]
            domain = None

            # Search smoke domain target for smoke modifiers
            for mod in smoke_obj.modifiers:
                if mod.name == 'Smoke':
                    if mod.smoke_type == 'FLOW':
                        if mod.flow_settings.smoke_flow_type == 'BOTH':
                            flowtype = 2
                        else:
                            if mod.flow_settings.smoke_flow_type == 'SMOKE':
                                flowtype = 0
                            else:
                                if mod.flow_settings.smoke_flow_type == 'FIRE':
                                    flowtype = 1

                    if mod.smoke_type == 'DOMAIN':
                        domain = smoke_obj
                        smoke_modifier = mod

            eps = 0.000001
            if domain is not None:
                # if bpy.app.version[0] >= 2 and bpy.app.version[1] >= 71:
                # Blender version 2.71 supports direct access to smoke data structure
                set = mod.domain_settings
                channeldata = []
                for v in set.density_grid:
                    channeldata.append(v.real)
                    print(v.real)
                ## Usage en voxel texture:
                # channeldata = []
                # if channel == 'density':
                # for v in set.density_grid:
                # channeldata.append(v.real)

                # if channel == 'fire':
                # for v in set.flame_grid:
                # channeldata.append(v.real)

                resolution = set.resolution_max
                big_res = []
                big_res.append(set.domain_resolution[0])
                big_res.append(set.domain_resolution[1])
                big_res.append(set.domain_resolution[2])

                if set.use_high_resolution:
                    big_res[0] = big_res[0] * (set.amplify + 1)
                    big_res[1] = big_res[1] * (set.amplify + 1)
                    big_res[2] = big_res[2] * (set.amplify + 1)
                # else:
                # p = []
                ##gather smoke domain settings
                # BBox = domain.bound_box
                # p.append([BBox[0][0], BBox[0][1], BBox[0][2]])
                # p.append([BBox[6][0], BBox[6][1], BBox[6][2]])
                # set = mod.domain_settings
                # resolution = set.resolution_max
                # smokecache = set.point_cache
                # ret = read_cache(smokecache, set.use_high_resolution, set.amplify + 1, flowtype)
                # res_x = ret[0]
                # res_y = ret[1]
                # res_z = ret[2]
                # density = ret[3]
                # fire = ret[4]

                # if res_x * res_y * res_z > 0:
                ##new cache format
                # big_res = []
                # big_res.append(res_x)
                # big_res.append(res_y)
                # big_res.append(res_z)
                # else:
                # max = domain.dimensions[0]
                # if (max - domain.dimensions[1]) < -eps:
                # max = domain.dimensions[1]

                # if (max - domain.dimensions[2]) < -eps:
                # max = domain.dimensions[2]

                # big_res = [int(round(resolution * domain.dimensions[0] / max, 0)),
                # int(round(resolution * domain.dimensions[1] / max, 0)),
                # int(round(resolution * domain.dimensions[2] / max, 0))]

                # if set.use_high_resolution:
                # big_res = [big_res[0] * (set.amplify + 1), big_res[1] * (set.amplify + 1),
                # big_res[2] * (set.amplify + 1)]

                # if channel == 'density':
                # channeldata = density

                # if channel == 'fire':
                # channeldata = fire

                # sc_fr = '%s/%s/%s/%05d' % (efutil.export_path, efutil.scene_filename(), bpy.context.scene.name, bpy.context.scene.frame_current)
                #               if not os.path.exists( sc_fr ):
                #                   os.makedirs(sc_fr)
                #
                #               smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name)
                #               smoke_path = '/'.join([sc_fr, smoke_filename])
                #
                #               with open(smoke_path, 'wb') as smoke_file:
                #                   # Binary densitygrid file format
                #                   #
                #                   # File header
                #                   smoke_file.write(b'SMOKE')        #magic number
                #                   smoke_file.write(struct.pack('<I', big_res[0]))
                #                   smoke_file.write(struct.pack('<I', big_res[1]))
                #                   smoke_file.write(struct.pack('<I', big_res[2]))
                # Density data
                #                   smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata))
                #
                #               LuxLog('Binary SMOKE file written: %s' % (smoke_path))

                # return big_res[0], big_res[1], big_res[2], channeldata

                mydf3 = df3.df3(big_res[0], big_res[1], big_res[2])
                sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size()
                for x in range(sim_sizeX):
                    for y in range(sim_sizeY):
                        for z in range(sim_sizeZ):
                            mydf3.set(
                                x,
                                y,
                                z,
                                channeldata[
                                    ((z * sim_sizeY + y) * sim_sizeX + x)
                                ],
                            )

                mydf3.exportDF3(smokePath)
                print('Binary smoke.df3 file written in preview directory')
                if comments:
                    file.write("\n//--Smoke--\n\n")

                # Note: We start with a default unit cube.
                #       This is mandatory to read correctly df3 data - otherwise we could just directly use bbox
                #       coordinates from the start, and avoid scale/translate operations at the end...
                file.write("box{<0,0,0>, <1,1,1>\n")
                file.write("    pigment{ rgbt 1 }\n")
                file.write("    hollow\n")
                file.write("    interior{ //---------------------\n")
                file.write("        media{ method 3\n")
                file.write(
                    "               emission <1,1,1>*1\n"
                )  # 0>1 for dark smoke to white vapour
                file.write("               scattering{ 1, // Type\n")
                file.write("                  <1,1,1>*0.1\n")
                file.write("                } // end scattering\n")
                file.write(
                    "                density{density_file df3 \"%s\"\n"
                    % (smokePath)
                )
                file.write("                        color_map {\n")
                file.write("                        [0.00 rgb 0]\n")
                file.write("                        [0.05 rgb 0]\n")
                file.write("                        [0.20 rgb 0.2]\n")
                file.write("                        [0.30 rgb 0.6]\n")
                file.write("                        [0.40 rgb 1]\n")
                file.write("                        [1.00 rgb 1]\n")
                file.write("                       } // end color_map\n")
                file.write("               } // end of density\n")
                file.write(
                    "               samples %i // higher = more precise\n"
                    % resolution
                )
                file.write(
                    "         } // end of media --------------------------\n"
                )
                file.write("    } // end of interior\n")

                # START OF TRANSFORMATIONS

                # Size to consider here are bbox dimensions (i.e. still in object space, *before* applying
                # loc/rot/scale and other transformations (like parent stuff), aka matrix_world).
                bbox = smoke_obj.bound_box
                dim = [
                    abs(bbox[6][0] - bbox[0][0]),
                    abs(bbox[6][1] - bbox[0][1]),
                    abs(bbox[6][2] - bbox[0][2]),
                ]

                # We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox).
                file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2]))

                # We offset our cube such that (0,0,0) coordinate matches Blender's object center.
                file.write(
                    "translate<%.6g,%.6g,%.6g>\n"
                    % (bbox[0][0], bbox[0][1], bbox[0][2])
                )

                # We apply object's transformations to get final loc/rot/size in world space!
                # Note: we could combine the two previous transformations with this matrix directly...
                writeMatrix(global_matrix @ smoke_obj.matrix_world)

                # END OF TRANSFORMATIONS

                file.write("}\n")

                # file.write("               interpolate 1\n")
                # file.write("               frequency 0\n")
                # file.write("   }\n")
                # file.write("}\n")

        ob_num = 0
        for ob in sel:
            # subtract original from the count of their instances as were not counted before 2.8
            if not (ob.is_instancer and ob.original != ob):
                ob_num += 1

                # XXX I moved all those checks here, as there is no need to compute names
                #     for object we won't export here!
                if ob.type in {
                    'LIGHT',
                    'CAMERA',  #'EMPTY', #empties can bear dupligroups
                    'META',
                    'ARMATURE',
                    'LATTICE',
                }:
                    continue
                smokeFlag = False
                for mod in ob.modifiers:
                    if mod and hasattr(mod, 'smoke_type'):
                        smokeFlag = True
                        if mod.smoke_type == 'DOMAIN':
                            exportSmoke(ob.name)
                        break  # don't render domain mesh or flow emitter mesh, skip to next object.
                if not smokeFlag:
                    # Export Hair
                    renderEmitter = True
                    if hasattr(ob, 'particle_systems'):
                        renderEmitter = False
                        if ob.show_instancer_for_render:
                            renderEmitter = True
                        for pSys in ob.particle_systems:
                            for mod in [
                                m
                                for m in ob.modifiers
                                if (m is not None)
                                and (m.type == 'PARTICLE_SYSTEM')
                            ]:
                                if (
                                    (pSys.settings.render_type == 'PATH')
                                    and mod.show_render
                                    and (pSys.name == mod.particle_system.name)
                                ):
                                    tstart = time.time()
                                    texturedHair = 0
                                    if (
                                        ob.material_slots[
                                            pSys.settings.material - 1
                                        ].material
                                        and ob.active_material is not None
                                    ):
                                        pmaterial = ob.material_slots[
                                            pSys.settings.material - 1
                                        ].material
                                        #XXX Todo: replace by pov_(Particles?)_texture_slot
                                        for th in pmaterial.texture_slots:
                                            if th and th.use:
                                                if (
                                                    (
                                                        th.texture.type
                                                        == 'IMAGE'
                                                        and th.texture.image
                                                    )
                                                    or th.texture.type
                                                    != 'IMAGE'
                                                ):
                                                    if th.use_map_color_diffuse:
                                                        texturedHair = 1
                                        if pmaterial.strand.use_blender_units:
                                            strandStart = (
                                                pmaterial.strand.root_size
                                            )
                                            strandEnd = (
                                                pmaterial.strand.tip_size
                                            )
                                            strandShape = pmaterial.strand.shape
                                        else:  # Blender unit conversion
                                            strandStart = (
                                                pmaterial.strand.root_size
                                                / 200.0
                                            )
                                            strandEnd = (
                                                pmaterial.strand.tip_size
                                                / 200.0
                                            )
                                            strandShape = pmaterial.strand.shape
                                    else:
                                        pmaterial = (
                                            "default"
                                        )  # No material assigned in blender, use default one
                                        strandStart = 0.01
                                        strandEnd = 0.01
                                        strandShape = 0.0
                                    # Set the number of particles to render count rather than 3d view display
                                    # pSys.set_resolution(scene, ob, 'RENDER') # DEPRECATED
                                    # When you render, the entire dependency graph will be
                                    # evaluated at render resolution, including the particles.
                                    # In the viewport it will be at viewport resolution.
                                    # So there is no need fo render engines to use this function anymore,
                                    # it's automatic now.
                                    steps = pSys.settings.display_step
                                    steps = (
                                        3 ** steps
                                    )  # or (power of 2 rather than 3) + 1 # Formerly : len(particle.hair_keys)

                                    totalNumberOfHairs = (
                                        pSys.settings.count
                                        + pSys.settings.rendered_child_count
                                    )
                                    # hairCounter = 0
                                    file.write(
                                        '#declare HairArray = array[%i] {\n'
                                        % totalNumberOfHairs
                                    )
                                    for pindex in range(0, totalNumberOfHairs):

                                        # if particle.is_exist and particle.is_visible:
                                        # hairCounter += 1
                                        # controlPointCounter = 0
                                        # Each hair is represented as a separate sphere_sweep in POV-Ray.

                                        file.write('sphere_sweep{')
                                        if pSys.settings.use_hair_bspline:
                                            file.write('b_spline ')
                                            file.write(
                                                '%i,\n' % (steps + 2)
                                            )  # +2 because the first point needs tripling to be more than a handle in POV
                                        else:
                                            file.write('linear_spline ')
                                            file.write('%i,\n' % (steps))
                                        # changing world coordinates to object local coordinates by multiplying with inverted matrix
                                        initCo = ob.matrix_world.inverted() @ (
                                            pSys.co_hair(
                                                ob, particle_no=pindex, step=0
                                            )
                                        )
                                        if (
                                            ob.material_slots[
                                                pSys.settings.material - 1
                                            ].material
                                            and ob.active_material is not None
                                        ):
                                            pmaterial = ob.material_slots[
                                                pSys.settings.material - 1
                                            ].material
                                            for th in pmaterial.texture_slots:
                                                if (
                                                    th
                                                    and th.use
                                                    and th.use_map_color_diffuse
                                                ):
                                                    # treat POV textures as bitmaps
                                                    if (
                                                        th.texture.type
                                                        == 'IMAGE'
                                                        and th.texture.image
                                                        and th.texture_coords
                                                        == 'UV'
                                                        and ob.data.uv_textures
                                                        is not None
                                                    ):  # or (th.texture.pov.tex_pattern_type != 'emulator' and th.texture_coords == 'UV' and ob.data.uv_textures is not None):
                                                        image = th.texture.image
                                                        image_width = image.size[
                                                            0
                                                        ]
                                                        image_height = image.size[
                                                            1
                                                        ]
                                                        image_pixels = image.pixels[
                                                            :
                                                        ]
                                                        uv_co = pSys.uv_on_emitter(
                                                            mod,
                                                            pSys.particles[
                                                                pindex
                                                            ],
                                                            pindex,
                                                            0,
                                                        )
                                                        x_co = round(
                                                            uv_co[0]
                                                            * (image_width - 1)
                                                        )
                                                        y_co = round(
                                                            uv_co[1]
                                                            * (image_height - 1)
                                                        )
                                                        pixelnumber = (
                                                            image_width * y_co
                                                        ) + x_co
                                                        r = image_pixels[
                                                            pixelnumber * 4
                                                        ]
                                                        g = image_pixels[
                                                            pixelnumber * 4 + 1
                                                        ]
                                                        b = image_pixels[
                                                            pixelnumber * 4 + 2
                                                        ]
                                                        a = image_pixels[
                                                            pixelnumber * 4 + 3
                                                        ]
                                                        initColor = (r, g, b, a)
                                                    else:
                                                        # only overwrite variable for each competing texture for now
                                                        initColor = th.texture.evaluate(
                                                            (
                                                                initCo[0],
                                                                initCo[1],
                                                                initCo[2],
                                                            )
                                                        )
                                        for step in range(0, steps):
                                            co = ob.matrix_world.inverted() @ (
                                                pSys.co_hair(
                                                    ob,
                                                    particle_no=pindex,
                                                    step=step,
                                                )
                                            )
                                            # for controlPoint in particle.hair_keys:
                                            if pSys.settings.clump_factor != 0:
                                                hDiameter = (
                                                    pSys.settings.clump_factor
                                                    / 200.0
                                                    * random.uniform(0.5, 1)
                                                )
                                            elif step == 0:
                                                hDiameter = strandStart
                                            else:
                                                hDiameter += (
                                                    strandEnd - strandStart
                                                ) / (
                                                    pSys.settings.display_step
                                                    + 1
                                                )  # XXX +1 or not?
                                            if (
                                                step == 0
                                                and pSys.settings.use_hair_bspline
                                            ):
                                                # Write three times the first point to compensate pov Bezier handling
                                                file.write(
                                                    '<%.6g,%.6g,%.6g>,%.7g,\n'
                                                    % (
                                                        co[0],
                                                        co[1],
                                                        co[2],
                                                        abs(hDiameter),
                                                    )
                                                )
                                                file.write(
                                                    '<%.6g,%.6g,%.6g>,%.7g,\n'
                                                    % (
                                                        co[0],
                                                        co[1],
                                                        co[2],
                                                        abs(hDiameter),
                                                    )
                                                )
                                                # file.write('<%.6g,%.6g,%.6g>,%.7g' % (particle.location[0], particle.location[1], particle.location[2], abs(hDiameter))) # Useless because particle location is the tip, not the root.
                                                # file.write(',\n')
                                            # controlPointCounter += 1
                                            # totalNumberOfHairs += len(pSys.particles)# len(particle.hair_keys)

                                            # Each control point is written out, along with the radius of the
                                            # hair at that point.
                                            file.write(
                                                '<%.6g,%.6g,%.6g>,%.7g'
                                                % (
                                                    co[0],
                                                    co[1],
                                                    co[2],
                                                    abs(hDiameter),
                                                )
                                            )

                                            # All coordinates except the last need a following comma.

                                            if step != steps - 1:
                                                file.write(',\n')
                                            else:
                                                if texturedHair:
                                                    # Write pigment and alpha (between Pov and Blender alpha 0 and 1 are reversed)
                                                    file.write(
                                                        '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n'
                                                        % (
                                                            initColor[0],
                                                            initColor[1],
                                                            initColor[2],
                                                            1.0 - initColor[3],
                                                        )
                                                    )
                                                # End the sphere_sweep declaration for this hair
                                                file.write('}\n')

                                        # All but the final sphere_sweep (each array element) needs a terminating comma.
                                        if pindex != totalNumberOfHairs:
                                            file.write(',\n')
                                        else:
                                            file.write('\n')

                                    # End the array declaration.

                                    file.write('}\n')
                                    file.write('\n')

                                    if not texturedHair:
                                        # Pick up the hair material diffuse color and create a default POV-Ray hair texture.

                                        file.write('#ifndef (HairTexture)\n')
                                        file.write(
                                            '  #declare HairTexture = texture {\n'
                                        )
                                        file.write(
                                            '    pigment {srgbt <%s,%s,%s,%s>}\n'
                                            % (
                                                pmaterial.diffuse_color[0],
                                                pmaterial.diffuse_color[1],
                                                pmaterial.diffuse_color[2],
                                                (
                                                    pmaterial.strand.width_fade
                                                    + 0.05
                                                ),
                                            )
                                        )
                                        file.write('  }\n')
                                        file.write('#end\n')
                                        file.write('\n')

                                    # Dynamically create a union of the hairstrands (or a subset of them).
                                    # By default use every hairstrand, commented line is for hand tweaking test renders.
                                    file.write(
                                        '//Increasing HairStep divides the amount of hair for test renders.\n'
                                    )
                                    file.write(
                                        '#ifndef(HairStep) #declare HairStep = 1; #end\n'
                                    )
                                    file.write('union{\n')
                                    file.write('  #local I = 0;\n')
                                    file.write(
                                        '  #while (I < %i)\n'
                                        % totalNumberOfHairs
                                    )
                                    file.write('    object {HairArray[I]')
                                    if not texturedHair:
                                        file.write(' texture{HairTexture}\n')
                                    else:
                                        file.write('\n')
                                    # Translucency of the hair:
                                    file.write('        hollow\n')
                                    file.write('        double_illuminate\n')
                                    file.write('        interior {\n')
                                    file.write('            ior 1.45\n')
                                    file.write('            media {\n')
                                    file.write(
                                        '                scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n'
                                    )
                                    file.write(
                                        '                absorption 10/<0.83, 0.75, 0.15>\n'
                                    )
                                    file.write('                samples 1\n')
                                    file.write('                method 2\n')
                                    file.write(
                                        '                density {cylindrical\n'
                                    )
                                    file.write(
                                        '                    color_map {\n'
                                    )
                                    file.write(
                                        '                        [0.0 rgb <0.83, 0.45, 0.35>]\n'
                                    )
                                    file.write(
                                        '                        [0.5 rgb <0.8, 0.8, 0.4>]\n'
                                    )
                                    file.write(
                                        '                        [1.0 rgb <1,1,1>]\n'
                                    )
                                    file.write('                    }\n')
                                    file.write('                }\n')
                                    file.write('            }\n')
                                    file.write('        }\n')
                                    file.write('    }\n')

                                    file.write('    #local I = I + HairStep;\n')
                                    file.write('  #end\n')

                                    writeMatrix(global_matrix @ ob.matrix_world)

                                    file.write('}')
                                    print(
                                        'Totals hairstrands written: %i'
                                        % totalNumberOfHairs
                                    )
                                    print(
                                        'Number of tufts (particle systems)',
                                        len(ob.particle_systems),
                                    )

                                    # Set back the displayed number of particles to preview count
                                    # pSys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED
                                    # When you render, the entire dependency graph will be
                                    # evaluated at render resolution, including the particles.
                                    # In the viewport it will be at viewport resolution.
                                    # So there is no need fo render engines to use this function anymore,
                                    # it's automatic now.
                                    if renderEmitter == False:
                                        continue  # don't render mesh, skip to next object.

                    #############################################
                    # Generating a name for object just like materials to be able to use it
                    # (baking for now or anything else).
                    # XXX I don't understand that if we are here, sel if a non-empty iterable,
                    #     so this condition is always True, IMO -- mont29
                    if ob.data:
                        name_orig = "OB" + ob.name
                        dataname_orig = "DATA" + ob.data.name
                    elif ob.is_instancer:
                        if ob.instance_type == 'COLLECTION':
                            name_orig = "OB" + ob.name
                            dataname_orig = "DATA" + ob.instance_collection.name
                        else:
                            # hoping only dupligroups have several source datablocks
                            # ob_dupli_list_create(scene) #deprecated in 2.8
                            depsgraph = bpy.context.evaluated_depsgraph_get()
                            for eachduplicate in depsgraph.object_instances:
                                if (
                                    eachduplicate.is_instance
                                ):  # Real dupli instance filtered because original included in list since 2.8
                                    dataname_orig = (
                                        "DATA" + eachduplicate.object.name
                                    )
                            # ob.dupli_list_clear() #just don't store any reference to instance since 2.8
                    elif ob.type == 'EMPTY':
                        name_orig = "OB" + ob.name
                        dataname_orig = "DATA" + ob.name
                    else:
                        name_orig = DEF_OBJ_NAME
                        dataname_orig = DEF_OBJ_NAME
                    name = string_strip_hyphen(bpy.path.clean_name(name_orig))
                    dataname = string_strip_hyphen(
                        bpy.path.clean_name(dataname_orig)
                    )
                    ##            for slot in ob.material_slots:
                    ##                if slot.material is not None and slot.link == 'OBJECT':
                    ##                    obmaterial = slot.material

                    #############################################

                    if info_callback:
                        info_callback(
                            "Object %2.d of %2.d (%s)"
                            % (ob_num, len(sel), ob.name)
                        )

                    # if ob.type != 'MESH':
                    #    continue
                    # me = ob.data

                    matrix = global_matrix @ ob.matrix_world
                    povdataname = store(scene, ob, name, dataname, matrix)
                    if povdataname is None:
                        print("This is an instance of " + name)
                        continue

                    print("Writing Down First Occurrence of " + name)

                    ############################################Povray Primitives
                    # special exportCurves() function takes care of writing
                    # lathe, sphere_sweep, birail, and loft except with modifiers
                    # converted to mesh
                    if not ob.is_modified(scene, 'RENDER'):
                        if ob.type == 'CURVE' and (
                            ob.pov.curveshape
                            in {'lathe', 'sphere_sweep', 'loft'}
                        ):
                            continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'ISOSURFACE':
                        tabWrite("#declare %s = isosurface{ \n" % povdataname)
                        tabWrite("function{ \n")
                        textName = ob.pov.iso_function_text
                        if textName:
                            node_tree = bpy.context.scene.node_tree
                            for node in node_tree.nodes:
                                if (
                                    node.bl_idname == "IsoPropsNode"
                                    and node.label == ob.name
                                ):
                                    for inp in node.inputs:
                                        if inp:
                                            tabWrite(
                                                "#declare %s = %.6g;\n"
                                                % (inp.name, inp.default_value)
                                            )

                            text = bpy.data.texts[textName]
                            for line in text.lines:
                                split = line.body.split()
                                if split[0] != "#declare":
                                    tabWrite("%s\n" % line.body)
                        else:
                            tabWrite("abs(x) - 2 + y")
                        tabWrite("}\n")
                        tabWrite("threshold %.6g\n" % ob.pov.threshold)
                        tabWrite("max_gradient %.6g\n" % ob.pov.max_gradient)
                        tabWrite("accuracy  %.6g\n" % ob.pov.accuracy)
                        tabWrite("contained_by { ")
                        if ob.pov.contained_by == "sphere":
                            tabWrite(
                                "sphere {0,%.6g}}\n" % ob.pov.container_scale
                            )
                        else:
                            tabWrite(
                                "box {-%.6g,%.6g}}\n"
                                % (
                                    ob.pov.container_scale,
                                    ob.pov.container_scale,
                                )
                            )
                        if ob.pov.all_intersections:
                            tabWrite("all_intersections\n")
                        else:
                            if ob.pov.max_trace > 1:
                                tabWrite("max_trace %.6g\n" % ob.pov.max_trace)
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        tabWrite("scale %.6g\n" % (1 / ob.pov.container_scale))
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'SUPERELLIPSOID':
                        tabWrite(
                            "#declare %s = superellipsoid{ <%.4f,%.4f>\n"
                            % (povdataname, ob.pov.se_n2, ob.pov.se_n1)
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'SUPERTORUS':
                        rMajor = ob.pov.st_major_radius
                        rMinor = ob.pov.st_minor_radius
                        ring = ob.pov.st_ring
                        cross = ob.pov.st_cross
                        accuracy = ob.pov.st_accuracy
                        gradient = ob.pov.st_max_gradient
                        ############Inline Supertorus macro
                        file.write(
                            "#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)\n"
                        )
                        file.write("   #local CP = 2/MinorControl;\n")
                        file.write("   #local RP = 2/MajorControl;\n")
                        file.write("   isosurface {\n")
                        file.write(
                            "      function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }\n"
                        )
                        file.write("      threshold 0\n")
                        file.write(
                            "      contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}\n"
                        )
                        file.write("      #if(MaxGradient >= 1)\n")
                        file.write("         max_gradient MaxGradient\n")
                        file.write("      #else\n")
                        file.write("         evaluate 1, 10, 0.1\n")
                        file.write("      #end\n")
                        file.write("      accuracy Accuracy\n")
                        file.write("   }\n")
                        file.write("#end\n")
                        ############
                        tabWrite(
                            "#declare %s = object{ Supertorus( %.4g,%.4g,%.4g,%.4g,%.4g,%.4g)\n"
                            % (
                                povdataname,
                                rMajor,
                                rMinor,
                                ring,
                                cross,
                                accuracy,
                                gradient,
                            )
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'PLANE':
                        tabWrite(
                            "#declare %s = plane{ <0,0,1>,1\n" % povdataname
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        # tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'BOX':
                        tabWrite("#declare %s = box { -1,1\n" % povdataname)
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        # tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'CONE':
                        br = ob.pov.cone_base_radius
                        cr = ob.pov.cone_cap_radius
                        bz = ob.pov.cone_base_z
                        cz = ob.pov.cone_cap_z
                        tabWrite(
                            "#declare %s = cone { <0,0,%.4f>,%.4f,<0,0,%.4f>,%.4f\n"
                            % (povdataname, bz, br, cz, cr)
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        # tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'CYLINDER':
                        r = ob.pov.cylinder_radius
                        x2 = ob.pov.cylinder_location_cap[0]
                        y2 = ob.pov.cylinder_location_cap[1]
                        z2 = ob.pov.cylinder_location_cap[2]
                        tabWrite(
                            "#declare %s = cylinder { <0,0,0>,<%6f,%6f,%6f>,%6f\n"
                            % (povdataname, x2, y2, z2, r)
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        # cylinders written at origin, translated below
                        write_object_modifiers(scene, ob, file)
                        # tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'HEIGHT_FIELD':
                        data = ""
                        filename = ob.pov.hf_filename
                        data += '"%s"' % filename
                        gamma = ' gamma %.4f' % ob.pov.hf_gamma
                        data += gamma
                        if ob.pov.hf_premultiplied:
                            data += ' premultiplied on'
                        if ob.pov.hf_smooth:
                            data += ' smooth'
                        if ob.pov.hf_water > 0:
                            data += ' water_level %.4f' % ob.pov.hf_water
                        # hierarchy = ob.pov.hf_hierarchy
                        tabWrite(
                            '#declare %s = height_field { %s\n'
                            % (povdataname, data)
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        tabWrite("rotate x*90\n")
                        tabWrite("translate <-0.5,0.5,0>\n")
                        tabWrite("scale <0,-1,0>\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'SPHERE':

                        tabWrite(
                            "#declare %s = sphere { 0,%6f\n"
                            % (povdataname, ob.pov.sphere_radius)
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        # tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'TORUS':
                        tabWrite(
                            "#declare %s = torus { %.4f,%.4f\n"
                            % (
                                povdataname,
                                ob.pov.torus_major_radius,
                                ob.pov.torus_minor_radius,
                            )
                        )
                        povMatName = "Default_texture"
                        if ob.active_material:
                            # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
                            try:
                                material = ob.active_material
                                writeObjectMaterial(material, ob)
                            except IndexError:
                                print(me)
                        # tabWrite("texture {%s}\n"%povMatName)
                        write_object_modifiers(scene, ob, file)
                        tabWrite("rotate x*90\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'PARAMETRIC':
                        tabWrite("#declare %s = parametric {\n" % povdataname)
                        tabWrite("function { %s }\n" % ob.pov.x_eq)
                        tabWrite("function { %s }\n" % ob.pov.y_eq)
                        tabWrite("function { %s }\n" % ob.pov.z_eq)
                        tabWrite(
                            "<%.4f,%.4f>, <%.4f,%.4f>\n"
                            % (
                                ob.pov.u_min,
                                ob.pov.v_min,
                                ob.pov.u_max,
                                ob.pov.v_max,
                            )
                        )
                        # Previous to 3.8 default max_gradient 1.0 was too slow
                        tabWrite("max_gradient 0.001\n")
                        if ob.pov.contained_by == "sphere":
                            tabWrite("contained_by { sphere{0, 2} }\n")
                        else:
                            tabWrite("contained_by { box{-2, 2} }\n")
                        tabWrite("max_gradient %.6f\n" % ob.pov.max_gradient)
                        tabWrite("accuracy %.6f\n" % ob.pov.accuracy)
                        tabWrite("precompute 10 x,y,z\n")
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    if ob.pov.object_as == 'POLYCIRCLE':
                        # TODO write below macro Once:
                        # if write_polytocircle_macro_once == 0:
                        file.write("/****************************\n")
                        file.write("This macro was written by 'And'.\n")
                        file.write(
                            "Link:(http://news.povray.org/povray.binaries.scene-files/)\n"
                        )
                        file.write("****************************/\n")
                        file.write("//from math.inc:\n")
                        file.write("#macro VPerp_Adjust(V, Axis)\n")
                        file.write(
                            "   vnormalize(vcross(vcross(Axis, V), Axis))\n"
                        )
                        file.write("#end\n")
                        file.write("//Then for the actual macro\n")
                        file.write(
                            "#macro Shape_Slice_Plane_2P_1V(point1, point2, clip_direct)\n"
                        )
                        file.write("#local p1 = point1 + <0,0,0>;\n")
                        file.write("#local p2 = point2 + <0,0,0>;\n")
                        file.write(
                            "#local clip_v = vnormalize(clip_direct + <0,0,0>);\n"
                        )
                        file.write("#local direct_v1 = vnormalize(p2 - p1);\n")
                        file.write("#if(vdot(direct_v1, clip_v) = 1)\n")
                        file.write(
                            '    #error "Shape_Slice_Plane_2P_1V error: Can\'t decide plane"\n'
                        )
                        file.write("#end\n\n")
                        file.write(
                            "#local norm = -vnormalize(clip_v - direct_v1*vdot(direct_v1,clip_v));\n"
                        )
                        file.write("#local d = vdot(norm, p1);\n")
                        file.write("plane{\n")
                        file.write("norm, d\n")
                        file.write("}\n")
                        file.write("#end\n\n")
                        file.write("//polygon to circle\n")
                        file.write(
                            "#macro Shape_Polygon_To_Circle_Blending(_polygon_n, _side_face, _polygon_circumscribed_radius, _circle_radius, _height)\n"
                        )
                        file.write("#local n = int(_polygon_n);\n")
                        file.write("#if(n < 3)\n")
                        file.write("    #error " "\n")
                        file.write("#end\n\n")
                        file.write(
                            "#local front_v = VPerp_Adjust(_side_face, z);\n"
                        )
                        file.write("#if(vdot(front_v, x) >= 0)\n")
                        file.write(
                            "    #local face_ang = acos(vdot(-y, front_v));\n"
                        )
                        file.write("#else\n")
                        file.write(
                            "    #local face_ang = -acos(vdot(-y, front_v));\n"
                        )
                        file.write("#end\n")
                        file.write("#local polyg_ext_ang = 2*pi/n;\n")
                        file.write(
                            "#local polyg_outer_r = _polygon_circumscribed_radius;\n"
                        )
                        file.write(
                            "#local polyg_inner_r = polyg_outer_r*cos(polyg_ext_ang/2);\n"
                        )
                        file.write("#local cycle_r = _circle_radius;\n")
                        file.write("#local h = _height;\n")
                        file.write(
                            "#if(polyg_outer_r < 0 | cycle_r < 0 | h <= 0)\n"
                        )
                        file.write(
                            '    #error "error: each side length must be positive"\n'
                        )
                        file.write("#end\n\n")
                        file.write("#local multi = 1000;\n")
                        file.write("#local poly_obj =\n")
                        file.write("polynomial{\n")
                        file.write("4,\n")
                        file.write("xyz(0,2,2): multi*1,\n")
                        file.write("xyz(2,0,1): multi*2*h,\n")
                        file.write(
                            "xyz(1,0,2): multi*2*(polyg_inner_r-cycle_r),\n"
                        )
                        file.write("xyz(2,0,0): multi*(-h*h),\n")
                        file.write(
                            "xyz(0,0,2): multi*(-pow(cycle_r - polyg_inner_r, 2)),\n"
                        )
                        file.write(
                            "xyz(1,0,1): multi*2*h*(-2*polyg_inner_r + cycle_r),\n"
                        )
                        file.write("xyz(1,0,0): multi*2*h*h*polyg_inner_r,\n")
                        file.write(
                            "xyz(0,0,1): multi*2*h*polyg_inner_r*(polyg_inner_r - cycle_r),\n"
                        )
                        file.write(
                            "xyz(0,0,0): multi*(-pow(polyg_inner_r*h, 2))\n"
                        )
                        file.write("sturm\n")
                        file.write("}\n\n")
                        file.write("#local mockup1 =\n")
                        file.write("difference{\n")
                        file.write("    cylinder{\n")
                        file.write(
                            "    <0,0,0.0>,<0,0,h>, max(polyg_outer_r, cycle_r)\n"
                        )
                        file.write("    }\n\n")
                        file.write("    #for(i, 0, n-1)\n")
                        file.write("        object{\n")
                        file.write("        poly_obj\n")
                        file.write("        inverse\n")
                        file.write(
                            "        rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n"
                        )
                        file.write("        }\n")
                        file.write("        object{\n")
                        file.write(
                            "        Shape_Slice_Plane_2P_1V(<polyg_inner_r,0,0>,<cycle_r,0,h>,x)\n"
                        )
                        file.write(
                            "        rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n"
                        )
                        file.write("        }\n")
                        file.write("    #end\n")
                        file.write("}\n\n")
                        file.write("object{\n")
                        file.write("mockup1\n")
                        file.write("rotate <0, 0, degrees(face_ang)>\n")
                        file.write("}\n")
                        file.write("#end\n")
                        # Use the macro
                        ngon = ob.pov.polytocircle_ngon
                        ngonR = ob.pov.polytocircle_ngonR
                        circleR = ob.pov.polytocircle_circleR
                        tabWrite(
                            "#declare %s = object { Shape_Polygon_To_Circle_Blending(%s, z, %.4f, %.4f, 2) rotate x*180 translate z*1\n"
                            % (povdataname, ngon, ngonR, circleR)
                        )
                        tabWrite("}\n")
                        continue  # Don't render proxy mesh, skip to next object

                    ############################################else try to export mesh
                    elif (
                        ob.is_instancer == False
                    ):  # except duplis which should be instances groups for now but all duplis later
                        if ob.type == 'EMPTY':
                            tabWrite(
                                "\n//dummy sphere to represent Empty location\n"
                            )
                            tabWrite(
                                "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
                                % povdataname
                            )

                        # TODO(sergey): PovRay is a render engine, so should be using dependency graph
                        # which was given to it via render engine API.
                        depsgraph = bpy.context.evaluated_depsgraph_get()
                        ob_eval = ob.evaluated_get(depsgraph)
                        try:
                            me = ob_eval.to_mesh()

                        # XXX Here? identify the specific exception for mesh object with no data
                        # XXX So that we can write something for the dataname !
                        except:

                            # also happens when curves cant be made into meshes because of no-data
                            continue

                        importance = ob.pov.importance_value
                        if me:
                            me.calc_loop_triangles()
                            me_materials = me.materials
                            me_faces = me.loop_triangles[:]
                            ## numpytest
                            #me_looptris = me.loops

                            ## otypes = ['int32'] is a 32-bit signed integer number numpy datatype
                            #get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
                            #faces_verts_idx = get_v_index(me_looptris)


                        # if len(me_faces)==0:
                        # tabWrite("\n//dummy sphere to represent empty mesh location\n")
                        # tabWrite("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)

                        if not me or not me_faces:
                            tabWrite(
                                "\n//dummy sphere to represent empty mesh location\n"
                            )
                            tabWrite(
                                "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
                                % povdataname
                            )
                            continue

                        uv_layers = me.uv_layers
                        if len(uv_layers) > 0:
                            if me.uv_layers.active and uv_layers.active.data:
                                uv_layer = uv_layers.active.data
                        else:
                            uv_layer = None

                        try:
                            # vcol_layer = me.vertex_colors.active.data
                            vcol_layer = me.vertex_colors.active.data
                        except AttributeError:
                            vcol_layer = None

                        faces_verts = [f.vertices[:] for f in me_faces]
                        faces_normals = [f.normal[:] for f in me_faces]
                        verts_normals = [v.normal[:] for v in me.vertices]

                        # Use named declaration to allow reference e.g. for baking. MR
                        file.write("\n")
                        tabWrite("#declare %s =\n" % povdataname)
                        tabWrite("mesh2 {\n")
                        tabWrite("vertex_vectors {\n")
                        tabWrite("%d" % len(me.vertices))  # vert count

                        tabStr = tab * tabLevel
                        for v in me.vertices:
                            if linebreaksinlists:
                                file.write(",\n")
                                file.write(
                                    tabStr + "<%.6f, %.6f, %.6f>" % v.co[:]
                                )  # vert count
                            else:
                                file.write(", ")
                                file.write(
                                    "<%.6f, %.6f, %.6f>" % v.co[:]
                                )  # vert count
                            # tabWrite("<%.6f, %.6f, %.6f>" % v.co[:])  # vert count
                        file.write("\n")
                        tabWrite("}\n")

                        # Build unique Normal list
                        uniqueNormals = {}
                        for fi, f in enumerate(me_faces):
                            fv = faces_verts[fi]
                            # [-1] is a dummy index, use a list so we can modify in place
                            if f.use_smooth:  # Use vertex normals
                                for v in fv:
                                    key = verts_normals[v]
                                    uniqueNormals[key] = [-1]
                            else:  # Use face normal
                                key = faces_normals[fi]
                                uniqueNormals[key] = [-1]

                        tabWrite("normal_vectors {\n")
                        tabWrite("%d" % len(uniqueNormals))  # vert count
                        idx = 0
                        tabStr = tab * tabLevel
                        for no, index in uniqueNormals.items():
                            if linebreaksinlists:
                                file.write(",\n")
                                file.write(
                                    tabStr + "<%.6f, %.6f, %.6f>" % no
                                )  # vert count
                            else:
                                file.write(", ")
                                file.write(
                                    "<%.6f, %.6f, %.6f>" % no
                                )  # vert count
                            index[0] = idx
                            idx += 1
                        file.write("\n")
                        tabWrite("}\n")

                        # Vertex colors
                        vertCols = {}  # Use for material colors also.

                        if uv_layer:
                            # Generate unique UV's
                            uniqueUVs = {}
                            # n = 0
                            for f in me_faces:  # me.faces in 2.7
                                uvs = [uv_layer[l].uv[:] for l in f.loops]

                                for uv in uvs:
                                    uniqueUVs[uv[:]] = [-1]

                            tabWrite("uv_vectors {\n")
                            # print unique_uvs
                            tabWrite("%d" % len(uniqueUVs))  # vert count
                            idx = 0
                            tabStr = tab * tabLevel
                            for uv, index in uniqueUVs.items():
                                if linebreaksinlists:
                                    file.write(",\n")
                                    file.write(tabStr + "<%.6f, %.6f>" % uv)
                                else:
                                    file.write(", ")
                                    file.write("<%.6f, %.6f>" % uv)
                                index[0] = idx
                                idx += 1
                            '''
                            else:
                                # Just add 1 dummy vector, no real UV's
                                tabWrite('1') # vert count
                                file.write(',\n\t\t<0.0, 0.0>')
                            '''
                            file.write("\n")
                            tabWrite("}\n")

                        if me.vertex_colors:
                            # Write down vertex colors as a texture for each vertex
                            tabWrite("texture_list {\n")
                            tabWrite(
                                "%d\n" % (len(me_faces) * 3)
                            )  # assumes we have only triangles
                            VcolIdx = 0
                            if comments:
                                file.write(
                                    "\n  //Vertex colors: one simple pigment texture per vertex\n"
                                )
                            for fi, f in enumerate(me_faces):
                                # annoying, index may be invalid
                                material_index = f.material_index
                                try:
                                    material = me_materials[material_index]
                                except:
                                    material = None
                                if (
                                    material
                                ):  # and material.use_vertex_color_paint: #Always use vertex color when there is some for now

                                    cols = [
                                        vcol_layer[l].color[:] for l in f.loops
                                    ]

                                    for col in cols:
                                        key = (
                                            col[0],
                                            col[1],
                                            col[2],
                                            material_index,
                                        )  # Material index!
                                        VcolIdx += 1
                                        vertCols[key] = [VcolIdx]
                                        if linebreaksinlists:
                                            tabWrite(
                                                "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
                                                % (col[0], col[1], col[2])
                                            )
                                        else:
                                            tabWrite(
                                                "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
                                                % (col[0], col[1], col[2])
                                            )
                                            tabStr = tab * tabLevel
                                else:
                                    if material:
                                        # Multiply diffuse with SSS Color
                                        if (
                                            material.pov_subsurface_scattering.use
                                        ):
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[
                                                        :
                                                    ],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                            key = (
                                                diffuse_color[0],
                                                diffuse_color[1],
                                                diffuse_color[2],
                                                material_index,
                                            )
                                            vertCols[key] = [-1]
                                        else:
                                            diffuse_color = material.diffuse_color[
                                                :
                                            ]
                                            key = (
                                                diffuse_color[0],
                                                diffuse_color[1],
                                                diffuse_color[2],
                                                material_index,
                                            )
                                            vertCols[key] = [-1]

                            tabWrite("\n}\n")
                            # Face indices
                            tabWrite("\nface_indices {\n")
                            tabWrite("%d" % (len(me_faces)))  # faces count
                            tabStr = tab * tabLevel

                            for fi, f in enumerate(me_faces):
                                fv = faces_verts[fi]
                                material_index = f.material_index

                                if vcol_layer:
                                    cols = [
                                        vcol_layer[l].color[:] for l in f.loops
                                    ]

                                if (
                                    not me_materials
                                    or me_materials[material_index] is None
                                ):  # No materials
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        # vert count
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (fv[0], fv[1], fv[2])
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>" % (fv[0], fv[1], fv[2])
                                        )  # vert count
                                else:
                                    material = me_materials[material_index]
                                    if (
                                        me.vertex_colors
                                    ):  # and material.use_vertex_color_paint:
                                        # Color per vertex - vertex color

                                        col1 = cols[0]
                                        col2 = cols[1]
                                        col3 = cols[2]

                                        ci1 = vertCols[
                                            col1[0],
                                            col1[1],
                                            col1[2],
                                            material_index,
                                        ][0]
                                        ci2 = vertCols[
                                            col2[0],
                                            col2[1],
                                            col2[2],
                                            material_index,
                                        ][0]
                                        ci3 = vertCols[
                                            col3[0],
                                            col3[1],
                                            col3[2],
                                            material_index,
                                        ][0]
                                    else:
                                        # Color per material - flat material color
                                        if (
                                            material.pov_subsurface_scattering.use
                                        ):
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[
                                                        :
                                                    ],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                        else:
                                            diffuse_color = material.diffuse_color[
                                                :
                                            ]
                                        ci1 = ci2 = ci3 = vertCols[
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            f.material_index,
                                        ][0]
                                        # ci are zero based index so we'll subtract 1 from them
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>, %d,%d,%d"
                                            % (
                                                fv[0],
                                                fv[1],
                                                fv[2],
                                                ci1 - 1,
                                                ci2 - 1,
                                                ci3 - 1,
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>, %d,%d,%d"
                                            % (
                                                fv[0],
                                                fv[1],
                                                fv[2],
                                                ci1 - 1,
                                                ci2 - 1,
                                                ci3 - 1,
                                            )
                                        )  # vert count

                            file.write("\n")
                            tabWrite("}\n")

                            # normal_indices indices
                            tabWrite("normal_indices {\n")
                            tabWrite("%d" % (len(me_faces)))  # faces count
                            tabStr = tab * tabLevel
                            for fi, fv in enumerate(faces_verts):

                                if me_faces[fi].use_smooth:
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[
                                                    verts_normals[fv[0]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[1]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[2]]
                                                ][0],
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[
                                                    verts_normals[fv[0]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[1]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[2]]
                                                ][0],
                                            )
                                        )  # vert count
                                else:
                                    idx = uniqueNormals[faces_normals[fi]][0]
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vert count

                            file.write("\n")
                            tabWrite("}\n")

                            if uv_layer:
                                tabWrite("uv_indices {\n")
                                tabWrite("%d" % (len(me_faces)))  # faces count
                                tabStr = tab * tabLevel
                                for f in me_faces:
                                    uvs = [uv_layer[l].uv[:] for l in f.loops]

                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )

                                file.write("\n")
                                tabWrite("}\n")

                            # XXX BOOLEAN
                            onceCSG = 0
                            for mod in ob.modifiers:
                                if onceCSG == 0:
                                    if mod:
                                        if mod.type == 'BOOLEAN':
                                            if ob.pov.boolean_mod == "POV":
                                                file.write(
                                                    "\tinside_vector <%.6g, %.6g, %.6g>\n"
                                                    % (
                                                        ob.pov.inside_vector[0],
                                                        ob.pov.inside_vector[1],
                                                        ob.pov.inside_vector[2],
                                                    )
                                                )
                                                onceCSG = 1

                            if me.materials:
                                try:
                                    material = me.materials[0]  # dodgy
                                    writeObjectMaterial(material, ob)
                                except IndexError:
                                    print(me)

                            # POV object modifiers such as
                            # hollow / sturm / double_illuminate etc.
                            write_object_modifiers(scene, ob, file)

                            # Importance for radiosity sampling added here:
                            tabWrite("radiosity { \n")
                            tabWrite("importance %3g \n" % importance)
                            tabWrite("}\n")

                            tabWrite("}\n")  # End of mesh block
                        else:
                            facesMaterials = []  # WARNING!!!!!!!!!!!!!!!!!!!!!!
                            if me_materials:
                                for f in me_faces:
                                    if f.material_index not in facesMaterials:
                                        facesMaterials.append(f.material_index)
                            # No vertex colors, so write material colors as vertex colors
                            for i, material in enumerate(me_materials):

                                if (
                                    material
                                    and material.pov.material_use_nodes == False
                                ):  # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                                    # Multiply diffuse with SSS Color
                                    if material.pov_subsurface_scattering.use:
                                        diffuse_color = [
                                            i * j
                                            for i, j in zip(
                                                material.pov_subsurface_scattering.color[
                                                    :
                                                ],
                                                material.diffuse_color[:],
                                            )
                                        ]
                                        key = (
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            i,
                                        )  # i == f.mat
                                        vertCols[key] = [-1]
                                    else:
                                        diffuse_color = material.diffuse_color[
                                            :
                                        ]
                                        key = (
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            i,
                                        )  # i == f.mat
                                        vertCols[key] = [-1]

                                    idx = 0
                                    LocalMaterialNames = []
                                    for col, index in vertCols.items():
                                        # if me_materials:
                                        mater = me_materials[col[3]]
                                        if me_materials is None:  # XXX working?
                                            material_finish = (
                                                DEF_MAT_NAME
                                            )  # not working properly,
                                            trans = 0.0

                                        else:
                                            shading.writeTextureInfluence(
                                                using_uberpov,
                                                mater,
                                                materialNames,
                                                LocalMaterialNames,
                                                path_image,
                                                lampCount,
                                                imageFormat,
                                                imgMap,
                                                imgMapTransforms,
                                                tabWrite,
                                                comments,
                                                string_strip_hyphen,
                                                safety,
                                                col,
                                                os,
                                                preview_dir,
                                                unpacked_images,
                                            )
                                        ###################################################################
                                        index[0] = idx
                                        idx += 1

                            # Vert Colors
                            tabWrite("texture_list {\n")
                            # In case there's is no material slot, give at least one texture
                            # (an empty one so it uses pov default)
                            if len(vertCols) == 0:
                                file.write(tabStr + "1")
                            else:
                                file.write(
                                    tabStr + "%s" % (len(vertCols))
                                )  # vert count

                            # below "material" alias, added check ob.active_material
                            # to avoid variable referenced before assignment error
                            try:
                                material = ob.active_material
                            except IndexError:
                                # when no material slot exists,
                                material = None

                            # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            if (
                                material
                                and ob.active_material is not None
                                and material.pov.material_use_nodes == False
                            ):
                                if material.pov.replacement_text != "":
                                    file.write("\n")
                                    file.write(
                                        " texture{%s}\n"
                                        % material.pov.replacement_text
                                    )

                                else:
                                    # Loop through declared materials list
                                    for cMN in LocalMaterialNames:
                                        if material != "Default":
                                            file.write(
                                                "\n texture{MAT_%s}\n" % cMN
                                            )
                                            # use string_strip_hyphen(materialNames[material]))
                                            # or Something like that to clean up the above?
                            elif material and material.pov.material_use_nodes:
                                for index in facesMaterials:
                                    faceMaterial = string_strip_hyphen(
                                        bpy.path.clean_name(
                                            me_materials[index].name
                                        )
                                    )
                                    file.write(
                                        "\n texture{%s}\n" % faceMaterial
                                    )
                            # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            else:
                                file.write(" texture{}\n")
                            tabWrite("}\n")

                            # Face indices
                            tabWrite("face_indices {\n")
                            tabWrite("%d" % (len(me_faces)))  # faces count
                            tabStr = tab * tabLevel

                            for fi, f in enumerate(me_faces):
                                fv = faces_verts[fi]
                                material_index = f.material_index

                                if vcol_layer:
                                    cols = [
                                        vcol_layer[l].color[:] for l in f.loops
                                    ]

                                if (
                                    not me_materials
                                    or me_materials[material_index] is None
                                ):  # No materials
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        # vert count
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (fv[0], fv[1], fv[2])
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>" % (fv[0], fv[1], fv[2])
                                        )  # vert count
                                else:
                                    material = me_materials[material_index]
                                    ci1 = ci2 = ci3 = f.material_index
                                    if (
                                        me.vertex_colors
                                    ):  # and material.use_vertex_color_paint:
                                        # Color per vertex - vertex color

                                        col1 = cols[0]
                                        col2 = cols[1]
                                        col3 = cols[2]

                                        ci1 = vertCols[
                                            col1[0],
                                            col1[1],
                                            col1[2],
                                            material_index,
                                        ][0]
                                        ci2 = vertCols[
                                            col2[0],
                                            col2[1],
                                            col2[2],
                                            material_index,
                                        ][0]
                                        ci3 = vertCols[
                                            col3[0],
                                            col3[1],
                                            col3[2],
                                            material_index,
                                        ][0]
                                    elif material.pov.material_use_nodes:
                                        ci1 = ci2 = ci3 = 0
                                    else:
                                        # Color per material - flat material color
                                        if (
                                            material.pov_subsurface_scattering.use
                                        ):
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[
                                                        :
                                                    ],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                        else:
                                            diffuse_color = material.diffuse_color[
                                                :
                                            ]
                                        ci1 = ci2 = ci3 = vertCols[
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            f.material_index,
                                        ][0]

                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>, %d,%d,%d"
                                            % (
                                                fv[0],
                                                fv[1],
                                                fv[2],
                                                ci1,
                                                ci2,
                                                ci3,
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>, %d,%d,%d"
                                            % (
                                                fv[0],
                                                fv[1],
                                                fv[2],
                                                ci1,
                                                ci2,
                                                ci3,
                                            )
                                        )  # vert count

                            file.write("\n")
                            tabWrite("}\n")

                            # normal_indices indices
                            tabWrite("normal_indices {\n")
                            tabWrite("%d" % (len(me_faces)))  # faces count
                            tabStr = tab * tabLevel
                            for fi, fv in enumerate(faces_verts):
                                if me_faces[fi].use_smooth:
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[
                                                    verts_normals[fv[0]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[1]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[2]]
                                                ][0],
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[
                                                    verts_normals[fv[0]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[1]]
                                                ][0],
                                                uniqueNormals[
                                                    verts_normals[fv[2]]
                                                ][0],
                                            )
                                        )  # vert count
                                else:
                                    idx = uniqueNormals[faces_normals[fi]][0]
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vertcount
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vert count

                            file.write("\n")
                            tabWrite("}\n")

                            if uv_layer:
                                tabWrite("uv_indices {\n")
                                tabWrite("%d" % (len(me_faces)))  # faces count
                                tabStr = tab * tabLevel
                                for f in me_faces:
                                    uvs = [uv_layer[l].uv[:] for l in f.loops]

                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tabStr
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )

                                file.write("\n")
                                tabWrite("}\n")

                            # XXX BOOLEAN
                            onceCSG = 0
                            for mod in ob.modifiers:
                                if onceCSG == 0:
                                    if mod:
                                        if mod.type == 'BOOLEAN':
                                            if ob.pov.boolean_mod == "POV":
                                                file.write(
                                                    "\tinside_vector <%.6g, %.6g, %.6g>\n"
                                                    % (
                                                        ob.pov.inside_vector[0],
                                                        ob.pov.inside_vector[1],
                                                        ob.pov.inside_vector[2],
                                                    )
                                                )
                                                onceCSG = 1

                            if me.materials:
                                try:
                                    material = me.materials[0]  # dodgy
                                    writeObjectMaterial(material, ob)
                                except IndexError:
                                    print(me)

                            # POV object modifiers such as
                            # hollow / sturm / double_illuminate etc.
                            write_object_modifiers(scene, ob, file)

                            # Importance for radiosity sampling added here:
                            tabWrite("radiosity { \n")
                            tabWrite("importance %3g \n" % importance)
                            tabWrite("}\n")

                            tabWrite("}\n")  # End of mesh block

                        ob_eval.to_mesh_clear()

        if csg:
            duplidata_ref = []
            _dupnames_seen = (
                dict()
            )  # avoid duplicate output during introspection
            for ob in sel:
                # matrix = global_matrix @ ob.matrix_world
                if ob.is_instancer:
                    tabWrite("\n//--DupliObjects in %s--\n\n" % ob.name)
                    # ob.dupli_list_create(scene) #deprecated in 2.8
                    depsgraph = bpy.context.evaluated_depsgraph_get()
                    dup = ""
                    if ob.is_modified(scene, 'RENDER'):
                        # modified object always unique so using object name rather than data name
                        dup = "#declare OB%s = union{\n" % (
                            string_strip_hyphen(bpy.path.clean_name(ob.name))
                        )
                    else:
                        dup = "#declare DATA%s = union{\n" % (
                            string_strip_hyphen(bpy.path.clean_name(ob.name))
                        )
                    for eachduplicate in depsgraph.object_instances:
                        if (
                            eachduplicate.is_instance
                        ):  # Real dupli instance filtered because original included in list since 2.8
                            _dupname = eachduplicate.object.name
                            _dupobj = bpy.data.objects[_dupname]
                            # BEGIN introspection for troubleshooting purposes
                            if not "name" in dir(_dupobj.data):
                                if _dupname not in _dupnames_seen:
                                    print(
                                        "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute"
                                        % (_dupname, type(_dupobj.data))
                                    )
                                    for _thing in dir(_dupobj):
                                        print(
                                            "||  %s.%s = %s"
                                            % (
                                                _dupname,
                                                _thing,
                                                getattr(_dupobj, _thing),
                                            )
                                        )
                                    _dupnames_seen[_dupname] = 1
                                    print(
                                        "''=>  Unparseable objects so far: %s"
                                        % (_dupnames_seen)
                                    )
                                else:
                                    _dupnames_seen[_dupname] += 1
                                continue  # don't try to parse data objects with no name attribute
                            # END introspection for troubleshooting purposes
                            duplidataname = "OB" + string_strip_hyphen(
                                bpy.path.clean_name(_dupobj.data.name)
                            )
                            dupmatrix = (
                                eachduplicate.matrix_world.copy()
                            )  # has to be copied to not store instance since 2.8
                            dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % (
                                string_strip_hyphen(
                                    bpy.path.clean_name(_dupobj.data.name)
                                ),
                                MatrixAsPovString(
                                    ob.matrix_world.inverted() @ dupmatrix
                                ),
                            )
                            # add object to a list so that it is not rendered for some instance_types
                            if (
                                ob.instance_type not in {'COLLECTION'}
                                and duplidataname not in duplidata_ref
                            ):
                                duplidata_ref.append(
                                    duplidataname
                                )  # older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))]
                    dup += "}\n"
                    # ob.dupli_list_clear()# just do not store any reference to instance since 2.8
                    tabWrite(dup)
                else:
                    continue
            print(
                "WARNING: Unparseable objects in current .blend file:\n''=> %s"
                % (_dupnames_seen)
            )
            print("duplidata_ref = %s" % (duplidata_ref))
            for data_name, inst in data_ref.items():
                for ob_name, matrix_str in inst:
                    if (
                        ob_name not in duplidata_ref
                    ):  # .items() for a dictionary
                        tabWrite(
                            "\n//----Blender Object Name:%s----\n" % ob_name
                        )
                        if ob.pov.object_as == '':
                            tabWrite("object { \n")
                            tabWrite("%s\n" % data_name)
                            tabWrite("%s\n" % matrix_str)
                            tabWrite("}\n")
                        else:
                            no_boolean = True
                            for mod in ob.modifiers:
                                if mod.type == 'BOOLEAN':
                                    operation = None
                                    no_boolean = False
                                    if mod.operation == 'INTERSECT':
                                        operation = 'intersection'
                                    else:
                                        operation = mod.operation.lower()
                                    mod_ob_name = string_strip_hyphen(
                                        bpy.path.clean_name(mod.object.name)
                                    )
                                    mod_matrix = (
                                        global_matrix @ mod.object.matrix_world
                                    )
                                    mod_ob_matrix = MatrixAsPovString(
                                        mod_matrix
                                    )
                                    tabWrite("%s { \n" % operation)
                                    tabWrite("object { \n")
                                    tabWrite("%s\n" % data_name)
                                    tabWrite("%s\n" % matrix_str)
                                    tabWrite("}\n")
                                    tabWrite("object { \n")
                                    tabWrite("%s\n" % ('DATA' + mod_ob_name))
                                    tabWrite("%s\n" % mod_ob_matrix)
                                    tabWrite("}\n")
                                    tabWrite("}\n")
                                    break
                            if no_boolean:
                                tabWrite("object { \n")
                                tabWrite("%s\n" % data_name)
                                tabWrite("%s\n" % matrix_str)
                                tabWrite("}\n")

    def exportWorld(world):
        """write world as POV backgrounbd and sky_sphere to exported file """
        render = scene.pov
        camera = scene.camera
        matrix = global_matrix @ camera.matrix_world
        if not world:
            return
        #############Maurice####################################
        # These lines added to get sky gradient (visible with PNG output)
        if world:
            # For simple flat background:
            if not world.pov.use_sky_blend:
                # Non fully transparent background could premultiply alpha and avoid anti-aliasing
                # display issue:
                if render.alpha_mode == 'TRANSPARENT':
                    tabWrite(
                        "background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n"
                        % (world.pov.horizon_color[:])
                    )
                # Currently using no alpha with Sky option:
                elif render.alpha_mode == 'SKY':
                    tabWrite(
                        "background {rgbt<%.3g, %.3g, %.3g, 0>}\n"
                        % (world.pov.horizon_color[:])
                    )
                # StraightAlpha:
                # XXX Does not exists anymore
                # else:
                # tabWrite("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:]))

            worldTexCount = 0
            # For Background image textures
            for (
                t
            ) in (
                world.pov_texture_slots
            ):  # risk to write several sky_spheres but maybe ok.
                if t:
                    tex = bpy.data.textures[t.texture]
                if tex.type is not None:
                    worldTexCount += 1
                    # XXX No enable checkbox for world textures yet (report it?)
                    # if t and tex.type == 'IMAGE' and t.use:
                    if tex.type == 'IMAGE':
                        image_filename = path_image(tex.image)
                        if tex.image.filepath != image_filename:
                            tex.image.filepath = image_filename
                        if image_filename != "" and t.use_map_blend:
                            texturesBlend = image_filename
                            # colvalue = t.default_value
                            t_blend = t

                        # Commented below was an idea to make the Background image oriented as camera
                        # taken here:
                        # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/
                        # Replace 4/3 by the ratio of each image found by some custom or existing
                        # function
                        # mappingBlend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \
                        #                "(atan((camLocation - camLookAt).x/(camLocation - " \
                        #                "camLookAt).y)) rotate x*degrees(atan((camLocation - " \
                        #                "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \
                        #                "degrees(atan((camLocation - camLookAt).z/(camLocation - " \
                        #                "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \
                        #                (t_blend.offset.x / 10 , t_blend.offset.y / 10 ,
                        #                 t_blend.offset.z / 10, t_blend.scale.x ,
                        #                 t_blend.scale.y , t_blend.scale.z))
                        # using camera rotation valuesdirectly from blender seems much easier
                        if t_blend.texture_coords == 'ANGMAP':
                            mappingBlend = ""
                        else:
                            # POV-Ray "scale" is not a number of repetitions factor, but its
                            # inverse, a standard scale factor.
                            # 0.5 Offset is needed relatively to scale because center of the
                            # UV scale is 0.5,0.5 in blender and 0,0 in POV
                            # Further Scale by 2 and translate by -1 are
                            # required for the sky_sphere not to repeat

                            mappingBlend = (
                                "scale 2 scale <%.4g,%.4g,%.4g> translate -1 "
                                "translate <%.4g,%.4g,%.4g> rotate<0,0,0> "
                                % (
                                    (1.0 / t_blend.scale.x),
                                    (1.0 / t_blend.scale.y),
                                    (1.0 / t_blend.scale.z),
                                    0.5
                                    - (0.5 / t_blend.scale.x)
                                    - t_blend.offset.x,
                                    0.5
                                    - (0.5 / t_blend.scale.y)
                                    - t_blend.offset.y,
                                    t_blend.offset.z,
                                )
                            )

                            # The initial position and rotation of the pov camera is probably creating
                            # the rotation offset should look into it someday but at least background
                            # won't rotate with the camera now.
                        # Putting the map on a plane would not introduce the skysphere distortion and
                        # allow for better image scale matching but also some waay to chose depth and
                        # size of the plane relative to camera.
                        tabWrite("sky_sphere {\n")
                        tabWrite("pigment {\n")
                        tabWrite(
                            "image_map{%s \"%s\" %s}\n"
                            % (
                                imageFormat(texturesBlend),
                                texturesBlend,
                                imgMapBG(t_blend),
                            )
                        )
                        tabWrite("}\n")
                        tabWrite("%s\n" % (mappingBlend))
                        # The following layered pigment opacifies to black over the texture for
                        # transmit below 1 or otherwise adds to itself
                        tabWrite(
                            "pigment {rgb 0 transmit %s}\n" % (tex.intensity)
                        )
                        tabWrite("}\n")
                        # tabWrite("scale 2\n")
                        # tabWrite("translate -1\n")

            # For only Background gradient

            if worldTexCount == 0:
                if world.pov.use_sky_blend:
                    tabWrite("sky_sphere {\n")
                    tabWrite("pigment {\n")
                    # maybe Should follow the advice of POV doc about replacing gradient
                    # for skysphere..5.5
                    tabWrite("gradient y\n")
                    tabWrite("color_map {\n")
                    # XXX Does not exists anymore
                    # if render.alpha_mode == 'STRAIGHT':
                    # tabWrite("[0.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.horizon_color[:]))
                    # tabWrite("[1.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.zenith_color[:]))
                    if render.alpha_mode == 'TRANSPARENT':
                        tabWrite(
                            "[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n"
                            % (world.pov.horizon_color[:])
                        )
                        # aa premult not solved with transmit 1
                        tabWrite(
                            "[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n"
                            % (world.pov.zenith_color[:])
                        )
                    else:
                        tabWrite(
                            "[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n"
                            % (world.pov.horizon_color[:])
                        )
                        tabWrite(
                            "[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n"
                            % (world.pov.zenith_color[:])
                        )
                    tabWrite("}\n")
                    tabWrite("}\n")
                    tabWrite("}\n")
                    # Sky_sphere alpha (transmit) is not translating into image alpha the same
                    # way as 'background'

            # if world.pov.light_settings.use_indirect_light:
            #    scene.pov.radio_enable=1

            # Maybe change the above to a function copyInternalRenderer settings when
            # user pushes a button, then:
            # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
            # and other such translations but maybe this would not be allowed either?

        ###############################################################

        mist = world.mist_settings

        if mist.use_mist:
            tabWrite("fog {\n")
            if mist.falloff == 'LINEAR':
                tabWrite(
                    "distance %.6f\n" % ((mist.start + mist.depth) * 0.368)
                )
            elif mist.falloff == 'QUADRATIC':  # n**2 or squrt(n)?
                tabWrite(
                    "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)
                )
            elif mist.falloff == 'INVERSE_QUADRATIC':  # n**2 or squrt(n)?
                tabWrite(
                    "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)
                )
            tabWrite(
                "color rgbt<%.3g, %.3g, %.3g, %.3g>\n"
                % (*world.pov.horizon_color, 1.0 - mist.intensity)
            )
            # tabWrite("fog_offset %.6f\n" % mist.start) #create a pov property to prepend
            # tabWrite("fog_alt %.6f\n" % mist.height) #XXX right?
            # tabWrite("turbulence 0.2\n")
            # tabWrite("turb_depth 0.3\n")
            tabWrite("fog_type 1\n")  # type2 for height
            tabWrite("}\n")
        if scene.pov.media_enable:
            tabWrite("media {\n")
            tabWrite(
                "scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n"
                % (
                    int(scene.pov.media_scattering_type),
                    (scene.pov.media_diffusion_scale),
                    *(scene.pov.media_diffusion_color[:]),
                )
            )
            if scene.pov.media_scattering_type == '5':
                tabWrite("eccentricity %.3g\n" % scene.pov.media_eccentricity)
            tabWrite("}\n")
            tabWrite(
                "absorption %.12f*<%.4g, %.4g, %.4g>\n"
                % (
                    scene.pov.media_absorption_scale,
                    *(scene.pov.media_absorption_color[:]),
                )
            )
            tabWrite("\n")
            tabWrite("samples %.d\n" % scene.pov.media_samples)
            tabWrite("}\n")

    def exportGlobalSettings(scene):
        """write all POV global settings to exported file """
        tabWrite("global_settings {\n")
        tabWrite("assumed_gamma 1.0\n")
        tabWrite("max_trace_level %d\n" % scene.pov.max_trace_level)

        # Deprecated (autodetected in pov3.8):
        # if scene.pov.charset != 'ascii':
            # file.write("    charset %s\n" % scene.pov.charset)
        if scene.pov.global_settings_advanced:
            if scene.pov.radio_enable == False:
                file.write("    adc_bailout %.6f\n" % scene.pov.adc_bailout)
            file.write(
                "    ambient_light <%.6f,%.6f,%.6f>\n"
                % scene.pov.ambient_light[:]
            )
            file.write(
                "    irid_wavelength <%.6f,%.6f,%.6f>\n"
                % scene.pov.irid_wavelength[:]
            )
            file.write(
                "    max_intersections %s\n" % scene.pov.max_intersections
            )
            file.write("    number_of_waves %s\n" % scene.pov.number_of_waves)
            file.write("    noise_generator %s\n" % scene.pov.noise_generator)
        if scene.pov.radio_enable:
            tabWrite("radiosity {\n")
            tabWrite("adc_bailout %.4g\n" % scene.pov.radio_adc_bailout)
            tabWrite("brightness %.4g\n" % scene.pov.radio_brightness)
            tabWrite("count %d\n" % scene.pov.radio_count)
            tabWrite("error_bound %.4g\n" % scene.pov.radio_error_bound)
            tabWrite("gray_threshold %.4g\n" % scene.pov.radio_gray_threshold)
            tabWrite(
                "low_error_factor %.4g\n" % scene.pov.radio_low_error_factor
            )
            tabWrite("maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse)
            tabWrite("minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse)
            tabWrite("nearest_count %d\n" % scene.pov.radio_nearest_count)
            tabWrite("pretrace_start %.3g\n" % scene.pov.radio_pretrace_start)
            tabWrite("pretrace_end %.3g\n" % scene.pov.radio_pretrace_end)
            tabWrite("recursion_limit %d\n" % scene.pov.radio_recursion_limit)
            tabWrite("always_sample %d\n" % scene.pov.radio_always_sample)
            tabWrite("normal %d\n" % scene.pov.radio_normal)
            tabWrite("media %d\n" % scene.pov.radio_media)
            tabWrite("subsurface %d\n" % scene.pov.radio_subsurface)
            tabWrite("}\n")
        onceSss = 1
        onceAmbient = 1
        oncePhotons = 1
        for material in bpy.data.materials:
            if material.pov_subsurface_scattering.use and onceSss:
                # In pov, the scale has reversed influence compared to blender. these number
                # should correct that
                tabWrite(
                    "mm_per_unit %.6f\n"
                    % (material.pov_subsurface_scattering.scale * 1000.0)
                )
                # 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

                tabWrite(
                    "subsurface { samples %d, %d }\n"
                    % (sslt_samples, sslt_samples / 10)
                )
                onceSss = 0

            if world and onceAmbient:
                tabWrite(
                    "ambient_light rgb<%.3g, %.3g, %.3g>\n"
                    % world.pov.ambient_color[:]
                )
                onceAmbient = 0

            if scene.pov.photon_enable:
                if oncePhotons and (
                    material.pov.refraction_type == "2"
                    or material.pov.photons_reflection == True
                ):
                    tabWrite("photons {\n")
                    tabWrite("spacing %.6f\n" % scene.pov.photon_spacing)
                    tabWrite(
                        "max_trace_level %d\n"
                        % scene.pov.photon_max_trace_level
                    )
                    tabWrite(
                        "adc_bailout %.3g\n" % scene.pov.photon_adc_bailout
                    )
                    tabWrite(
                        "gather %d, %d\n"
                        % (
                            scene.pov.photon_gather_min,
                            scene.pov.photon_gather_max,
                        )
                    )
                    if scene.pov.photon_map_file_save_load in {'save'}:
                        filePhName = 'Photon_map_file.ph'
                        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)
                        tabWrite('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):
                            tabWrite('load_file "%s"\n' % fullFileName)
                    tabWrite("}\n")
                    oncePhotons = 0

        tabWrite("}\n")

    def exportCustomCode():
        """write all POV user defined custom code to exported file """
        # Write CurrentAnimation Frame for use in Custom POV Code
        file.write(
            "#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current
        )
        # Change path and uncomment to add an animated include file by hand:
        file.write(
            "//#include \"/home/user/directory/animation_include_file.inc\"\n"
        )
        for txt in bpy.data.texts:
            if txt.pov.custom_code == 'both':
                # Why are the newlines needed?
                file.write("\n")
                file.write(txt.as_string())
                file.write("\n")

    # sel = renderable_objects(scene) #removed for booleans
    if comments:
        file.write(
            "//----------------------------------------------\n"
            "//--Exported with POV-Ray exporter for Blender--\n"
            "//----------------------------------------------\n\n"
        )
    file.write("#version 3.7;\n")    #Switch below as soon as 3.8 beta gets easy linked
    #file.write("#version 3.8;\n")
    file.write(
        "#declare Default_texture = texture{pigment {rgb 0.8} "
        "finish {brilliance 3.8} }\n\n"
    )
    if comments:
        file.write("\n//--Global settings--\n\n")

    exportGlobalSettings(scene)

    if comments:
        file.write("\n//--Custom Code--\n\n")
    exportCustomCode()

    if comments:
        file.write("\n//--Patterns Definitions--\n\n")
    LocalPatternNames = []
    for texture in bpy.data.textures:  # ok?
        if texture.users > 0:
            currentPatName = string_strip_hyphen(
                bpy.path.clean_name(texture.name)
            )
            # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above
            LocalPatternNames.append(currentPatName)
            # use above list to prevent writing texture instances several times and assign in mats?
            if (
                texture.type not in {'NONE', 'IMAGE'}
                and texture.pov.tex_pattern_type == 'emulator'
            ) or (
                texture.type in {'NONE', 'IMAGE'}
                and texture.pov.tex_pattern_type != 'emulator'
            ):
                file.write("\n#declare PAT_%s = \n" % currentPatName)
                file.write(shading.exportPattern(texture, string_strip_hyphen))
            file.write("\n")
    if comments:
        file.write("\n//--Background--\n\n")

    exportWorld(scene.world)

    if comments:
        file.write("\n//--Cameras--\n\n")

    exportCamera()

    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':
                    if mod.object not in csg_list:
                        csg_list.append(mod.object)
    if csg_list != []:
        csg = False
        sel = no_renderable_objects(scene)
        exportMeshes(scene, sel, csg)

    csg = True
    sel = renderable_objects(scene)

    exportLamps(
        [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')]
    )

    if comments:
        file.write("\n//--Rainbows--\n\n")
    exportRainbows(
        [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')]
    )

    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
        elif c.type == 'CURVE' and (
            c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'}
        ):
            exportCurves(scene, c)

    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.writeMaterial(
        using_uberpov,
        DEF_MAT_NAME,
        scene,
        tabWrite,
        safety,
        comments,
        uniqueName,
        materialNames,
        None,
    )  # default material
    for material in bpy.data.materials:
        if material.users > 0:
            if material.pov.material_use_nodes:
                ntree = material.node_tree
                povMatName = string_strip_hyphen(
                    bpy.path.clean_name(material.name)
                )
                if len(ntree.nodes) == 0:
                    file.write(
                        '#declare %s = texture {%s}\n' % (povMatName, color)
                    )
                else:
                    shading.write_nodes(scene, povMatName, ntree, file)

                for node in ntree.nodes:
                    if node:
                        if node.bl_idname == "PovrayOutputNode":
                            if node.inputs["Texture"].is_linked:
                                for link in ntree.links:
                                    if (
                                        link.to_node.bl_idname
                                        == "PovrayOutputNode"
                                    ):
                                        povMatName = (
                                            string_strip_hyphen(
                                                bpy.path.clean_name(
                                                    link.from_node.name
                                                )
                                            )
                                            + "_%s" % povMatName
                                        )
                            else:
                                file.write(
                                    '#declare %s = texture {%s}\n'
                                    % (povMatName, color)
                                )
            else:
                shading.writeMaterial(
                    using_uberpov,
                    DEF_MAT_NAME,
                    scene,
                    tabWrite,
                    safety,
                    comments,
                    uniqueName,
                    materialNames,
                    material,
                )
            # attributes are all the variables needed by the other python file...
    if comments:
        file.write("\n")

    exportMeta([m for m in sel if m.type == 'META'])

    if comments:
        file.write("//--Mesh objects--\n")


    #tbefore = time.time()
    exportMeshes(scene, sel, csg)
    #totime = time.time() - tbefore
    #print("exportMeshes took" + str(totime))

    # What follow used to happen here:
    # exportCamera()
    # exportWorld(scene.world)
    # exportGlobalSettings(scene)
    # MR:..and the order was important for implementing pov 3.7 baking
    #      (mesh camera) comment for the record
    # CR: Baking should be a special case than. If "baking", than we could change the order.

    # print("pov file closed %s" % file.closed)
    file.close()
    # print("pov file closed %s" % file.closed)


def write_pov_ini(
    scene, filename_ini, filename_log, filename_pov, filename_image
):
    """Write ini file."""
    feature_set = bpy.context.preferences.addons[
        __package__
    ].preferences.branch_feature_set_povray
    using_uberpov = feature_set == 'uberpov'
    # scene = bpy.data.scenes[0]
    scene = bpy.context.scene
    render = scene.render

    x = int(render.resolution_x * render.resolution_percentage * 0.01)
    y = int(render.resolution_y * render.resolution_percentage * 0.01)

    file = open(filename_ini, "w")
    file.write("Version=3.8\n")
    # write povray text stream to temporary file of same name with _log suffix
    # file.write("All_File='%s'\n" % filename_log)
    # DEBUG.OUT log if none specified:
    file.write("All_File=1\n")

    file.write("Input_File_Name='%s'\n" % filename_pov)
    file.write("Output_File_Name='%s'\n" % filename_image)

    file.write("Width=%d\n" % x)
    file.write("Height=%d\n" % y)

    # Border render.
    if render.use_border:
        file.write("Start_Column=%4g\n" % render.border_min_x)
        file.write("End_Column=%4g\n" % (render.border_max_x))

        file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y))
        file.write("End_Row=%4g\n" % (1.0 - render.border_min_y))

    file.write(
        "Bounding_Method=2\n"
    )  # The new automatic BSP is faster in most scenes

    # Activated (turn this back off when better live exchange is done between the two programs
    # (see next comment)
    file.write("Display=1\n")
    file.write("Pause_When_Done=0\n")
    # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the
    # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
    file.write("Output_File_Type=N\n")
    # file.write("Output_File_Type=T\n") # TGA, best progressive loading
    file.write("Output_Alpha=1\n")

    if scene.pov.antialias_enable:
        # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray
        # needs higher sampling.
        # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5}
        if using_uberpov:
            method = {"0": 1, "1": 2, "2": 3}
        else:
            method = {"0": 1, "1": 2, "2": 2}
        file.write("Antialias=on\n")
        file.write("Antialias_Depth=%d\n" % scene.pov.antialias_depth)
        file.write("Antialias_Threshold=%.3g\n" % scene.pov.antialias_threshold)
        if using_uberpov and scene.pov.antialias_method == '2':
            file.write(
                "Sampling_Method=%s\n" % method[scene.pov.antialias_method]
            )
            file.write(
                "Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence
            )
        else:
            file.write(
                "Sampling_Method=%s\n" % method[scene.pov.antialias_method]
            )
        file.write("Antialias_Gamma=%.3g\n" % scene.pov.antialias_gamma)
        if scene.pov.jitter_enable:
            file.write("Jitter=on\n")
            file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount)
        else:
            file.write("Jitter=off\n")  # prevent animation flicker

    else:
        file.write("Antialias=off\n")
    # print("ini file closed %s" % file.closed)
    file.close()
    # print("ini file closed %s" % file.closed)


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
            else:
                print(
                    "User Preferences path to povray %r NOT FOUND, checking $PATH"
                    % pov_binary
                )

        # Windows Only
        # assume if there is a 64bit binary that the user has a 64bit capable OS
        if sys.platform[:3] == "win":
            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

            # Then try 32bits UberPOV
            pov_binary = os.path.join(win_home, "bin", "uberpov32.exe")
            if os.path.exists(pov_binary):
                return pov_binary

            # Then try 32bits POV
            pov_binary = os.path.join(win_home, "bin", "pvengine.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, povPath, renderImagePath):
        """gather all necessary output files paths user defined and auto generated and export there"""
        import tempfile

        scene = bpy.context.scene
        if scene.pov.tempfiles_enable:
            self._temp_file_in = tempfile.NamedTemporaryFile(
                suffix=".pov", delete=False
            ).name
            # 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 = povPath + ".pov"
            # PNG with POV 3.7, can show the background color with alpha. In the long run using the
            # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
            self._temp_file_out = renderImagePath + ".png"
            # self._temp_file_out = renderImagePath + ".tga"
            self._temp_file_ini = povPath + ".ini"
            logPath = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/')
            self._temp_file_log = os.path.join(logPath, "alltext.out")
            '''
            self._temp_file_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(
            scene,
            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 newArg in scene.pov.command_line_switches.split(" "):
                extra_args.append(newArg)

        self._is_windows = False
        if sys.platform[:3] == "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 c 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."""
        import tempfile

        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

            poll_result = self._process.poll()
            # 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:
                povPath = scene.pov.text_block
                renderImagePath = os.path.splitext(povPath)[0]
                self._temp_file_out = os.path.join(preview_dir, renderImagePath)
                self._temp_file_in = os.path.join(preview_dir, povPath)
                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]
            file = open("%s" % self._temp_file_in, "w")
            # Why are the newlines needed?
            file.write("\n")
            file.write(text.as_string())
            file.write("\n")
            file.close()

            # 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(
                scene,
                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 newArg in scene.pov.command_line_switches.split(" "):
                    extra_args.append(newArg)

            if sys.platform[:3] == "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 (
                    sys.platform[:3] != "win" and scene.pov.sdl_window_enable
                ):  # 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'

            blendSceneName = bpy.data.filepath.split(os.path.sep)[-1].split(
                "."
            )[0]
            povSceneName = ""
            povPath = ""
            renderImagePath = ""

            # 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
                povPath = bpy.path.abspath(scene.pov.scene_path).replace(
                    '\\', '/'
                )
                if povPath == "":
                    if bpy.data.is_saved:
                        povPath = bpy.path.abspath("//")
                    else:
                        povPath = tempfile.gettempdir()
                elif povPath.endswith("/"):
                    if povPath == "/":
                        povPath = bpy.path.abspath("//")
                    else:
                        povPath = bpy.path.abspath(scene.pov.scene_path)

                if not os.path.exists(povPath):
                    try:
                        os.makedirs(povPath)
                    except:
                        import traceback

                        traceback.print_exc()

                        print(
                            "POV-Ray 3.7: Cannot create scenes directory: %r"
                            % povPath
                        )
                        self.update_stats(
                            "",
                            "POV-Ray 3.7: Cannot create scenes directory %r"
                            % povPath,
                        )
                        time.sleep(2.0)
                        # return

                '''
                # Bug in POV-Ray RC3
                renderImagePath = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/')
                if renderImagePath == "":
                    if bpy.data.is_saved:
                        renderImagePath = bpy.path.abspath("//")
                    else:
                        renderImagePath = tempfile.gettempdir()
                    #print("Path: " + renderImagePath)
                elif path.endswith("/"):
                    if renderImagePath == "/":
                        renderImagePath = bpy.path.abspath("//")
                    else:
                        renderImagePath = bpy.path.abspath(scene.pov.renderimage_path)
                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 blendSceneName != "":
                        povSceneName = blendSceneName
                    else:
                        povSceneName = "untitled"
                else:
                    povSceneName = scene.pov.scene_name
                    if os.path.isfile(povSceneName):
                        povSceneName = os.path.basename(povSceneName)
                    povSceneName = povSceneName.split('/')[-1].split('\\')[-1]
                    if not povSceneName:
                        print("POV-Ray 3.7: Invalid scene name")
                        self.update_stats("", "POV-Ray 3.7: Invalid scene name")
                        time.sleep(2.0)
                        # return
                    povSceneName = os.path.splitext(povSceneName)[0]

                print("Scene name: " + povSceneName)
                print("Export path: " + povPath)
                povPath = os.path.join(povPath, povSceneName)
                povPath = os.path.realpath(povPath)

                # for now this has to be the same like the pov output. Bug in POV-Ray RC3.
                # renderImagePath = renderImagePath + "\\" + povSceneName
                renderImagePath = povPath  # Bugfix for POV-Ray RC3 bug
                # renderImagePath = os.path.realpath(renderImagePath)  # Bugfix for POV-Ray RC3 bug

                # print("Export path: %s" % povPath)
                # print("Render Image path: %s" % renderImagePath)

            # start export
            self.update_stats("", "POV-Ray 3.7: Exporting data from Blender")
            self._export(depsgraph, povPath, renderImagePath)
            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')
                print(msg)
                # Also print to the interactive console used in POV centric workspace
                # To do: get a grip on new line encoding
                # and make this a function to be used elsewhere
                for win in bpy.context.window_manager.windows:
                    if win.screen != None:
                        scr = win.screen
                        for area in scr.areas:
                            if area.type == 'CONSOLE':
                                #context override
                                #ctx = {'window': win, 'screen': scr, 'area':area}#bpy.context.copy()
                                ctx = {}
                                ctx['area'] = area
                                ctx['region'] = area.regions[-1]
                                ctx['space_data'] = area.spaces.active
                                ctx['screen'] = scr#C.screen
                                ctx['window'] = win

                                #bpy.ops.console.banner(ctx, text = "Hello world")
                                bpy.ops.console.clear_line(ctx)
                                stdmsg = msg.split('\n') #XXX todo , test and see
                                for i in stdmsg:
                                    bpy.ops.console.insert(ctx, text = i)

            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

            if sys.platform[:3] == "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

            #Does Linux support say command?
            elif sys.platform[:3] != "win" :
                finished_render_message = "\'Render completed\'"
                # We don't want the say command to block Python,
                # so we add an ampersand after the message
                os.system("say %s &" % (finished_render_message))

##################################################################################
#################################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
        texPrevName = (
            string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev"
        )

        ## Make sure Preview directory exists and is empty
        if not os.path.isdir(preview_dir):
            os.mkdir(preview_dir)

        iniPrevFile = os.path.join(preview_dir, "Preview.ini")
        inputPrevFile = os.path.join(preview_dir, "Preview.pov")
        outputPrevFile = os.path.join(preview_dir, texPrevName)
        ##################### ini ##########################################
        fileIni = open("%s" % iniPrevFile, "w")
        fileIni.write('Version=3.8\n')
        fileIni.write('Input_File_Name="%s"\n' % inputPrevFile)
        fileIni.write('Output_File_Name="%s.png"\n' % outputPrevFile)
        fileIni.write('Library_Path="%s"\n' % preview_dir)
        fileIni.write('Width=256\n')
        fileIni.write('Height=256\n')
        fileIni.write('Pause_When_Done=0\n')
        fileIni.write('Output_File_Type=N\n')
        fileIni.write('Output_Alpha=1\n')
        fileIni.write('Antialias=on\n')
        fileIni.write('Sampling_Method=2\n')
        fileIni.write('Antialias_Depth=3\n')
        fileIni.write('-d\n')
        fileIni.close()
        ##################### pov ##########################################
        filePov = open("%s" % inputPrevFile, "w")
        PATname = "PAT_" + string_strip_hyphen(bpy.path.clean_name(tex.name))
        filePov.write("#declare %s = \n" % PATname)
        filePov.write(shading.exportPattern(tex, string_strip_hyphen))

        filePov.write("#declare Plane =\n")
        filePov.write("mesh {\n")
        filePov.write(
            "    triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
        )
        filePov.write(
            "    triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
        )
        filePov.write("    texture{%s}\n" % PATname)
        filePov.write("}\n")
        filePov.write("object {Plane}\n")
        filePov.write("light_source {\n")
        filePov.write("    <0,4.38,-1.92e-07>\n")
        filePov.write("    color rgb<4, 4, 4>\n")
        filePov.write("    parallel\n")
        filePov.write("    point_at  <0, 0, -1>\n")
        filePov.write("}\n")
        filePov.write("camera {\n")
        filePov.write("    location  <0, 0, 0>\n")
        filePov.write("    look_at  <0, 0, -1>\n")
        filePov.write("    right <-1.0, 0, 0>\n")
        filePov.write("    up <0, 1, 0>\n")
        filePov.write("    angle  96.805211\n")
        filePov.write("    rotate  <-90.000003, -0.000000, 0.000000>\n")
        filePov.write("    translate <0.000000, 0.000000, 0.000000>\n")
        filePov.write("}\n")
        filePov.close()
        ##################### end write ##########################################

        pov_binary = PovrayRender._locate_binary()

        if sys.platform[:3] == "win":
            p1 = subprocess.Popen(
                ["%s" % pov_binary, "/EXIT", "%s" % iniPrevFile],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
            )
        else:
            p1 = subprocess.Popen(
                ["%s" % pov_binary, "-d", "%s" % iniPrevFile],
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
            )
        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")
        pathPrev = "%s.png" % outputPrevFile
        im.image = bpy.data.images.load(pathPrev)
        name = pathPrev
        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 engain
        scene.pov.text_block = ""
        return {'FINISHED'}


classes = (PovrayRender, RenderPovTexturePreview, RunPovTextRender)


def register():
    # from bpy.utils import register_class

    for cls in classes:
        register_class(cls)


def unregister():
    from bpy.utils import unregister_class

    for cls in reversed(classes):
        unregister_class(cls)
