Skeleton impurities (roots,effectors) now included by default, zaa_reader and msh_reader combined in chunked_file_reader, skin and skeleton parentage issues worked out. TODO: Fill material properties on import, decide what to do with SkeletonProperties.

This commit is contained in:
William Herald Snyder
2022-01-07 13:45:50 -05:00
parent c320310084
commit 5eea77adf3
8 changed files with 436 additions and 388 deletions

View File

@@ -5,17 +5,34 @@ from typing import Dict
from .msh_scene import Scene
from .msh_model import *
from .msh_material import *
from .msh_reader import Reader
from .msh_utilities import *
from .crc import *
from .chunked_file_reader import Reader
# Current model position
model_counter = 0
mndx_remap = {}
# Used to remap MNDX to the MODL's actual position
mndx_remap : Dict[int, int] = {}
# How much to print
debug_level = 0
def read_scene(input_file, anim_only=False) -> Scene:
'''
Debug levels just indicate how much info should be printed.
0 = nothing
1 = just blurbs about valuable info in the chunks
2 = #1 + full chunk structure
'''
def read_scene(input_file, anim_only=False, debug=0) -> Scene:
global debug_level
debug_level = debug
scene = Scene()
scene.models = []
@@ -27,54 +44,58 @@ def read_scene(input_file, anim_only=False) -> Scene:
global model_counter
model_counter = 0
with Reader(file=input_file, debug=True) as hedr:
with Reader(file=input_file, debug=debug_level>0) as head:
while hedr.could_have_child():
head.skip_until("HEDR")
next_header = hedr.peak_next_header()
with head.read_child() as hedr:
if next_header == "MSH2":
while hedr.could_have_child():
with hedr.read_child() as msh2:
next_header = hedr.peak_next_header()
if next_header == "MSH2":
with hedr.read_child() as msh2:
if not anim_only:
materials_list = []
while (msh2.could_have_child()):
next_header = msh2.peak_next_header()
if next_header == "SINF":
with msh2.read_child() as sinf:
pass
elif next_header == "MATL":
with msh2.read_child() as matl:
materials_list += _read_matl_and_get_materials_list(matl)
for i,mat in enumerate(materials_list):
scene.materials[mat.name] = mat
elif next_header == "MODL":
with msh2.read_child() as modl:
scene.models.append(_read_modl(modl, materials_list))
else:
msh2.skip_bytes(1)
elif next_header == "SKL2":
with hedr.read_child() as skl2:
num_bones = skl2.read_u32()
scene.skeleton = [skl2.read_u32(5)[0] for i in range(num_bones)]
if not anim_only:
materials_list = []
elif next_header == "ANM2":
with hedr.read_child() as anm2:
scene.animation = _read_anm2(anm2)
while (msh2.could_have_child()):
else:
hedr.skip_bytes(1)
next_header = msh2.peak_next_header()
if next_header == "SINF":
with msh2.read_child() as sinf:
pass
elif next_header == "MATL":
with msh2.read_child() as matl:
materials_list += _read_matl_and_get_materials_list(matl)
for i,mat in enumerate(materials_list):
scene.materials[mat.name] = mat
elif next_header == "MODL":
with msh2.read_child() as modl:
scene.models.append(_read_modl(modl, materials_list))
else:
msh2.skip_bytes(1)
elif next_header == "SKL2":
with hedr.read_child() as skl2:
num_bones = skl2.read_u32()
scene.skeleton = [skl2.read_u32(5)[0] for i in range(num_bones)]
elif next_header == "ANM2":
with hedr.read_child() as anm2:
scene.animation = _read_anm2(anm2)
else:
hedr.skip_bytes(1)
if scene.skeleton:
# Print models in skeleton
if scene.skeleton and debug_level > 0:
print("Skeleton models: ")
for model in scene.models:
for i in range(len(scene.skeleton)):
@@ -84,7 +105,11 @@ def read_scene(input_file, anim_only=False) -> Scene:
scene.skeleton.pop(i)
break
'''
Iterate through every vertex weight in the scene and
change its index to directly reference its bone's index.
It will reference the MNDX of its bone's MODL by default.
'''
for model in scene.models:
if model.geometry:
for seg in model.geometry:
@@ -173,7 +198,7 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
index = mndx.read_u32()
global model_counter
print(mndx.indent + "MNDX doesn't match counter, expected: {} found: {}".format(model_counter, index))
#print(mndx.indent + "MNDX doesn't match counter, expected: {} found: {}".format(model_counter, index))
global mndx_remap
mndx_remap[index] = model_counter
@@ -203,6 +228,7 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
with modl.read_child() as geom:
while geom.could_have_child():
#print("Searching for next seg or envl child..")
next_header_geom = geom.peak_next_header()
if next_header_geom == "SEGM":
@@ -239,7 +265,9 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model:
else:
modl.skip_bytes(1)
print(modl.indent + "Read model " + model.name + " of type: " + str(model.model_type)[10:])
global debug_level
if debug_level > 0:
print(modl.indent + "Read model " + model.name + " of type: " + str(model.model_type)[10:])
return model
@@ -253,7 +281,9 @@ def _read_tran(tran: Reader) -> ModelTransform:
xform.rotation = tran.read_quat()
xform.translation = tran.read_vec()
print(tran.indent + "Rot: {} Loc: {}".format(str(xform.rotation), str(xform.translation)))
global debug_level
if debug_level > 0:
print(tran.indent + "Rot: {} Loc: {}".format(str(xform.rotation), str(xform.translation)))
return xform
@@ -301,12 +331,16 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
geometry_seg.texcoords.append(Vector(uv0l.read_f32(2)))
elif next_header == "NDXL":
with segm.read_child() as ndxl:
pass
'''
num_polygons = ndxl.read_u32()
for _ in range(num_polygons):
polygon = ndxl.read_u16(ndxl.read_u16())
geometry_seg.polygons.append(polygon)
'''
elif next_header == "NDXT":
with segm.read_child() as ndxt:
@@ -374,6 +408,7 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment:
geometry_seg.weights.append(weight_set)
else:
#print("Skipping...")
segm.skip_bytes(1)
return geometry_seg
@@ -390,7 +425,8 @@ def _read_anm2(anm2: Reader) -> Animation:
if next_header == "CYCL":
with anm2.read_child() as cycl:
pass
# Dont even know what CYCL's data does. Tried playing
# with the values but didn't change anything in zenasset or ingame...
'''
num_anims = cycl.read_u32()
@@ -399,15 +435,19 @@ def _read_anm2(anm2: Reader) -> Animation:
cycl.skip_bytes(64)
print("CYCL play style {}".format(cycl.read_u32(4)[1]))
'''
pass
elif next_header == "KFR3":
with anm2.read_child() as kfr3:
num_bones = kfr3.read_u32()
bone_crcs = []
for _ in range(num_bones):
bone_crc = kfr3.read_u32()
bone_crcs.append(bone_crc)
frames = ([],[])
@@ -423,6 +463,18 @@ def _read_anm2(anm2: Reader) -> Animation:
frames[1].append(RotationFrame(kfr3.read_u32(), kfr3.read_quat()))
anim.bone_frames[bone_crc] = frames
for bone_crc in sorted(bone_crcs):
global debug_level
if debug_level > 0:
print("\t{}: ".format(hex(bone_crc)))
bone_frames = anim.bone_frames[bone_crc]
loc_frames = bone_frames[0]
rot_frames = bone_frames[1]
else:
anm2.skip_bytes(1)