157 lines
5.0 KiB
Python
157 lines
5.0 KiB
Python
import struct
|
|
|
|
IMSH_MAGIC = b"IMSH"
|
|
IMSH_VERSION = 2
|
|
|
|
IMSH_HEADER_STRUCT = struct.Struct("<4sIIII" + "IHH" + "I" + "Q")
|
|
IMSH_SUBMESH_STRUCT = struct.Struct("<IIQ" + "IHH" + "ffffff" + "IIQ")
|
|
IMSH_LOD_STRUCT = struct.Struct("<IIQQ")
|
|
|
|
IMSH_HEADER_SIZE = IMSH_HEADER_STRUCT.size
|
|
IMSH_SUBMESH_SIZE = IMSH_SUBMESH_STRUCT.size
|
|
IMSH_LOD_SIZE = IMSH_LOD_STRUCT.size
|
|
|
|
MODEL_VERTEX_STRIDE = 48
|
|
|
|
def _h_from(value, typ, meta):
|
|
return (int(value) & 0xFFFFFFFF, int(typ) & 0xFFFF, int(meta) & 0xFFFF)
|
|
|
|
def _h_is_zero(h):
|
|
return (h[0] | h[1] | h[2]) == 0
|
|
|
|
def load_imesh_from_bytes(data, base_off=0):
|
|
n = len(data)
|
|
if base_off < 0 or base_off > n:
|
|
raise ValueError("IMSH: bad base_off")
|
|
if base_off + IMSH_HEADER_SIZE > n:
|
|
raise ValueError("IMSH: too small")
|
|
|
|
(magic, version, flags, submesh_count, reserved0,
|
|
model_value, model_type, model_meta,
|
|
_pad0,
|
|
submesh_table_offset) = IMSH_HEADER_STRUCT.unpack_from(data, base_off)
|
|
|
|
if magic != IMSH_MAGIC:
|
|
raise ValueError("IMSH: bad magic")
|
|
if version != IMSH_VERSION:
|
|
raise ValueError("IMSH: bad version")
|
|
|
|
submesh_table_offset = int(submesh_table_offset)
|
|
if submesh_table_offset < 0 or submesh_table_offset >= (n - base_off):
|
|
raise ValueError("IMSH: submesh table offset out of range")
|
|
|
|
smt_off = base_off + submesh_table_offset
|
|
need = smt_off + int(submesh_count) * IMSH_SUBMESH_SIZE
|
|
if need > n:
|
|
raise ValueError("IMSH: submesh table out of range")
|
|
|
|
handle = _h_from(model_value, model_type, model_meta)
|
|
|
|
refs = []
|
|
submeshes = []
|
|
|
|
for i in range(int(submesh_count)):
|
|
o = smt_off + i * IMSH_SUBMESH_SIZE
|
|
|
|
(sm_flags,
|
|
material_name_len,
|
|
material_name_offset,
|
|
mat_value, mat_type, mat_meta,
|
|
aabb_min_x, aabb_min_y, aabb_min_z,
|
|
aabb_max_x, aabb_max_y, aabb_max_z,
|
|
lod_count,
|
|
sm_reserved0,
|
|
lods_offset) = IMSH_SUBMESH_STRUCT.unpack_from(data, o)
|
|
|
|
material_name_len = int(material_name_len)
|
|
material_name_offset = int(material_name_offset)
|
|
lod_count = int(lod_count)
|
|
lods_offset = int(lods_offset)
|
|
|
|
mat_handle = _h_from(mat_value, mat_type, mat_meta)
|
|
if not _h_is_zero(mat_handle):
|
|
refs.append(mat_handle)
|
|
else:
|
|
mat_handle = None
|
|
|
|
mat_name = None
|
|
if material_name_len > 0:
|
|
mo = base_off + material_name_offset
|
|
me = mo + material_name_len
|
|
if mo < base_off or me > n:
|
|
raise ValueError("IMSH: material name out of range")
|
|
mat_name = data[mo:me].decode("utf-8", errors="ignore")
|
|
|
|
if lod_count <= 0:
|
|
raise ValueError("IMSH: lod_count=0")
|
|
|
|
lods_abs = base_off + lods_offset
|
|
lods_need = lods_abs + lod_count * IMSH_LOD_SIZE
|
|
if lods_abs < base_off or lods_need > n:
|
|
raise ValueError("IMSH: lod table out of range")
|
|
|
|
lods = []
|
|
for li in range(lod_count):
|
|
lo = lods_abs + li * IMSH_LOD_SIZE
|
|
vcount, icount, voff, ioff = IMSH_LOD_STRUCT.unpack_from(data, lo)
|
|
|
|
vcount = int(vcount)
|
|
icount = int(icount)
|
|
voff = int(voff)
|
|
ioff = int(ioff)
|
|
|
|
if vcount <= 0 or icount <= 0:
|
|
raise ValueError("IMSH: empty lod")
|
|
|
|
vb = base_off + voff
|
|
ib = base_off + ioff
|
|
vbytes = vcount * MODEL_VERTEX_STRIDE
|
|
ibytes = icount * 4
|
|
|
|
if vb < base_off or vb + vbytes > n:
|
|
raise ValueError("IMSH: vertices out of range")
|
|
if ib < base_off or ib + ibytes > n:
|
|
raise ValueError("IMSH: indices out of range")
|
|
|
|
lods.append({
|
|
"vertex_count": vcount,
|
|
"index_count": icount,
|
|
"vertices_offset": voff,
|
|
"indices_offset": ioff,
|
|
"vertices_size": vbytes,
|
|
"indices_size": ibytes,
|
|
"vertex_stride": MODEL_VERTEX_STRIDE,
|
|
})
|
|
|
|
submeshes.append({
|
|
"flags": int(sm_flags),
|
|
"material_handle": mat_handle,
|
|
"material_name": mat_name,
|
|
"aabb_min": (float(aabb_min_x), float(aabb_min_y), float(aabb_min_z)),
|
|
"aabb_max": (float(aabb_max_x), float(aabb_max_y), float(aabb_max_z)),
|
|
"lod_count": lod_count,
|
|
"lods_offset": lods_offset,
|
|
"lods": lods,
|
|
})
|
|
|
|
info = {
|
|
"version": int(version),
|
|
"flags": int(flags),
|
|
"submesh_count": int(submesh_count),
|
|
"submesh_table_offset": int(submesh_table_offset),
|
|
"decode_offset": int(base_off),
|
|
"header_size": int(IMSH_HEADER_SIZE),
|
|
"submesh_record_size": int(IMSH_SUBMESH_SIZE),
|
|
"lod_record_size": int(IMSH_LOD_SIZE),
|
|
"vertex_stride": int(MODEL_VERTEX_STRIDE),
|
|
}
|
|
|
|
return {
|
|
"handle": handle,
|
|
"refs": refs,
|
|
"submeshes": submeshes,
|
|
"info": info,
|
|
"blob": data,
|
|
"base_off": int(base_off),
|
|
}
|