From c0c978af8bf1926dd0e9464e33130334b0cb9d48 Mon Sep 17 00:00:00 2001 From: William Herald Snyder Date: Sat, 5 Dec 2020 00:53:59 -0500 Subject: [PATCH] Bone parenting fixed, anm2 reading, menu option for anim import --- addons/io_scene_swbf_msh/__init__.py | 15 ++++- addons/io_scene_swbf_msh/msh_anim_gather.py | 14 +++-- addons/io_scene_swbf_msh/msh_model.py | 2 +- addons/io_scene_swbf_msh/msh_reader.py | 8 +++ addons/io_scene_swbf_msh/msh_scene_read.py | 68 ++++++++++++--------- addons/io_scene_swbf_msh/msh_scene_save.py | 12 ++-- addons/io_scene_swbf_msh/msh_to_blend.py | 45 ++++---------- 7 files changed, 89 insertions(+), 75 deletions(-) diff --git a/addons/io_scene_swbf_msh/__init__.py b/addons/io_scene_swbf_msh/__init__.py index 530b654..387c0f2 100644 --- a/addons/io_scene_swbf_msh/__init__.py +++ b/addons/io_scene_swbf_msh/__init__.py @@ -151,9 +151,22 @@ class ImportMSH(Operator, ImportHelper): maxlen=255, # Max internal buffer length, longer would be clamped. ) + animation_only: BoolProperty( + name="Import Animation Only", + description="Import animation and append as a new action to currently selected armature.", + default=False + ) + + def execute(self, context): with open(self.filepath, 'rb') as input_file: - extract_scene(self.filepath, read_scene(input_file)) + scene = read_scene(input_file, self.animation_only) + + if not self.animation_only: + extract_scene(self.filepath, scene) + else: + extract_and_apply_anim(self.filepath, scene) + return {'FINISHED'} def menu_func_import(self, context): diff --git a/addons/io_scene_swbf_msh/msh_anim_gather.py b/addons/io_scene_swbf_msh/msh_anim_gather.py index 0129587..40b90a2 100644 --- a/addons/io_scene_swbf_msh/msh_anim_gather.py +++ b/addons/io_scene_swbf_msh/msh_anim_gather.py @@ -17,6 +17,8 @@ def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation: action = armature.animation_data.action anim = Animation(); + root_crc = crc(root_name) + if not action: framerange = Vector((0.0,1.0)) else: @@ -27,9 +29,9 @@ def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation: anim.end_index = num_frames - 1 - anim.bone_frames[root_name] = ([], []) + anim.bone_frames[root_crc] = ([], []) for bone in armature.data.bones: - anim.bone_frames[bone.name] = ([], []) + anim.bone_frames[crc(bone.name)] = ([], []) for frame in range(num_frames): @@ -40,8 +42,8 @@ def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation: rframe_dummy = RotationFrame(frame, convert_rotation_space(Quaternion())) tframe_dummy = TranslationFrame(frame, Vector((0.0,0.0,0.0))) - anim.bone_frames[root_name][0].append(tframe_dummy) - anim.bone_frames[root_name][1].append(rframe_dummy) + anim.bone_frames[root_crc][0].append(tframe_dummy) + anim.bone_frames[root_crc][1].append(rframe_dummy) for bone in armature.pose.bones: @@ -56,8 +58,8 @@ def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation: rframe = RotationFrame(frame, convert_rotation_space(rot)) tframe = TranslationFrame(frame, convert_vector_space(loc)) - anim.bone_frames[bone.name][0].append(tframe) - anim.bone_frames[bone.name][1].append(rframe) + anim.bone_frames[crc(bone.name)][0].append(tframe) + anim.bone_frames[crc(bone.name)][1].append(rframe) return anim diff --git a/addons/io_scene_swbf_msh/msh_model.py b/addons/io_scene_swbf_msh/msh_model.py index a1df175..e2d624b 100644 --- a/addons/io_scene_swbf_msh/msh_model.py +++ b/addons/io_scene_swbf_msh/msh_model.py @@ -98,7 +98,7 @@ class Animation: """ Class representing 'CYCL' + 'KFR3' sections in a .msh file """ name: str = "fullanimation" - bone_frames: Dict[str, Tuple[List[TranslationFrame], List[RotationFrame]]] = field(default_factory=dict) + bone_frames: Dict[int, Tuple[List[TranslationFrame], List[RotationFrame]]] = field(default_factory=dict) framerate: float = 29.97 start_index : int = 0 diff --git a/addons/io_scene_swbf_msh/msh_reader.py b/addons/io_scene_swbf_msh/msh_reader.py index 25528f9..2840013 100644 --- a/addons/io_scene_swbf_msh/msh_reader.py +++ b/addons/io_scene_swbf_msh/msh_reader.py @@ -84,6 +84,14 @@ class Reader: return result[0] if num == 1 else result + def read_quat(self): + rot = self.read_f32(4) + return Quaternion((rot[3], rot[0], rot[1], rot[2])) + + def read_vec(self): + return Vector(self.read_f32(3)) + + def read_child(self): child = Reader(self.file, parent=self, indent=int(len(self.indent) / 2) + 1, debug=self.debug) diff --git a/addons/io_scene_swbf_msh/msh_scene_read.py b/addons/io_scene_swbf_msh/msh_scene_read.py index ab8d274..d8b68d2 100644 --- a/addons/io_scene_swbf_msh/msh_scene_read.py +++ b/addons/io_scene_swbf_msh/msh_scene_read.py @@ -15,7 +15,7 @@ model_counter = 0 mndx_remap = {} -def read_scene(input_file) -> Scene: +def read_scene(input_file, anim_only=False) -> Scene: scene = Scene() scene.models = [] @@ -36,30 +36,31 @@ def read_scene(input_file) -> Scene: if "MSH2" in next_header: with hedr.read_child() as msh2: + + if not anim_only: + materials_list = [] - materials_list = [] + while (msh2.could_have_child()): - while (msh2.could_have_child()): + next_header = msh2.peak_next_header() - next_header = msh2.peak_next_header() + if "SINF" in next_header: + with msh2.read_child() as sinf: + pass - if "SINF" in next_header: - with msh2.read_child() as sinf: - pass + elif "MATL" in next_header: + 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 "MATL" in next_header: - 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 "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)) - - else: - msh2.skip_bytes(1) + else: + msh2.skip_bytes(1) elif "SKL2" in next_header: with hedr.read_child() as skl2: @@ -68,7 +69,7 @@ def read_scene(input_file) -> Scene: elif "ANM2" in next_header: with hedr.read_child() as anm2: - _read_anm2(anm2, scene.models) + scene.animation = _read_anm2(anm2) else: hedr.skip_bytes(1) @@ -250,9 +251,8 @@ def _read_tran(tran: Reader) -> ModelTransform: tran.skip_bytes(12) #ignore scale - rot = tran.read_f32(4) - xform.rotation = Quaternion((rot[3], rot[0], rot[1], rot[2])) - xform.translation = Vector(tran.read_f32(3)) + xform.rotation = tran.read_quat() + xform.translation = tran.read_vec() print(tran.indent + "Rot: {} Loc: {}".format(str(xform.rotation), str(xform.translation))) @@ -381,11 +381,9 @@ def _read_segm(segm: Reader, materials_list: List[Material]) -> GeometrySegment: -def _read_anm2(anm2: Reader, models): +def _read_anm2(anm2: Reader) -> Animation: - hash_dict = {} - for model in models: - hash_dict[crc(model.name)] = model.name + anim = Animation() while anm2.could_have_child(): @@ -402,14 +400,26 @@ def _read_anm2(anm2: Reader, models): for _ in range(num_bones): - kfr3.read_u32() + bone_crc = kfr3.read_u32() + + frames = ([],[]) frametype = kfr3.read_u32() num_loc_frames = kfr3.read_u32() num_rot_frames = kfr3.read_u32() - kfr3.skip_bytes(16 * num_loc_frames + 20 * num_rot_frames) + for i in range(num_loc_frames): + frames[0].append(TranslationFrame(kfr3.read_u32(), kfr3.read_vec())) + + for i in range(num_rot_frames): + frames[1].append(RotationFrame(kfr3.read_u32(), kfr3.read_quat())) + + anim.bone_frames[bone_crc] = frames + else: + anm2.skip_bytes(1) + + return anim diff --git a/addons/io_scene_swbf_msh/msh_scene_save.py b/addons/io_scene_swbf_msh/msh_scene_save.py index 9e65fbf..bb28ddc 100644 --- a/addons/io_scene_swbf_msh/msh_scene_save.py +++ b/addons/io_scene_swbf_msh/msh_scene_save.py @@ -251,15 +251,15 @@ def _write_bln2(bln2: Writer, anim: Animation): bones = anim.bone_frames.keys() bln2.write_u32(len(bones)) - for boneName in bones: - bln2.write_u32(crc(boneName), 0) + for bone_crc in bones: + bln2.write_u32(bone_crc, 0) def _write_skl2(skl2: Writer, anim: Animation): bones = anim.bone_frames.keys() skl2.write_u32(len(bones)) - for boneName in bones: - skl2.write_u32(crc(boneName), 0) #default values from docs + for bone_crc in bones: + skl2.write_u32(bone_crc, 0) #default values from docs skl2.write_f32(1.0, 0.0, 0.0) ''' @@ -284,8 +284,8 @@ def _write_anm2(anm2: Writer, anim: Animation): kfr3.write_u32(len(anim.bone_frames)) - for boneName in anim.bone_frames: - kfr3.write_u32(crc(boneName)) + for bone_crc in anim.bone_frames: + kfr3.write_u32(bone_crc) kfr3.write_u32(0) #what is keyframe type? translation_frames, rotation_frames = anim.bone_frames[boneName] diff --git a/addons/io_scene_swbf_msh/msh_to_blend.py b/addons/io_scene_swbf_msh/msh_to_blend.py index 7cee0af..d28c116 100644 --- a/addons/io_scene_swbf_msh/msh_to_blend.py +++ b/addons/io_scene_swbf_msh/msh_to_blend.py @@ -18,44 +18,25 @@ import os -#def import_anim(scene : Scene): + + + + + def parent_object_to_bone(obj, armature, bone_name): - bpy.ops.object.select_all(action='DESELECT') - armature.select_set(True) - bpy.context.view_layer.objects.active = armature - bpy.ops.object.mode_set(mode='EDIT') - armature.data.edit_bones.active = armature.data.edit_bones[bone_name] - bpy.ops.object.mode_set(mode='OBJECT') + worldmat = obj.matrix_world + obj.parent = None + obj.parent = armature + obj.parent_type = 'BONE' + obj.parent_bone = bone_name - bpy.ops.object.select_all(action='DESELECT') - bpy.context.view_layer.objects.active = None + obj.matrix_basis = Matrix() + obj.matrix_parent_inverse = Matrix() - - obj.select_set(True) - bpy.context.view_layer.objects.active = obj - - bpy.ops.object.parent_clear(type="CLEAR_KEEP_TRANSFORM") - bpy.context.view_layer.objects.active = None - - - obj.select_set(True) - armature.select_set(True) - bpy.context.view_layer.objects.active = armature - - bpy.ops.object.mode_set(mode='POSE') - - armature.pose.bones[bone_name].bone.select = True - - bpy.ops.object.parent_set(type="BONE") - armature.pose.bones[bone_name].bone.select = False - - - - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.select_all(action='DESELECT') + obj.matrix_world = worldmat