Ejemplo n.º 1
0
def make_fog_plane_jms_models(fog_planes,
                              nodes,
                              make_fans=True,
                              optimize=False):
    jms_models = []
    materials = [JmsMaterial("+unused$", "<none>", "+unused$")]

    plane_index = 0
    for fog_plane in fog_planes:
        tris = edge_loop_to_tris(len(fog_plane.vertices.STEPTREE),
                                 make_fan=make_fans)
        verts = [
            JmsVertex(0,
                      vert[0] * 100,
                      vert[1] * 100,
                      vert[2] * 100,
                      tex_v=1.0) for vert in fog_plane.vertices.STEPTREE
        ]

        jms_model = JmsModel("bsp", 0, nodes, materials, (),
                             ("fog_plane_%s" % plane_index, ), verts, tris)

        if optimize:
            jms_model.optimize_geometry(True)

        jms_models.append(jms_model)
        plane_index += 1

    return jms_models
Ejemplo n.º 2
0
def make_mirror_jms_models(clusters, nodes, make_fans=True):
    jms_models = []
    mirrors = []

    for cluster in clusters:
        mirrors.extend(cluster.mirrors.STEPTREE)

    mirror_index = 0
    for mirror in mirrors:
        tris = edge_loop_to_tris(len(mirror.vertices.STEPTREE),
                                 make_fan=make_fans)
        verts = [
            JmsVertex(0,
                      vert[0] * 100,
                      vert[1] * 100,
                      vert[2] * 100,
                      tex_v=1.0) for vert in mirror.vertices.STEPTREE
        ]

        jms_models.append(
            JmsModel("bsp", 0, nodes,
                     [JmsMaterial(mirror.shader.filepath.split("\\")[-1])], (),
                     ("mirror_%s" % mirror_index, ), verts, tris))

        mirror_index += 1

    return jms_models
Ejemplo n.º 3
0
def make_bsp_coll_jms_models(bsps,
                             materials,
                             nodes,
                             node_transforms=(),
                             ignore_flags=False,
                             make_fans=True):
    jms_models = []
    bsp_index = 0
    for bsp in bsps:
        coll_edge_loops = get_bsp_surface_edge_loops(bsp, ignore_flags)
        node_transform = node_transforms[bsp_index] if node_transforms else None

        coll_materials = []
        mat_info_to_mat_id = {}
        # create materials from the provided materials and the
        # info on the collision properties of each surface.
        for mat_info in coll_edge_loops:
            src_material = materials[mat_info[0]]
            material = JmsMaterial(src_material.name)
            if not ignore_flags:
                if len(mat_info) > 1: material.double_sided = mat_info[1]
                if len(mat_info) > 2: material.large_collideable = mat_info[2]
                if len(mat_info) > 3: material.ladder = mat_info[3]
                if len(mat_info) > 4: material.breakable = mat_info[4]
                material.collision_only = not material.large_collideable
                material.double_sided &= not material.large_collideable
                material.name = material.name + material.properties
                material.shader_path = material.shader_path + material.properties
                material.properties = ""

            mat_info_to_mat_id[mat_info] = len(coll_materials)
            coll_materials.append(material)

        verts = make_bsp_jms_verts(bsp, node_transform)

        tri_count = 0
        # figure out how many triangles we'll be creating
        for mat_info in coll_edge_loops:
            for edge_loop in coll_edge_loops[mat_info]:
                tri_count += len(edge_loop) - 2

        tri_index = 0
        tris = [None] * tri_count
        # create triangles from the edge loops
        for mat_info in coll_edge_loops:
            mat_id = mat_info_to_mat_id[mat_info]
            for edge_loop in coll_edge_loops[mat_info]:
                loop_tris = edge_loop_to_tris(edge_loop,
                                              mat_id=mat_id,
                                              make_fan=make_fans)
                tris[tri_index:tri_index + len(loop_tris)] = loop_tris
                tri_index += len(loop_tris)

        jms_models.append(
            JmsModel("bsp", 0, nodes, coll_materials, [],
                     ("collision_%s" % bsp_index, ), verts, tris))
        bsp_index += 1

    return jms_models
