예제 #1
0
def spiderman(bsp_path):
    tag = sbsp_def.build(filepath=bsp_path)

    bsp = tag.data.tagdata
    bsp_surfaces = bsp.collision_bsp.collision_bsp_array[
        0].surfaces.surfaces_array

    for surface in bsp_surfaces:
        surface.flags.climbable = True

    tag.serialize(backup=False, temp=False)
예제 #2
0
def fix_bsp(bsp_tag_path, report_only=False):
    tag = sbsp_def.build(filepath=bsp_tag_path)

    collision_bsp = tag.data.tagdata.collision_bsp.collision_bsp_array[0]
    bsp3d_nodes = collision_bsp.bsp3d_nodes.bsp3d_nodes_array
    bsp2d_nodes = collision_bsp.bsp2d_nodes.bsp2d_nodes_array
    bsp2d_refs = collision_bsp.bsp2d_references.bsp2d_references_array
    leaves = collision_bsp.leaves.leaves_array
    planes = collision_bsp.planes.planes_array
    surfaces = collision_bsp.surfaces.surfaces_array
    edges = collision_bsp.edges.edges_array
    verts = collision_bsp.vertices.vertices_array

    def gather_bsp2d_node_surfaces(bsp2d_node_index):
        if bsp2d_node_index & 0x80000000 != 0:
            surface_index = bsp2d_node_index & 0x7FFFFFFF
            return [surface_index]
        bsp2d_node = bsp2d_nodes[bsp2d_node_index]
        left_surfaces = gather_bsp2d_node_surfaces(bsp2d_node.left_child)
        right_surfaces = gather_bsp2d_node_surfaces(bsp2d_node.right_child)
        return left_surfaces + right_surfaces

    def gather_bsp2d_ref_surfaces(bsp2d_ref_index):
        bsp2d_ref = bsp2d_refs[bsp2d_ref_index]
        return gather_bsp2d_node_surfaces(bsp2d_ref.bsp2d_node)

    def gather_leaf_surfaces(leaf_index):
        leaf = leaves[leaf_index]
        bsp2d_ref_count = leaf.bsp2d_reference_count
        bsp2d_ref_first = leaf.first_bsp2d_reference
        surfaces = []
        for r in range(bsp2d_ref_first, bsp2d_ref_first + bsp2d_ref_count):
            surfaces += gather_bsp2d_ref_surfaces(r)
        return surfaces

    def gather_bsp3d_node_surfaces(bsp3d_node_index):
        if bsp3d_node_index == -1:
            return []
        elif bsp3d_node_index & 0x80000000 != 0:
            leaf_index = bsp3d_node_index & 0x7FFFFFFF
            return gather_leaf_surfaces(leaf_index)
        bsp3d_node = bsp3d_nodes[bsp3d_node_index]
        front_surfaces = gather_bsp3d_node_surfaces(bsp3d_node.front_child)
        back_surfaces = gather_bsp3d_node_surfaces(bsp3d_node.back_child)
        return front_surfaces + back_surfaces

    def get_extended_surface_outer_edges(plane_surface_index, surface_indices):
        unvisited_surface_indices = set([plane_surface_index])
        visited_surface_indices = set()
        outer_edge_indices = set()
        surface_indices = set(surface_indices)

        while len(unvisited_surface_indices) > 0:
            surface_index = unvisited_surface_indices.pop()
            visited_surface_indices.add(surface_index)

            surface = surfaces[surface_index]
            curr_edge_index = surface.first_edge

            while True:
                curr_edge = edges[curr_edge_index]
                forward = curr_edge.left_surface == surface_index
                neighbour_surface_index = curr_edge.right_surface if forward else curr_edge.left_surface
                if neighbour_surface_index in surface_indices:
                    if neighbour_surface_index not in visited_surface_indices:
                        unvisited_surface_indices.add(neighbour_surface_index)
                else:
                    outer_edge_indices.add(curr_edge_index)
                next_edge_index = curr_edge[
                    "forward_edge" if forward else "reverse_edge"]
                if next_edge_index == surface.first_edge:
                    break
                curr_edge_index = next_edge_index

        return outer_edge_indices

    # True if the edge passes through the convex polyhedron defined by node
    # plane half-spaces. It is not considered inside of it is co-planar.
    # See: http://web.cse.ohio-state.edu/~parent.1/classes/681/Lectures/14.ObjectIntersection.pdf
    def edge_inside_polyhedron(edge_index, halfspaces):
        edge = edges[edge_index]
        edge_start = to_np_point(verts[edge.start_vertex])
        edge_end = to_np_point(verts[edge.end_vertex])

        t_entry_max = None
        t_exit_min = None

        for plane_index, is_front, _bsp3d_node in halfspaces:
            plane = to_np_plane(planes[plane_index & 0x7FFFFFFF], is_front)
            start_cmp = point_cmp_plane(edge_start, plane)
            end_cmp = point_cmp_plane(edge_end, plane)
            # If line is co-planar with one of the planes it cannot be inside the
            # volume. This thredshold is super sensitive... too high and we miss
            # phantom BSP on very similar planes to parent node planes, but too
            # low and we get false positive phantom BSP from low precision.
            if zeroish(start_cmp, 0.0001) and zeroish(end_cmp, 0.0001):
                return False
            t_intersection, entering = t_line_plane_intersection(
                edge_start, edge_end, plane)
            if t_intersection is not None:
                if entering:
                    if t_entry_max is None or t_intersection > t_entry_max:
                        t_entry_max = t_intersection
                else:
                    if t_exit_min is None or t_intersection < t_exit_min:
                        t_exit_min = t_intersection

        # If we miss the planes then there's no way it can be interecting the volume
        if t_entry_max is None or t_exit_min is None:
            return False

        entry_point = lerp_points(edge_start, edge_end, t_entry_max)
        exit_point = lerp_points(edge_start, edge_end, t_exit_min)

        # If entry and exit are essentially the same point, ignore
        if zeroish(dist(entry_point, exit_point), 0.05):
            return False

        # We still need to check if the intersection points are outside the volume
        for plane_index, is_front, _bsp3d_node in halfspaces:
            plane = to_np_plane(planes[plane_index & 0x7FFFFFFF], is_front)
            entry_cmp = point_cmp_plane(entry_point, plane)
            exit_cmp = point_cmp_plane(exit_point, plane)
            if not zeroish(entry_cmp, 0.00001) and entry_cmp < 0.0:
                return False
            if not zeroish(exit_cmp, 0.00001) and exit_cmp < 0.0:
                return False

        return t_entry_max < t_exit_min

    # Determines if the plane is not obstructed by the surfaces under this node
    def plane_unoccluded(dividing_plane_index, child_bsp3d_node_index,
                         bsp3d_node_index, parent_halfspaces):
        # meaning of flagged plane index is unknown...
        if dividing_plane_index & 0x80000000:
            dividing_plane_index = dividing_plane_index & 0x7FFFFFFF
            print("UNEXPECTED FLAGGED PLANE: Converting to " +
                  str(dividing_plane_index))

        # first we need the set of all surface indices under this node
        surface_indices = gather_bsp3d_node_surfaces(child_bsp3d_node_index)

        # Next, identify which surface was used to define parent node's plane
        plane_surface_index = None
        for s in surface_indices:
            surface = surfaces[s]
            if surface.plane & 0x7FFFFFFF == dividing_plane_index:
                plane_surface_index = s

        if plane_surface_index is None:
            return False

        # Get the bounding edges of the extended surface which was used for the plane
        outer_edge_indices = get_extended_surface_outer_edges(
            plane_surface_index, surface_indices)

        # todo: ignore cases where surrounding faces are convex

        # If any of the bounding edges pass inside the node's space, then the parent plane is exposed
        for outer_edge_index in outer_edge_indices:
            if edge_inside_polyhedron(outer_edge_index, parent_halfspaces):
                print(
                    "Phantom BSP detected: plane={} surface={} edge={} leaf={} path={}/{}"
                    .format(
                        dividing_plane_index, plane_surface_index,
                        outer_edge_index, child_bsp3d_node_index & 0x7FFFFFFF,
                        "/".join([
                            str(n & 0x7FFFFFFF)
                            for (_p, _f, n) in parent_halfspaces
                        ]), bsp3d_node_index))
                return True
        return False

    # Recurses through bsp3d nodes detecting and fixing phantom BSP
    def find_phantom_and_fix(bsp3d_node_index, parent_halfspaces):
        # Ignore leaves (flagged) and non-existent nodes (-1)
        if bsp3d_node_index & 0x80000000 != 0:
            return

        bsp3d_node = bsp3d_nodes[bsp3d_node_index]
        front_halfspaces = parent_halfspaces + [
            (bsp3d_node.plane, True, bsp3d_node_index)
        ]
        back_halfspaces = parent_halfspaces + [
            (bsp3d_node.plane, False, bsp3d_node_index)
        ]

        # Currently just checking for -1 back child and leaf front child, the
        # most common case. What a -1 front child looks like is unknown and how
        # to fix it (if it even needs fixing) is TBD with more research.
        if bsp3d_node.back_child == -1 and bsp3d_node.front_child & 0x80000000 != 0:
            if plane_unoccluded(bsp3d_node.plane, bsp3d_node.front_child,
                                bsp3d_node_index, parent_halfspaces):
                if not report_only:
                    bsp3d_node.back_child = bsp3d_node.front_child

        find_phantom_and_fix(bsp3d_node.front_child, front_halfspaces)
        # We may have set the child indices to be the same, so don't need to fix twice
        if bsp3d_node.back_child != bsp3d_node.front_child:
            find_phantom_and_fix(bsp3d_node.back_child, back_halfspaces)

    find_phantom_and_fix(0, [])

    if not report_only:
        tag.serialize(backup=False, temp=False)
