Skip to content
Snippets Groups Projects
scripting.py 24.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    """Support POV Scene Description Language snippets or full includes: import,
    
    load, create or edit"""
    
    import bpy
    from bpy.props import StringProperty, BoolProperty, CollectionProperty
    from bpy_extras.io_utils import ImportHelper
    
    from bpy.utils import register_class, unregister_class
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    from mathutils import Vector
    
    from math import pi, sqrt
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    def export_custom_code(file):
    
        """write all POV user defined custom code to exported file"""
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        # 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')
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        for txt in bpy.data.texts:
    
            if txt.pov.custom_code == "both":
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                # Why are the newlines needed?
                file.write("\n")
                file.write(txt.as_string())
                file.write("\n")
    
    
    
    # ----------------------------------- IMPORT
    
    class SCENE_OT_POV_Import(bpy.types.Operator, ImportHelper):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        """Load Povray files"""
    
        bl_idname = "import_scene.pov"
        bl_label = "POV-Ray files (.pov/.inc)"
    
        bl_options = {"PRESET", "UNDO"}
        COMPAT_ENGINES = {"POVRAY_RENDER"}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        # -----------
        # File props.
        files: CollectionProperty(
    
            type=bpy.types.OperatorFileListElement, options={"HIDDEN", "SKIP_SAVE"}
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        )
    
        directory: StringProperty(maxlen=1024, subtype="FILE_PATH", options={"HIDDEN", "SKIP_SAVE"})
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        filename_ext = {".pov", ".inc"}
    
        filter_glob: StringProperty(default="*.pov;*.inc", options={"HIDDEN"})
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
        import_at_cur: BoolProperty(
            name="Import at Cursor Location", description="Ignore Object Matrix", default=False
        )
    
        def execute(self, context):
            from mathutils import Matrix
    
            verts = []
            faces = []
            materials = []
    
            blend_mats = []  # XXX
            pov_mats = []  # XXX
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            colors = []
            mat_names = []
            lenverts = None
            lenfaces = None
            suffix = -1
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            name_search = False
            verts_search = False
            faces_search = False
            plane_search = False
            box_search = False
            cylinder_search = False
            sphere_search = False
            cone_search = False
    
            tex_search = False  # XXX
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            cache = []
            matrixes = {}
            write_matrix = False
            index = None
            value = None
    
            # file_pov = bpy.path.abspath(self.filepath) # was used for single files
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
            def mat_search(cache):
                r = g = b = 0.5
                f = t = 0
                color = None
                for item, value in enumerate(cache):
    
                    # if value == 'texture': # add more later
    
                        # Todo: create function for all color models.
                        # instead of current pass statements
                        # distinguish srgb from rgb into blend option
    
                        if cache[item + 2] in {"rgb", "srgb"}:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            pass
    
                        elif cache[item + 2] in {"rgbf", "srgbf"}:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            pass
    
                        elif cache[item + 2] in {"rgbt", "srgbt"}:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            try:
                                r, g, b, t = (
                                    float(cache[item + 3]),
                                    float(cache[item + 4]),
                                    float(cache[item + 5]),
                                    float(cache[item + 6]),
                                )
                            except BaseException as e:
                                print(e.__doc__)
    
                                print("An exception occurred: {}".format(e))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                r = g = b = t = float(cache[item + 2])
                            color = (r, g, b, t)
    
    
                        elif cache[item + 2] in {"rgbft", "srgbft"}:
    
                if colors == [] or color not in colors:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    colors.append(color)
                    name = ob.name + "_mat"
                    mat_names.append(name)
                    mat = bpy.data.materials.new(name)
                    mat.diffuse_color = (r, g, b)
    
                    mat.pov.alpha = 1 - t
                    if mat.pov.alpha != 1:
                        mat.pov.use_transparency = True
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    ob.data.materials.append(mat)
    
                else:
                    for i, value in enumerate(colors):
                        if color == value:
                            ob.data.materials.append(bpy.data.materials[mat_names[i]])
    
            for file in self.files:
                print("Importing file: " + file.name)
                file_pov = self.directory + file.name
    
                # Ignore any non unicode character
    
                with open(file_pov, 'r', encoding='utf-8', errors="ignore") as infile:
                    for line in infile:
                        string = line.replace("{", " ")
                        string = string.replace("}", " ")
                        string = string.replace("<", " ")
                        string = string.replace(">", " ")
                        string = string.replace(",", " ")
                        lw = string.split()
                        # lenwords = len(lw) # Not used... why written?
                        if lw:
                            if lw[0] == "object":
                                write_matrix = True
                            if write_matrix:
                                if lw[0] not in {"object", "matrix"}:
                                    index = lw[0]
                                if lw[0] in {"matrix"}:
                                    value = [
                                        float(lw[1]),
                                        float(lw[2]),
                                        float(lw[3]),
                                        float(lw[4]),
                                        float(lw[5]),
                                        float(lw[6]),
                                        float(lw[7]),
                                        float(lw[8]),
                                        float(lw[9]),
                                        float(lw[10]),
                                        float(lw[11]),
                                        float(lw[12]),
                                    ]
                                    matrixes[index] = value
                                    write_matrix = False
                with open(file_pov, 'r', encoding='utf-8', errors="ignore") as infile:
                    for line in infile:
                        S = line.replace("{", " { ")
                        S = S.replace("}", " } ")
                        S = S.replace(",", " ")
                        S = S.replace("<", "")
                        S = S.replace(">", " ")
                        S = S.replace("=", " = ")
                        S = S.replace(";", " ; ")
                        S = S.split()
                        # lenS = len(S) # Not used... why written?
                        for word in S:
                            # -------- Primitives Import -------- #
                            if word == "cone":
                                cone_search = True
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                name_search = False
    
                            if cone_search:
                                cache.append(word)
                                if cache[-1] == "}":
                                    try:
                                        x0 = float(cache[2])
                                        y0 = float(cache[3])
                                        z0 = float(cache[4])
                                        r0 = float(cache[5])
                                        x1 = float(cache[6])
                                        y1 = float(cache[7])
                                        z1 = float(cache[8])
                                        r1 = float(cache[9])
                                        # Y is height in most pov files, not z
                                        bpy.ops.pov.addcone(base=r0, cap=r1, height=(y1 - y0))
                                        ob = context.object
                                        ob.location = (x0, y0, z0)
                                        # ob.scale = (r,r,r)
                                        mat_search(cache)
                                    except ValueError:
                                        pass
                                    cache = []
                                    cone_search = False
                            if word == "plane":
                                plane_search = True
                                name_search = False
                            if plane_search:
                                cache.append(word)
                                if cache[-1] == "}":
                                    try:
                                        bpy.ops.pov.addplane()
                                        ob = context.object
                                        mat_search(cache)
                                    except ValueError:
                                        pass
                                    cache = []
                                    plane_search = False
                            if word == "box":
                                box_search = True
                                name_search = False
                            if box_search:
                                cache.append(word)
                                if cache[-1] == "}":
                                    try:
                                        x0 = float(cache[2])
                                        y0 = float(cache[3])
                                        z0 = float(cache[4])
                                        x1 = float(cache[5])
                                        y1 = float(cache[6])
                                        z1 = float(cache[7])
                                        # imported_corner_1=(x0, y0, z0)
                                        # imported_corner_2 =(x1, y1, z1)
                                        center = ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2)
                                        bpy.ops.pov.addbox()
                                        ob = context.object
                                        ob.location = center
                                        mat_search(cache)
    
                                    except ValueError:
                                        pass
                                    cache = []
                                    box_search = False
                            if word == "cylinder":
                                cylinder_search = True
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                name_search = False
    
                            if cylinder_search:
                                cache.append(word)
                                if cache[-1] == "}":
                                    try:
                                        x0 = float(cache[2])
                                        y0 = float(cache[3])
                                        z0 = float(cache[4])
                                        x1 = float(cache[5])
                                        y1 = float(cache[6])
                                        z1 = float(cache[7])
                                        imported_cyl_loc = (x0, y0, z0)
                                        imported_cyl_loc_cap = (x1, y1, z1)
    
                                        r = float(cache[8])
    
                                        vec = Vector(imported_cyl_loc_cap) - Vector(imported_cyl_loc)
                                        depth = vec.length
                                        rot = Vector((0, 0, 1)).rotation_difference(
                                            vec
                                        )  # Rotation from Z axis.
                                        trans = rot @ Vector(  # XXX Not used, why written?
                                            (0, 0, depth / 2)
                                        )  # Such that origin is at center of the base of the cylinder.
                                        # center = ((x0 + x1)/2,(y0 + y1)/2,(z0 + z1)/2)
                                        scale_z = sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2) / 2
                                        bpy.ops.pov.addcylinder(
                                            R=r,
                                            imported_cyl_loc=imported_cyl_loc,
                                            imported_cyl_loc_cap=imported_cyl_loc_cap,
    
                                        ob = context.object
                                        ob.location = (x0, y0, z0)
                                        # todo: test and search where to add the below currently commented
                                        # since Blender defers the evaluation until the results are needed.
                                        # bpy.context.view_layer.update()
                                        # as explained here: https://docs.blender.org/api/current/info_gotcha.html?highlight=gotcha#no-updates-after-setting-values
                                        ob.rotation_euler = rot.to_euler()
                                        ob.scale = (1, 1, scale_z)
    
                                        # scale data rather than obj?
                                        # bpy.ops.object.mode_set(mode='EDIT')
                                        # bpy.ops.mesh.reveal()
                                        # bpy.ops.mesh.select_all(action='SELECT')
                                        # bpy.ops.transform.resize(value=(1,1,scale_z), orient_type='LOCAL')
                                        # bpy.ops.mesh.hide(unselected=False)
                                        # bpy.ops.object.mode_set(mode='OBJECT')
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                        mat_search(cache)
    
    
                                    except ValueError:
                                        pass
                                    cache = []
                                    cylinder_search = False
                            if word == "sphere":
                                sphere_search = True
                                name_search = False
                            if sphere_search:
                                cache.append(word)
                                if cache[-1] == "}":
                                    x = y = z = r = 0
                                    try:
                                        x = float(cache[2])
                                        y = float(cache[3])
                                        z = float(cache[4])
                                        r = float(cache[5])
    
                                    except ValueError:
                                        pass
                                    except BaseException as e:
                                        print(e.__doc__)
                                        print("An exception occurred: {}".format(e))
                                        x = y = z = float(cache[2])
                                        r = float(cache[3])
                                    bpy.ops.pov.addsphere(R=r, imported_loc=(x, y, z))
                                    ob = context.object
                                    ob.location = (x, y, z)
                                    ob.scale = (r, r, r)
                                    mat_search(cache)
                                    cache = []
                                    sphere_search = False
                            # -------- End Primitives Import -------- #
                            if word == "#declare":
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                name_search = True
    
                            if name_search:
                                cache.append(word)
                                if word == "mesh2":
                                    name_search = False
                                    if cache[-2] == "=":
                                        name = cache[-3]
                                    else:
                                        suffix += 1
                                    cache = []
                                if word in {"texture", ";"}:
                                    name_search = False
                                    cache = []
                            if word == "vertex_vectors":
                                verts_search = True
                            if verts_search:
                                cache.append(word)
                                if word == "}":
                                    verts_search = False
                                    lenverts = cache[2]
                                    cache.pop()
                                    cache.pop(0)
                                    cache.pop(0)
                                    cache.pop(0)
                                    for j in range(int(lenverts)):
                                        x = j * 3
                                        y = (j * 3) + 1
                                        z = (j * 3) + 2
                                        verts.append((float(cache[x]), float(cache[y]), float(cache[z])))
                                    cache = []
                            # if word == 'face_indices':
                            # faces_search = True
                            if word == "texture_list":  # XXX
                                tex_search = True  # XXX
                            if tex_search:  # XXX
                                if (
                                    word not in {"texture_list", "texture", "{", "}", "face_indices"}
                                    and not word.isdigit()
                                ):  # XXX
                                    pov_mats.append(word)  # XXX
                            if word == "face_indices":
                                tex_search = False  # XXX
                                faces_search = True
                            if faces_search:
                                cache.append(word)
                                if word == "}":
                                    faces_search = False
                                    lenfaces = cache[2]
                                    cache.pop()
                                    cache.pop(0)
                                    cache.pop(0)
                                    cache.pop(0)
                                    lf = int(lenfaces)
                                    var = int(len(cache) / lf)
                                    for k in range(lf):
                                        if var == 3:
                                            v0 = k * 3
                                            v1 = k * 3 + 1
                                            v2 = k * 3 + 2
                                            faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
                                        if var == 4:
                                            v0 = k * 4
                                            v1 = k * 4 + 1
                                            v2 = k * 4 + 2
                                            m = k * 4 + 3
                                            materials.append((int(cache[m])))
                                            faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
                                        if var == 6:
                                            v0 = k * 6
                                            v1 = k * 6 + 1
                                            v2 = k * 6 + 2
                                            m0 = k * 6 + 3
                                            m1 = k * 6 + 4
                                            m2 = k * 6 + 5
                                            materials.append(
                                                (int(cache[m0]), int(cache[m1]), int(cache[m2]))
                                            )
                                            faces.append((int(cache[v0]), int(cache[v1]), int(cache[v2])))
                                    # mesh = pov_define_mesh(None, verts, [], faces, name, hide_geometry=False)
                                    # ob = object_utils.object_data_add(context, mesh, operator=None)
    
                                    me = bpy.data.meshes.new(name)  # XXX
                                    ob = bpy.data.objects.new(name, me)  # XXX
                                    bpy.context.collection.objects.link(ob)  # XXX
                                    me.from_pydata(verts, [], faces)  # XXX
    
                                    for mat in bpy.data.materials:  # XXX
                                        blend_mats.append(mat.name)  # XXX
                                    for m_name in pov_mats:  # XXX
                                        if m_name not in blend_mats:  # XXX
                                            bpy.data.materials.new(m_name)  # XXX
                                            mat_search(cache)
                                        ob.data.materials.append(bpy.data.materials[m_name])  # XXX
                                    if materials:  # XXX
                                        for idx, val in enumerate(materials):  # XXX
                                            try:  # XXX
                                                ob.data.polygons[idx].material_index = val  # XXX
                                            except TypeError:  # XXX
                                                ob.data.polygons[idx].material_index = int(val[0])  # XXX
    
                                    blend_mats = []  # XXX
                                    pov_mats = []  # XXX
                                    materials = []  # XXX
                                    cache = []
                                    name_search = True
                                    if name in matrixes and not self.import_at_cur:
                                        global_matrix = Matrix.Rotation(pi / 2.0, 4, "X")
                                        ob = bpy.context.object
                                        matrix = ob.matrix_world
                                        v = matrixes[name]
                                        matrix[0][0] = v[0]
                                        matrix[1][0] = v[1]
                                        matrix[2][0] = v[2]
                                        matrix[0][1] = v[3]
                                        matrix[1][1] = v[4]
                                        matrix[2][1] = v[5]
                                        matrix[0][2] = v[6]
                                        matrix[1][2] = v[7]
                                        matrix[2][2] = v[8]
                                        matrix[0][3] = v[9]
                                        matrix[1][3] = v[10]
                                        matrix[2][3] = v[11]
                                        matrix = global_matrix * ob.matrix_world
                                        ob.matrix_world = matrix
                                    verts = []
                                    faces = []
    
                            # if word == 'pigment':
                            # try:
                            # #all indices have been incremented once to fit a bad test file
                            # r,g,b,t = float(S[2]),float(S[3]),float(S[4]),float(S[5])
                            # color = (r,g,b,t)
    
                            # except IndexError:
                            # #all indices have been incremented once to fit alternate test file
                            # r,g,b,t = float(S[3]),float(S[4]),float(S[5]),float(S[6])
                            # color = (r,g,b,t)
                            # except UnboundLocalError:
                            # # In case no transmit is specified ? put it to 0
                            # r,g,b,t = float(S[2]),float(S[3]),float(S[4],0)
                            # color = (r,g,b,t)
    
                            # except ValueError:
                            # color = (0.8,0.8,0.8,0)
                            # pass
    
                            # if colors == [] or (colors != [] and color not in colors):
                            # colors.append(color)
                            # name = ob.name+"_mat"
                            # mat_names.append(name)
                            # mat = bpy.data.materials.new(name)
                            # mat.diffuse_color = (r,g,b)
                            # mat.pov.alpha = 1-t
                            # if mat.pov.alpha != 1:
                            # mat.pov.use_transparency=True
                            # ob.data.materials.append(mat)
                            # print (colors)
                            # else:
                            # for m in range(len(colors)):
                            # if color == colors[m]:
                            # ob.data.materials.append(bpy.data.materials[mat_names[m]])
    
            # To keep Avogadro Camera angle:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            # for obj in bpy.context.view_layer.objects:
            # if obj.type == "CAMERA":
            # track = obj.constraints.new(type = "TRACK_TO")
            # track.target = ob
            # track.track_axis ="TRACK_NEGATIVE_Z"
            # track.up_axis = "UP_Y"
            # obj.location = (0,0,0)
    
    classes = (SCENE_OT_POV_Import,)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    def register():
    
        for cls in classes:
            register_class(cls)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    
    def unregister():
    
        for cls in reversed(classes):
            unregister_class(cls)