From 603068b5b5256b97fb50bfdae31590b5d7968c59 Mon Sep 17 00:00:00 2001 From: William Herald Snyder Date: Sun, 11 Oct 2020 12:11:15 -0400 Subject: [PATCH] Hardcoded spa1_prop_door amims test --- addons/io_scene_swbf_msh/crc.py | 142 +++++++++++++++++++ addons/io_scene_swbf_msh/msh_anim_gather.py | 48 +++++++ addons/io_scene_swbf_msh/msh_model.py | 11 +- addons/io_scene_swbf_msh/msh_model_gather.py | 7 +- addons/io_scene_swbf_msh/msh_scene.py | 6 +- addons/io_scene_swbf_msh/msh_scene_save.py | 57 +++++++- 6 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 addons/io_scene_swbf_msh/crc.py create mode 100644 addons/io_scene_swbf_msh/msh_anim_gather.py diff --git a/addons/io_scene_swbf_msh/crc.py b/addons/io_scene_swbf_msh/crc.py new file mode 100644 index 0000000..8b59617 --- /dev/null +++ b/addons/io_scene_swbf_msh/crc.py @@ -0,0 +1,142 @@ +from struct import pack + + +class CRCError(Exception): + def __init__(self, val): + self.val = val + + def __str__(self): + return '{0}'.format(self.val) + +# CRC lookup table. +TABLE_32 = ( + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, + 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, + 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, + 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, + 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, + 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, + 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, + 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, + 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, + 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, + 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, + 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, + 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, + 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, + 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, + 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, + 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, + 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, + 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, + 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, + 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, + 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, + 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, + 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, + 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, + 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, + 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, + 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, + 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, + 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, + 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, + 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, + 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, + 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, + 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, + 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, + 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, + 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, + 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, + 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, + 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, + 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 +) + +# Used to calculate the lowercase CRC. +TOLOWER = ( + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +) + + +def return_lowest_bits(n): + '''Simulate unsigned behavior.''' + return n & 0xFFFFFFFF + + +def crc(string): + '''Calculate the Zero CRC from string and return it as number.''' + crc_ = 0 + crc_ = return_lowest_bits(~crc_) + if string: + for char in string: + ind = (crc_ >> 24) + ind = ind ^ TOLOWER[ord(char)] + crc_ = return_lowest_bits(crc_ << 8) ^ TABLE_32[ind] + return return_lowest_bits(~crc_) + + +def strcrc(string): + '''Calculate the Zero CRC and return it in a structure + usable in .msh files.''' + return pack(' List[Animation]: + + anim_data = Animation(); + + action = armature.animation_data.action + + framerange = action.frame_range + increment = (framerange.y - framerange.x) / 20.0 + offset = framerange.x; + + anim_data.bone_transforms[armature.parent.name] = [] + for bone in armature.data.bones: + anim_data.bone_transforms[bone.name] = [] + + for frame in range(21): + frame_time = offset + frame * increment + bpy.context.scene.frame_set(frame_time) + + anim_data.bone_transforms[armature.parent.name].append(ModelTransform()) #for testing + + for bone in armature.pose.bones: + xform = ModelTransform() + xform.translation = convert_vector_space(bone.location) + xform.rotation = convert_rotation_space(bone.rotation_quaternion) + + anim_data.bone_transforms[bone.name].append(xform) + + return [anim_data] + + + + + + diff --git a/addons/io_scene_swbf_msh/msh_model.py b/addons/io_scene_swbf_msh/msh_model.py index 589a274..794a7f4 100644 --- a/addons/io_scene_swbf_msh/msh_model.py +++ b/addons/io_scene_swbf_msh/msh_model.py @@ -2,7 +2,7 @@ saved to a .msh file. """ from dataclasses import dataclass, field -from typing import List +from typing import List, Dict from enum import Enum from mathutils import Vector, Quaternion @@ -65,3 +65,12 @@ class Model: geometry: List[GeometrySegment] = None collisionprimitive: CollisionPrimitive = None + +@dataclass +class Animation: + """ Class representing 'CYCL' + 'KFR3' sections in a .msh file """ + + name: str = "open" + anim_type: str = "HardSkinned" + bone_transforms: Dict[str, List[ModelTransform]] = field(default_factory=dict) + diff --git a/addons/io_scene_swbf_msh/msh_model_gather.py b/addons/io_scene_swbf_msh/msh_model_gather.py index a55a5ef..567aa2f 100644 --- a/addons/io_scene_swbf_msh/msh_model_gather.py +++ b/addons/io_scene_swbf_msh/msh_model_gather.py @@ -10,7 +10,7 @@ from .msh_model import * from .msh_model_utilities import * from .msh_utilities import * -SKIPPED_OBJECT_TYPES = {"LATTICE", "CAMERA", "LIGHT", "SPEAKER", "LIGHT_PROBE"} +SKIPPED_OBJECT_TYPES = {"LATTICE", "CAMERA", "LIGHT", "SPEAKER", "LIGHT_PROBE", "ARMATURE"} MESH_OBJECT_TYPES = {"MESH", "CURVE", "SURFACE", "META", "FONT", "GPENCIL"} MAX_MSH_VERTEX_COUNT = 32767 @@ -44,7 +44,10 @@ def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]: model.transform.translation = convert_vector_space(local_translation) if obj.parent is not None: - model.parent = obj.parent.name + if obj.parent.type == "Armature": + model.parent = obj.parent.parent.name + else: + model.parent = obj.parent.name if obj.type in MESH_OBJECT_TYPES: mesh = obj.to_mesh() diff --git a/addons/io_scene_swbf_msh/msh_scene.py b/addons/io_scene_swbf_msh/msh_scene.py index 6c6f7fd..05debe8 100644 --- a/addons/io_scene_swbf_msh/msh_scene.py +++ b/addons/io_scene_swbf_msh/msh_scene.py @@ -6,7 +6,7 @@ from typing import List, Dict from copy import copy import bpy from mathutils import Vector -from .msh_model import Model +from .msh_model import Model, Animation from .msh_model_gather import gather_models from .msh_model_utilities import sort_by_parent, has_multiple_root_models, reparent_model_roots, get_model_world_matrix from .msh_model_triangle_strips import create_models_triangle_strips @@ -14,6 +14,7 @@ from .msh_material import * from .msh_material_gather import gather_materials from .msh_material_utilities import remove_unused_materials from .msh_utilities import * +from .msh_anim_gather import * @dataclass class SceneAABB: @@ -43,6 +44,7 @@ class Scene: name: str = "Scene" materials: Dict[str, Material] = field(default_factory=dict) models: List[Model] = field(default_factory=list) + anims: List[Animation] = field(default_factory=list) def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str) -> Scene: """ Create a msh Scene from the active Blender scene. """ @@ -69,6 +71,8 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t scene.materials = remove_unused_materials(scene.materials, scene.models) + scene.anims = gather_animdata(bpy.context.scene.objects["Armature"]) + return scene def create_scene_aabb(scene: Scene) -> SceneAABB: diff --git a/addons/io_scene_swbf_msh/msh_scene_save.py b/addons/io_scene_swbf_msh/msh_scene_save.py index 71914a0..b851713 100644 --- a/addons/io_scene_swbf_msh/msh_scene_save.py +++ b/addons/io_scene_swbf_msh/msh_scene_save.py @@ -8,6 +8,8 @@ from .msh_material import * from .msh_writer import Writer from .msh_utilities import * +from .crc import * + def save_scene(output_file, scene: Scene): """ Saves scene to the supplied file. """ @@ -26,6 +28,10 @@ def save_scene(output_file, scene: Scene): with msh2.create_child("MODL") as modl: _write_modl(modl, model, index, material_index) + with hedr.create_child("ANM2") as anm2: #simple for now + for anim in scene.anims: + _write_anm2(anm2, anim) + with hedr.create_child("CL1L"): pass @@ -34,8 +40,8 @@ def _write_sinf(sinf: Writer, scene: Scene): name.write_string(scene.name) with sinf.create_child("FRAM") as fram: - fram.write_i32(0, 1) - fram.write_f32(29.97003) + fram.write_i32(0, 20) #test values + fram.write_f32(10.0) #test values with sinf.create_child("BBOX") as bbox: aabb = create_scene_aabb(scene) @@ -189,3 +195,50 @@ def _write_segm(segm: Writer, segment: GeometrySegment, material_index: Dict[str for index in islice(strip, 2, len(strip)): strp.write_u16(index) + + +def _write_anm2(anm2: Writer, anim: Animation): + + with anm2.create_child("CYCL") as cycl: + + cycl.write_u32(1) + cycl.write_string(anim.name) + + for _ in range(64 - (len(anim.name) + 1)): + cycl.write_u8(0) + + cycl.write_f32(10.0) #test framerate + cycl.write_u32(0) #what does play style refer to? + cycl.write_u32(0, 20) #first frame indices + + + with anm2.create_child("KFR3") as kfr3: + + kfr3.write_u32(len(anim.bone_transforms.keys())) + + for boneName in anim.bone_transforms.keys(): + kfr3.write_u32(crc(boneName)) + kfr3.write_u32(0) #what is keyframe type? + + kfr3.write_u32(21, 21) #basic testing + + print(boneName) + + for i, xform in enumerate(anim.bone_transforms[boneName]): + kfr3.write_u32(i) + kfr3.write_f32(xform.translation.x, xform.translation.y, xform.translation.z) + + print(xform.translation) + + for i, xform in enumerate(anim.bone_transforms[boneName]): + kfr3.write_u32(i) + kfr3.write_f32(xform.rotation.x, xform.rotation.y, xform.rotation.z, xform.rotation.w) + + + + + + + + +