예제 #3
0
def offset_level(bsp_path, scenario_path, offset):
    bsp_tag = sbsp_def.build(filepath=bsp_path)
    bsp = bsp_tag.data.tagdata
    scenario_tag = scnr_def.build(filepath=scenario_path)
    scenario = scenario_tag.data.tagdata

    for collision_bsp in bsp.collision_bsp.STEPTREE:
        for plane in collision_bsp.planes.STEPTREE:
            offset_plane(plane, offset)
        for vert in collision_bsp.vertices.STEPTREE:
            offset_point(vert, offset)

    for lightmap in bsp.lightmaps.STEPTREE:
        for material in lightmap.materials.STEPTREE:
            # material planes not included because zero length normals
            offset_point(material.centroid, offset)
            vert_count = material.vertices_count
            vert_buffer = material.uncompressed_vertices.STEPTREE
            for i in range(vert_count):
                vert_offset = i * 56
                x, y, z = vert_unpacker(vert_buffer[vert_offset:vert_offset +
                                                    12])
                vert_packer(vert_buffer, vert_offset, x + offset[0],
                            y + offset[1], z + offset[2])

    for flare_marker in bsp.lens_flare_markers.STEPTREE:
        offset_point(flare_marker.position, offset)

    for cluster in bsp.clusters.STEPTREE:
        for subcluster in cluster.subclusters.STEPTREE:
            subcluster.world_bounds_x[0] += offset[0]
            subcluster.world_bounds_x[1] += offset[0]
            subcluster.world_bounds_y[0] += offset[1]
            subcluster.world_bounds_y[1] += offset[1]
            subcluster.world_bounds_z[0] += offset[2]
            subcluster.world_bounds_z[1] += offset[2]
        for mirror in cluster.mirrors.STEPTREE:
            offset_plane(mirror.plane, offset)
            for vert in mirror.vertices.STEPTREE:
                offset_point(vert, offset)

    for cluster_portal in bsp.cluster_portals.STEPTREE:
        offset_point(cluster_portal.centroid, offset)
        for vert in cluster_portal.vertices.STEPTREE:
            offset_point(vert, offset)

    for surface in bsp.breakable_surfaces.STEPTREE:
        offset_point(surface.centroid, offset)

    for fog_plane in bsp.fog_planes.STEPTREE:
        offset_plane(fog_plane.plane, offset)
        for vert in fog_plane.vertices.STEPTREE:
            offset_point(vert, offset)

    for weather_polyhedra in bsp.weather_polyhedras.STEPTREE:
        offset_point(weather_polyhedra.bounding_sphere_center, offset)
        for plane in weather_polyhedra.planes.STEPTREE:
            offset_plane(plane, offset)

    for marker in bsp.markers.STEPTREE:
        offset_point(marker.position, offset)

    for decal in bsp.runtime_decals.STEPTREE:
        offset_point(decal.position, offset)

    for leaf_map_portal in bsp.leaf_map_portals.STEPTREE:
        for vert in leaf_map_portal.vertices.STEPTREE:
            offset_point(vert, offset)

    bsp.world_bounds_x[0] += offset[0]
    bsp.world_bounds_x[1] += offset[0]
    bsp.world_bounds_y[0] += offset[1]
    bsp.world_bounds_y[1] += offset[1]
    bsp.world_bounds_z[0] += offset[2]
    bsp.world_bounds_z[1] += offset[2]
    bsp.vehicle_floor += offset[2]
    bsp.vehicle_ceiling += offset[2]

    for player_spawn in scenario.player_starting_locations.STEPTREE:
        offset_point(player_spawn.position, offset)

    bsp_tag.serialize(backup=False, temp=False)
    scenario_tag.serialize(backup=False, temp=False)