Ejemplo n.º 4
0
def extract_physics(tagdata, tag_path="", **kw):
    do_write_jms = kw.get('write_jms', True)
    filepath = ""
    if do_write_jms:
        filepath = os.path.join(kw['out_dir'], os.path.dirname(tag_path),
                                "physics", "physics.jms")
        if not kw.get('overwrite', True) and os.path.isfile(filepath):
            return

    jms_model = JmsModel()
    child_node_ct = 1
    for mp in tagdata.mass_points.STEPTREE:
        child_node_ct = max(child_node_ct, mp.model_node + 1)
        fi, fj, fk = mp.forward
        ui, uj, uk = mp.up
        si, sj, sk = uj * fk - fj * uk, uk * fi - fk * ui, ui * fj - fi * uj

        matrix = Matrix(((fi, fj, fk), (si, sj, sk), (ui, uj, uk)))
        i, j, k, w = matrix_to_quaternion(matrix)
        # no idea why I have to invert these
        w = -w
        if w < 0:
            i, j, k, w = -i, -j, -k, -w

        jms_model.markers.append(
            JmsMarker(
                mp.name,
                "physics",
                -1,
                mp.model_node,
                i,
                j,
                k,
                w,
                mp.position.x * 100,
                mp.position.y * 100,
                mp.position.z * 100,
                mp.radius * 100,
            ))

    jms_model.nodes = generate_fake_nodes(child_node_ct)

    if do_write_jms:
        write_jms(filepath, jms_model)
    else:
        return jms_model
Ejemplo n.º 5
0
def make_bsp_renderable_jms_models(sbsp_body, base_nodes):
    jms_models = []

    lightmaps = sbsp_body.lightmaps.STEPTREE
    all_tris = sbsp_body.surfaces.STEPTREE

    shader_index_by_mat_name = {}
    mat_indices_by_mat_name = {}
    shader_mats = []
    for i in range(len(lightmaps)):
        materials = lightmaps[i].materials.STEPTREE
        for j in range(len(materials)):
            material = materials[j]
            mat_name = PureWindowsPath(material.shader.filepath).name.lower()
            mat_name += "!$" if material.flags.fog_plane else "!"

            if mat_name not in mat_indices_by_mat_name:
                shader_index_by_mat_name[mat_name] = len(shader_mats)
                shader_mats.append(JmsMaterial(mat_name))
                mat_indices_by_mat_name[mat_name] = []
                shader_mats[-1].shader_path = (shader_mats[-1].shader_path +
                                               shader_mats[-1].properties)
                shader_mats[-1].properties = ""

            mat_indices_by_mat_name[mat_name].append((i, j))

    uncomp_vert_unpacker = PyStruct("<14f").unpack_from
    for mat_name in sorted(mat_indices_by_mat_name):
        verts = []
        tris = []
        for i, j in mat_indices_by_mat_name[mat_name]:
            material = lightmaps[i].materials.STEPTREE[j]

            mat_index = shader_index_by_mat_name.get(mat_name)
            if mat_index is None:
                continue

            vert_data = material.uncompressed_vertices.data
            v_base = len(verts)

            tris.extend(
                JmsTriangle(0, mat_index, tri[0] + v_base, tri[2] +
                            v_base, tri[1] + v_base)
                for tri in all_tris[material.surfaces:material.surfaces +
                                    material.surface_count])

            for i in range(0, material.vertices_count * 56, 56):
                x, y, z, ni, nj, nk, bi, bj, bk, ti, tj, tk, u, v =\
                   uncomp_vert_unpacker(vert_data, i)
                verts.append(
                    JmsVertex(0, x * 100, y * 100, z * 100, ni, nj, nk, -1, 0,
                              u, 1 - v, 0, bi, bj, bk, ti, tj, tk))

        jms_models.append(
            JmsModel("bsp", 0, base_nodes, shader_mats, [], ("renderable", ),
                     verts, tris))

    return jms_models
