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:
@@ -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'}
|
||||
|
||||
|
@@ -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),
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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]
|
||||
|
Reference in New Issue
Block a user