From b749e475367d5d8ea2df292977953ec8e7c5b0b0 Mon Sep 17 00:00:00 2001 From: William Herald Snyder Date: Thu, 10 Dec 2020 23:47:45 -0500 Subject: [PATCH] Correct pose-relative transforms for impure skeletons --- addons/io_scene_swbf_msh/msh_scene_read.py | 81 ++++---- addons/io_scene_swbf_msh/msh_to_blend.py | 220 +++++++++++++-------- 2 files changed, 181 insertions(+), 120 deletions(-) diff --git a/addons/io_scene_swbf_msh/msh_scene_read.py b/addons/io_scene_swbf_msh/msh_scene_read.py index d8b68d2..ba68a10 100644 --- a/addons/io_scene_swbf_msh/msh_scene_read.py +++ b/addons/io_scene_swbf_msh/msh_scene_read.py @@ -33,7 +33,7 @@ def read_scene(input_file, anim_only=False) -> Scene: next_header = hedr.peak_next_header() - if "MSH2" in next_header: + if next_header == "MSH2": with hedr.read_child() as msh2: @@ -44,30 +44,29 @@ def read_scene(input_file, anim_only=False) -> Scene: next_header = msh2.peak_next_header() - if "SINF" in next_header: + if next_header == "SINF": with msh2.read_child() as sinf: pass - elif "MATL" in next_header: + 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 "MODL" in next_header: - while ("MODL" in msh2.peak_next_header()): - with msh2.read_child() as modl: - scene.models.append(_read_modl(modl, materials_list)) + elif next_header == "MODL": + with msh2.read_child() as modl: + scene.models.append(_read_modl(modl, materials_list)) else: msh2.skip_bytes(1) - elif "SKL2" in next_header: + 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 "ANM2" in next_header: + elif next_header == "ANM2": with hedr.read_child() as anm2: scene.animation = _read_anm2(anm2) @@ -118,36 +117,36 @@ def _read_matd(matd: Reader) -> Material: next_header = matd.peak_next_header() - if "NAME" in next_header: + if next_header == "NAME": with matd.read_child() as name: mat.name = name.read_string() - elif "DATA" in next_header: + elif next_header == "DATA": with matd.read_child() as data: data.read_f32(4) # Diffuse Color (Seams to get ignored by modelmunge) mat.specular_color = data.read_f32(4) data.read_f32(4) # Ambient Color (Seams to get ignored by modelmunge and Zero(?)) data.read_f32() # Specular Exponent/Decay (Gets ignored by RedEngine in SWBFII for all known materials) - elif "ATRB" in next_header: + elif next_header == "ATRB": with matd.read_child() as atrb: mat.flags = atrb.read_u8() mat.rendertype = atrb.read_u8() mat.data = atrb.read_u8(2) - elif "TX0D" in next_header: + elif next_header == "TX0D": with matd.read_child() as tx0d: mat.texture0 = tx0d.read_string() - elif "TX1D" in next_header: + elif next_header == "TX1D": with matd.read_child() as tx1d: mat.texture1 = tx1d.read_string() - elif "TX2D" in next_header: + elif next_header == "TX2D": with matd.read_child() as tx2d: mat.texture2 = tx2d.read_string() - elif "TX3D" in next_header: + elif next_header == "TX3D": with matd.read_child() as tx3d: mat.texture3 = tx3d.read_string() @@ -165,11 +164,11 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model: next_header = modl.peak_next_header() - if "MTYP" in next_header: + if next_header == "MTYP": with modl.read_child() as mtyp: model.model_type = ModelType(mtyp.read_u32()) - elif "MNDX" in next_header: + elif next_header == "MNDX": with modl.read_child() as mndx: index = mndx.read_u32() @@ -181,23 +180,23 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model: model_counter += 1 - elif "NAME" in next_header: + elif next_header == "NAME": with modl.read_child() as name: model.name = name.read_string() - elif "PRNT" in next_header: + elif next_header == "PRNT": with modl.read_child() as prnt: model.parent = prnt.read_string() - elif "FLGS" in next_header: + elif next_header == "FLGS": with modl.read_child() as flgs: model.hidden = flgs.read_u32() - elif "TRAN" in next_header: + elif next_header == "TRAN": with modl.read_child() as tran: model.transform = _read_tran(tran) - elif "GEOM" in next_header: + elif next_header == "GEOM": model.geometry = [] envelope = [] @@ -206,11 +205,11 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model: while geom.could_have_child(): next_header_geom = geom.peak_next_header() - if "SEGM" in next_header_geom: + if next_header_geom == "SEGM": with geom.read_child() as segm: model.geometry.append(_read_segm(segm, materials_list)) - elif "ENVL" in next_header_geom: + elif next_header_geom == "ENVL": with geom.read_child() as envl: num_indicies = envl.read_u32() envelope += [envl.read_u32() for _ in range(num_indicies)] @@ -228,7 +227,7 @@ def _read_modl(modl: Reader, materials_list: List[Material]) -> Model: index = vertex_weight.bone weight_set[i] = VertexWeight(vertex_weight.weight, envelope[vertex_weight.bone]) - elif "SWCI" in next_header: + elif next_header == "SWCI": prim = CollisionPrimitive() with modl.read_child() as swci: prim.shape = CollisionPrimitiveShape(swci.read_u32()) @@ -267,25 +266,25 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: next_header = segm.peak_next_header() - if "MATI" in next_header: + if next_header == "MATI": with segm.read_child() as mati: geometry_seg.material_name = materials_list[mati.read_u32()].name - elif "POSL" in next_header: + elif next_header == "POSL": with segm.read_child() as posl: num_positions = posl.read_u32() for _ in range(num_positions): geometry_seg.positions.append(Vector(posl.read_f32(3))) - elif "NRML" in next_header: + elif next_header == "NRML": with segm.read_child() as nrml: num_normals = nrml.read_u32() for _ in range(num_positions): geometry_seg.normals.append(Vector(nrml.read_f32(3))) - elif "CLRL" in next_header: + elif next_header == "CLRL": geometry_seg.colors = [] with segm.read_child() as clrl: @@ -294,14 +293,14 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: for _ in range(num_colors): geometry_seg.colors += unpack_color(clrl.read_u32()) - elif "UV0L" in next_header: + elif next_header == "UV0L": with segm.read_child() as uv0l: num_texcoords = uv0l.read_u32() for _ in range(num_texcoords): geometry_seg.texcoords.append(Vector(uv0l.read_f32(2))) - elif "NDXL" in next_header: + elif next_header == "NDXL": with segm.read_child() as ndxl: num_polygons = ndxl.read_u32() @@ -309,14 +308,14 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: polygon = ndxl.read_u16(ndxl.read_u16()) geometry_seg.polygons.append(polygon) - elif "NDXT" in next_header: + elif next_header == "NDXT": with segm.read_child() as ndxt: num_tris = ndxt.read_u32() for _ in range(num_tris): geometry_seg.triangles.append(ndxt.read_u16(3)) - elif "STRP" in next_header: + elif next_header == "STRP": strips : List[List[int]] = [] with segm.read_child() as strp: @@ -357,7 +356,7 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: #if segm.read_u16 != 0: #trailing 0 bug https://schlechtwetterfront.github.io/ze_filetypes/msh.html#STRP # segm.skip_bytes(-2) - elif "WGHT" in next_header: + elif next_header == "WGHT": with segm.read_child() as wght: geometry_seg.weights = [] @@ -389,11 +388,19 @@ def _read_anm2(anm2: Reader) -> Animation: next_header = anm2.peak_next_header() - if "CYCL" in next_header: + if next_header == "CYCL": with anm2.read_child() as cycl: pass - elif "KFR3" in next_header: + ''' + num_anims = cycl.read_u32() + + for _ in range(num_anims): + cycl.skip_bytes(64) + print("CYCL play style {}".format(cycl.read_u32(4)[1])) + ''' + + elif next_header == "KFR3": with anm2.read_child() as kfr3: num_bones = kfr3.read_u32() diff --git a/addons/io_scene_swbf_msh/msh_to_blend.py b/addons/io_scene_swbf_msh/msh_to_blend.py index 14779e7..cf65bb2 100644 --- a/addons/io_scene_swbf_msh/msh_to_blend.py +++ b/addons/io_scene_swbf_msh/msh_to_blend.py @@ -20,75 +20,94 @@ import os def extract_and_apply_anim(filename, scene): - arma = bpy.context.view_layer.objects.active + arma = bpy.context.view_layer.objects.active - if arma.type != 'ARMATURE': - raise Exception("Select an armature to attach the imported animation to!") + if arma.type != 'ARMATURE': + raise Exception("Select an armature to attach the imported animation to!") - if scene.animation is None: - raise Exception("No animation found in msh file") - - else: - head, tail = os.path.split(filename) - anim_name = tail.split(".")[0] - action = bpy.data.actions.new(anim_name) + if scene.animation is None: + raise Exception("No animation found in msh file!") + + else: + head, tail = os.path.split(filename) + anim_name = tail.split(".")[0] + action = bpy.data.actions.new(anim_name) - if not arma.animation_data: - arma.animation_data_create() - - bone_bind_poses = {} - for bone in arma.data.bones: - local_mat = bone.matrix_local - if bone.parent: - local_mat = bone.parent.matrix_local.inverted() @ local_mat - bone_bind_poses[bone.name] = local_mat + if not arma.animation_data: + arma.animation_data_create() - for bone in arma.pose.bones: - if crc(bone.name) in scene.animation.bone_frames: - #print("Inserting anim data for bone: {}".format(bone.name)) + + bone_bind_poses = {} + bone_stack_mats = {} + + ''' + for bone in arma.data.bones: + local_mat = bone.matrix_local + if bone.parent: + local_mat = bone.parent.matrix_local.inverted() @ local_mat + bone_bind_poses[bone.name] = local_mat + ''' + + for bone in arma.data.bones: + bone_obj = bpy.data.objects[bone.name] + bone_obj_parent = bone_obj.parent + + bind_mat = bone_obj.matrix_local + stack_mat = Matrix.Identity(4) - bone_local_mat = bone_bind_poses[bone.name] + while(True): + if bone_obj_parent is None or bone_obj_parent.name in arma.data.bones: + break + bind_mat = bone_obj_parent.matrix_local @ bind_mat + stack_mat = bone_obj_parent.matrix_local @ stack_mat + bone_obj_parent = bone_obj_parent.parent - translation_frames, rotation_frames = scene.animation.bone_frames[crc(bone.name)] - - loc_data_path = "pose.bones[\"{}\"].location".format(bone.name) - rot_data_path = "pose.bones[\"{}\"].rotation_quaternion".format(bone.name) - - fcurve_rot_w = action.fcurves.new(rot_data_path, index=0) - fcurve_rot_x = action.fcurves.new(rot_data_path, index=1) - fcurve_rot_y = action.fcurves.new(rot_data_path, index=2) - fcurve_rot_z = action.fcurves.new(rot_data_path, index=3) - - print("\nBone name: " + bone.name) - print("\tRot: {}".format(quat_to_str(rotation_frames[0].rotation))) - - for frame in rotation_frames: - i = frame.index - q = (bone_local_mat.inverted() @ convert_rotation_space(frame.rotation).to_matrix().to_4x4()).to_quaternion() - - fcurve_rot_w.keyframe_points.insert(i,q.w) - fcurve_rot_x.keyframe_points.insert(i,q.x) - fcurve_rot_y.keyframe_points.insert(i,q.y) - fcurve_rot_z.keyframe_points.insert(i,q.z) - - print("\tLoc: {}".format(vec_to_str(translation_frames[0].translation))) + bone_bind_poses[bone.name] = bind_mat.inverted() @ stack_mat - fcurve_loc_x = action.fcurves.new(loc_data_path, index=0) - fcurve_loc_y = action.fcurves.new(loc_data_path, index=1) - fcurve_loc_z = action.fcurves.new(loc_data_path, index=2) - for frame in translation_frames: - i = frame.index - t = convert_vector_space(frame.translation) - bone_local_mat.translation + for bone in arma.pose.bones: + if crc(bone.name) in scene.animation.bone_frames: + #print("Inserting anim data for bone: {}".format(bone.name)) - fcurve_loc_x.keyframe_points.insert(i,t.x) - fcurve_loc_y.keyframe_points.insert(i,t.y) - fcurve_loc_z.keyframe_points.insert(i,t.z) + bind_mat = bone_bind_poses[bone.name] - arma.animation_data.action = action + translation_frames, rotation_frames = scene.animation.bone_frames[crc(bone.name)] + + loc_data_path = "pose.bones[\"{}\"].location".format(bone.name) + rot_data_path = "pose.bones[\"{}\"].rotation_quaternion".format(bone.name) + + + fcurve_rot_w = action.fcurves.new(rot_data_path, index=0) + fcurve_rot_x = action.fcurves.new(rot_data_path, index=1) + fcurve_rot_y = action.fcurves.new(rot_data_path, index=2) + fcurve_rot_z = action.fcurves.new(rot_data_path, index=3) + + for frame in rotation_frames: + i = frame.index + q = (bind_mat @ convert_rotation_space(frame.rotation).to_matrix().to_4x4()).to_quaternion() + + fcurve_rot_w.keyframe_points.insert(i,q.w) + fcurve_rot_x.keyframe_points.insert(i,q.x) + fcurve_rot_y.keyframe_points.insert(i,q.y) + fcurve_rot_z.keyframe_points.insert(i,q.z) + + + fcurve_loc_x = action.fcurves.new(loc_data_path, index=0) + fcurve_loc_y = action.fcurves.new(loc_data_path, index=1) + fcurve_loc_z = action.fcurves.new(loc_data_path, index=2) + + for frame in translation_frames: + i = frame.index + t = (bind_mat @ Matrix.Translation(convert_vector_space(frame.translation))).translation + + fcurve_loc_x.keyframe_points.insert(i,t.x) + fcurve_loc_y.keyframe_points.insert(i,t.y) + fcurve_loc_z.keyframe_points.insert(i,t.z) + + arma.animation_data.action = action @@ -187,6 +206,14 @@ def extract_refined_skeleton(scene: Scene): refined_skeleton_models = [] + ''' + for bone in skeleton_models: + + if bone.parent: + if + ''' + + for bone in skeleton_models: if bone.parent: @@ -213,7 +240,7 @@ def extract_refined_skeleton(scene: Scene): else: curr_ancestor = model_dict[curr_ancestor.parent] stacked_transform = model_transform_to_matrix(curr_ancestor.transform) @ stacked_transform - + return sort_by_parent(refined_skeleton_models) @@ -245,30 +272,30 @@ def extract_models(scene: Scene, materials_map): weights_offsets = {} if model.geometry: - for i,seg in enumerate(model.geometry): + for i,seg in enumerate(model.geometry): - if i == 0: - mat_name = seg.material_name + if i == 0: + mat_name = seg.material_name - verts += [tuple(convert_vector_space(v)) for v in seg.positions] + verts += [tuple(convert_vector_space(v)) for v in seg.positions] - if seg.weights: - weights_offsets[offset] = seg.weights + if seg.weights: + weights_offsets[offset] = seg.weights - if seg.texcoords is not None: - full_texcoords += seg.texcoords - else: - full_texcoords += [(0.0,0.0) for _ in range(len(seg.positions))] + if seg.texcoords is not None: + full_texcoords += seg.texcoords + else: + full_texcoords += [(0.0,0.0) for _ in range(len(seg.positions))] - if seg.triangles: - faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles] - else: - for strip in seg.triangle_strips: - for i in range(len(strip) - 2): - face = tuple([offset + strip[j] for j in range(i,i+3)]) - faces.append(face) + if seg.triangles: + faces += [tuple([ind + offset for ind in tri]) for tri in seg.triangles] + else: + for strip in seg.triangle_strips: + for i in range(len(strip) - 2): + face = tuple([offset + strip[j] for j in range(i,i+3)]) + faces.append(face) - offset += len(seg.positions) + offset += len(seg.positions) new_mesh.from_pydata(verts, [], faces) new_mesh.update() @@ -379,6 +406,36 @@ def extract_scene(filepath: str, scene: Scene): skel = extract_refined_skeleton(scene) armature = refined_skeleton_to_armature(skel, model_map) + + for bone in armature.data.bones: + bone_local = bone.matrix_local + if bone.parent: + bone_local = bone.parent.matrix_local.inverted() @ bone_local + + bone_obj_local = bpy.data.objects[bone.name].matrix_local + obj_loc, obj_rot, _ = bone_obj_local.decompose() + + loc, rot, _ = bone_local.decompose() + + locdiff = obj_loc - loc + quatdiff = obj_rot - rot + + if quatdiff.magnitude > .01: + print("Big quat diff here") + print("\t{}: obj quat: {} bone quat: {}".format(bone.name, quat_to_str(obj_rot), quat_to_str(rot))) + + + #if locdiff.magnitude > .01: + # print("Big loc diff here") + # print("\t{}: obj loc: {} bone loc: {}".format(bone.name, vec_to_str(obj_loc), vec_to_str(loc))) + + + + + + + + reparent_obj = None for model in scene.models: if model.model_type == ModelType.SKIN: @@ -398,15 +455,12 @@ def extract_scene(filepath: str, scene: Scene): armature.select_set(False) bpy.context.view_layer.objects.active = None - print("About to parent to bones....") - if armature is not None: for bone in armature.data.bones: for model in scene.models: - if model.parent in armature.data.bones and model.name not in armature.data.bones: - parent_object_to_bone(model_map[model.name], armature, model.parent) + if model.parent in armature.data.bones and model.model_type != ModelType.NULL: + pass#parent_object_to_bone(model_map[model.name], armature, model.parent) - print("Done parenting to bones") ''' if reparent_obj is not None and armature.name != reparent_obj.name: @@ -418,14 +472,14 @@ def extract_scene(filepath: str, scene: Scene): armature.select_set(False) reparent_obj.select_set(False) bpy.context.view_layer.objects.active = None - + ''' for model in scene.models: if model.name in bpy.data.objects: obj = bpy.data.objects[model.name] - if get_is_model_hidden(obj) and len(obj.children) == 0: + if get_is_model_hidden(obj) and len(obj.children) == 0 and model.model_type != ModelType.NULL: obj.hide_set(True) - ''' +