Hardcoded spa1_prop_door amims test
This commit is contained in:
parent
043127c1f8
commit
603068b5b5
142
addons/io_scene_swbf_msh/crc.py
Normal file
142
addons/io_scene_swbf_msh/crc.py
Normal 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
|
48
addons/io_scene_swbf_msh/msh_anim_gather.py
Normal file
48
addons/io_scene_swbf_msh/msh_anim_gather.py
Normal 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]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user