Ejemplo n.º 6
0
def make_cluster_portal_jms_models(planes,
                                   clusters,
                                   cluster_portals,
                                   nodes,
                                   make_fans=True,
                                   optimize=False):
    jms_models = []
    materials = [
        JmsMaterial("+portal", "<none>", "+portal"),
        JmsMaterial("+portal&", "<none>", "+portal&")
    ]
    materials[1].properties = ""

    cluster_index = 0
    portals_seen = set()
    verts = []
    tris = []
    for cluster in clusters:
        for portal_index in cluster.portals.STEPTREE:
            if portal_index[0] in portals_seen:
                continue

            portals_seen.add(portal_index[0])
            portal = cluster_portals[portal_index[0]]
            shader = 1 if portal.flags.ai_cant_hear_through_this else 0
            portal_plane = planes[portal.plane_index]

            tris.extend(
                edge_loop_to_tris(len(portal.vertices.STEPTREE),
                                  mat_id=shader,
                                  base=len(verts),
                                  make_fan=make_fans))
            verts.extend(
                JmsVertex(
                    0, vert[0] * 100, vert[1] * 100, vert[2] * 100, tex_v=1.0)
                for vert in portal.vertices.STEPTREE)

    jms_model = JmsModel("bsp", 0, nodes, materials, (), ("cluster_portals", ),
                         verts, tris)

    if optimize:
        jms_model.optimize_geometry(True)

    jms_models.append(jms_model)
    return jms_models
Ejemplo n.º 7
0
def make_marker_jms_model(sbsp_markers, nodes):
    markers = []
    for m in sbsp_markers:
        markers.append(
            JmsMarker(m.name, "bsp", 0, 0, m.rotation.i, m.rotation.j,
                      m.rotation.k, m.rotation.w, m.position.x * 100,
                      m.position.y * 100, m.position.z * 100))

    return JmsModel("bsp", 0, nodes, [], markers, ("markers", ))
Ejemplo n.º 8
0
def make_bsp_lightmap_jms_models(sbsp_body, base_nodes):
    jms_models = []

    lightmaps = sbsp_body.lightmaps.STEPTREE
    all_tris = sbsp_body.surfaces.STEPTREE

    shader_index_by_mat_name = {}
    shader_mats = []
    for i in range(len(lightmaps)):
        lm_index = lightmaps[i].bitmap_index
        if lm_index not in shader_index_by_mat_name and lm_index >= 0:
            shader_index_by_mat_name[lm_index] = len(shader_index_by_mat_name)
            shader_mats.append(JmsMaterial("lightmap_%s" % lm_index))

    uncomp_vert_xyz_unpacker = PyStruct("<3f").unpack_from
    uncomp_vert_ijkuv_unpacker = PyStruct("<5f").unpack_from

    for lightmap in lightmaps:
        verts = []
        tris = []
        mat_index = shader_index_by_mat_name.get(lightmap.bitmap_index, -1)
        if mat_index < 0:
            continue

        for material in lightmap.materials.STEPTREE:
            v_base = len(verts)
            tris.extend(
                JmsTriangle(0, mat_index, tri[0] + v_base, tri[2] +
                            v_base, tri[1] + v_base)
                for tri in all_tris[material.surfaces:material.surfaces +
                                    material.surface_count])

            vert_off = 0
            lm_vert_off = 56 * material.vertices_count
            vert_data = material.uncompressed_vertices.data
            for i in range(material.lightmap_vertices_count):
                x, y, z = uncomp_vert_xyz_unpacker(vert_data, vert_off)
                i, j, k, u, v = uncomp_vert_ijkuv_unpacker(
                    vert_data, lm_vert_off)
                vert_off += 56
                lm_vert_off += 20
                verts.append(
                    JmsVertex(0, x * 100, y * 100, z * 100, i, j, k, -1, 0, u,
                              1 - v))

        jms_models.append(
            JmsModel("bsp", 0, base_nodes, shader_mats, [],
                     ("lightmap_%s" % lightmap.bitmap_index, ), verts, tris))

    return jms_models
Ejemplo n.º 9
0
def make_lens_flare_jms_model(lens_flare_markers, lens_flares, nodes):
    markers = []
    for m in lens_flare_markers:
        i, j, k, w = euler_to_quaternion(m.direction.i / 128,
                                         m.direction.j / 128,
                                         m.direction.k / 128)
        lens_flare_name = lens_flares[m.lens_flare_index].shader.filepath

        markers.append(
            JmsMarker(
                lens_flare_name.split("\\")[-1], "bsp", 0, 0, i, j, k, w,
                m.position.x * 100, m.position.y * 100, m.position.z * 100))

    return JmsModel("bsp", 0, nodes, [], markers, ("lens_flares", ))