예제 #4
0
from reclaimer.hek.defs.sbsp import sbsp_def
from inspect import getmembers
import collada
import numpy as np
from scipy.spatial.transform import Rotation as R

#https://github.com/Sigmmma/reclaimer/blob/master/reclaimer/hek/defs/coll.py
bsp_path = "./dangercanyon.scenario_structure_bsp"
bsp = sbsp_def.build(filepath=bsp_path).data.tagdata
bsp3d_nodes = bsp.collision_bsp.collision_bsp_array[0].bsp3d_nodes.bsp3d_nodes_array
bsp2d_nodes = bsp.collision_bsp.collision_bsp_array[0].bsp2d_nodes.bsp2d_nodes_array
bsp2d_references = bsp.collision_bsp.collision_bsp_array[0].bsp2d_references.bsp2d_references_array
bsp_leaves = bsp.collision_bsp.collision_bsp_array[0].leaves.leaves_array
bsp_planes = bsp.collision_bsp.collision_bsp_array[0].planes.planes_array
bsp_surfaces = bsp.collision_bsp.collision_bsp_array[0].surfaces.surfaces_array
bsp_edges = bsp.collision_bsp.collision_bsp_array[0].edges.edges_array
bsp_verts = bsp.collision_bsp.collision_bsp_array[0].vertices.vertices_array

