Skip to content
Snippets Groups Projects
object_mesh_topology.py 77.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    # <pep8 compliant>
    
    """Translate to POV the control point compounded geometries like polygon
    
    meshes or curve based shapes."""
    
    
    # --------
    # -- Faster mesh export ...one day
    # import numpy as np
    # --------
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    import bpy
    from . import texturing  # for how textures influence shaders
    from .scenography import export_smoke
    
    def matrix_as_pov_string(matrix):
    
        """Translate some transform matrix from Blender UI
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        to POV syntax and return that string """
    
        return "matrix <" \
               "%.6f, %.6f, %.6f, " \
               "%.6f, %.6f, %.6f, " \
               "%.6f, %.6f, %.6f, " \
               "%.6f, %.6f, %.6f" \
               ">\n" % (
                   matrix[0][0],
                   matrix[1][0],
                   matrix[2][0],
                   matrix[0][1],
                   matrix[1][1],
                   matrix[2][1],
                   matrix[0][2],
                   matrix[1][2],
                   matrix[2][2],
                   matrix[0][3],
                   matrix[1][3],
                   matrix[2][3],
               )
    
    
    def write_object_csg_inside_vector(ob, file):
        """Write inside vector for use by pov CSG, only once per object using boolean"""
        has_csg_inside_vector = False
        for modif in ob.modifiers:
            if (
                    not has_csg_inside_vector
                    and modif.type == 'BOOLEAN'
                    and ob.pov.boolean_mod == "POV"
            ):
                file.write(
                    "\tinside_vector <%.6g, %.6g, %.6g>\n"
                    % (
                        ob.pov.inside_vector[0],
                        ob.pov.inside_vector[1],
                        ob.pov.inside_vector[2],
                    )
                )
                has_csg_inside_vector = True
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    #    objectNames = {}
    DEF_OBJ_NAME = "Default"
    
    
    def export_meshes(
    
            preview_dir,
            file,
            scene,
            sel,
            csg,
            string_strip_hyphen,
            safety,
            write_object_modifiers,
            material_names_dictionary,
            write_object_material_interior,
            exported_lights_count,
            unpacked_images,
            image_format,
            img_map,
            img_map_transforms,
            path_image,
            smoke_path,
            global_matrix,
            write_matrix,
            using_uberpov,
            comments,
            linebreaksinlists,
            tab,
            tab_level,
            tab_write,
            info_callback,
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    ):
        """write all meshes as POV mesh2{} syntax to exported file """
    
        # # some numpy functions to speed up mesh export NOT IN USE YET
        # # Current 2.93 beta numpy linking has troubles so definitions commented off for now
    
        # # TODO: also write a numpy function to read matrices at object level?
        # # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
        # def read_verts_co(self, mesh):
    
        # #'float64' would be a slower 64-bit floating-point number numpy datatype
        # # using 'float32' vert coordinates for now until any issue is reported
        # mverts_co = np.zeros((len(mesh.vertices) * 3), dtype=np.float32)
        # mesh.vertices.foreach_get("co", mverts_co)
        # return np.reshape(mverts_co, (len(mesh.vertices), 3))
    
    
        # def read_verts_idx(self, mesh):
    
        # mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
        # mesh.vertices.foreach_get("index", mverts_idx)
        # return np.reshape(mverts_idx, (len(mesh.vertices), 1))
    
    
        # def read_verts_norms(self, mesh):
    
        # #'float64' would be a slower 64-bit floating-point number numpy datatype
        # # using less accurate 'float16' normals for now until any issue is reported
        # mverts_no = np.zeros((len(mesh.vertices) * 3), dtype=np.float16)
        # mesh.vertices.foreach_get("normal", mverts_no)
        # return np.reshape(mverts_no, (len(mesh.vertices), 3))
    
    
        # def read_faces_idx(self, mesh):
    
        # mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
        # mesh.loop_triangles.foreach_get("index", mfaces_idx)
        # return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))
    
    
        # def read_faces_verts_indices(self, mesh):
    
        # mfaces_verts_idx = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
        # mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
        # return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))
    
    
        # # Why is below different from vertex indices?
        # def read_faces_verts_loops(self, mesh):
    
        # mfaces_verts_loops = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.int64)
        # mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
        # return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))
    
    
        # def read_faces_norms(self, mesh):
    
        # #'float64' would be a slower 64-bit floating-point number numpy datatype
        # # using less accurate 'float16' normals for now until any issue is reported
        # mfaces_no = np.zeros((len(mesh.loop_triangles) * 3), dtype=np.float16)
        # mesh.loop_triangles.foreach_get("normal", mfaces_no)
        # return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))
    
    
        # def read_faces_smooth(self, mesh):
    
        # mfaces_smth = np.zeros((len(mesh.loop_triangles) * 1), dtype=np.bool)
        # mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
        # return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))
    
    
        # def read_faces_material_indices(self, mesh):
    
        # mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
        # mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
        # return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        #        obmatslist = []
        #        def hasUniqueMaterial():
        #            # Grab materials attached to object instances ...
    
        #            if hasattr(obj, 'material_slots'):
        #                for ms in obj.material_slots:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        #                    if ms.material is not None and ms.link == 'OBJECT':
        #                        if ms.material in obmatslist:
        #                            return False
        #                        else:
        #                            obmatslist.append(ms.material)
        #                            return True
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        #            # Grab materials attached to object instances ...
    
        #            if hasattr(obj, 'material_slots'):
        #                for ms in obj.material_slots:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        #                    if ms.material is not None and ms.link == 'OBJECT':
        #                        # If there is at least one material slot linked to the object
        #                        # and not the data (mesh), always create a new, "private" data instance.
        #                        return True
        #            return False
        # For objects using local material(s) only!
    
        # This is a mapping between a tuple (dataname, material_names_dictionary, ...),
        # and the POV dataname.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # As only objects using:
        #     * The same data.
        #     * EXACTLY the same materials, in EXACTLY the same sockets.
        # ... can share a same instance in POV export.
        obmats2data = {}
    
    
        def check_object_materials(obj, obj_name, dataname):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """Compare other objects exported material slots to avoid rewriting duplicates"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                has_local_mats = False
                key = [dataname]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    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:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    return obmats2data[key]
            return None
    
        data_ref = {}
    
        def store(scene, ob, name, dataname, matrix):
            # The Object needs to be written at least once but if its data is
            # already in data_ref this has already been done.
            # This func returns the "povray" name of the data, or None
            # if no writing is needed.
            if ob.is_modified(scene, 'RENDER'):
                # Data modified.
                # Create unique entry in data_ref by using object name
                # (always unique in Blender) as data name.
                data_ref[name] = [(name, matrix_as_pov_string(matrix))]
                return name
            # Here, we replace dataname by the value returned by check_object_materials, only if
            # it is not evaluated to False (i.e. only if the object uses some local material(s)).
            dataname = check_object_materials(ob, name, dataname) or dataname
            if dataname in data_ref:
                # Data already known, just add the object instance.
                data_ref[dataname].append((name, matrix_as_pov_string(matrix)))
                # No need to write data
                return None
            # Else (no return yet): Data not yet processed, create a new entry in data_ref.
            data_ref[dataname] = [(name, matrix_as_pov_string(matrix))]
            return dataname
    
        ob_num = 0
    
        depsgraph = bpy.context.evaluated_depsgraph_get()
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        for ob in sel:
            # Using depsgraph
            ob = bpy.data.objects[ob.name].evaluated_get(depsgraph)
    
            # subtract original from the count of their instances as were not counted before 2.8
            if not (ob.is_instancer and ob.original != ob):
                ob_num += 1
    
                # XXX I moved all those checks here, as there is no need to compute names
                #     for object we won't export here!
                if ob.type in {
                    'LIGHT',
    
                    'CAMERA',  # 'EMPTY', #empties can bear dupligroups
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    'META',
                    'ARMATURE',
                    'LATTICE',
                }:
                    continue
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for mod in ob.modifiers:
                    if mod and hasattr(mod, 'fluid_type'):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        if mod.fluid_type == 'DOMAIN':
                            if mod.domain_settings.domain_type == 'GAS':
                                export_smoke(
                                    file, ob.name, smoke_path, comments, global_matrix, write_matrix
                                )
                            break  # don't render domain mesh, skip to next object.
                        if mod.fluid_type == 'FLOW':  # The domain contains all the smoke. so that's it.
                            if mod.flow_settings.flow_type == 'SMOKE':  # Check how liquids behave
                                break  # don't render smoke flow emitter mesh either, skip to next object.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    if hasattr(ob, 'particle_systems'):
    
                        # Importing function Export Hair
                        # here rather than at the top recommended for addons startup footprint
                        from .object_particles import export_hair
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        for p_sys in ob.particle_systems:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                m
                                for m in ob.modifiers
                                if (m is not None) and (m.type == 'PARTICLE_SYSTEM')
                            ]:
                                if (
    
                                        (p_sys.settings.render_type == 'PATH')
                                        and particle_mod.show_render
                                        and (p_sys.name == particle_mod.particle_system.name)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                ):
    
                                    export_hair(file, ob, particle_mod, p_sys, global_matrix, write_matrix)
                        if not ob.show_instancer_for_render:
                            continue  # don't render emitter mesh, skip to next object.
    
                    # ------------------------------------------------
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    # Generating a name for object just like materials to be able to use it
                    # (baking for now or anything else).
                    # XXX I don't understand that if we are here, sel if a non-empty iterable,
                    #     so this condition is always True, IMO -- mont29
    
                    # EMPTY type objects treated  a little further below -- MR
    
                    # modified elif to if below as non EMPTY objects can also be instancers
                    if ob.is_instancer:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        if ob.instance_type == 'COLLECTION':
                            name_orig = "OB" + ob.name
                            dataname_orig = "DATA" + ob.instance_collection.name
                        else:
                            # hoping only dupligroups have several source datablocks
                            # ob_dupli_list_create(scene) #deprecated in 2.8
                            for eachduplicate in depsgraph.object_instances:
                                # Real dupli instance filtered because
                                # original included in list since 2.8
                                if eachduplicate.is_instance:
                                    dataname_orig = "DATA" + eachduplicate.object.name
    
                            # obj.dupli_list_clear() #just don't store any reference to instance since 2.8
                    elif ob.data:  # not an EMPTY type object
    
                        name_orig = "OB" + ob.name
                        dataname_orig = "DATA" + ob.data.name
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    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))
    
                    #                if slot.material is not None and slot.link == 'OBJECT':
                    #                    obmaterial = slot.material
    
                    # ------------------------------------------------
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                    if info_callback:
                        info_callback("Object %2.d of %2.d (%s)" % (ob_num, len(sel), ob.name))
    
    
                    me = ob.data
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                    matrix = global_matrix @ ob.matrix_world
                    povdataname = store(scene, ob, name, dataname, matrix)
                    if povdataname is None:
                        print("This is an instance of " + name)
                        continue
    
                    print("Writing Down First Occurrence of " + name)
    
    
                    # ------------ Mesh Primitives ------------ #
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    # special export_curves() function takes care of writing
                    # lathe, sphere_sweep, birail, and loft except with modifiers
                    # converted to mesh
                    if not ob.is_modified(scene, 'RENDER'):
                        if ob.type == 'CURVE' and (
    
                                ob.pov.curveshape in {'lathe', 'sphere_sweep', 'loft'}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        ):
                            continue  # Don't render proxy mesh, skip to next object
    
                    # pov_mat_name = "Default_texture" # Not used...remove?
    
                    # Implicit else-if (as not skipped by previous "continue")
                    # which itself has no "continue" (to combine custom pov code)?, so Keep this last.
                    # For originals, but not their instances, attempt to export mesh:
                    if not ob.is_instancer:
                        # except duplis which should be instances groups for now but all duplis later
                        if ob.type == 'EMPTY':
                            # XXX Should we only write this once and instantiate the same for every
                            # empty in the final matrix writing, or even no matrix and just a comment
                            # with empty object transforms ?
                            tab_write("\n//dummy sphere to represent Empty location\n")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            tab_write(
    
                                "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
                                % povdataname
    
                            continue  # Don't render empty object but this is later addition, watch it.
    
                        ob_eval = ob  # not sure this is needed in case to_mesh_clear could damage obj ?
                        try:
                            me = ob_eval.to_mesh()
    
                        # Here identify the exception for mesh object with no data: Runtime-Error ?
                        # So we can write something for the dataname or maybe treated "if not me" below
                        except BaseException as e:
                            print(e.__doc__)
                            print('An exception occurred: {}'.format(e))
                            # also happens when curves cant be made into meshes because of no-data
                            continue
    
                        importance = ob.pov.importance_value
                        if me:
                            me.calc_loop_triangles()
                            me_materials = me.materials
                            me_faces = me.loop_triangles[:]
                            # --- numpytest
                            # me_looptris = me.loops
    
                            # Below otypes = ['int32'] is a 32-bit signed integer number numpy datatype
                            # get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
                            # faces_verts_idx = get_v_index(me_looptris)
    
                        # if len(me_faces)==0:
                        # tab_write("\n//dummy sphere to represent empty mesh location\n")
                        # tab_write("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)
    
                        if not me or not me_faces:
                            tab_write("\n//dummy sphere to represent empty mesh location\n")
                            tab_write(
                                "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
                                % povdataname
                            )
                            continue
    
                        uv_layers = me.uv_layers
                        if len(uv_layers) > 0:
                            if me.uv_layers.active and uv_layers.active.data:
                                uv_layer = uv_layers.active.data
                        else:
                            uv_layer = None
    
                        try:
                            # vcol_layer = me.vertex_colors.active.data
                            vcol_layer = me.vertex_colors.active.data
                        except AttributeError:
                            vcol_layer = None
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                        faces_verts = [f.vertices[:] for f in me_faces]
                        faces_normals = [f.normal[:] for f in me_faces]
                        verts_normals = [v.normal[:] for v in me.vertices]
    
                        # Use named declaration to allow reference e.g. for baking. MR
                        file.write("\n")
                        tab_write("#declare %s =\n" % povdataname)
                        tab_write("mesh2 {\n")
                        tab_write("vertex_vectors {\n")
                        tab_write("%d" % len(me.vertices))  # vert count
    
                        tab_str = tab * tab_level
                        for v in me.vertices:
                            if linebreaksinlists:
                                file.write(",\n")
                                file.write(tab_str + "<%.6f, %.6f, %.6f>" % v.co[:])  # vert count
                            else:
                                file.write(", ")
                                file.write("<%.6f, %.6f, %.6f>" % v.co[:])  # vert count
                            # tab_write("<%.6f, %.6f, %.6f>" % v.co[:])  # vert count
                        file.write("\n")
                        tab_write("}\n")
    
                        # Build unique Normal list
                        uniqueNormals = {}
                        for fi, f in enumerate(me_faces):
                            fv = faces_verts[fi]
                            # [-1] is a dummy index, use a list so we can modify in place
                            if f.use_smooth:  # Use vertex normals
                                for v in fv:
                                    key = verts_normals[v]
                                    uniqueNormals[key] = [-1]
                            else:  # Use face normal
                                key = faces_normals[fi]
                                uniqueNormals[key] = [-1]
    
                        tab_write("normal_vectors {\n")
                        tab_write("%d" % len(uniqueNormals))  # vert count
                        idx = 0
                        tab_str = tab * tab_level
                        for no, index in uniqueNormals.items():
                            if linebreaksinlists:
                                file.write(",\n")
                                file.write(tab_str + "<%.6f, %.6f, %.6f>" % no)  # vert count
                            else:
                                file.write(", ")
                                file.write("<%.6f, %.6f, %.6f>" % no)  # vert count
                            index[0] = idx
                            idx += 1
                        file.write("\n")
                        tab_write("}\n")
    
                        # Vertex colors
                        vertCols = {}  # Use for material colors also.
    
                        if uv_layer:
                            # Generate unique UV's
                            uniqueUVs = {}
                            # n = 0
                            for f in me_faces:  # me.faces in 2.7
    
                                uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                                for uv in uvs:
                                    uniqueUVs[uv[:]] = [-1]
    
                            tab_write("uv_vectors {\n")
                            # print unique_uvs
                            tab_write("%d" % len(uniqueUVs))  # vert count
                            idx = 0
                            tab_str = tab * tab_level
                            for uv, index in uniqueUVs.items():
                                if linebreaksinlists:
                                    file.write(",\n")
                                    file.write(tab_str + "<%.6f, %.6f>" % uv)
                                else:
                                    file.write(", ")
                                    file.write("<%.6f, %.6f>" % uv)
                                index[0] = idx
                                idx += 1
                            '''
                            else:
                                # Just add 1 dummy vector, no real UV's
                                tab_write('1') # vert count
                                file.write(',\n\t\t<0.0, 0.0>')
                            '''
                            file.write("\n")
                            tab_write("}\n")
    
                        if me.vertex_colors:
                            # Write down vertex colors as a texture for each vertex
                            tab_write("texture_list {\n")
                            tab_write("%d\n" % (len(me_faces) * 3))  # assumes we have only triangles
                            VcolIdx = 0
                            if comments:
                                file.write(
                                    "\n  //Vertex colors: one simple pigment texture per vertex\n"
                                )
                            for fi, f in enumerate(me_faces):
                                # annoying, index may be invalid
                                material_index = f.material_index
                                try:
                                    material = me_materials[material_index]
                                except BaseException as e:
                                    print(e.__doc__)
                                    print('An exception occurred: {}'.format(e))
                                    material = None
                                if (
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                ):  # and material.use_vertex_color_paint: #Always use vertex color when there is some for now
    
    
                                    cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                                    for col in cols:
                                        key = (
                                            col[0],
                                            col[1],
                                            col[2],
                                            material_index,
                                        )  # Material index!
                                        VcolIdx += 1
                                        vertCols[key] = [VcolIdx]
                                        if linebreaksinlists:
                                            tab_write(
                                                "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
                                                % (col[0], col[1], col[2])
                                            )
                                        else:
                                            tab_write(
                                                "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
                                                % (col[0], col[1], col[2])
                                            )
                                            tab_str = tab * tab_level
                                else:
                                    if material:
                                        # Multiply diffuse with SSS Color
                                        if material.pov_subsurface_scattering.use:
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[:],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                            key = (
                                                diffuse_color[0],
                                                diffuse_color[1],
                                                diffuse_color[2],
                                                material_index,
                                            )
                                            vertCols[key] = [-1]
                                        else:
                                            diffuse_color = material.diffuse_color[:]
                                            key = (
                                                diffuse_color[0],
                                                diffuse_color[1],
                                                diffuse_color[2],
                                                material_index,
                                            )
                                            vertCols[key] = [-1]
    
                            tab_write("\n}\n")
                            # Face indices
                            tab_write("\nface_indices {\n")
                            tab_write("%d" % (len(me_faces)))  # faces count
                            tab_str = tab * tab_level
    
                            for fi, f in enumerate(me_faces):
                                fv = faces_verts[fi]
                                material_index = f.material_index
    
                                if vcol_layer:
    
                                    cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
    
                                        not me_materials or me_materials[material_index] is None
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                ):  # No materials
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        # vert count
                                        file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
                                    else:
                                        file.write(", ")
                                        file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2]))  # vert count
                                else:
                                    material = me_materials[material_index]
                                    if me.vertex_colors:  # and material.use_vertex_color_paint:
                                        # Color per vertex - vertex color
    
                                        col1 = cols[0]
                                        col2 = cols[1]
                                        col3 = cols[2]
    
                                        ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
                                        ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
                                        ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
                                    else:
                                        # Color per material - flat material color
                                        if material.pov_subsurface_scattering.use:
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[:],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                        else:
                                            diffuse_color = material.diffuse_color[:]
                                        ci1 = ci2 = ci3 = vertCols[
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            f.material_index,
                                        ][0]
                                        # ci are zero based index so we'll subtract 1 from them
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>, %d,%d,%d"
                                            % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>, %d,%d,%d"
                                            % (fv[0], fv[1], fv[2], ci1 - 1, ci2 - 1, ci3 - 1)
                                        )  # vert count
    
                            file.write("\n")
                            tab_write("}\n")
    
                            # normal_indices indices
                            tab_write("normal_indices {\n")
                            tab_write("%d" % (len(me_faces)))  # faces count
                            tab_str = tab * tab_level
                            for fi, fv in enumerate(faces_verts):
    
                                if me_faces[fi].use_smooth:
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[verts_normals[fv[0]]][0],
                                                uniqueNormals[verts_normals[fv[1]]][0],
                                                uniqueNormals[verts_normals[fv[2]]][0],
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[verts_normals[fv[0]]][0],
                                                uniqueNormals[verts_normals[fv[1]]][0],
                                                uniqueNormals[verts_normals[fv[2]]][0],
                                            )
                                        )  # vert count
                                else:
                                    idx = uniqueNormals[faces_normals[fi]][0]
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str + "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write("<%d,%d,%d>" % (idx, idx, idx))  # vert count
    
                            file.write("\n")
                            tab_write("}\n")
    
                            if uv_layer:
                                tab_write("uv_indices {\n")
                                tab_write("%d" % (len(me_faces)))  # faces count
                                tab_str = tab * tab_level
                                for f in me_faces:
    
                                    uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
    
                                file.write("\n")
                                tab_write("}\n")
    
                            # XXX BOOLEAN
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                            if me.materials:
                                try:
                                    material = me.materials[0]  # dodgy
    
                                    write_object_material_interior(material, ob, tab_write)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                except IndexError:
                                    print(me)
    
                            # POV object modifiers such as
                            # hollow / sturm / double_illuminate etc.
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                            # Importance for radiosity sampling added here:
                            tab_write("radiosity { \n")
                            tab_write("importance %3g \n" % importance)
                            tab_write("}\n")
    
                            tab_write("}\n")  # End of mesh block
                        else:
                            facesMaterials = []  # WARNING!!!!!!!!!!!!!!!!!!!!!!
                            if me_materials:
                                for f in me_faces:
                                    if f.material_index not in facesMaterials:
                                        facesMaterials.append(f.material_index)
                            # No vertex colors, so write material colors as vertex colors
                            for i, material in enumerate(me_materials):
    
                                if (
    
                                        material and material.pov.material_use_nodes is False
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                ):  # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                                    # Multiply diffuse with SSS Color
                                    if material.pov_subsurface_scattering.use:
                                        diffuse_color = [
                                            i * j
                                            for i, j in zip(
                                                material.pov_subsurface_scattering.color[:],
                                                material.diffuse_color[:],
                                            )
                                        ]
                                        key = (
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            i,
                                        )  # i == f.mat
                                        vertCols[key] = [-1]
                                    else:
                                        diffuse_color = material.diffuse_color[:]
                                        key = (
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            i,
                                        )  # i == f.mat
                                        vertCols[key] = [-1]
    
                                    idx = 0
    
                                    local_material_names = []  # XXX track and revert
    
                                    material_finish = None
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                    for col, index in vertCols.items():
                                        # if me_materials:
                                        mater = me_materials[col[3]]
    
                                        if me_materials is not None:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                            texturing.write_texture_influence(
                                                using_uberpov,
                                                mater,
                                                material_names_dictionary,
                                                local_material_names,
                                                path_image,
                                                exported_lights_count,
                                                image_format,
                                                img_map,
                                                img_map_transforms,
                                                tab_write,
                                                comments,
                                                string_strip_hyphen,
                                                safety,
                                                col,
                                                preview_dir,
                                                unpacked_images,
                                            )
    
                                        # ------------------------------------------------
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                        index[0] = idx
                                        idx += 1
    
                            # Vert Colors
                            tab_write("texture_list {\n")
                            # In case there's is no material slot, give at least one texture
                            # (an empty one so it uses pov default)
                            if len(vertCols) == 0:
                                file.write(tab_str + "1")
                            else:
                                file.write(tab_str + "%s" % (len(vertCols)))  # vert count
    
    
                            # below "material" alias, added check obj.active_material
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            # to avoid variable referenced before assignment error
                            try:
                                material = ob.active_material
                            except IndexError:
                                # when no material slot exists,
                                material = None
    
                            # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            if (
    
                                    material
                                    and ob.active_material is not None
                                    and not material.pov.material_use_nodes
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            ):
                                if material.pov.replacement_text != "":
                                    file.write("\n")
                                    file.write(" texture{%s}\n" % material.pov.replacement_text)
    
                                else:
                                    # Loop through declared materials list
                                    for cMN in local_material_names:
                                        if material != "Default":
                                            file.write("\n texture{MAT_%s}\n" % cMN)
                                            # use string_strip_hyphen(material_names_dictionary[material]))
                                            # or Something like that to clean up the above?
                            elif material and material.pov.material_use_nodes:
                                for index in facesMaterials:
                                    faceMaterial = string_strip_hyphen(
                                        bpy.path.clean_name(me_materials[index].name)
                                    )
                                    file.write("\n texture{%s}\n" % faceMaterial)
                            # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            else:
                                file.write(" texture{}\n")
                            tab_write("}\n")
    
                            # Face indices
                            tab_write("face_indices {\n")
                            tab_write("%d" % (len(me_faces)))  # faces count
                            tab_str = tab * tab_level
    
                            for fi, f in enumerate(me_faces):
                                fv = faces_verts[fi]
                                material_index = f.material_index
    
                                if vcol_layer:
    
                                    cols = [vcol_layer[loop_index].color[:] for loop_index in f.loops]
    
                                        not me_materials or me_materials[material_index] is None
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                ):  # No materials
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        # vert count
                                        file.write(tab_str + "<%d,%d,%d>" % (fv[0], fv[1], fv[2]))
                                    else:
                                        file.write(", ")
                                        file.write("<%d,%d,%d>" % (fv[0], fv[1], fv[2]))  # vert count
                                else:
                                    material = me_materials[material_index]
                                    ci1 = ci2 = ci3 = f.material_index
                                    if me.vertex_colors:  # and material.use_vertex_color_paint:
                                        # Color per vertex - vertex color
    
                                        col1 = cols[0]
                                        col2 = cols[1]
                                        col3 = cols[2]
    
                                        ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
                                        ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
                                        ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
                                    elif material.pov.material_use_nodes:
                                        ci1 = ci2 = ci3 = 0
                                    else:
                                        # Color per material - flat material color
                                        if material.pov_subsurface_scattering.use:
                                            diffuse_color = [
                                                i * j
                                                for i, j in zip(
                                                    material.pov_subsurface_scattering.color[:],
                                                    material.diffuse_color[:],
                                                )
                                            ]
                                        else:
                                            diffuse_color = material.diffuse_color[:]
                                        ci1 = ci2 = ci3 = vertCols[
                                            diffuse_color[0],
                                            diffuse_color[1],
                                            diffuse_color[2],
                                            f.material_index,
                                        ][0]
    
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>, %d,%d,%d"
                                            % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>, %d,%d,%d"
                                            % (fv[0], fv[1], fv[2], ci1, ci2, ci3)
                                        )  # vert count
    
                            file.write("\n")
                            tab_write("}\n")
    
                            # normal_indices indices
                            tab_write("normal_indices {\n")
                            tab_write("%d" % (len(me_faces)))  # faces count
                            tab_str = tab * tab_level
                            for fi, fv in enumerate(faces_verts):
                                if me_faces[fi].use_smooth:
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[verts_normals[fv[0]]][0],
                                                uniqueNormals[verts_normals[fv[1]]][0],
                                                uniqueNormals[verts_normals[fv[2]]][0],
                                            )
                                        )  # vert count
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueNormals[verts_normals[fv[0]]][0],
                                                uniqueNormals[verts_normals[fv[1]]][0],
                                                uniqueNormals[verts_normals[fv[2]]][0],
                                            )
                                        )  # vert count
                                else:
                                    idx = uniqueNormals[faces_normals[fi]][0]
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str + "<%d,%d,%d>" % (idx, idx, idx)
                                        )  # vertcount
                                    else:
                                        file.write(", ")
                                        file.write("<%d,%d,%d>" % (idx, idx, idx))  # vert count
    
                            file.write("\n")
                            tab_write("}\n")
    
                            if uv_layer:
                                tab_write("uv_indices {\n")
                                tab_write("%d" % (len(me_faces)))  # faces count
                                tab_str = tab * tab_level
                                for f in me_faces:
    
                                    uvs = [uv_layer[loop_index].uv[:] for loop_index in f.loops]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
                                    if linebreaksinlists:
                                        file.write(",\n")
                                        file.write(
                                            tab_str
                                            + "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
                                    else:
                                        file.write(", ")
                                        file.write(
                                            "<%d,%d,%d>"
                                            % (
                                                uniqueUVs[uvs[0]][0],
                                                uniqueUVs[uvs[1]][0],
                                                uniqueUVs[uvs[2]][0],
                                            )
                                        )
    
                                file.write("\n")
                                tab_write("}\n")
    
                            # XXX BOOLEAN
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            if me.materials:
                                try:
                                    material = me.materials[0]  # dodgy
    
                                    write_object_material_interior(material, ob, tab_write)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                except IndexError:
                                    print(me)