Ejemplo n.º 10
0
def make_weather_polyhedra_jms_models(polyhedras,
                                      nodes,
                                      make_fans=True,
                                      tolerance=0.0000001):
    jms_models = []
    materials = [JmsMaterial("+weatherpoly", "<none>", "+weatherpoly")]

    polyhedra_index = 0
    for polyhedra in polyhedras:
        verts, tris = planes_to_verts_and_tris(
            polyhedra.planes.STEPTREE,
            polyhedra.bounding_sphere_center,
            make_fans=make_fans,
            round_adjust=Ray(polyhedra.bounding_sphere_center).mag * tolerance)

        jms_models.append(
            JmsModel("bsp", 0, nodes, materials, (),
                     ("weather_polyhedra_%s" % polyhedra_index, ), verts,
                     tris))

        polyhedra_index += 1

    return jms_models
Ejemplo n.º 11
0
def jms_model_from_dae(filepath, model_name=None):
    if collada is None:
        return

    if model_name is None:
        model_name = "__unnamed"

    collada_model = collada.Collada(filepath)
    print(collada_model.geometries)

    model = JmsModel(model_name)

    regions = model.regions
    mats = model.materials
    nodes = model.nodes
    tris = model.tris
    verts = model.verts

    for collada_mat in collada_model.materials:
        mat_name = collada_mat.name

        # correct DAE not being able to use + in material names
        if mat_name.startswith('_'):
            mat_name = mat_name.lstrip("_")
            if mat_name in special_mat_names:
                mat_name = "+" + mat_name

        mats.append(JmsMaterial(mat_name))

    if not mats:
        mats.append(JmsMaterial("__unnamed"))

    return
    # idk if i'll ever implement this. collada is just too complex

    return model