dae = collada.Collada()

vert_floats = [[v.x, v.y, v.z] for v in bsp_verts]
normal_floats = [[p.i, p.j, p.k] for p in bsp_planes]

mtl_effect_surface = collada.material.Effect("mtl_effect_surface", [], "phong", diffuse=(0.5, 0.5, 0.5), specular=(0, 1, 0))
mtl_surface = collada.material.Material("mtl_surface", "mtl_surface", mtl_effect_surface)
dae.effects.append(mtl_effect_surface)
dae.materials.append(mtl_surface)

sfc_count = 0

def gen_surface_geometry(bsp_vert_indices, bsp_plane_index, surface_name):
예제 #5
0
def sbsp_to_mod2(sbsp_path,
                 include_lens_flares=True,
                 include_markers=True,
                 include_weather_polyhedra=True,
                 include_fog_planes=True,
                 include_portals=True,
                 include_collision=True,
                 include_renderable=True,
                 include_mirrors=True,
                 include_lightmaps=True,
                 fan_weather_polyhedra=True,
                 fan_fog_planes=True,
                 fan_portals=True,
                 fan_collision=True,
                 fan_mirrors=True,
                 optimize_fog_planes=False,
                 optimize_portals=False,
                 weather_polyhedra_tolerance=0.0000001):

    print("    Loading sbsp tag...")
    sbsp_tag = sbsp_def.build(filepath=sbsp_path)
    mod2_tag = mod2_def.build()

    sbsp_body = sbsp_tag.data.tagdata
    coll_mats = [
        JmsMaterial(mat.shader.filepath.split("\\")[-1])
        for mat in sbsp_body.collision_materials.STEPTREE
    ]

    base_nodes = [JmsNode("frame")]
    jms_models = []

    if include_markers:
        print("    Converting markers...")
        try:
            jms_models.append(
                make_marker_jms_model(sbsp_body.markers.STEPTREE, base_nodes))
        except Exception:
            print(format_exc())
            print("    Could not convert markers")

    if include_lens_flares:
        print("    Converting lens flares...")
        try:
            jms_models.append(
                make_lens_flare_jms_model(
                    sbsp_body.lens_flare_markers.STEPTREE,
                    sbsp_body.lens_flares.STEPTREE, base_nodes))
        except Exception:
            print(format_exc())
            print("    Could not convert lens flares")

    if include_fog_planes:
        print("    Converting fog planes...")
        try:
            jms_models.extend(
                make_fog_plane_jms_models(sbsp_body.fog_planes.STEPTREE,
                                          base_nodes, fan_fog_planes,
                                          optimize_fog_planes))
        except Exception:
            print(format_exc())
            print("    Could not convert fog planes")

    if include_mirrors:
        print("    Converting mirrors...")
        try:
            jms_models.extend(
                make_mirror_jms_models(sbsp_body.clusters.STEPTREE, base_nodes,
                                       fan_mirrors))
        except Exception:
            print(format_exc())
            print("    Could not convert mirrors")

    if include_portals and sbsp_body.collision_bsp.STEPTREE:
        print("    Converting portals...")
        try:
            jms_models.extend(
                make_cluster_portal_jms_models(
                    sbsp_body.collision_bsp.STEPTREE[0].planes.STEPTREE,
                    sbsp_body.clusters.STEPTREE,
                    sbsp_body.cluster_portals.STEPTREE, base_nodes,
                    fan_portals, optimize_portals))
        except Exception:
            print(format_exc())
            print("    Could not convert portals")

    if include_weather_polyhedra:
        print("    Converting weather polyhedra...")
        try:
            jms_models.extend(
                make_weather_polyhedra_jms_models(
                    sbsp_body.weather_polyhedras.STEPTREE, base_nodes,
                    fan_weather_polyhedra, weather_polyhedra_tolerance))
        except Exception:
            print(format_exc())
            print("    Could not convert weather polyhedra")

    if include_collision:
        print("    Converting collision...")
        try:
            jms_models.extend(
                make_bsp_coll_jms_models(sbsp_body.collision_bsp.STEPTREE,
                                         coll_mats, base_nodes, None, False,
                                         fan_collision))
        except Exception:
            print(format_exc())
            print("    Could not convert collision")

    if include_renderable:
        print("    Converting renderable...")
        try:
            jms_models.extend(
                make_bsp_renderable_jms_models(sbsp_body, base_nodes))
        except Exception:
            print(format_exc())
            print("    Could not convert renderable")

    if include_lightmaps:
        print("    Converting lightmaps...")
        try:
            jms_models.extend(
                make_bsp_lightmap_jms_models(sbsp_body, base_nodes))
        except Exception:
            print(format_exc())
            print("    Could not convert lightmaps")

    print("    Compiling gbxmodel...")
    mod2_tag.filepath = str(Path(sbsp_path).with_suffix('')) + "_SBSP.gbxmodel"
    compile_gbxmodel(mod2_tag, MergedJmsModel(*jms_models), True)
    return mod2_tag