Integrated Teancum's feedback (Normal rendertype changed to Standard + armature root bone properly parented to skin if one is present + animation export options simplified with batch export capability and relevant docs changes + minor changes to import/export options and docs to make behavior obvious)

This commit is contained in:
William Herald Snyder
2022-01-28 17:28:12 -05:00
parent 091e295649
commit c3f5f0bed3
6 changed files with 91 additions and 62 deletions

View File

@@ -56,7 +56,7 @@ import bpy
from bpy_extras.io_utils import ExportHelper, ImportHelper
from bpy.props import BoolProperty, EnumProperty, CollectionProperty
from bpy.types import Operator
from .msh_scene_utilities import create_scene
from .msh_scene_utilities import create_scene, set_scene_animation
from .msh_scene_save import save_scene
from .msh_scene_read import read_scene
from .msh_material_properties import *
@@ -103,32 +103,47 @@ class ExportMSH(Operator, ExportHelper):
default=True
)
export_with_animation: BoolProperty(
name="Export With Animation",
description="Includes animation data extracted from the action currently set on armature.",
default=False
)
export_as_skeleton: BoolProperty(
name="Export Objects As Skeleton",
description="Check if you intend to export skeleton data for consumption by ZenAsset.",
default=False
)
animation_export: EnumProperty(name="Export Animation(s)",
description="If/how animation data should be exported.",
items=(
('NONE', "None", "Do not include animation data in the export."),
('ACTIVE', "Active", "Export animation extracted from the scene's Armature's active Action."),
('BATCH', "Batch", "Export a separate animation file for each Action in the scene.")
),
default='NONE')
def execute(self, context):
with open(self.filepath, 'wb') as output_file:
save_scene(
output_file=output_file,
scene=create_scene(
generate_triangle_strips=self.generate_triangle_strips,
apply_modifiers=self.apply_modifiers,
export_target=self.export_target,
skel_only=self.export_as_skeleton,
export_anim=self.export_with_animation
),
)
scene, armature_obj = create_scene(
generate_triangle_strips=self.generate_triangle_strips,
apply_modifiers=self.apply_modifiers,
export_target=self.export_target,
skel_only=self.animation_export != 'NONE') # Exclude geometry data (except root stuff) if we're doing anims
if self.animation_export != 'NONE' and not armature_obj:
raise Exception("Could not find an armature object from which to export animations!")
def write_scene_to_file(filepath : str, scene_to_write : Scene):
with open(filepath, 'wb') as output_file:
save_scene(output_file=output_file, scene=scene_to_write)
if self.animation_export == 'ACTIVE':
set_scene_animation(scene, armature_obj)
write_scene_to_file(self.filepath, scene)
elif self.animation_export == 'BATCH':
export_dir = self.filepath if os.path.isdir(self.filepath) else os.path.dirname(self.filepath)
for action in bpy.data.actions:
anim_save_path = os.path.join(export_dir, action.name + ".msh")
armature_obj.animation_data.action = action
set_scene_animation(scene, armature_obj)
write_scene_to_file(anim_save_path, scene)
else:
write_scene_to_file(self.filepath, scene)
return {'FINISHED'}
@@ -139,13 +154,11 @@ def menu_func_export(self, context):
class ImportMSH(Operator, ImportHelper):
""" Import an SWBF .msh file. """
bl_idname = "swbf_msh.import"
bl_label = "Import SWBF .msh File"
bl_label = "Import SWBF .msh File(s)"
filename_ext = ".msh"
files: CollectionProperty(
@@ -160,8 +173,8 @@ class ImportMSH(Operator, ImportHelper):
)
animation_only: BoolProperty(
name="Import Animation Only",
description="Import animation and append as a new action to currently selected armature.",
name="Import Animation(s)",
description="Import on or more animations from the selected files and append each as a new Action to currently selected Armature.",
default=False
)
@@ -176,10 +189,10 @@ class ImportMSH(Operator, ImportHelper):
with open(filepath, 'rb') as input_file:
scene = read_scene(input_file, self.animation_only)
if not self.animation_only:
extract_scene(filepath, scene)
else:
extract_and_apply_anim(filepath, scene)
if not self.animation_only:
extract_scene(filepath, scene)
else:
extract_and_apply_anim(filepath, scene)
return {'FINISHED'}

View File

@@ -10,7 +10,7 @@ from .msh_material_utilities import _REVERSE_RENDERTYPES_MAPPING
UI_MATERIAL_RENDERTYPES = (
('NORMAL_BF2', "00 Normal (SWBF2)", UI_RENDERTYPE_NORMAL_BF2_DESC),
('NORMAL_BF2', "00 Standard (SWBF2)", UI_RENDERTYPE_NORMAL_BF2_DESC),
('SCROLLING_BF2', "03 Scrolling (SWBF2)", UI_RENDERTYPE_SCROLLING_BF2_DESC),
('ENVMAPPED_BF2', "06 Envmapped (SWBF2)", UI_RENDERTYPE_ENVMAPPED_BF2_DESC),
('ANIMATED_BF2', "07 Animated (SWBF2)", UI_RENDERTYPE_ANIMATED_BF2_DESC),

View File

@@ -399,7 +399,7 @@ def expand_armature(armature: bpy.types.Object) -> List[Model]:
# set model parent to SKIN object if there is one
# set model parent to armature parent if there is one
else:
for child_obj in armature.children:
for child_obj in armature.original.children:
if child_obj.vertex_groups and not get_is_model_hidden(child_obj) and not child_obj.parent_bone:
model.parent = child_obj.name
break

View File

@@ -19,7 +19,21 @@ from .msh_anim_gather import extract_anim
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool, export_anim: bool) -> Scene:
def set_scene_animation(scene : Scene, armature_obj : bpy.types.Object):
if not scene or not armature_obj:
return
root = scene.models[0]
scene.animation = extract_anim(armature_obj, root.name)
def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_target: str, skel_only: bool) -> Tuple[Scene, bpy.types.Object]:
""" Create a msh Scene from the active Blender scene. """
scene = Scene()
@@ -47,17 +61,11 @@ def create_scene(generate_triangle_strips: bool, apply_modifiers: bool, export_t
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 or root.model_type == ModelType.BONE):
# For ZenAsset
inject_dummy_data(root)
return scene
return scene, armature_obj
def create_scene_aabb(scene: Scene) -> SceneAABB:

View File

@@ -35,6 +35,10 @@ def extract_and_apply_anim(filename : str, scene : Scene):
else:
head, tail = os.path.split(filename)
anim_name = tail.split(".")[0]
if anim_name in bpy.data.actions:
bpy.data.actions.remove(bpy.data.actions[anim_name], do_unlink=True)
action = bpy.data.actions.new(anim_name)
action.use_fake_user = True
@@ -248,6 +252,7 @@ def extract_models(scene: Scene, materials_map : Dict[str, bpy.types.Material])
model_map[model.name] = new_obj
new_obj.name = model.name
if model.parent:
new_obj.parent = model_map[model.parent]