Working for complex skinned exports, still crashes ZE, menus need work
This commit is contained in:
parent
ff3a517312
commit
fb072f8d59
|
@ -97,9 +97,15 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
export_anims: BoolProperty(
|
export_animated: BoolProperty(
|
||||||
name="Export Animations",
|
name="Export Animated Object",
|
||||||
description="Action export test",
|
description="Always check if the object will be animated.",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
export_skeleton_only: BoolProperty(
|
||||||
|
name="Export Skeleton",
|
||||||
|
description="Check if you intend to export skeleton data only.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,8 +118,10 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
scene=create_scene(
|
scene=create_scene(
|
||||||
generate_triangle_strips=self.generate_triangle_strips,
|
generate_triangle_strips=self.generate_triangle_strips,
|
||||||
apply_modifiers=self.apply_modifiers,
|
apply_modifiers=self.apply_modifiers,
|
||||||
export_target=self.export_target),
|
export_target=self.export_target,
|
||||||
separate_anims=self.export_anims
|
skel_only=self.export_skeleton_only
|
||||||
|
),
|
||||||
|
is_animated=self.export_animated
|
||||||
)
|
)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -12,65 +12,42 @@ from .msh_utilities import *
|
||||||
from .msh_model_gather import *
|
from .msh_model_gather import *
|
||||||
|
|
||||||
|
|
||||||
def gather_animdata(armature: bpy.types.Armature) -> List[Animation]:
|
def extract_anim(armature: bpy.types.Armature) -> Animation:
|
||||||
|
|
||||||
anim_data = Animation();
|
|
||||||
|
|
||||||
action = armature.animation_data.action
|
action = armature.animation_data.action
|
||||||
|
anim = Animation();
|
||||||
|
|
||||||
|
if not action:
|
||||||
|
framerange = Vector((0.0,1.0))
|
||||||
|
else:
|
||||||
|
framerange = action.frame_range
|
||||||
|
|
||||||
framerange = action.frame_range
|
num_frames = math.floor(framerange.y - framerange.x) + 1
|
||||||
increment = (framerange.y - framerange.x) / 20.0
|
increment = (framerange.y - framerange.x) / (num_frames - 1)
|
||||||
offset = framerange.x;
|
|
||||||
|
|
||||||
anim_data.bone_transforms[armature.parent.name] = []
|
anim.end_index = num_frames - 1
|
||||||
|
|
||||||
for bone in armature.data.bones:
|
for bone in armature.data.bones:
|
||||||
anim_data.bone_transforms[bone.name] = []
|
anim.bone_transforms[bone.name] = []
|
||||||
|
|
||||||
for frame in range(21):
|
for frame in range(num_frames):
|
||||||
frame_time = offset + frame * increment
|
|
||||||
|
frame_time = framerange.x + frame * increment
|
||||||
bpy.context.scene.frame_set(frame_time)
|
bpy.context.scene.frame_set(frame_time)
|
||||||
|
|
||||||
anim_data.bone_transforms[armature.parent.name].append(ModelTransform()) #for testing
|
|
||||||
|
|
||||||
for bone in armature.pose.bones:
|
for bone in armature.pose.bones:
|
||||||
|
|
||||||
xform = ModelTransform()
|
transform = bone.matrix
|
||||||
xform.translation = to_skeleton_vector_space(bone.location)
|
|
||||||
xform.rotation = to_skeleton_rotation_space(bone.rotation_quaternion)
|
|
||||||
|
|
||||||
anim_data.bone_transforms[bone.name].append(xform)
|
|
||||||
|
|
||||||
|
|
||||||
return [anim_data]
|
|
||||||
|
|
||||||
|
if bone.parent:
|
||||||
def get_basepose(armature: bpy.types.Armature) -> Animation:
|
transform = bone.parent.matrix.inverted() @ transform
|
||||||
|
|
||||||
anim_data = Animation();
|
loc, rot, _ = transform.decompose()
|
||||||
anim_data.name = "basepose"
|
|
||||||
|
|
||||||
bpy.context.scene.frame_set(0.0)
|
|
||||||
|
|
||||||
anim_data.bone_transforms[armature.parent.name] = []
|
|
||||||
for bone in armature.data.bones:
|
|
||||||
anim_data.bone_transforms[bone.name] = []
|
|
||||||
|
|
||||||
for frame in range(2):
|
|
||||||
|
|
||||||
anim_data.bone_transforms[armature.parent.name].append(ModelTransform()) #for testing
|
|
||||||
|
|
||||||
for bone in armature.pose.bones:
|
|
||||||
|
|
||||||
xform = ModelTransform()
|
xform = ModelTransform()
|
||||||
xform.translation = to_skeleton_vector_space(bone.location)
|
xform.rotation = convert_rotation_space(rot)
|
||||||
xform.rotation = to_skeleton_rotation_space(bone.rotation_quaternion)
|
xform.translation = convert_vector_space(loc)
|
||||||
|
|
||||||
anim_data.bone_transforms[bone.name].append(xform)
|
|
||||||
|
|
||||||
|
|
||||||
return anim_data
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
anim.bone_transforms[bone.name].append(xform)
|
||||||
|
|
||||||
|
return anim
|
||||||
|
|
|
@ -80,6 +80,10 @@ class Model:
|
||||||
class Animation:
|
class Animation:
|
||||||
""" Class representing 'CYCL' + 'KFR3' sections in a .msh file """
|
""" Class representing 'CYCL' + 'KFR3' sections in a .msh file """
|
||||||
|
|
||||||
name: str = "wiggle"
|
name: str = "fullanimation"
|
||||||
bone_transforms: Dict[str, List[ModelTransform]] = field(default_factory=dict)
|
bone_transforms: Dict[str, List[ModelTransform]] = field(default_factory=dict)
|
||||||
|
|
||||||
|
framerate: float = 10.0
|
||||||
|
start_index : int = 0
|
||||||
|
end_index : int = 0
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ SKIPPED_OBJECT_TYPES = {"LATTICE", "CAMERA", "LIGHT", "SPEAKER", "LIGHT_PROBE"}
|
||||||
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
|
||||||
|
|
||||||
def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]:
|
def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool) -> List[Model]:
|
||||||
""" Gathers the Blender objects from the current scene and returns them as a list of
|
""" Gathers the Blender objects from the current scene and returns them as a list of
|
||||||
Model objects. """
|
Model objects. """
|
||||||
|
|
||||||
|
@ -36,15 +36,26 @@ def gather_models(apply_modifiers: bool, export_target: str) -> List[Model]:
|
||||||
|
|
||||||
if obj.type == "ARMATURE":
|
if obj.type == "ARMATURE":
|
||||||
models_list += expand_armature(obj)
|
models_list += expand_armature(obj)
|
||||||
continue
|
|
||||||
|
|
||||||
local_translation, local_rotation, _ = obj.matrix_local.decompose()
|
if skeleton_only:
|
||||||
|
continue
|
||||||
|
|
||||||
model = Model()
|
model = Model()
|
||||||
model.name = obj.name
|
model.name = obj.name
|
||||||
model.model_type = get_model_type(obj)
|
model.model_type = get_model_type(obj)
|
||||||
model.hidden = get_is_model_hidden(obj)
|
model.hidden = get_is_model_hidden(obj)
|
||||||
model.transform.rotation = convert_rotation_space(local_rotation)
|
|
||||||
|
transform = obj.matrix_local
|
||||||
|
|
||||||
|
if obj.parent_bone:
|
||||||
|
model.parent = obj.parent_bone
|
||||||
|
transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ transform
|
||||||
|
else:
|
||||||
|
if obj.parent is not None:
|
||||||
|
model.parent = obj.parent.name
|
||||||
|
|
||||||
|
local_translation, local_rotation, _ = transform.decompose()
|
||||||
|
model.transform.rotation = convert_rotation_space(local_rotation)
|
||||||
model.transform.translation = convert_vector_space(local_translation)
|
model.transform.translation = convert_vector_space(local_translation)
|
||||||
|
|
||||||
if obj.type in MESH_OBJECT_TYPES:
|
if obj.type in MESH_OBJECT_TYPES:
|
||||||
|
@ -126,11 +137,11 @@ def create_mesh_geometry(mesh: bpy.types.Mesh, has_weights: bool) -> List[Geomet
|
||||||
|
|
||||||
if use_smooth_normal or mesh.use_auto_smooth:
|
if use_smooth_normal or mesh.use_auto_smooth:
|
||||||
if mesh.has_custom_normals:
|
if mesh.has_custom_normals:
|
||||||
vertex_normal = mesh.loops[loop_index].normal
|
vertex_normal = Vector( mesh.loops[loop_index].normal )
|
||||||
else:
|
else:
|
||||||
vertex_normal = mesh.vertices[vertex_index].normal
|
vertex_normal = Vector( mesh.vertices[vertex_index].normal )
|
||||||
else:
|
else:
|
||||||
vertex_normal = face_normal
|
vertex_normal = Vector(face_normal)
|
||||||
|
|
||||||
def get_cache_vertex():
|
def get_cache_vertex():
|
||||||
yield mesh.vertices[vertex_index].co.x
|
yield mesh.vertices[vertex_index].co.x
|
||||||
|
@ -343,6 +354,8 @@ def select_objects(export_target: str) -> List[bpy.types.Object]:
|
||||||
|
|
||||||
return objects + parents
|
return objects + parents
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def expand_armature(obj: bpy.types.Object) -> List[Model]:
|
def expand_armature(obj: bpy.types.Object) -> List[Model]:
|
||||||
bones: List[Model] = []
|
bones: List[Model] = []
|
||||||
|
|
||||||
|
@ -355,7 +368,7 @@ def expand_armature(obj: bpy.types.Object) -> List[Model]:
|
||||||
transform = bone.parent.matrix_local.inverted() @ transform
|
transform = bone.parent.matrix_local.inverted() @ transform
|
||||||
model.parent = bone.parent.name
|
model.parent = bone.parent.name
|
||||||
else:
|
else:
|
||||||
model.parent = "DummyRoot" # obj.name
|
model.parent = obj.name
|
||||||
|
|
||||||
local_translation, local_rotation, _ = transform.decompose()
|
local_translation, local_rotation, _ = transform.decompose()
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
from .msh_model import *
|
from .msh_model import *
|
||||||
from .msh_utilities import *
|
from .msh_utilities import *
|
||||||
|
import mathutils
|
||||||
|
import math
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
def scale_segments(scale: Vector, segments: List[GeometrySegment]):
|
def scale_segments(scale: Vector, segments: List[GeometrySegment]):
|
||||||
|
@ -125,15 +127,3 @@ def convert_scale_space(vec: Vector) -> Vector:
|
||||||
def convert_rotation_space(quat: Quaternion) -> Quaternion:
|
def convert_rotation_space(quat: Quaternion) -> Quaternion:
|
||||||
return Quaternion((-quat.w, quat.x, -quat.z, -quat.y))
|
return Quaternion((-quat.w, quat.x, -quat.z, -quat.y))
|
||||||
|
|
||||||
|
|
||||||
def to_skeleton_vector_space(vec : Vector):
|
|
||||||
vnew = convert_vector_space(vec)
|
|
||||||
vnew.x *= -1.0
|
|
||||||
return vnew
|
|
||||||
|
|
||||||
def to_skeleton_rotation_space(quat : Quaternion):
|
|
||||||
qnew = convert_rotation_space(quat)
|
|
||||||
qnew.x *= -1.0
|
|
||||||
qnew.y *= -1.0
|
|
||||||
qnew.z *= -1.0
|
|
||||||
return qnew
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ class Scene:
|
||||||
anims: List[Animation] = 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, skel_only: bool) -> Scene:
|
||||||
""" Create a msh Scene from the active Blender scene. """
|
""" Create a msh Scene from the active Blender scene. """
|
||||||
|
|
||||||
scene = Scene()
|
scene = Scene()
|
||||||
|
@ -56,7 +56,7 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t
|
||||||
|
|
||||||
scene.materials = gather_materials()
|
scene.materials = gather_materials()
|
||||||
|
|
||||||
scene.models = gather_models(apply_modifiers=apply_modifiers, export_target=export_target)
|
scene.models = gather_models(apply_modifiers=apply_modifiers, export_target=export_target, skeleton_only=skel_only)
|
||||||
scene.models = sort_by_parent(scene.models)
|
scene.models = sort_by_parent(scene.models)
|
||||||
|
|
||||||
if generate_triangle_strips:
|
if generate_triangle_strips:
|
||||||
|
@ -70,13 +70,15 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t
|
||||||
if has_multiple_root_models(scene.models):
|
if has_multiple_root_models(scene.models):
|
||||||
scene.models = reparent_model_roots(scene.models)
|
scene.models = reparent_model_roots(scene.models)
|
||||||
|
|
||||||
|
|
||||||
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"])
|
#creates a dummy basepose if no Action is selected
|
||||||
|
if "Armature" in bpy.context.scene.objects.keys():
|
||||||
|
scene.anims = [extract_anim(bpy.context.scene.objects["Armature"])]
|
||||||
|
|
||||||
return scene
|
return scene
|
||||||
|
|
||||||
|
|
||||||
def create_scene_aabb(scene: Scene) -> SceneAABB:
|
def create_scene_aabb(scene: Scene) -> SceneAABB:
|
||||||
""" Create a SceneAABB for a Scene. """
|
""" Create a SceneAABB for a Scene. """
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from .msh_utilities import *
|
||||||
|
|
||||||
from .crc import *
|
from .crc import *
|
||||||
|
|
||||||
def save_scene(output_file, scene: Scene, separate_anims: bool):
|
def save_scene(output_file, scene: Scene, is_animated: bool):
|
||||||
""" Saves scene to the supplied file. """
|
""" Saves scene to the supplied file. """
|
||||||
|
|
||||||
with Writer(file=output_file, chunk_id="HEDR") as hedr:
|
with Writer(file=output_file, chunk_id="HEDR") as hedr:
|
||||||
|
@ -26,12 +26,19 @@ def save_scene(output_file, scene: Scene, separate_anims: bool):
|
||||||
material_index = _write_matl_and_get_material_index(matl, scene)
|
material_index = _write_matl_and_get_material_index(matl, scene)
|
||||||
|
|
||||||
for index, model in enumerate(scene.models):
|
for index, model in enumerate(scene.models):
|
||||||
if separate_anims and (model.model_type not in {ModelType.NULL, ModelType.BONE}):
|
|
||||||
continue
|
print(model.name)
|
||||||
|
|
||||||
with msh2.create_child("MODL") as modl:
|
with msh2.create_child("MODL") as modl:
|
||||||
_write_modl(modl, model, index, material_index, model_index)
|
_write_modl(modl, model, index, material_index, model_index)
|
||||||
|
|
||||||
if separate_anims:
|
if is_animated:
|
||||||
|
with hedr.create_child("SKL2") as skl2:
|
||||||
|
_write_skl2(skl2, scene)
|
||||||
|
|
||||||
|
with hedr.create_child("BLN2") as bln2:
|
||||||
|
_write_bln2(bln2, scene)
|
||||||
|
|
||||||
with hedr.create_child("ANM2") as anm2: #simple for now
|
with hedr.create_child("ANM2") as anm2: #simple for now
|
||||||
for anim in scene.anims:
|
for anim in scene.anims:
|
||||||
_write_anm2(anm2, anim)
|
_write_anm2(anm2, anim)
|
||||||
|
@ -44,8 +51,15 @@ 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, 20) #test values
|
min_index = 0
|
||||||
fram.write_f32(10.0) #test values
|
max_index = 1
|
||||||
|
|
||||||
|
if scene.anims and len(scene.anims) > 0:
|
||||||
|
min_index = min([anim.start_index for anim in scene.anims])
|
||||||
|
max_index = min([anim.end_index for anim in scene.anims])
|
||||||
|
|
||||||
|
fram.write_i32(min_index, max_index)
|
||||||
|
fram.write_f32(10.0)
|
||||||
|
|
||||||
with sinf.create_child("BBOX") as bbox:
|
with sinf.create_child("BBOX") as bbox:
|
||||||
aabb = create_scene_aabb(scene)
|
aabb = create_scene_aabb(scene)
|
||||||
|
@ -130,6 +144,12 @@ def _write_modl(modl: Writer, model: Model, index: int, material_index: Dict[str
|
||||||
|
|
||||||
if model.geometry is not None:
|
if model.geometry is not None:
|
||||||
with modl.create_child("GEOM") as geom:
|
with modl.create_child("GEOM") as geom:
|
||||||
|
|
||||||
|
with geom.create_child("BBOX") as bbox:
|
||||||
|
bbox.write_f32(0.0, 0.0, 0.0, 1.0)
|
||||||
|
bbox.write_f32(0, 0, 0)
|
||||||
|
bbox.write_f32(1.0,1.0,1.0,2.0)
|
||||||
|
|
||||||
for segment in model.geometry:
|
for segment in model.geometry:
|
||||||
with geom.create_child("SEGM") as segm:
|
with geom.create_child("SEGM") as segm:
|
||||||
_write_segm(segm, segment, material_index)
|
_write_segm(segm, segment, material_index)
|
||||||
|
@ -168,7 +188,7 @@ def _write_segm(segm: Writer, segment: GeometrySegment, material_index: Dict[str
|
||||||
with segm.create_child("NRML") as nrml:
|
with segm.create_child("NRML") as nrml:
|
||||||
nrml.write_u32(len(segment.normals))
|
nrml.write_u32(len(segment.normals))
|
||||||
|
|
||||||
for normal in segment.normals:
|
for i,normal in enumerate(segment.normals):
|
||||||
nrml.write_f32(normal.x, normal.y, normal.z)
|
nrml.write_f32(normal.x, normal.y, normal.z)
|
||||||
|
|
||||||
if segment.colors is not None:
|
if segment.colors is not None:
|
||||||
|
@ -214,6 +234,8 @@ SKINNING CHUNKS
|
||||||
def _write_wght(wght: Writer, weights: List[List[VertexWeight]]):
|
def _write_wght(wght: Writer, weights: List[List[VertexWeight]]):
|
||||||
wght.write_u32(len(weights))
|
wght.write_u32(len(weights))
|
||||||
|
|
||||||
|
print("Writing WGHT: ")
|
||||||
|
|
||||||
for weight_list in weights:
|
for weight_list in weights:
|
||||||
weight_list += [VertexWeight(0.0, 0)] * 4
|
weight_list += [VertexWeight(0.0, 0)] * 4
|
||||||
weight_list = sorted(weight_list, key=lambda w: w.weight, reverse=True)
|
weight_list = sorted(weight_list, key=lambda w: w.weight, reverse=True)
|
||||||
|
@ -221,18 +243,46 @@ def _write_wght(wght: Writer, weights: List[List[VertexWeight]]):
|
||||||
|
|
||||||
total_weight = max(sum(map(lambda w: w.weight, weight_list)), 1e-5)
|
total_weight = max(sum(map(lambda w: w.weight, weight_list)), 1e-5)
|
||||||
|
|
||||||
|
print_str = ""
|
||||||
|
|
||||||
for weight in weight_list:
|
for weight in weight_list:
|
||||||
|
|
||||||
|
print_str += "({}, {}) ".format(weight.bone, weight.weight / total_weight)
|
||||||
|
|
||||||
wght.write_i32(weight.bone)
|
wght.write_i32(weight.bone)
|
||||||
wght.write_f32(weight.weight / total_weight)
|
wght.write_f32(weight.weight / total_weight)
|
||||||
|
|
||||||
|
print(" {}".format(print_str))
|
||||||
|
|
||||||
def _write_envl(envl: Writer, model: Model, model_index: Dict[str, int]):
|
def _write_envl(envl: Writer, model: Model, model_index: Dict[str, int]):
|
||||||
envl.write_u32(len(model.bone_map))
|
envl.write_u32(len(model.bone_map))
|
||||||
|
|
||||||
|
print("Writing ENVL: ")
|
||||||
|
|
||||||
for bone_name in model.bone_map:
|
for bone_name in model.bone_map:
|
||||||
|
print(" {:10} Index: {}".format(bone_name, model_index[bone_name]))
|
||||||
envl.write_u32(model_index[bone_name])
|
envl.write_u32(model_index[bone_name])
|
||||||
|
|
||||||
'''
|
'''
|
||||||
ANIMATION CHUNKS
|
SKELETON CHUNKS
|
||||||
|
'''
|
||||||
|
def _write_bln2(bln2: Writer, scene: Scene):
|
||||||
|
bones = scene.anims[0].bone_transforms.keys()
|
||||||
|
bln2.write_u32(len(bones))
|
||||||
|
|
||||||
|
for boneName in bones:
|
||||||
|
bln2.write_u32(crc(boneName), 0)
|
||||||
|
|
||||||
|
def _write_skl2(skl2: Writer, scene: Scene):
|
||||||
|
bones = scene.anims[0].bone_transforms.keys()
|
||||||
|
skl2.write_u32(len(bones))
|
||||||
|
|
||||||
|
for boneName in bones:
|
||||||
|
skl2.write_u32(crc(boneName), 0) #default values
|
||||||
|
skl2.write_f32(1.0, 0.0, 0.0) #from docs
|
||||||
|
|
||||||
|
'''
|
||||||
|
ANIMATION CHUNK
|
||||||
'''
|
'''
|
||||||
def _write_anm2(anm2: Writer, anim: Animation):
|
def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
|
|
||||||
|
@ -241,12 +291,12 @@ def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
cycl.write_u32(1)
|
cycl.write_u32(1)
|
||||||
cycl.write_string(anim.name)
|
cycl.write_string(anim.name)
|
||||||
|
|
||||||
for _ in range(63 - len(anim.name) ):
|
for _ in range(63 - len(anim.name)):
|
||||||
cycl.write_u8(0)
|
cycl.write_u8(0)
|
||||||
|
|
||||||
cycl.write_f32(10.0) #test framerate
|
cycl.write_f32(anim.framerate)
|
||||||
cycl.write_u32(0) #what does play style refer to?
|
cycl.write_u32(0) #what does play style refer to?
|
||||||
cycl.write_u32(0, 20) #first frame indices
|
cycl.write_u32(anim.start_index, anim.end_index) #first frame indices
|
||||||
|
|
||||||
|
|
||||||
with anm2.create_child("KFR3") as kfr3:
|
with anm2.create_child("KFR3") as kfr3:
|
||||||
|
@ -257,7 +307,8 @@ def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
kfr3.write_u32(crc(boneName))
|
kfr3.write_u32(crc(boneName))
|
||||||
kfr3.write_u32(0) #what is keyframe type?
|
kfr3.write_u32(0) #what is keyframe type?
|
||||||
|
|
||||||
kfr3.write_u32(21, 21) #basic testing
|
num_frames = 1 + anim.end_index - anim.start_index
|
||||||
|
kfr3.write_u32(num_frames, num_frames) #basic testing
|
||||||
|
|
||||||
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
for i, xform in enumerate(anim.bone_transforms[boneName]):
|
||||||
kfr3.write_u32(i)
|
kfr3.write_u32(i)
|
||||||
|
|
Loading…
Reference in New Issue