Ejemplo n.º 12
0
def jms_model_from_obj(obj_string, model_name=None):
    if not isinstance(obj_string, str):
        raise TypeError("Argument must be of type 'str', not '%s'." %
                        type(obj_string))

    if model_name is None:
        model_name = "__unnamed"

    model = JmsModel(model_name)
    model.nodes = [JmsNode("frame")]
    model.regions = ["__unnamed"]

    mats = model.materials
    tris = model.tris

    f_num = 0
    mat_num = 0
    mat_names = {}
    v_locs = []
    v_uvs = []
    v_norms = []
    v_datas = {}
    v_data_by_idx = []
    norms_to_calc = {}
    sg_error = False
    ngon_error = False

    # Reminder that obj's use 1 based indexing
    i = 0
    for line in obj_string.split("\n"):
        i += 1
        line = line.lstrip(' ')
        if not line or line[0] == "#":
            continue

        line_parts = line.replace('\t', ' ').split(' ', 1)
        if len(line_parts) < 2:
            continue

        token, line = line_parts

        if token == "v":
            loc_data = [d for d in line.split(' ') if d]
            if len(loc_data) != 3:
                raise ValueError("Invalid vertex coord on line %s." % i)
            v_locs.append(loc_data)

        elif token == "vt":
            uv_data = [d for d in line.split(' ') if d]
            if len(uv_data) not in (2, 3):
                raise ValueError("Invalid vertex texture coord on line %s." %
                                 i)
            v_uvs.append(uv_data)

        elif token == "vn":
            norm_data = [d for d in line.split(' ') if d]
            if len(norm_data) != 3:
                raise ValueError("Invalid vertex normal on line %s." % i)
            v_norms.append(norm_data)

        elif token == "f":
            tri_data = [d for d in line.split(' ') if d]
            if len(tri_data) != 3:
                ngon_error = True
                continue

            v_indices = [0, 0, 0]
            for j in range(len(tri_data)):
                v_data = tuple(tri_data[j].replace(' ', '').split('/'))
                if not len(v_data[0]):
                    raise ValueError("Invalid triangle vert description.")

                if v_data not in v_datas:
                    v_data_by_idx.append(v_data)
                    v_datas[v_data] = len(v_data_by_idx)

                v_idx = v_datas[v_data] - 1
                v_indices[j] = v_idx
                if len(v_data) < 3 or not v_data[2]:
                    norms_to_calc.setdefault(v_idx, []).append(f_num)

            tris.append(JmsTriangle(0, mat_num, *v_indices))
            f_num += 1

        elif token == "usemtl":
            name = line
            if name in mat_names:
                mat_num = mat_names[name]
            else:
                mat_num = len(mat_names)
                mats.append(JmsMaterial(name))
                mat_names[name] = mat_num

        elif token == "s" and not sg_error:
            sg_error = True

        else:
            # dont care about anything else
            pass

    if sg_error or ngon_error:
        if ngon_error:
            print("Quads and n-gons are not supported.")

        if sg_error:
            print(
                "Smoothing groups not supported! Export with vertex normals.")

        return

    model.verts = verts = [None] * len(v_data_by_idx)
    for i in range(len(v_data_by_idx)):
        v_data = v_data_by_idx[i]

        loc_idx = int(v_data[0]) - 1
        v_loc = v_locs[loc_idx]
        pos_x = float(v_loc[0])
        pos_y = float(v_loc[1])
        pos_z = float(v_loc[2])

        if len(v_data) > 1 and v_data[1]:
            v_uv = v_uvs[int(v_data[1]) - 1]
            tex_u = float(v_uv[0])
            tex_v = float(v_uv[1])
        else:
            tex_u = pos_x
            tex_v = pos_y

        if len(v_data) > 2 and v_data[2]:
            v_norm = v_norms[int(v_data[2]) - 1]
            norm_i = float(v_norm[0])
            norm_j = float(v_norm[1])
            norm_k = float(v_norm[2])
        else:
            norm_i = 1
            norm_j = norm_k = 0

        # We want to rotate the model, so we don't use the vertex coords
        # exactly as they are given(swap y and z and make z negative).
        verts[i] = JmsVertex(0, pos_x, -pos_z, pos_y, norm_i, -norm_k, norm_j,
                             -1, 0.0, tex_u, tex_v)

    sqrt = math.sqrt
    for i in norms_to_calc:
        vert = verts[i]
        i = j = k = 0
        face_ct = 0

        for f_num in norms_to_calc[i]:
            face_ct += 1
            face = tris[f_num]
            v0 = verts[face.v0]
            v1 = verts[face.v1]
            v2 = verts[face.v2]
            vax = v1.pos_x - v0.pos_x
            vay = v1.pos_y - v0.pos_y
            vaz = v1.pos_z - v0.pos_z

            vbx = v2.pos_x - v0.pos_x
            vby = v2.pos_y - v0.pos_y
            vbz = v2.pos_z - v0.pos_z

            fi = vay * vbz - vaz * vby
            fj = vaz * vbx - vax * vbz
            fk = vax * vby - vay * vbx

            mag = fi**2 + fj**2 + fk**2
            if mag > 0:
                mag = sqrt(mag)
                i += fi / mag
                j += fj / mag
                k += fk / mag

        if face_ct:
            vert.norm_i = i / face_ct
            vert.norm_j = j / face_ct
            vert.norm_k = k / face_ct

    if not mats:
        mats.append(JmsMaterial("__unnamed"))

    return model
