space928 / Blender-O3D-IO-Public

A plugin supporting blender 2.79.x-3.x.x for importing and exporting OMSI .sco, .cfg, and .o3d files
GNU General Public License v3.0
36 stars 5 forks source link

Handle tree replacement #76

Open github-actions[bot] opened 10 months ago

github-actions[bot] commented 10 months ago

https://api.github.com/space928/Blender-O3D-IO-Public/blob/734b4df774329c68085f978beb7d29e123b98b88/o3d_io/io_omsi_tile.py#L339


    return lerp(il, ih, y_frac)

def import_map_objects(filepath, map_file, terr_heights, import_x, parent_collection, spline_defs, loaded_objs=None):
    if loaded_objs is None:
        loaded_objs = {}
    blender_insts = []

    omsi_dir = os.path.abspath(os.path.join(os.path.dirname(filepath), os.pardir, os.pardir))
    # log("Assuming OMSI directory of: ", omsi_dir)

    objs = parse_map_data(map_file, omsi_dir)

    log("Loaded {0} objects!".format(len(objs)))

    for obj in objs:
        pos = mathutils.Vector(obj["pos"])
        path = obj["path"]
        rot = mathutils.Vector([-math.radians(x) for x in obj["rot"]]).zyx
        obj_name = os.path.basename(path)[:-4]

        if "spline" in obj:
            # Weird edge case in OMSI where spline_attachments with a negative spline distance
            if pos.z < 0 or pos.z > spline_defs[obj["spline"]].length:
                continue

        if path in loaded_objs:
            # Save time by duplicating existing objects
            container_obj, new_objs = clone_object(loaded_objs, obj_name, parent_collection, path)
            blender_insts.extend(new_objs)
        else:
            # bpy.ops.mesh.primitive_cube_add(location=pos)
            imported_objs = []
            try:
                imported_objs = io_o3d_import.do_import(path, bpy.context, import_x, "", True, False, parent_collection)
            except:
                log("Exception encountered loading: " + path)

            container_obj = bpy.data.objects.new(obj_name, None)
            parent_collection.objects.link(container_obj)

            loaded_objs[path] = [o for o in imported_objs]
            for o in imported_objs:
                o.parent = container_obj

            imported_objs.append(container_obj)

            blender_insts.extend(imported_objs)

        align_tangent = "tangent_to_spline" in obj
        if obj["tree_data"] is not None:
            # TODO: Handle tree replacement
            container_obj["tree_data"] = obj["tree_data"]
        if "attach" in obj:
            container_obj["attach"] = obj["attach"]

        # TODO: Copy across any [object] specific parameters to the container bl_object

        if "spline" in obj:
            container_obj["cfg_data"] = obj
            container_obj["spline_id"] = spline_defs[obj["spline"]].id
            container_obj["rep_distance"] = obj["rep_distance"]
            container_obj["rep_range"] = obj["rep_range"]
            container_obj["tangent_to_spline"] = obj["tangent_to_spline"]
            if "repeater_x" in obj:
                container_obj["repeater_x"] = obj["repeater_x"]
                container_obj["repeater_y"] = obj["repeater_y"]
            # log(f"Rep {obj['id']} {obj['rep_distance']} {obj['rep_range']}\t:::")
            d = obj["rep_distance"]
            while d < obj["rep_range"] and (d + pos.z < spline_defs[obj["spline"]].length):
                # Clone the object and transform it
                container_obj_rep, new_objs = clone_object(loaded_objs, obj_name, parent_collection, path)
                blender_insts.extend(new_objs)

                pos_s = pos.xzy
                pos_s += mathutils.Vector((0, d, 0))
                pos_rep, spl_rot = spline_defs[obj["spline"]].evaluate_spline(pos_s, True, True)
                if align_tangent:
                    spl_rot.x = -spl_rot.x
                    spl_rot.y = -spl_rot.y
                else:
                    spl_rot.x = 0
                    spl_rot.y = 0

                # log(f"\t\t inst:{pos_s} -> {pos_rep}")
                container_obj_rep.location = pos_rep
                container_obj_rep.rotation_euler = rot + spl_rot
                container_obj_rep["rep_parent"] = obj

                d += obj["rep_distance"]

            # Position is relative to the spline
            pos, spl_rot = spline_defs[obj["spline"]].evaluate_spline(pos.xzy, True, True)
            if align_tangent:
                spl_rot.x = -spl_rot.x
                spl_rot.y = -spl_rot.y
            else:
                spl_rot.x = 0
                spl_rot.y = 0
            rot += spl_rot
        else:
            pos.z += get_interpolated_height(terr_heights, pos.x, pos.y)

        container_obj.location = pos
        container_obj.rotation_euler = rot

    return blender_insts

