Hardcoded spa1_prop_door amims test

This commit is contained in:
William Herald Snyder 2020-10-11 12:11:15 -04:00
parent 043127c1f8
commit 603068b5b5
6 changed files with 265 additions and 6 deletions

View File

@ -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('<I', crc(string))
def compare_crc_adv(possible_strings, crc_):
for string in possible_strings:
if crc_ == strcrc(string):
return string

View File

@ -0,0 +1,48 @@
""" Gathers the Blender objects from the current scene and returns them as a list of
Model objects. """
import bpy
import math
from enum import Enum
from typing import List, Set, Dict, Tuple
from itertools import zip_longest
from .msh_model import *
from .msh_model_utilities import *
from .msh_utilities import *
from .msh_model_gather import *
def gather_animdata(armature: bpy.types.Armature) -> 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]

View File

@ -2,7 +2,7 @@
saved to a .msh file. """ saved to a .msh file. """
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List from typing import List, Dict
from enum import Enum from enum import Enum
from mathutils import Vector, Quaternion from mathutils import Vector, Quaternion
@ -65,3 +65,12 @@ class Model:
geometry: List[GeometrySegment] = None geometry: List[GeometrySegment] = None
collisionprimitive: CollisionPrimitive = 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)

View File

@ -10,7 +10,7 @@ from .msh_model import *
from .msh_model_utilities import * from .msh_model_utilities import *
from .msh_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"} MESH_OBJECT_TYPES = {"MESH", "CURVE", "SURFACE", "META", "FONT", "GPENCIL"}
MAX_MSH_VERTEX_COUNT = 32767 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) model.transform.translation = convert_vector_space(local_translation)
if obj.parent is not None: 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: if obj.type in MESH_OBJECT_TYPES:
mesh = obj.to_mesh() mesh = obj.to_mesh()

View File

@ -6,7 +6,7 @@ from typing import List, Dict
from copy import copy from copy import copy
import bpy import bpy
from mathutils import Vector 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_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_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 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_gather import gather_materials
from .msh_material_utilities import remove_unused_materials from .msh_material_utilities import remove_unused_materials
from .msh_utilities import * from .msh_utilities import *
from .msh_anim_gather import *
@dataclass @dataclass
class SceneAABB: class SceneAABB:
@ -43,6 +44,7 @@ class Scene:
name: str = "Scene" name: str = "Scene"
materials: Dict[str, Material] = field(default_factory=dict) materials: Dict[str, Material] = field(default_factory=dict)
models: List[Model] = field(default_factory=list) 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: def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str) -> Scene:
""" Create a msh Scene from the active Blender 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.materials = remove_unused_materials(scene.materials, scene.models)
scene.anims = gather_animdata(bpy.context.scene.objects["Armature"])
return scene return scene
def create_scene_aabb(scene: Scene) -> SceneAABB: def create_scene_aabb(scene: Scene) -> SceneAABB:

View File

@ -8,6 +8,8 @@ from .msh_material import *
from .msh_writer import Writer from .msh_writer import Writer
from .msh_utilities import * from .msh_utilities import *
from .crc import *
def save_scene(output_file, scene: Scene): def save_scene(output_file, scene: Scene):
""" Saves scene to the supplied file. """ """ Saves scene to the supplied file. """
@ -26,6 +28,10 @@ def save_scene(output_file, scene: Scene):
with msh2.create_child("MODL") as modl: with msh2.create_child("MODL") as modl:
_write_modl(modl, model, index, material_index) _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"): with hedr.create_child("CL1L"):
pass pass
@ -34,8 +40,8 @@ def _write_sinf(sinf: Writer, scene: Scene):
name.write_string(scene.name) name.write_string(scene.name)
with sinf.create_child("FRAM") as fram: with sinf.create_child("FRAM") as fram:
fram.write_i32(0, 1) fram.write_i32(0, 20) #test values
fram.write_f32(29.97003) fram.write_f32(10.0) #test values
with sinf.create_child("BBOX") as bbox: with sinf.create_child("BBOX") as bbox:
aabb = create_scene_aabb(scene) 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)): for index in islice(strip, 2, len(strip)):
strp.write_u16(index) 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)