diff --git a/.github/workflows/black-lint.yml b/.github/workflows/black-lint.yml index 317336173..ce3a147a7 100644 --- a/.github/workflows/black-lint.yml +++ b/.github/workflows/black-lint.yml @@ -10,4 +10,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: psf/black@stable + - uses: psf/black@23.12.1 \ No newline at end of file diff --git a/fast64_internal/sm64/animation/classes.py b/fast64_internal/sm64/animation/classes.py index e74e7fe71..b55c7b520 100644 --- a/fast64_internal/sm64/animation/classes.py +++ b/fast64_internal/sm64/animation/classes.py @@ -1,17 +1,14 @@ from collections import OrderedDict -import dataclasses from io import StringIO -import os from typing import Optional +import dataclasses +import os -from ..sm64_utility import SM64_ShortArray - -from .c_parser import CParser, Initialization - +from ...utility import PluginError, decodeSegmentedAddr, is_bit_active from ..sm64_constants import MAX_U16 - +from ..sm64_utility import SM64_ShortArray from .utility import RomReading, eval_num_from_str -from ...utility import PluginError, decodeSegmentedAddr, is_bit_active +from .c_parser import CParser, Initialization @dataclasses.dataclass diff --git a/fast64_internal/sm64/animation/exporting.py b/fast64_internal/sm64/animation/exporting.py index 9c726a6b4..a161e7a02 100644 --- a/fast64_internal/sm64/animation/exporting.py +++ b/fast64_internal/sm64/animation/exporting.py @@ -1,13 +1,14 @@ import os + import bpy import mathutils -from ..sm64_utility import radian_to_sm64_degree -from ..sm64_constants import NULL from ...utility import ( writeIfNotFound, writeInsertableFile, ) +from ..sm64_utility import radian_to_sm64_degree +from ..sm64_constants import NULL from .classes import SM64_Anim, SM64_AnimPair from .utility import anim_name_to_enum, get_anim_pose_bones diff --git a/fast64_internal/sm64/animation/importing.py b/fast64_internal/sm64/animation/importing.py index 7b6c382fa..38613a0d2 100644 --- a/fast64_internal/sm64/animation/importing.py +++ b/fast64_internal/sm64/animation/importing.py @@ -1,8 +1,8 @@ from collections import OrderedDict -import math -import os from typing import Optional from io import BufferedReader +import math +import os import dataclasses import bpy diff --git a/fast64_internal/sm64/animation/operators.py b/fast64_internal/sm64/animation/operators.py index b59fec138..806a777da 100644 --- a/fast64_internal/sm64/animation/operators.py +++ b/fast64_internal/sm64/animation/operators.py @@ -1,5 +1,6 @@ import math import os + import bpy from bpy.utils import register_class, unregister_class from bpy.types import Context, Object, Scene, Operator @@ -13,6 +14,7 @@ from ...utility import ( PluginError, applyBasicTweaks, + copyPropertyGroup, filepath_checks, toAlnum, raisePluginError, @@ -21,7 +23,6 @@ writeInsertableFile, ) from ...utility_anim import stashActionInArmature - from ..sm64_utility import import_rom_checks from ..sm64_level_parser import parseLevelAtPointer from ..sm64_constants import level_pointers @@ -197,22 +198,24 @@ def execute_operator(self, context): variants.move(self.array_index, self.array_index + 1) elif self.type == "ADD": variants.add() + added_variant = variants[-1] + added_variant.action = action + + copyPropertyGroup(action_props.headers[self.array_index + 1], added_variant) + variants.move(len(variants) - 1, self.array_index + 1) + action_props.update_header_variant_numbers() + + added_variant.expand_tab = True + added_variant.custom_name = added_variant.get_anim_name( + context.scene.fast64.sm64.anim_export.actor_name, action + ) elif self.type == "REMOVE": variants.remove(self.array_index) if self.type == "CLEAR": for _ in range(len(variants)): variants.remove(0) - if self.type == "ADD": - variants[-1].action = action - array_index = self.array_index - if len(variants) > 1: - array_index += 1 - variants[-1].copyHeader( - context.scene.fast64.sm64.anim_export.actor_name, action, action_props.headers[array_index] - ) - action_props.update_header_variant_numbers() return {"FINISHED"} diff --git a/fast64_internal/sm64/animation/panels.py b/fast64_internal/sm64/animation/panels.py index 7ddcb9858..95196f825 100644 --- a/fast64_internal/sm64/animation/panels.py +++ b/fast64_internal/sm64/animation/panels.py @@ -1,10 +1,10 @@ import bpy from bpy.utils import register_class, unregister_class from bpy.path import abspath +from bpy.types import Context from ...panels import SM64_Panel from ...utility import multilineLabel - from ..sm64_utility import import_rom_checks @@ -13,7 +13,7 @@ class SM64_ExportAnimPanel(SM64_Panel): bl_label = "SM64 Animation Exporting" goal = "Export Object/Actor/Anim" - def draw(self, context: bpy.types.Context): + def draw(self, context: Context): context.scene.fast64.sm64.anim_export.draw_props(self.layout.column(), context.scene.fast64.sm64.export_type) @@ -23,7 +23,7 @@ class SM64_ImportAnimPanel(SM64_Panel): goal = "Import" isImport = True - def draw(self, context: bpy.types.Context): + def draw(self, context: Context): col = self.layout.column() sm64_props = context.scene.fast64.sm64 @@ -32,14 +32,17 @@ def draw(self, context: bpy.types.Context): try: if sm64_props.anim_import.import_type == "Binary": import_rom_checks(abspath(sm64_props.import_rom)) - except Exception as e: - multilineLabel(self.layout.box(), str(e), "ERROR") + except Exception as exc: + multilineLabel(self.layout.box(), str(exc), "ERROR") binary_col_enabled = False sm64_props.anim_import.draw_props(col, binary_col_enabled) -sm64_anim_panels = [SM64_ExportAnimPanel, SM64_ImportAnimPanel] +sm64_anim_panels = ( + SM64_ExportAnimPanel, + SM64_ImportAnimPanel, +) def sm64_anim_panel_register(): diff --git a/fast64_internal/sm64/animation/properties.py b/fast64_internal/sm64/animation/properties.py index 205835dc7..fc3215516 100644 --- a/fast64_internal/sm64/animation/properties.py +++ b/fast64_internal/sm64/animation/properties.py @@ -1,4 +1,5 @@ import os + import bpy from bpy.types import PropertyGroup, Action, UILayout, Object from bpy.utils import register_class, unregister_class @@ -27,7 +28,6 @@ toAlnum, writeBoxExportType, ) - from ..sm64_constants import ( MAX_U16, MIN_S16, @@ -72,8 +72,9 @@ class SM64_AnimHeaderProps(PropertyGroup): trans_divisor: IntProperty( name="Translation Divisor", - description="(animYTransDivisor in decomp) When the translation divisor is not 0, the translation multiplier" - " will be calculated by the object´s animation translation multiplier divided by the translation divisor", + description="(animYTransDivisor) If set to 0, the translation multiplier will be 1. " + "Otherwise, the translation multiplier is determined by dividing the object's " + "translation dividend (animYTrans) by this divisor", min=MIN_S16, max=MAX_S16, ) @@ -141,28 +142,21 @@ def get_anim_name(self, actor_name: str, action: Action): def get_anim_enum(self, actor_name: str, action: Action): return anim_name_to_enum(self.get_anim_name(actor_name, action)) - def copyHeader(self, actor_name, action, header: "SM64_AnimHeaderProps"): - # TODO: FIX - # ["custom_name", "header_variant", "expand_tab"] - copyPropertyGroup(header, self) - - self.custom_name = f"{header.get_anim_name(actor_name, action)}_variant{self.header_variant}" - - def get_int_flags(header): + def get_int_flags(self): flags: int = 0 - if header.no_loop: + if self.no_loop: flags |= 1 << 0 - if header.backwards: + if self.backwards: flags |= 1 << 1 - if header.no_acceleration: + if self.no_acceleration: flags |= 1 << 2 - if header.only_horizontal_trans: + if self.only_horizontal_trans: flags |= 1 << 3 - if header.only_vertical_trans: + if self.only_vertical_trans: flags |= 1 << 4 - if header.disabled: + if self.disabled: flags |= 1 << 5 - if header.no_trans: + if self.no_trans: flags |= 1 << 6 return flags @@ -274,13 +268,13 @@ def draw_props( ): col = layout.column() - previewOp = col.operator(SM64_PreviewAnimOperator.bl_idname, icon="PLAY") - previewOp.played_header = self.header_variant - previewOp.played_action = action.name + preview_op = col.operator(SM64_PreviewAnimOperator.bl_idname, icon="PLAY") + preview_op.played_header = self.header_variant + preview_op.played_action = action.name - addOp = col.row().operator(SM64_TableOperations.bl_idname, text="Add Header to Table", icon="ADD") - addOp.array_index, addOp.type = -1, "ADD" - addOp.actionName, addOp.header_variant = action.name, self.header_variant + add_op = col.row().operator(SM64_TableOperations.bl_idname, text="Add Header to Table", icon="ADD") + add_op.array_index, add_op.type = -1, "ADD" + add_op.actionName, add_op.header_variant = action.name, self.header_variant if export_type == "Binary": self.draw_binary(col, is_binary_dma) @@ -377,8 +371,8 @@ def drawHeaderVariant( removeOp = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="REMOVE") removeOp.array_index, removeOp.type, removeOp.actionName = array_index, "REMOVE", action.name - addOp = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="ADD") - addOp.array_index, addOp.type, addOp.actionName = array_index, "ADD", action.name + add_op = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="ADD") + add_op.array_index, add_op.type, add_op.actionName = array_index, "ADD", action.name moveUpCol = opRow.column() moveUpCol.enabled = array_index != 0 @@ -393,7 +387,7 @@ def drawHeaderVariant( col.prop( header, "expand_tab", - text=f"Variation {array_index + 1}", + text=f"Variation {array_index}", icon="TRIA_DOWN" if header.expand_tab else "TRIA_RIGHT", ) if not header.expand_tab: @@ -421,8 +415,8 @@ def drawVariants( return opRow = col.row() - addOp = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="ADD") - addOp.array_index, addOp.type, addOp.actionName = len(self.header_variants), "ADD", action.name + add_op = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="ADD") + add_op.array_index, add_op.type, add_op.actionName = -1, "ADD", action.name if self.header_variants: clearOp = opRow.operator(SM64_AnimVariantOperations.bl_idname, icon="TRASH") @@ -435,7 +429,7 @@ def drawVariants( box.separator(factor=2.0) self.drawHeaderVariant(action, box, variant, i, actor_name, generate_enums, is_binary_dma, export_type) - def drawReferences(self, layout: UILayout): + def draw_references(self, layout: UILayout): col = layout.column() col.prop(self, "reference_tables") @@ -462,12 +456,12 @@ def draw_props( prop_split(col, self, "start_address", "Start Address") prop_split(col, self, "end_address", "End Address") elif export_type == "C": - nameSplit = col.split(factor=0.5) - nameSplit.prop(self, "override_file_name") + name_split = col.split(factor=0.5) + name_split.prop(self, "override_file_name") if self.override_file_name: - nameSplit.prop(self, "custom_file_name", text="") + name_split.prop(self, "custom_file_name", text="") else: - box = nameSplit.row().box() + box = name_split.row().box() box.scale_y = 0.5 box.label(text=self.get_anim_file_name(action)) @@ -482,7 +476,7 @@ def draw_props( box.label(text=f"{action.fast64.sm64.get_max_frame(action)}") if export_type == "C" or not is_binary_dma: - self.drawReferences(col) + self.draw_references(col) col.separator() col.label(text="Main Variant") @@ -626,12 +620,12 @@ def draw_props(self, layout: UILayout, actor_name: str, export_type: str): col.prop(self, "generate_enums") col.prop(self, "override_files") - nameSplit = col.split(factor=0.4) - nameSplit.prop(self, "override_table_name") + name_split = col.split(factor=0.4) + name_split.prop(self, "override_table_name") if self.override_table_name: - nameSplit.prop(self, "custom_table_name", text="") + name_split.prop(self, "custom_table_name", text="") else: - box = nameSplit.row().box() + box = name_split.row().box() box.scale_y = 0.5 box.label(text=self.get_anim_table_name(actor_name)) @@ -641,9 +635,9 @@ def draw_props(self, layout: UILayout, actor_name: str, export_type: str): col.separator() # TODO: Add selected action button should add all variations in action. - # addOp = col.row().operator(SM64_TableOperations.bl_idname, text="Add Selected Action", icon="ADD") - # addOp.array_index, addOp.type = len(self.elements), "ADD" - # addOp.actionName, addOp.header_variant = getSelectedAction(exportProps, False).name, 0 + # add_op = col.row().operator(SM64_TableOperations.bl_idname, text="Add Selected Action", icon="ADD") + # add_op.array_index, add_op.type = len(self.elements), "ADD" + # add_op.actionName, add_op.header_variant = getSelectedAction(exportProps, False).name, 0 if self.elements: col.prop( @@ -678,16 +672,16 @@ class SM64_AnimExportProps(PropertyGroup): table: PointerProperty(type=SM64_AnimTableProps) 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, ) - custom_path: StringProperty(name="Directory", subtype="FILE_PATH") actor_name: StringProperty(name="Name", default="mario") - group_name: StringProperty(name="Group Name", default="group0") + group_name: StringProperty( + name="Group Name", default="group0" + ) # Ideally, this pr will be merged after combined exports, so this should be updated to use the group enum there header_type: EnumProperty(items=enumAnimExportTypes, name="Header Export", default="Actor") level_name: StringProperty(name="Level", default="bob") level_option: EnumProperty(items=enumLevelNames, name="Level", default="bob") @@ -710,22 +704,22 @@ def is_c_dma_structure(self): def get_animation_paths(self, create_directories: bool = False): custom_export = self.header_type == "Custom" - exportPath, level_name = getPathAndLevel( + export_path, level_name = getPathAndLevel( custom_export, self.custom_path, self.level_option, self.level_name, ) - dirName = toAlnum(self.actor_name) + dir_name = toAlnum(self.actor_name) if self.header_type == "DMA": - anim_dir_path = os.path.join(exportPath, self.dma_folder) + anim_dir_path = os.path.join(export_path, self.dma_folder) dir_path = "" geo_dir_path = "" else: - dir_path = getExportDir(custom_export, exportPath, self.header_type, level_name, "", dirName)[0] - geo_dir_path = os.path.join(dir_path, dirName) + dir_path = getExportDir(custom_export, export_path, self.header_type, level_name, "", dir_name)[0] + geo_dir_path = os.path.join(dir_path, dir_name) anim_dir_path = os.path.join(geo_dir_path, "anims") if create_directories: if not os.path.exists(dir_path): @@ -839,14 +833,12 @@ class SM64_AnimImportProps(PropertyGroup): is_segmented_address: BoolProperty(name="Is Segmented Address") level: EnumProperty(items=level_enums, name="Level", default="IC") - # Table read_entire_table: BoolProperty( name="Read All Animations", ) table_index: IntProperty(name="Table Index", min=0) ignore_null: BoolProperty(name="Ignore NULL Delimiter") - # DMA dma_table_address: StringProperty(name="DMA Table Address", default="0x4EC000") mario_animation: IntProperty(name="Selected Preset Mario Animation", default=0) diff --git a/fast64_internal/sm64/animation/utility.py b/fast64_internal/sm64/animation/utility.py index 2ea680218..438563987 100644 --- a/fast64_internal/sm64/animation/utility.py +++ b/fast64_internal/sm64/animation/utility.py @@ -1,3 +1,5 @@ +import ast +import dataclasses import math import bpy @@ -7,7 +9,12 @@ from ..sm64_geolayout_bone import animatableBoneTypes +@dataclasses.dataclass class RomReading: + """ + Simple class that simplifies reading data continously from a starting address. + """ + def __init__(self, data: bytes, start_address: int): self.start_address = start_address self.address = start_address @@ -23,12 +30,11 @@ def read_value(self, size, offset: int = None, signed=True): def animation_operator_checks(context, requires_animation_data=True): if len(context.selected_objects) > 1: - raise PluginError("Multiple objects selected, make sure to select only one.") + raise PluginError("Multiple objects selected at once, make sure to select only one armature.") if len(context.selected_objects) == 0: raise PluginError("No armature selected.") armature_obj: Object = context.selected_objects[0] - if not isinstance(armature_obj.data, Armature): raise PluginError("Selected object is not an armature.") @@ -53,8 +59,8 @@ def get_action(action_name: str): def sm64_to_radian(signed_sm64_angle: int) -> float: - sm64_angle = int.from_bytes(signed_sm64_angle.to_bytes(4, "big", signed=True), "big", signed=False) - degree = sm64_angle * (360.0 / (2.0**16.0)) + unsigned_sm64_angle = signed_sm64_angle + (1 << 16) + degree = unsigned_sm64_angle * (360.0 / (2.0**16.0)) return math.radians(degree % 360.0) @@ -81,8 +87,8 @@ def get_anim_pose_bones(armature_obj: Armature): return anim_bones -def eval_num_from_str(string: str): +def eval_num_from_str(string: str): # TODO: Reconsider this, if a good idea, put into general utility.py to standardize try: return ast.literal_eval(string) - except SyntaxError as e: - raise SyntaxError(f"{str(e)}.\nIf value is in hexadecimal, use 0x before it.") + except SyntaxError as exc: + raise SyntaxError(f"{str(exc)}.\nIf value is in hexadecimal, use 0x before it.") from exc