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
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
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
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
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
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
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
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
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
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
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