Ejemplo n.º 13
0
def extract_model(tagdata, tag_path="", **kw):
    do_write_jms = kw.get('write_jms', True)
    if do_write_jms:
        jms_models = None
        filepath_base = os.path.join(kw['out_dir'], os.path.dirname(tag_path),
                                     "models")
    else:
        jms_models = []
        filepath_base = ""

    global_markers = {}
    materials = []
    regions = []
    nodes = []

    for b in tagdata.markers.STEPTREE:
        marker_name = b.name

        for inst in b.marker_instances.STEPTREE:
            try:
                region = tagdata.regions.STEPTREE[inst.region_index]
            except Exception:
                print("Invalid region index in marker '%s'" % marker_name)
                continue

            try:
                perm = region.permutations.STEPTREE[inst.permutation_index]
                perm_name = perm.name
                if (perm.flags.cannot_be_chosen_randomly
                        and not perm_name.startswith("~")):
                    perm_name += "~"
            except Exception:
                print("Invalid permutation index in marker '%s'" % marker_name)
                continue

            perm_markers = global_markers.setdefault(perm_name, [])

            trans = inst.translation
            rot = inst.rotation
            perm_markers.append(
                JmsMarker(marker_name, perm_name, inst.region_index,
                          inst.node_index, rot.i, rot.j, rot.k, rot.w,
                          trans.x * 100, trans.y * 100, trans.z * 100, 1.0))

    for b in tagdata.nodes.STEPTREE:
        trans = b.translation
        rot = b.rotation
        nodes.append(
            JmsNode(b.name, b.first_child_node, b.next_sibling_node, rot.i,
                    rot.j, rot.k, rot.w, trans.x * 100, trans.y * 100,
                    trans.z * 100, b.parent_node))

    for b in tagdata.shaders.STEPTREE:
        materials.append(
            JmsMaterial(b.shader.filepath.split("/")[-1].split("\\")[-1]))

    markers_by_perm = {}
    geoms_by_perm_lod_region = {}

    u_scale = tagdata.base_map_u_scale
    v_scale = tagdata.base_map_v_scale

    for region in tagdata.regions.STEPTREE:
        region_index = len(regions)
        regions.append(region.name)
        for perm in region.permutations.STEPTREE:
            perm_name = perm.name
            if (perm.flags.cannot_be_chosen_randomly
                    and not perm_name.startswith("~")):
                perm_name += "~"

            geoms_by_lod_region = geoms_by_perm_lod_region.setdefault(
                perm_name, {})

            perm_markers = markers_by_perm.setdefault(perm_name, [])
            if hasattr(perm, "local_markers"):
                for m in perm.local_markers.STEPTREE:
                    trans = m.translation
                    rot = m.rotation
                    perm_markers.append(
                        JmsMarker(m.name, perm_name, region_index,
                                  m.node_index, rot.i, rot.j, rot.k, rot.w,
                                  trans.x * 100, trans.y * 100, trans.z * 100,
                                  1.0))

            last_geom_index = -1
            for lod in range(5):
                geoms_by_region = geoms_by_lod_region.get(lod, {})
                region_geoms = geoms_by_region.get(region.name, [])

                geom_index = perm[perm.NAME_MAP["superlow_geometry_block"] +
                                  (4 - lod)]

                if (geom_index in region_geoms
                        or geom_index == last_geom_index):
                    continue

                geoms_by_lod_region[lod] = geoms_by_region
                geoms_by_region[region.name] = region_geoms
                region_geoms.append(geom_index)
                last_geom_index = geom_index

    try:
        use_local_nodes = tagdata.flags.parts_have_local_nodes
    except Exception:
        use_local_nodes = False
    def_node_map = list(range(128))
    def_node_map.append(-1)

    # use big endian since it will have been byteswapped
    comp_vert_unpacker = PyStruct(">3f3I2h2bh").unpack_from
    uncomp_vert_unpacker = PyStruct(">14f2h2f").unpack_from

    for perm_name in sorted(geoms_by_perm_lod_region):
        geoms_by_lod_region = geoms_by_perm_lod_region[perm_name]
        perm_markers = markers_by_perm.get(perm_name)

        for lod in sorted(geoms_by_lod_region):
            if lod == -1:
                continue

            jms_name = perm_name + {
                4: " superlow",
                3: " low",
                2: " medium",
                1: " high",
                0: " superhigh"
            }.get(lod, "")

            filepath = os.path.join(filepath_base, jms_name + ".jms")

            markers = list(perm_markers)
            markers.extend(global_markers.get(perm_name, ()))
            verts = []
            tris = []

            geoms_by_region = geoms_by_lod_region[lod]
            for region_name in sorted(geoms_by_region):
                region_index = regions.index(region_name)
                geoms = geoms_by_region[region_name]

                for geom_index in geoms:
                    try:
                        geom_block = tagdata.geometries.STEPTREE[geom_index]
                    except Exception:
                        print("Invalid geometry index '%s'" % geom_index)
                        continue

                    for part in geom_block.parts.STEPTREE:
                        v_origin = len(verts)
                        shader_index = part.shader_index

                        try:
                            node_map = list(part.local_nodes)
                            node_map.append(-1)
                            compressed = False
                        except (AttributeError, KeyError):
                            compressed = True

                        if not use_local_nodes:
                            node_map = def_node_map

                        try:
                            unparsed = isinstance(part.triangles.STEPTREE.data,
                                                  bytearray)
                        except Exception:
                            unparsed = False

                        # TODO: Make this work in meta(parse verts and tris)
                        try:
                            if compressed and unparsed:
                                vert_data = part.compressed_vertices.STEPTREE.data
                                for off in range(0, len(vert_data), 32):
                                    v = comp_vert_unpacker(vert_data, off)
                                    n = v[3]
                                    ni = (n & 1023) / 1023
                                    nj = ((n >> 11) & 1023) / 1023
                                    nk = ((n >> 22) & 511) / 511
                                    if (n >> 10) & 1: ni = ni - 1.0
                                    if (n >> 21) & 1: nj = nj - 1.0
                                    if (n >> 31) & 1: nk = nk - 1.0

                                    verts.append(
                                        JmsVertex(v[8] // 3, v[0] * 100,
                                                  v[1] * 100, v[2] * 100, ni,
                                                  nj, nk, v[9] // 3,
                                                  1.0 - (v[10] / 32767),
                                                  u_scale * v[6] / 32767, 1.0 -
                                                  v_scale * v[7] / 32767))
                            elif compressed:
                                for v in part.compressed_vertices.STEPTREE:
                                    n = v[3]
                                    ni = (n & 1023) / 1023
                                    nj = ((n >> 11) & 1023) / 1023
                                    nk = ((n >> 22) & 511) / 511
                                    if (n >> 10) & 1: ni = ni - 1.0
                                    if (n >> 21) & 1: nj = nj - 1.0
                                    if (n >> 31) & 1: nk = nk - 1.0

                                    verts.append(
                                        JmsVertex(v[8] // 3, v[0] * 100,
                                                  v[1] * 100, v[2] * 100, ni,
                                                  nj, nk, v[9] // 3,
                                                  1.0 - (v[10] / 32767),
                                                  u_scale * v[6] / 32767, 1.0 -
                                                  v_scale * v[7] / 32767))
                            elif not compressed and unparsed:
                                vert_data = part.uncompressed_vertices.STEPTREE.data
                                for off in range(0, len(vert_data), 68):
                                    v = uncomp_vert_unpacker(vert_data, off)
                                    verts.append(
                                        JmsVertex(node_map[v[14]], v[0] * 100,
                                                  v[1] * 100, v[2] * 100, v[3],
                                                  v[4], v[5], node_map[v[15]],
                                                  max(0, min(1, v[17])),
                                                  u_scale * v[12],
                                                  1.0 - v_scale * v[13]))
                            else:
                                for v in part.uncompressed_vertices.STEPTREE:
                                    verts.append(
                                        JmsVertex(node_map[v[14]], v[0] * 100,
                                                  v[1] * 100, v[2] * 100, v[3],
                                                  v[4], v[5], node_map[v[15]],
                                                  max(0, min(1, v[17])),
                                                  u_scale * v[12],
                                                  1.0 - v_scale * v[13]))
                        except Exception:
                            print(format_exc())
                            print("If you see this, tell Moses to stop "
                                  "f*****g with the vertex definition.")

                        try:
                            if unparsed:
                                tri_block = part.triangles.STEPTREE.data
                                tri_list = [-1] * (len(tri_block) // 2)
                                for i in range(len(tri_list)):
                                    # assuming big endian
                                    tri_list[i] = (tri_block[i * 2 + 1] +
                                                   (tri_block[i * 2] << 8))
                                    if tri_list[i] > 32767:
                                        tri_list[i] = -1
                            else:
                                tri_block = part.triangles.STEPTREE
                                tri_list = []
                                for triangle in tri_block:
                                    tri_list.extend(triangle)

                            swap = True
                            for i in range(len(tri_list) - 2):
                                v0 = tri_list[i]
                                v1 = tri_list[i + 1 + swap]
                                v2 = tri_list[i + 2 - swap]
                                if v0 != -1 and v1 != -1 and v2 != -1:
                                    # remove degens
                                    if v0 != v1 and v0 != v2 and v1 != v2:
                                        tris.append(
                                            JmsTriangle(
                                                region_index, shader_index,
                                                v0 + v_origin, v1 + v_origin,
                                                v2 + v_origin))
                                swap = not swap
                        except Exception:
                            print(format_exc())
                            print("Could not parse triangle blocks.")

            jms_model = JmsModel(jms_name, tagdata.node_list_checksum, nodes,
                                 materials, markers, regions, verts, tris)
            if do_write_jms:
                write_jms(filepath, jms_model)
            else:
                jms_models.append(jms_model)

    return jms_models