From b3d0735f12bbe4bc2211aa48ee12d365f469f34f Mon Sep 17 00:00:00 2001 From: Lila <87947656+Lilaa3@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:06:59 +0100 Subject: [PATCH] Objects can now have their own data --- fast64_internal/sm64/__init__.py | 24 +- fast64_internal/sm64/animation/__init__.py | 12 +- fast64_internal/sm64/animation/classes.py | 10 +- fast64_internal/sm64/animation/operators.py | 167 ++++---- fast64_internal/sm64/animation/panels.py | 66 ++-- fast64_internal/sm64/animation/properties.py | 393 +++++++++---------- fast64_internal/sm64/settings/properties.py | 5 +- fast64_internal/sm64/sm64_objects.py | 5 +- 8 files changed, 361 insertions(+), 321 deletions(-) diff --git a/fast64_internal/sm64/__init__.py b/fast64_internal/sm64/__init__.py index 0e5ba96c7..ae4ac9cfc 100644 --- a/fast64_internal/sm64/__init__.py +++ b/fast64_internal/sm64/__init__.py @@ -74,12 +74,12 @@ ) from .animation import ( - sm64_anim_panel_register, - sm64_anim_panel_unregister, - sm64_anim_properties_register, - sm64_anim_properties_unregister, - sm64_anim_operator_register, - sm64_anim_operator_unregister, + anim_panel_register, + anim_panel_unregister, + anim_props_register, + anim_props_unregister, + anim_operator_register, + anim_operator_unregister, ) from .tools import tools_panels_register, tools_panels_unregister, tools_operators_register, tools_operators_unregister @@ -102,7 +102,7 @@ def sm64_panel_register(): sm64_spline_panel_register() sm64_dl_writer_panel_register() sm64_dl_parser_panel_register() - sm64_anim_panel_register() + anim_panel_register() def sm64_panel_unregister(): @@ -118,11 +118,13 @@ def sm64_panel_unregister(): sm64_spline_panel_unregister() sm64_dl_writer_panel_unregister() sm64_dl_parser_panel_unregister() - sm64_anim_panel_unregister() + anim_panel_unregister() def sm64_register(register_panels: bool): tools_operators_register() + anim_props_register() + anim_operator_register() sm64_col_register() sm64_bone_register() sm64_cam_register() @@ -133,8 +135,6 @@ def sm64_register(register_panels: bool): sm64_spline_register() sm64_dl_writer_register() sm64_dl_parser_register() - sm64_anim_properties_register() - sm64_anim_operator_register() settings_props_register() if register_panels: @@ -143,6 +143,8 @@ def sm64_register(register_panels: bool): def sm64_unregister(unregister_panels: bool): tools_operators_unregister() + anim_props_unregister() + anim_operator_unregister() sm64_col_unregister() sm64_bone_unregister() sm64_cam_unregister() @@ -153,8 +155,6 @@ def sm64_unregister(unregister_panels: bool): sm64_spline_unregister() sm64_dl_writer_unregister() sm64_dl_parser_unregister() - sm64_anim_properties_unregister() - sm64_anim_operator_unregister() settings_props_unregister() if unregister_panels: diff --git a/fast64_internal/sm64/animation/__init__.py b/fast64_internal/sm64/animation/__init__.py index ac8ac5057..48e1786c1 100644 --- a/fast64_internal/sm64/animation/__init__.py +++ b/fast64_internal/sm64/animation/__init__.py @@ -1,12 +1,12 @@ from .operators import ( - sm64_anim_operator_register, - sm64_anim_operator_unregister, + anim_operator_register, + anim_operator_unregister, ) from .properties import ( - sm64_anim_properties_register, - sm64_anim_properties_unregister, + anim_props_register, + anim_props_unregister, ) from .panels import ( - sm64_anim_panel_register, - sm64_anim_panel_unregister, + anim_panel_register, + anim_panel_unregister, ) diff --git a/fast64_internal/sm64/animation/classes.py b/fast64_internal/sm64/animation/classes.py index f0e85fe79..ddd0bb4ee 100644 --- a/fast64_internal/sm64/animation/classes.py +++ b/fast64_internal/sm64/animation/classes.py @@ -387,12 +387,14 @@ def to_props(self, action, actor_name: str, remove_name_footer: bool = True, use main_header = self.headers[0] if not self.action_name: - if isinstance(main_header.reference, str): - self.action_name = main_header.reference - if remove_name_footer: - self.action_name = self.action_name.lstrip("anim_") + if main_header.file_name: + self.action_name = main_header.file_name.rstrip(".c").rstrip(".inc") elif isinstance(main_header.reference, int): self.action_name = hex(main_header.reference) + if remove_name_footer: + index = self.action_name.find("anim_") + if index != -1: + self.action_name = self.action_name[index + 5 :] action.name = self.action_name action_props.indices_table, action_props.indices_address = ( diff --git a/fast64_internal/sm64/animation/operators.py b/fast64_internal/sm64/animation/operators.py index 000fee3ae..46af50b6c 100644 --- a/fast64_internal/sm64/animation/operators.py +++ b/fast64_internal/sm64/animation/operators.py @@ -2,7 +2,7 @@ import bpy from bpy.utils import register_class, unregister_class -from bpy.types import Context, Object, Scene, Operator +from bpy.types import Context, Object, Scene, Operator, Action from bpy.path import abspath from bpy.props import ( EnumProperty, @@ -51,28 +51,29 @@ if TYPE_CHECKING: from ..settings.properties import SM64_Properties - from .properties import SM64_AnimExportProps, SM64_AnimImportProps, SM64_AnimTableProps + from .properties import ( + SM64_AnimProps, + SM64_AnimImportProps, + SM64_AnimTableProps, + SM64_ActionProps, + ) def emulate_no_loop(scene: Scene): - export_props = scene.fast64.sm64.anim_export - - if not export_props.played_action: - return - - try: - header = export_props.played_action.fast64.sm64.header_from_index(export_props.played_header) - except ValueError: - export_props.played_action = None - return - if not bpy.context.screen.is_animation_playing: - export_props.played_action = None + animation_props: SM64_AnimProps = scene.fast64.sm64.animation + played_action: Action = animation_props.played_action + action_props: SM64_ActionProps = played_action.fast64.sm64 + if ( + not played_action + or animation_props.played_header >= len(action_props.headers) + or not bpy.context.screen.is_animation_playing + ): + played_action = None return - frame = scene.frame_current - loop_start, loop_end = export_props.played_loop_start, export_props.played_loop_end - + header = action_props.header_from_index(animation_props.played_header) + loop_start, loop_end = header.get_frame_range(played_action)[1:3] if header.backwards: if frame < loop_start: if header.no_loop: @@ -98,15 +99,19 @@ def execute_operator(self, context: Context): animation_operator_checks(context) scene = context.scene - export_props = scene.fast64.sm64.anim_export + scene_anim_props = scene.fast64.sm64.animation + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = context.object.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = scene_anim_props if self.played_action: played_action = get_action(self.played_action) else: - played_action = export_props.selected_action + played_action = animation_props.selected_action header = played_action.fast64.sm64.header_from_index(self.played_header) - start_frame, loop_start, loop_end = header.get_frame_range(played_action) + start_frame = header.get_frame_range(played_action)[0] scene.frame_set(start_frame) scene.render.fps = 30 @@ -118,14 +123,8 @@ def execute_operator(self, context: Context): bpy.ops.screen.animation_play() - export_props.played_header = self.played_header - export_props.played_action = played_action - - export_props.played_start_frame, export_props.played_loop_start, export_props.played_loop_end = ( - start_frame, - loop_start, - loop_end, - ) + scene_anim_props.played_header = self.played_header + scene_anim_props.played_action = played_action return {"FINISHED"} @@ -155,8 +154,11 @@ class SM64_TableOperations(Operator): header_variant: IntProperty() def execute_operator(self, context: Context): - export_props = context.scene.fast64.sm64.anim_export - table_elements = export_props.table.elements + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = context.object.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = context.scene.fast64.sm64.animation + table_elements = animation_props.table.elements if self.array_index < len(table_elements): table_element = table_elements[self.array_index] @@ -232,7 +234,7 @@ def execute_operator(self, context): added_variant.override_name = False added_variant.override_enum = False added_variant.custom_name = added_variant.get_anim_name( - context.scene.fast64.sm64.anim_export.actor_name, action + context.scene.fast64.sm64.animation.actor_name, action ) elif self.type == "REMOVE": variants.remove(self.array_index) @@ -266,35 +268,38 @@ def execute_operator(self, context: Context): scene = context.scene sm64_props: SM64_Properties = scene.fast64.sm64 - export_props: SM64_AnimExportProps = sm64_props.anim_export - table_props: SM64_AnimTableProps = export_props.table + armature_obj: Object = context.selected_objects[0] + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = armature_obj.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = sm64_props.animation + table_props: SM64_AnimTableProps = animation_props.table actions = table_props.actions - armature_obj: Object = context.selected_objects[0] print("Stashing all actions in table") for action in actions: stashActionInArmature(armature_obj, action) - actor_name = export_props.actor_name + actor_name = animation_props.actor_name print("Reading table data") table: SM64_AnimTable = table_props.to_table_class( armature_obj, sm64_props.blender_to_sm64_scale, - sm64_props.export_type == "C" or not export_props.is_binary_dma, - export_props.quick_read, + sm64_props.export_type == "C" or not animation_props.is_binary_dma, + animation_props.quick_read, actor_name, ) print("Exporting table data") if sm64_props.export_type == "C": - header_type = export_props.header_type + header_type = animation_props.header_type - anim_dir_path, dir_path, geo_dir_path, level_name = export_props.get_animation_paths(True) + anim_dir_path, dir_path, geo_dir_path, level_name = animation_props.get_animation_paths(True) if header_type != "Custom": applyBasicTweaks(abspath(sm64_props.decomp_path)) @@ -338,15 +343,15 @@ def execute_operator(self, context: Context): if not header_type in {"Custom", "DMA"}: update_includes( level_name, - export_props.group_name, + animation_props.group_name, toAlnum(actor_name), dir_path, header_type, True, ) elif sm64_props.export_type == "Insertable Binary": - data, ptrs = table.to_binary_combined(export_props.is_binary_dma, 0) - path = abspath(os.path.join(export_props.directory_path, table_props.insertable_file_name)) + data, ptrs = table.to_binary_combined(animation_props.is_binary_dma, 0) + path = abspath(os.path.join(animation_props.directory_path, table_props.insertable_file_name)) writeInsertableFile(path, insertableBinaryTypes["Animation Table"], ptrs, 0, data) else: raise PluginError(f"Unimplemented export type ({sm64_props.export_type})") @@ -376,27 +381,32 @@ def execute_operator(self, context: Context): animation_operator_checks(context) scene = context.scene - sm64_props = scene.fast64.sm64 - export_props = sm64_props.anim_export - table_props = export_props.table + sm64_props: SM64_Properties = scene.fast64.sm64 - action = export_props.selected_action - action_props = action.fast64.sm64 armature_obj: Object = context.selected_objects[0] + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = armature_obj.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = sm64_props.animation + table_props: SM64_AnimTableProps = animation_props.table + + action = animation_props.selected_action + action_props = action.fast64.sm64 stashActionInArmature(armature_obj, action) - actor_name = export_props.actor_name + actor_name = animation_props.actor_name animation: SM64_Anim = action_props.to_animation_class( action, armature_obj, sm64_props.blender_to_sm64_scale, - sm64_props.export_type == "C" or not export_props.is_binary_dma, + sm64_props.export_type == "C" or not animation_props.is_binary_dma, + animation_props.quick_read, actor_name, ) if sm64_props.export_type == "C": - header_type = export_props.header_type + header_type = animation_props.header_type - anim_dir_path, dir_path, geo_dir_path, level_name = export_props.get_animation_paths( + anim_dir_path, dir_path, geo_dir_path, level_name = animation_props.get_animation_paths( create_directories=True ) anim_file_name = action_props.get_anim_file_name(action) @@ -408,7 +418,7 @@ def execute_operator(self, context: Context): with open(anim_path, "w", newline="\n") as file: file.write( animation.to_c( - export_props.is_c_dma_structure, + animation_props.is_c_dma_structure, sm64_props.refresh_version, ) ) @@ -437,7 +447,7 @@ def execute_operator(self, context: Context): if not header_type in {"Custom", "DMA"}: update_includes( level_name, - export_props.group_name, + animation_props.group_name, toAlnum(actor_name), dir_path, header_type, @@ -474,10 +484,15 @@ def execute_operator(self, context): bpy.ops.object.mode_set(mode="OBJECT") animation_operator_checks(context, False) - sm64_props = context.scene.fast64.sm64 - import_props = sm64_props.anim_import + scene = context.scene + sm64_props: SM64_Properties = scene.fast64.sm64 - armatureObj: Object = context.selected_objects[0] + armature_obj: Object = context.selected_objects[0] + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = armature_obj.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = sm64_props.animation + import_props: SM64_AnimImportProps = animation_props.importing animations: dict[str, SM64_Anim] = {} table: SM64_AnimTable = SM64_AnimTable() @@ -505,8 +520,8 @@ def execute_operator(self, context): raise PluginError("Unimplemented import type.") for _, data in animations.items(): - animation_import_to_blender(armatureObj, sm64_props.blender_to_sm64_scale, data) - sm64_props.anim_export.table.from_anim_table_class(table) + animation_import_to_blender(armature_obj, sm64_props.blender_to_sm64_scale, data) + sm64_props.animation.table.from_anim_table_class(table) self.report({"INFO"}, "Success!") return {"FINISHED"} @@ -534,11 +549,14 @@ def execute_operator(self, context): scene = context.scene sm64_props: SM64_Properties = scene.fast64.sm64 - export_props: SM64_AnimExportProps = sm64_props.anim_export - import_props: SM64_AnimImportProps = sm64_props.anim_import - table_props: SM64_AnimTableProps = export_props.table - armature_obj: Object = context.selected_objects[0] + if context.space_data.context == "OBJECT": + animation_props: SM64_AnimProps = armature_obj.fast64.sm64.animation + else: + animation_props: SM64_AnimProps = sm64_props.animation + + import_props: SM64_AnimImportProps = animation_props.importing + table_props: SM64_AnimTableProps = animation_props.table animations: dict[str, SM64_Anim] = {} table = SM64_AnimTable() @@ -597,7 +615,7 @@ def execute_operator(self, context): context.selected_objects[0], sm64_props.blender_to_sm64_scale, data, - export_props.actor_name, + animation_props.actor_name, import_props.remove_name_footer, import_props.use_custom_name, ) @@ -628,10 +646,16 @@ class SM64_SearchMarioAnimEnum(Operator): mario_animations: EnumProperty(items=marioAnimationNames) def execute(self, context): - anim_import = context.scene.fast64.sm64.anim_import + scene = context.scene + sm64_props: SM64_Properties = scene.fast64.sm64 + armature_obj: Object = context.selected_objects[0] + if context.space_data.context == "OBJECT": + import_props: SM64_AnimImportProps = armature_obj.fast64.sm64.animation.importing + else: + import_props: SM64_AnimImportProps = sm64_props.animation.anim_import.importing context.region.tag_redraw() - anim_import.mario_animation = int(self.mario_animations) + importing.mario_animation = int(self.mario_animations) self.report({"INFO"}, "Selected: " + self.mario_animations) return {"FINISHED"} @@ -640,7 +664,7 @@ def invoke(self, context, event): return {"RUNNING_MODAL"} -sm64_anim_operators = ( +operators = ( # Exporting SM64_ExportAnimTable, SM64_ExportAnim, @@ -654,16 +678,15 @@ def invoke(self, context, event): ) -def sm64_anim_operator_register(): - for cls in sm64_anim_operators: +def anim_operator_register(): + for cls in operators: register_class(cls) bpy.app.handlers.frame_change_pre.append(emulate_no_loop) -def sm64_anim_operator_unregister(): - for cls in reversed(sm64_anim_operators): +def anim_operator_unregister(): + for cls in reversed(operators): unregister_class(cls) - if emulate_no_loop in bpy.app.handlers.frame_change_pre: - bpy.app.handlers.frame_change_pre.remove(emulate_no_loop) + bpy.app.handlers.frame_change_pre.remove(emulate_no_loop) diff --git a/fast64_internal/sm64/animation/panels.py b/fast64_internal/sm64/animation/panels.py index 6a9c616bc..30a8491f4 100644 --- a/fast64_internal/sm64/animation/panels.py +++ b/fast64_internal/sm64/animation/panels.py @@ -1,42 +1,62 @@ from bpy.utils import register_class, unregister_class -from bpy.types import Context +from bpy.types import Context, Panel from ...panels import SM64_Panel -class SM64_ExportAnimPanel(SM64_Panel): - bl_idname = "SM64_PT_export_anim" - bl_label = "SM64 Animation Exporting" - goal = "Export Object/Actor/Anim" +from typing import TYPE_CHECKING - def draw(self, context: Context): - context.scene.fast64.sm64.anim_export.draw_props(self.layout.column(), context.scene.fast64.sm64.export_type) +if TYPE_CHECKING: + from ..settings.properties import SM64_Properties -class SM64_ImportAnimPanel(SM64_Panel): - bl_idname = "SM64_PT_import_anim" - bl_label = "SM64 Animation Importing" - goal = "Import" - isImport = True +class SM64_AnimPanel(SM64_Panel): + bl_idname = "SM64_PT_export_anim" + bl_label = "SM64 Animations" + goal = "Export Object/Actor/Anim" def draw(self, context: Context): - col = self.layout.column() - - sm64_props = context.scene.fast64.sm64 + sm64_props: SM64_Properties = context.scene.fast64.sm64 + sm64_props.animation.draw_props( + self.layout.column(), + sm64_props.export_type, + sm64_props.show_importing_menus, + sm64_props.import_rom, + ) + + +class SM64_ObjAnimPanel(Panel): + bl_label = "Object Animation Inspector" + bl_idname = "OBJECT_PT_SM64_Obj_Anim_Inspector" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + bl_options = {"HIDE_HEADER"} + + @classmethod + def poll(cls, context): + scene = context.scene + if scene.gameEditorMode != "SM64": + return False + scene_goal = scene.fast64.sm64.goal + return scene_goal == "All" or scene_goal == "Export Object/Actor/Anim" - sm64_props.anim_import.draw_props(col, sm64_props.import_rom) + def draw(self, context: Context): + box = self.layout.box().column() + box.box().label(text=self.bl_label) + context.object.fast64.sm64.animation.draw_props(box, context.scene.fast64.sm64.export_type) -sm64_anim_panels = ( - SM64_ExportAnimPanel, - SM64_ImportAnimPanel, +panels = ( + SM64_AnimPanel, + SM64_ObjAnimPanel, ) -def sm64_anim_panel_register(): - for cls in sm64_anim_panels: +def anim_panel_register(): + for cls in panels: register_class(cls) -def sm64_anim_panel_unregister(): - for cls in reversed(sm64_anim_panels): +def anim_panel_unregister(): + for cls in reversed(panels): unregister_class(cls) diff --git a/fast64_internal/sm64/animation/properties.py b/fast64_internal/sm64/animation/properties.py index 52c6df8cb..77fe6a18c 100644 --- a/fast64_internal/sm64/animation/properties.py +++ b/fast64_internal/sm64/animation/properties.py @@ -63,7 +63,7 @@ class SM64_AnimHeaderProps(PropertyGroup): expand_tab_in_action: BoolProperty(name="Header Properties", default=True) - header_variant: IntProperty(name="Header Variant Number", default=0) + header_variant: IntProperty(name="Header Variant Number", min=0) override_name: BoolProperty(name="Override Name") custom_name: StringProperty(name="Name", default="anim_00") @@ -334,7 +334,7 @@ def draw_props( if export_type == "Binary": self.draw_binary(col, is_dma) - elif export_type == "C": + if draw_names: self.draw_names(col, action, actor_name, generate_enums) col.separator() prop_split(col, self, "trans_divisor", "Translation Divisor") @@ -359,8 +359,8 @@ class SM64_ActionProps(PropertyGroup): end_address: StringProperty(name="End Address", default=hex(18874112)) header: PointerProperty(type=SM64_AnimHeaderProps) + variants_tab: BoolProperty(name="Header Variants") header_variants: CollectionProperty(type=SM64_AnimHeaderProps) - expand_variants_tab: BoolProperty(name="Header Variants", default=True) @property def headers(self) -> list[SM64_AnimHeaderProps]: @@ -568,10 +568,10 @@ def draw_variants( col.prop( self, - "expand_variants_tab", - icon="TRIA_DOWN" if self.expand_variants_tab else "TRIA_RIGHT", + "variants_tab", + icon="TRIA_DOWN" if self.variants_tab else "TRIA_RIGHT", ) - if not self.expand_variants_tab: + if not self.variants_tab: return op_row = col.row() @@ -666,8 +666,8 @@ def draw_props( col = col.column() col.scale_y = header_scale_y - if specific_variant is None: - self.draw_variants( + if specific_variant: + self.headers[specific_variant].draw_props( layout=col, action=action, draw_table_operations=draw_table_operations, @@ -678,7 +678,7 @@ def draw_props( is_dma=is_dma, ) else: - self.headers[specific_variant].draw_props( + self.draw_variants( layout=col, action=action, draw_table_operations=draw_table_operations, @@ -701,8 +701,6 @@ def header(self) -> SM64_AnimHeaderProps: class SM64_AnimTableProps(PropertyGroup): - expand_tab: BoolProperty(name="Table Export", default=True) - update_table: BoolProperty( name="Update Table On Action Export", description="Update table outside of table exports", @@ -1006,32 +1004,173 @@ def draw_props( actions.append(table_element.action) -class SM64_AnimExportProps(PropertyGroup): - """Scene SM64 animation export properties found under scene.fast64.sm64.anim_export""" +class SM64_AnimImportProps(PropertyGroup): + """Scene SM64 animation import properties found under scene.fast64.sm64.anim_import""" - action_settings_tab: BoolProperty(name="Action Export", default=True) + import_type: EnumProperty(items=enumAnimImportTypes, name="Type", default="C") - played_header: IntProperty(min=0, default=0) - played_action: PointerProperty(name="Action", type=Action) - played_start_frame: IntProperty(min=0, default=0) - played_loop_start: IntProperty(min=0, default=0) - played_loop_end: IntProperty(min=0, default=0) + binary_import_type: EnumProperty( + items=enumAnimBinaryImportTypes, + name="Type", + default="Animation", + ) - selected_action: PointerProperty(name="Action", type=Action) + assume_bone_count: BoolProperty( + name="Assume Bone Count", + default=True, + description="When enabled, the selected armature's bone count will be used instead of the header's, " + "as old fast64 binary exports did no export this value", + ) + address: StringProperty(name="Address", default="0600FC48") + is_segmented_address: BoolProperty(name="Is Segmented Address") + level: EnumProperty(items=level_enums, name="Level", default="IC") - table: PointerProperty(type=SM64_AnimTableProps) + read_entire_table: BoolProperty( + name="Read All Animations", + ) + table_index: IntProperty(name="Table Index", min=0) + ignore_null: BoolProperty(name="Ignore NULL Delimiter") - quick_read: BoolProperty( - name="Quick Data Read", default=True, description="Read fcurves directly, should work with the majority of rigs" + dma_table_address: StringProperty(name="DMA Table Address", default="0x4EC000") + mario_animation: IntProperty(name="Selected Preset Mario Animation") + + insertable_read_from_rom: BoolProperty( + name="Read From Import ROM", + description="When enabled, the importer will read from the import ROM given a non defined address", + ) + + path: StringProperty( + name="Path", + subtype="FILE_PATH", + default="anims/", + ) + remove_name_footer: BoolProperty( + name="Remove Name Footers", + description='Remove "anim_" from imported animations', + default=True, + ) + use_custom_name: BoolProperty( + name="Use Custom Name", + default=True, ) + @property + def mario_or_table_index(self): + return ( + self.mario_animation + if self.binary_import_type == "DMA" and self.mario_animation != -1 + else self.table_index + ) + + def draw_c(self, layout: UILayout): + col = layout.column() + + col.prop(self, "remove_name_footer") + col.prop(self, "use_custom_name") + + def draw_binary(self, layout: UILayout, import_rom_path: str): + col = layout.column() + try: + import_rom_checks(abspath(import_rom_path)) + except Exception as exc: + multilineLabel(col.box(), str(exc), "ERROR") + col = col.column() + col.enabled = False + + prop_split(col, self, "binary_import_type", "Binary Type") + + if self.binary_import_type == "DMA": + prop_split(col, self, "dma_table_address", "DMA Table Address") + + col.prop(self, "read_entire_table") + if not self.read_entire_table: + col.operator(SM64_SearchMarioAnimEnum.bl_idname, icon="VIEWZOOM") + if self.mario_animation == -1: + prop_split(col, self, "table_index", "Entry") + else: + col.box().label(text=f"{marioAnimationNames[self.mario_animation + 1][1]}") + else: + prop_split(col, self, "level", "Level") + + col.prop(self, "is_segmented_address") + prop_split(col, self, "address", "Address") + + if self.binary_import_type == "Table": + col.prop(self, "read_entire_table") + if not self.read_entire_table: + prop_split(col, self, "table_index", "List Index") + col.prop(self, "ignore_null") + + def draw_insertable_binary(self, layout: UILayout, import_rom_path: str): + col = layout.column() + + col.label(text="Type will be read from the data type of the files") + col.separator() + + from_rom_box = col.box().column() + from_rom_box.prop(self, "insertable_read_from_rom") + if self.insertable_read_from_rom: + try: + import_rom_checks(abspath(import_rom_path)) + except Exception as exc: + multilineLabel(from_rom_box.box(), str(exc), "ERROR") + from_rom_box = from_rom_box.column() + from_rom_box.enabled = False + prop_split(from_rom_box, self, "level", "Level") + from_rom_box.prop(self, "is_segmented_address") + prop_split(from_rom_box, self, "address", "Address") + + table_box = col.box().column() + table_box.label(text="Table Imports") + table_box.prop(self, "read_entire_table") + if not self.read_entire_table: + prop_split(table_box, self, "table_index", "List Index") + table_box.prop(self, "ignore_null") + + def draw_props(self, layout: UILayout, import_rom_path: str): + col = layout.column() + + prop_split(col, self, "import_type", "Type") + col.separator() + + if self.import_type in {"C", "Insertable Binary"}: + prop_split(col, self, "path", "Path") + col.label(text="Folders and individual files are supported as the path", icon="INFO") + path_ui_warnings(col, abspath(self.path)) + + if self.import_type == "C": + self.draw_c(col) + elif self.import_type == "Binary": + self.draw_binary(col, import_rom_path) + elif self.import_type == "Insertable Binary": + self.draw_insertable_binary(col, import_rom_path) + + col.separator() + col.operator(SM64_ImportAnim.bl_idname, icon="IMPORT") + if self.import_type in {"Binary", "C"}: + col.operator(SM64_ImportAllMarioAnims.bl_idname, icon="IMPORT") + + +class SM64_AnimProps(PropertyGroup): + """Scene SM64 animation properties found under scene.fast64.sm64.animation""" + + played_header: IntProperty(min=0) + played_action: PointerProperty(name="Action", type=Action) + + table_tab: BoolProperty(name="Table") + table: PointerProperty(type=SM64_AnimTableProps) + importing_tab: BoolProperty(name="Importing") + importing: PointerProperty(type=SM64_AnimImportProps) + + action_tab: BoolProperty(name="Action Export", default=True) + selected_action: PointerProperty(name="Action", type=Action) + directory_path: StringProperty(name="Directory Path", subtype="FILE_PATH") dma_folder: StringProperty(name="DMA Folder", default="assets/anims/") use_dma_structure: BoolProperty( name="Use DMA Structure", description="When enabled, the Mario animation converter order is used (headers, indicies, values)", - default=False, ) actor_name_prop: StringProperty(name="Name", default="mario") group_name: StringProperty( @@ -1047,6 +1186,10 @@ class SM64_AnimExportProps(PropertyGroup): binary_overwrite_dma_entry: BoolProperty(name="Overwrite DMA Entry") is_binary_dma: BoolProperty(name="Is DMA", default=True) + quick_read: BoolProperty( + name="Quick Data Read", default=True, description="Read fcurves directly, should work with the majority of rigs" + ) + @property def actor_name(self): return self.actor_name_prop if self.header_type != "DMA" else None @@ -1105,12 +1248,8 @@ def get_animation_paths(self, create_directories: bool = False): def draw_action_properties(self, layout: UILayout, is_dma: bool, export_type: str): col = layout.column() - col.prop( - self, - "action_settings_tab", - icon="TRIA_DOWN" if self.action_settings_tab else "TRIA_RIGHT", - ) - if not self.action_settings_tab: + col.prop(self, "action_tab", icon="TRIA_DOWN" if self.action_tab else "TRIA_RIGHT") + if not self.action_tab: return col.prop(self, "selected_action") @@ -1127,13 +1266,8 @@ def draw_action_properties(self, layout: UILayout, is_dma: bool, export_type: st def draw_table_properties(self, layout: UILayout, is_dma: bool, export_type: str): col = layout.column() - - col.prop( - self.table, - "expand_tab", - icon="TRIA_DOWN" if self.table.expand_tab else "TRIA_RIGHT", - ) - if self.table.expand_tab: + col.prop(self, "table_tab", icon="TRIA_DOWN" if self.table_tab else "TRIA_RIGHT") + if self.table_tab: self.table.draw_props( col, is_dma, @@ -1142,6 +1276,12 @@ def draw_table_properties(self, layout: UILayout, is_dma: bool, export_type: str self.actor_name, ) + def draw_importing_properties(self, layout: UILayout, import_rom_path: str | None = None): + col = layout.column() + col.prop(self, "importing_tab", icon="TRIA_DOWN" if self.importing_tab else "TRIA_RIGHT") + if self.importing_tab: + self.importing.draw_props(col, import_rom_path) + def draw_binary_settings(self, layout: UILayout, export_type: str): col = layout.column() col.prop(self, "is_binary_dma") @@ -1197,7 +1337,13 @@ def draw_c_settings(self, layout: UILayout): self.table.draw_non_exclusive_settings(box, self.actor_name) col.separator() - def draw_props(self, layout: UILayout, export_type: str): + def draw_props( + self, + layout: UILayout, + export_type: str = "C", + show_importing: bool = True, + import_rom_path: str = "", + ): col = layout.column() is_dma = (export_type != "C" and self.is_binary_dma) or self.header_type == "DMA" @@ -1211,178 +1357,25 @@ def draw_props(self, layout: UILayout, export_type: str): col.separator() self.draw_action_properties(col.box(), is_dma, export_type) self.draw_table_properties(col.box(), is_dma, export_type) + if show_importing: + self.draw_importing_properties(col.box(), import_rom_path) -# Importing - - -class SM64_AnimImportProps(PropertyGroup): - """Scene SM64 animation import properties found under scene.fast64.sm64.anim_import""" - - import_type: EnumProperty(items=enumAnimImportTypes, name="Type", default="C") - - binary_import_type: EnumProperty( - items=enumAnimBinaryImportTypes, - name="Type", - default="Animation", - ) - - assume_bone_count: BoolProperty( - name="Assume Bone Count", - default=True, - description="When enabled, the selected armature's bone count will be used instead of the header's, " - "as old fast64 binary exports did no export this value", - ) - address: StringProperty(name="Address", default="0600FC48") - is_segmented_address: BoolProperty(name="Is Segmented Address") - level: EnumProperty(items=level_enums, name="Level", default="IC") - - read_entire_table: BoolProperty( - name="Read All Animations", - ) - table_index: IntProperty(name="Table Index", min=0) - ignore_null: BoolProperty(name="Ignore NULL Delimiter") - - dma_table_address: StringProperty(name="DMA Table Address", default="0x4EC000") - mario_animation: IntProperty(name="Selected Preset Mario Animation") - - insertable_read_from_rom: BoolProperty( - name="Read From Import ROM", - description="When enabled, the importer will read from the import ROM given a non defined address", - ) - - path: StringProperty( - name="Path", - subtype="FILE_PATH", - default="anims/", - ) - remove_name_footer: BoolProperty( - name="Remove Name Footers", - description='Remove "anim_" from imported animations', - default=True, - ) - use_custom_name: BoolProperty( - name="Use Custom Name", - default=False, - ) - - @property - def mario_or_table_index(self): - return ( - self.mario_animation - if self.binary_import_type == "DMA" and self.mario_animation != -1 - else self.table_index - ) - - def draw_c(self, layout: UILayout): - col = layout.column() - - col.prop(self, "remove_name_footer") - col.prop(self, "use_custom_name") - - def draw_binary(self, layout: UILayout, import_rom_path: str): - col = layout.column() - try: - import_rom_checks(abspath(import_rom_path)) - except Exception as exc: - multilineLabel(col.box(), str(exc), "ERROR") - col = col.column() - col.enabled = False - - prop_split(col, self, "binary_import_type", "Binary Type") - - if self.binary_import_type == "DMA": - prop_split(col, self, "dma_table_address", "DMA Table Address") - - col.prop(self, "read_entire_table") - if not self.read_entire_table: - col.operator(SM64_SearchMarioAnimEnum.bl_idname, icon="VIEWZOOM") - if self.mario_animation == -1: - prop_split(col, self, "table_index", "Entry") - else: - col.box().label(text=f"{marioAnimationNames[self.mario_animation + 1][1]}") - else: - prop_split(col, self, "level", "Level") - - col.prop(self, "is_segmented_address") - prop_split(col, self, "address", "Address") - - if self.binary_import_type == "Table": - col.prop(self, "read_entire_table") - if not self.read_entire_table: - prop_split(col, self, "table_index", "List Index") - col.prop(self, "ignore_null") - - def draw_insertable_binary(self, layout: UILayout, import_rom_path: str): - col = layout.column() - - col.label(text="Type will be read from the data type of the files") - col.separator() - - from_rom_box = col.box().column() - from_rom_box.prop(self, "insertable_read_from_rom") - if self.insertable_read_from_rom: - try: - import_rom_checks(abspath(import_rom_path)) - except Exception as exc: - multilineLabel(from_rom_box.box(), str(exc), "ERROR") - from_rom_box = from_rom_box.column() - from_rom_box.enabled = False - prop_split(from_rom_box, self, "level", "Level") - from_rom_box.prop(self, "is_segmented_address") - prop_split(from_rom_box, self, "address", "Address") - - table_box = col.box().column() - table_box.label(text="Table Imports") - table_box.prop(self, "read_entire_table") - if not self.read_entire_table: - prop_split(table_box, self, "table_index", "List Index") - table_box.prop(self, "ignore_null") - - def draw_props(self, layout: UILayout, import_rom_path: str): - col = layout.column() - - prop_split(col, self, "import_type", "Type") - col.separator() - - if self.import_type in {"C", "Insertable Binary"}: - prop_split(col, self, "path", "Path") - col.label(text="Folders and individual files are supported as the path", icon="INFO") - path_ui_warnings(col, abspath(self.path)) - - if self.import_type == "C": - self.draw_c(col) - else: - if self.import_type == "Binary": - self.draw_binary(col, import_rom_path) - elif self.import_type == "Insertable Binary": - self.draw_insertable_binary(col, import_rom_path) - - binary_col.prop(self, "assume_bone_count") - - col.separator() - col.operator(SM64_ImportAnim.bl_idname, icon="IMPORT") - if self.import_type in {"Binary", "C"}: - col.operator(SM64_ImportAllMarioAnims.bl_idname, icon="IMPORT") - - -sm64_anim_properties = ( - # Exporting +properties = ( SM64_AnimHeaderProps, SM64_TableElement, - SM64_AnimTableProps, SM64_ActionProps, - SM64_AnimExportProps, - # Importing + SM64_AnimTableProps, SM64_AnimImportProps, + SM64_AnimProps, ) -def sm64_anim_properties_register(): - for cls in sm64_anim_properties: +def anim_props_register(): + for cls in properties: register_class(cls) -def sm64_anim_properties_unregister(): - for cls in reversed(sm64_anim_properties): +def anim_props_unregister(): + for cls in reversed(properties): unregister_class(cls) diff --git a/fast64_internal/sm64/settings/properties.py b/fast64_internal/sm64/settings/properties.py index 32ea5eb59..8c6c0f696 100644 --- a/fast64_internal/sm64/settings/properties.py +++ b/fast64_internal/sm64/settings/properties.py @@ -22,7 +22,7 @@ prop_split, ) -from ..animation.properties import SM64_AnimExportProps, SM64_AnimImportProps +from ..animation.properties import SM64_AnimProps def decomp_path_update(self, context: Context): @@ -69,8 +69,7 @@ class SM64_Properties(PropertyGroup): compression_format: EnumProperty(items=enum_compression_formats, name="Compression", default="mio0") disable_scroll: BoolProperty(name="Disable Scrolling Textures") - anim_import: PointerProperty(type=SM64_AnimImportProps) - anim_export: PointerProperty(type=SM64_AnimExportProps) + animation: PointerProperty(type=SM64_AnimProps) def is_binary_export(self): return self.export_type in ["Binary", "Insertable Binary"] diff --git a/fast64_internal/sm64/sm64_objects.py b/fast64_internal/sm64/sm64_objects.py index 7dd2e5e79..0b18823b6 100644 --- a/fast64_internal/sm64/sm64_objects.py +++ b/fast64_internal/sm64/sm64_objects.py @@ -1,6 +1,5 @@ import math, bpy, mathutils from bpy.utils import register_class, unregister_class -from re import findall from .sm64_function_map import func_map from ..utility import ( @@ -16,6 +15,8 @@ prop_split, ) +from .animation.properties import SM64_AnimProps + from .sm64_constants import ( levelIDNames, enumLevelNames, @@ -1891,6 +1892,8 @@ class SM64_ObjectProperties(bpy.types.PropertyGroup): version: bpy.props.IntProperty(name="SM64_ObjectProperties Version", default=0) cur_version = 3 # version after property migration + animation: bpy.props.PointerProperty(type=SM64_AnimProps) + geo_asm: bpy.props.PointerProperty(type=SM64_GeoASMProperties) level: bpy.props.PointerProperty(type=SM64_LevelProperties) area: bpy.props.PointerProperty(type=SM64_AreaProperties)