Name agnostic armature handling, better export options
This commit is contained in:
parent
a83c74ebf7
commit
617118bfd8
|
@ -97,15 +97,15 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
default=True
|
default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
export_animated: BoolProperty(
|
export_with_animation: BoolProperty(
|
||||||
name="Export Animated Object",
|
name="Export With Animation",
|
||||||
description="Always check if the object will be animated.",
|
description="Includes animation data extracted from the action currently set on armature.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
export_skeleton_only: BoolProperty(
|
export_as_skeleton: BoolProperty(
|
||||||
name="Export Skeleton",
|
name="Export Objects As Skeleton",
|
||||||
description="Check if you intend to export skeleton data only.",
|
description="Check if you intend to export skeleton data for consumption by ZenAsset.",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,9 +119,9 @@ class ExportMSH(Operator, ExportHelper):
|
||||||
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,
|
||||||
skel_only=self.export_skeleton_only
|
skel_only=self.export_as_skeleton,
|
||||||
|
export_anim=self.export_with_animation
|
||||||
),
|
),
|
||||||
is_animated=self.export_animated
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
|
@ -12,7 +12,7 @@ from .msh_utilities import *
|
||||||
from .msh_model_gather import *
|
from .msh_model_gather import *
|
||||||
|
|
||||||
|
|
||||||
def extract_anim(armature: bpy.types.Armature) -> Animation:
|
def extract_anim(armature: bpy.types.Armature, root_name: str) -> Animation:
|
||||||
|
|
||||||
action = armature.animation_data.action
|
action = armature.animation_data.action
|
||||||
anim = Animation();
|
anim = Animation();
|
||||||
|
@ -27,7 +27,7 @@ def extract_anim(armature: bpy.types.Armature) -> Animation:
|
||||||
|
|
||||||
anim.end_index = num_frames - 1
|
anim.end_index = num_frames - 1
|
||||||
|
|
||||||
anim.bone_frames["DummyRoot"] = ([], [])
|
anim.bone_frames[root_name] = ([], [])
|
||||||
for bone in armature.data.bones:
|
for bone in armature.data.bones:
|
||||||
anim.bone_frames[bone.name] = ([], [])
|
anim.bone_frames[bone.name] = ([], [])
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ def extract_anim(armature: bpy.types.Armature) -> Animation:
|
||||||
rframe_dummy = RotationFrame(frame, convert_rotation_space(Quaternion()))
|
rframe_dummy = RotationFrame(frame, convert_rotation_space(Quaternion()))
|
||||||
tframe_dummy = TranslationFrame(frame, Vector((0.0,0.0,0.0)))
|
tframe_dummy = TranslationFrame(frame, Vector((0.0,0.0,0.0)))
|
||||||
|
|
||||||
anim.bone_frames["DummyRoot"][0].append(tframe_dummy)
|
anim.bone_frames[root_name][0].append(tframe_dummy)
|
||||||
anim.bone_frames["DummyRoot"][1].append(rframe_dummy)
|
anim.bone_frames[root_name][1].append(rframe_dummy)
|
||||||
|
|
||||||
|
|
||||||
for bone in armature.pose.bones:
|
for bone in armature.pose.bones:
|
||||||
|
|
|
@ -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, skeleton_only: bool) -> List[Model]:
|
def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool) -> Tuple[List[Model], bpy.types.Object]:
|
||||||
""" 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. """
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
|
|
||||||
models_list: List[Model] = []
|
models_list: List[Model] = []
|
||||||
|
|
||||||
|
armature_found = None
|
||||||
|
|
||||||
for uneval_obj in select_objects(export_target):
|
for uneval_obj in select_objects(export_target):
|
||||||
if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents:
|
if uneval_obj.type in SKIPPED_OBJECT_TYPES and uneval_obj.name not in parents:
|
||||||
continue
|
continue
|
||||||
|
@ -36,6 +38,7 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
|
|
||||||
if obj.type == "ARMATURE":
|
if obj.type == "ARMATURE":
|
||||||
models_list += expand_armature(obj)
|
models_list += expand_armature(obj)
|
||||||
|
armature_found = obj
|
||||||
continue
|
continue
|
||||||
|
|
||||||
model = Model()
|
model = Model()
|
||||||
|
@ -44,7 +47,6 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
model.hidden = get_is_model_hidden(obj)
|
model.hidden = get_is_model_hidden(obj)
|
||||||
|
|
||||||
transform = obj.matrix_local
|
transform = obj.matrix_local
|
||||||
transform_reset = Matrix.Identity(4)
|
|
||||||
|
|
||||||
if obj.parent_bone:
|
if obj.parent_bone:
|
||||||
model.parent = obj.parent_bone
|
model.parent = obj.parent_bone
|
||||||
|
@ -52,18 +54,8 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
# matrix_local, when called on an armature child also parented to a bone, appears to be broken.
|
# matrix_local, when called on an armature child also parented to a bone, appears to be broken.
|
||||||
# At the very least, the results contradict the docs...
|
# At the very least, the results contradict the docs...
|
||||||
armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world
|
armature_relative_transform = obj.parent.matrix_world.inverted() @ obj.matrix_world
|
||||||
bone_relative_transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform
|
transform = obj.parent.data.bones[obj.parent_bone].matrix_local.inverted() @ armature_relative_transform
|
||||||
|
|
||||||
transform = bone_relative_transform
|
|
||||||
|
|
||||||
'''
|
|
||||||
# Since the transforms of direct bone children are discarded by ZEngine (but not ZEditor), we apply the transform
|
|
||||||
# before geometry extraction, then apply the inversion after.
|
|
||||||
if obj.type in MESH_OBJECT_TYPES:
|
|
||||||
obj.data.transform(bone_relative_transform)
|
|
||||||
transform_reset = bone_relative_transform.inverted()
|
|
||||||
transform = Matrix.Identity(4)
|
|
||||||
'''
|
|
||||||
else:
|
else:
|
||||||
if obj.parent is not None:
|
if obj.parent is not None:
|
||||||
if obj.parent.type == "ARMATURE":
|
if obj.parent.type == "ARMATURE":
|
||||||
|
@ -94,8 +86,6 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
if obj.vertex_groups:
|
if obj.vertex_groups:
|
||||||
model.bone_map = [group.name for group in obj.vertex_groups]
|
model.bone_map = [group.name for group in obj.vertex_groups]
|
||||||
|
|
||||||
obj.data.transform(transform_reset)
|
|
||||||
|
|
||||||
|
|
||||||
if get_is_collision_primitive(obj):
|
if get_is_collision_primitive(obj):
|
||||||
model.collisionprimitive = get_collision_primitive(obj)
|
model.collisionprimitive = get_collision_primitive(obj)
|
||||||
|
@ -104,7 +94,7 @@ def gather_models(apply_modifiers: bool, export_target: str, skeleton_only: bool
|
||||||
models_list.append(model)
|
models_list.append(model)
|
||||||
|
|
||||||
|
|
||||||
return models_list
|
return (models_list, armature_found)
|
||||||
|
|
||||||
|
|
||||||
def create_parents_set() -> Set[str]:
|
def create_parents_set() -> Set[str]:
|
||||||
|
|
|
@ -44,10 +44,10 @@ 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)
|
animation: Animation = None
|
||||||
|
|
||||||
|
|
||||||
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool) -> Scene:
|
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool, export_anim: 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, skeleton_only=skel_only)
|
scene.models, armature_obj = 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:
|
||||||
|
@ -72,12 +72,17 @@ 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)
|
||||||
|
|
||||||
#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"])]
|
|
||||||
|
|
||||||
root = scene.models[0]
|
root = scene.models[0]
|
||||||
|
|
||||||
|
if export_anim:
|
||||||
|
if armature_obj is not None:
|
||||||
|
scene.animation = extract_anim(armature_obj, root.name)
|
||||||
|
else:
|
||||||
|
raise Exception("Export Error: Could not find an armature object from which to export an animation!")
|
||||||
|
|
||||||
if skel_only and root.model_type == ModelType.NULL:
|
if skel_only and root.model_type == ModelType.NULL:
|
||||||
|
# For ZenAsset
|
||||||
inject_dummy_data(root)
|
inject_dummy_data(root)
|
||||||
|
|
||||||
return scene
|
return scene
|
||||||
|
|
|
@ -10,7 +10,7 @@ from .msh_utilities import *
|
||||||
|
|
||||||
from .crc import *
|
from .crc import *
|
||||||
|
|
||||||
def save_scene(output_file, scene: Scene, is_animated: bool):
|
def save_scene(output_file, scene: Scene):
|
||||||
""" 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:
|
||||||
|
@ -32,16 +32,15 @@ def save_scene(output_file, scene: Scene, is_animated: bool):
|
||||||
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 is_animated:
|
if scene.animation is not None:
|
||||||
with hedr.create_child("SKL2") as skl2:
|
with hedr.create_child("SKL2") as skl2:
|
||||||
_write_skl2(skl2, scene)
|
_write_skl2(skl2, scene.animation)
|
||||||
|
|
||||||
with hedr.create_child("BLN2") as bln2:
|
with hedr.create_child("BLN2") as bln2:
|
||||||
_write_bln2(bln2, scene)
|
_write_bln2(bln2, scene.animation)
|
||||||
|
|
||||||
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:
|
_write_anm2(anm2, scene.animation)
|
||||||
_write_anm2(anm2, anim)
|
|
||||||
|
|
||||||
with hedr.create_child("CL1L"):
|
with hedr.create_child("CL1L"):
|
||||||
pass
|
pass
|
||||||
|
@ -247,23 +246,23 @@ def _write_envl(envl: Writer, model: Model, model_index: Dict[str, int]):
|
||||||
'''
|
'''
|
||||||
SKELETON CHUNKS
|
SKELETON CHUNKS
|
||||||
'''
|
'''
|
||||||
def _write_bln2(bln2: Writer, scene: Scene):
|
def _write_bln2(bln2: Writer, anim: Animation):
|
||||||
bones = scene.anims[0].bone_frames.keys()
|
bones = anim.bone_frames.keys()
|
||||||
bln2.write_u32(len(bones))
|
bln2.write_u32(len(bones))
|
||||||
|
|
||||||
for boneName in bones:
|
for boneName in bones:
|
||||||
bln2.write_u32(crc(boneName), 0)
|
bln2.write_u32(crc(boneName), 0)
|
||||||
|
|
||||||
def _write_skl2(skl2: Writer, scene: Scene):
|
def _write_skl2(skl2: Writer, anim: Animation):
|
||||||
bones = scene.anims[0].bone_frames.keys()
|
bones = anim.bone_frames.keys()
|
||||||
skl2.write_u32(len(bones))
|
skl2.write_u32(len(bones))
|
||||||
|
|
||||||
for boneName in bones:
|
for boneName in bones:
|
||||||
skl2.write_u32(crc(boneName), 0) #default values
|
skl2.write_u32(crc(boneName), 0) #default values from docs
|
||||||
skl2.write_f32(1.0, 0.0, 0.0) #from docs
|
skl2.write_f32(1.0, 0.0, 0.0)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
ANIMATION CHUNK
|
ANIMATION CHUNKS
|
||||||
'''
|
'''
|
||||||
def _write_anm2(anm2: Writer, anim: Animation):
|
def _write_anm2(anm2: Writer, anim: Animation):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue