Skip to content
Snippets Groups Projects
render.py 233 KiB
Newer Older
  • Learn to ignore specific revisions
  •                                         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
    
                                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(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)
    
                                        # 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!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
                                    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
    
                                        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
    
                                        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")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
                                #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")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                                tabWrite("}\n")  # End of mesh block
    
    
                            ob_eval.to_mesh_clear()
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
            if csg:
                duplidata_ref = []
    
                _dupnames_seen = dict()  # avoid duplicate output during introspection
    
                    #matrix = global_matrix @ ob.matrix_world
    
                        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))]
    
                        #ob.dupli_list_clear()# just do not store any reference to instance since 2.8
    
                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")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def exportWorld(world):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """write world as POV backgrounbd and sky_sphere to exported file """
    
            render = scene.pov
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            camera = scene.camera
    
            matrix = global_matrix @ camera.matrix_world
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            if not world:
                return
    
            #############Maurice####################################
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            #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[:]))
    
                    # XXX Does not exists anymore
                    #else:
    
                        #tabWrite("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:]))
    
                worldTexCount = 0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                #For Background image textures
    
                for t in world.texture_slots:  # risk to write several sky_spheres but maybe ok.
    
                    if t and t.texture.type is not None:
    
                        worldTexCount += 1
    
                    # XXX No enable checkbox for world textures yet (report it?)
                    #if t and t.texture.type == 'IMAGE' and t.use:
                    if t and t.texture.type == 'IMAGE':
    
    Campbell Barton's avatar
    Campbell Barton committed
                        image_filename = path_image(t.texture.image)
    
                        if t.texture.image.filepath != image_filename:
                            t.texture.image.filepath = image_filename
    
                        if image_filename != "" and t.use_map_blend:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            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))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        #using camera rotation valuesdirectly from blender seems much easier
    
                        if t_blend.texture_coords == 'ANGMAP':
                            mappingBlend = ""
    
                            # 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.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" % (t.texture.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")
    
                        # 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[:]))
    
                            tabWrite("[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n" % (world.pov.zenith_color[:]))
    
                            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:
    
                # Maybe change the above to a function copyInternalRenderer settings when
    
                #scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
    
                # and other such translations but maybe this would not be allowed either?
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
            ###############################################################
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Campbell Barton's avatar
    Campbell Barton committed
            mist = world.mist_settings
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
            if mist.use_mist:
    
                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("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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
        def exportGlobalSettings(scene):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
            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)
    
                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)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            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
    
    Campbell Barton's avatar
    Campbell Barton committed
                    tabWrite("subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10))
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    tabWrite("ambient_light rgb<%.3g, %.3g, %.3g>\n" % world.pov.ambient_color[:])
    
                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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """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")
    
                    # Why are the newlines needed?
                    file.write("\n")
                    file.write(txt.as_string())
                    file.write("\n")
    
        #sel = renderable_objects(scene) #removed for booleans
    
            file.write("//----------------------------------------------\n" \
                       "//--Exported with POV-Ray exporter for Blender--\n" \
                       "//----------------------------------------------\n\n")
    
        file.write("#declare Default_texture = texture{pigment {rgb 0.8} "
                   "finish {brilliance 3.8} }\n\n")
    
            file.write("\n//--Global settings--\n\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        exportGlobalSettings(scene)
    
            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")
    
            file.write("\n//--Background--\n\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        exportWorld(scene.world)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        exportCamera()
    
        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)
    
    
            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")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        # Convert all materials to strings we can access directly per vertex.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        #exportMaterials()
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        shading.writeMaterial(using_uberpov, DEF_MAT_NAME, scene, tabWrite, safety, comments, uniqueName, materialNames, None)  # default material
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        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)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # attributes are all the variables needed by the other python file...
    
        exportMeta([m for m in sel if m.type == 'META'])
    
        exportMeshes(scene, sel, csg)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        #What follow used to happen here:
        #exportCamera()
        #exportWorld(scene.world)
        #exportGlobalSettings(scene)
    
        # MR:..and the order was important for implementing pov 3.7 baking
    
        # CR: Baking should be a special case than. If "baking", than we could change the order.
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
        #print("pov file closed %s" % file.closed)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        file.close()
    
        #print("pov file closed %s" % file.closed)
    
    def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_image):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Write ini file."""
    
        feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray
    
        using_uberpov = (feature_set=='uberpov')
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        render = scene.render
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        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")
    
        #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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
        file.write("Width=%d\n" % x)
        file.write("Height=%d\n" % y)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
        # 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))
    
    
    Campbell Barton's avatar
    Campbell Barton committed
            file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y))
            file.write("End_Row=%4g\n" % (1.0 - render.border_min_y))
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
        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")
    
        # 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")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
        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_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_Amount=%3g\n" % scene.pov.jitter_amount)
    
                file.write("Jitter=off\n")  # prevent animation flicker
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        else:
    
            file.write("Antialias=off\n")
        #print("ini file closed %s" % file.closed)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        file.close()
    
        #print("ini file closed %s" % file.closed)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    class PovrayRender(bpy.types.RenderEngine):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Define the external renderer"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        bl_idname = 'POVRAY_RENDER'
    
        bl_label = "Persitence Of Vision"
    
        bl_use_shading_nodes_custom = False
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """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
    
                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
    
    
                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):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """gather all necessary output files paths user defined and auto generated and export there"""
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            import tempfile
    
                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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                # os.makedirs(user_dir, exist_ok=True)  # handled with previews
                os.makedirs(preview_dir, exist_ok=True)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                write_pov(self._temp_file_in, scene, info_callback)
            else:
                pass
    
        def _render(self, depsgraph):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Export necessary files and render image."""
    
            scene = bpy.context.scene
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            try:
    
                os.remove(self._temp_file_out)  # so as not to load the old file
    
    Campbell Barton's avatar
    Campbell Barton committed
            except OSError:
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                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)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
            if scene.pov.command_line_switches != "":
                for newArg in scene.pov.command_line_switches.split(" "):
    
            if sys.platform[:3] == "win":
    
                if"/EXIT" not in extra_args and not scene.pov.pov_editor:
                    extra_args.append("/EXIT")
    
                # added -d option to prevent render window popup which leads to segfault on linux
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
            # 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
    
                print("Command line arguments passed: " + str(extra_args))
                return True
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def _cleanup(self):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Delete temp files and unpacked ones"""
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            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)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Export necessary files from text editor and render image."""
    
            import tempfile
    
            r = scene.render
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            x = int(r.resolution_x * r.resolution_percentage * 0.01)
            y = int(r.resolution_y * r.resolution_percentage * 0.01)
    
            # This makes some tests on the render, returning True if all goes good, and False if