def clone_object(loaded_objs, obj_name, parent_collection, path):
    blender_insts = []
    if bpy.app.version < (2, 80):
        container_obj = bpy.data.objects.new(obj_name, None)
        parent_collection.objects.link(container_obj)
        blender_insts.append(container_obj)
        for o in loaded_objs[path]:
            cop = o.copy()
            bpy.context.scene.objects.link(cop)
            parent_collection.objects.link(cop)
            cop.parent = container_obj
            blender_insts.append(cop)
    else:
        container_obj = bpy.data.objects.new(obj_name, None)
        parent_collection.objects.link(container_obj)
        blender_insts.append(container_obj)
        for o in loaded_objs[path]:
            cop = o.copy()
            parent_collection.objects.link(cop)
            cop.parent = container_obj
            blender_insts.append(cop)
    return container_obj, blender_insts

def parse_map_data(map_file, omsi_dir):
    objs = []

    if "[object]" in map_file:
        for lines in map_file["[object]"]:
            # I'm not sure what line 0, it's almost always a 0, rarely it's a 1 and only on Berlin is it ever 2
            path = lines[1]
            obj_id = int(lines[2])
            pos = [float(lines[3 + x]) for x in range(3)]
            rot = [float(lines[6 + x]) for x in range(3)]  # ZYX (Z-Up)

            try:
                obj_type = int(lines[9])
            except ValueError:
                obj_type = 0

            tree_data = None
            if obj_type == 4:
                tree_data = {
                    "texture": lines[10],
                    "height": float(lines[11]),
                    "aspect": float(lines[12])
                }
            elif obj_type == 7:
                pass  # Bus stop
            elif obj_type == 1:
                pass  # Label

            objs.append({
                "cfg_type": "object",
                "path": os.path.join(omsi_dir, path),
                "id": obj_id,
                "pos": pos, "rot": rot,
                "tree_data": tree_data
            })

    if "[attachObj]" in map_file:
        for lines in map_file["[attachObj]"]:
            path = lines[1]
            obj_id = int(lines[2])
            pos = [float(lines[4 + x]) for x in range(3)]
            rot = [float(lines[5 + x]) for x in range(3)]  # ZYX (Z-Up)

            try:
                obj_type = int(lines[10])
            except ValueError:
                obj_type = 0

            tree_data = None
            if obj_type == 4:
                tree_data = {
                    "cfg_type": "attachObj",
                    "texture": lines[11],
                    "height": float(lines[12]),
                    "aspect": float(lines[13])
                }
            elif obj_type == 7:
                pass  # Bus stop
            elif obj_type == 1:
                pass  # Label

            objs.append({
                "path": os.path.join(omsi_dir, path),
                "id": obj_id,
                "pos": pos, "rot": rot,
                "tree_data": tree_data,
                "attach": int(lines[3])
            })

    if "[splineAttachement]" in map_file:
        for lines in map_file["[splineAttachement]"]:
            try:
                obj_type = int(lines[13])
            except ValueError:
                obj_type = 0

            tree_data = None
            if obj_type == 4:
                tree_data = {
                    "texture": lines[14],
                    "height": float(lines[15]),
                    "aspect": float(lines[16])
                }
            elif obj_type == 7:
                pass  # Bus stop
            elif obj_type == 1:
                pass  # Label

            objs.append({
                "cfg_type": "splineAttachement",
                "path": os.path.join(omsi_dir, lines[1]),
                "id": int(lines[2]),
                "pos": [float(lines[4 + x]) for x in range(3)],
                "rot": [float(lines[7 + x]) for x in range(3)],
                "tree_data": tree_data,
                "spline": int(lines[3]),
                "rep_distance": float(lines[10]),
                "rep_range": float(lines[11]),
                "tangent_to_spline": int(lines[12]) == 1
            })

    if "[splineAttachement_repeater]" in map_file:
        for lines in map_file["[splineAttachement_repeater]"]:
            try:
                obj_type = int(lines[15])
            except ValueError:
                obj_type = 0

            tree_data = None
            if obj_type == 4:
                tree_data = {
                    "texture": lines[16],
                    "height": float(lines[17]),
                    "aspect": float(lines[18])
                }
            elif obj_type == 7:
                pass  # Bus stop
            elif obj_type == 1:
                pass  # Label

            objs.append({
                "cfg_type": "splineAttachement_repeater",
                "path": os.path.join(omsi_dir, lines[3]),
                "id": int(lines[4]),
                "pos": [float(lines[6 + x]) for x in range(3)],
                "rot": [float(lines[9 + x]) for x in range(3)],
                "tree_data": tree_data,
                "spline": int(lines[5]),
                "rep_distance": float(lines[12]),
                "rep_range": float(lines[13]),
                "tangent_to_spline": int(lines[14]) == 1,
                "repeater_x": int(lines[1]),  # I still don't know what these do
                "repeater_y": int(lines[2]),  # I still don't know what these do
            })

    return objs