From 88ef0f47da05f6ad31d0448fda34f0063828d23c Mon Sep 17 00:00:00 2001 From: Lila Date: Sun, 31 Aug 2025 11:52:44 +0100 Subject: [PATCH] [SM64] Concept: Use desgraph updates to set current versions at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of always running upgrade code on new objects/bones/scenes on file load, we make sure they get the new version as soon as possible. This is ideal considering we are gathering properties from keys that could be very likely exist in another addon. That said, I feel like there has to be a better approach to this? I can“t really figure out one. This does work it just feels hacky I guess, even if this is how materials work. --- fast64_internal/sm64/animation/properties.py | 13 +-- fast64_internal/sm64/custom_cmd/properties.py | 8 +- fast64_internal/sm64/settings/properties.py | 88 ++++++++++++------- fast64_internal/sm64/sm64_geolayout_bone.py | 17 +++- fast64_internal/sm64/sm64_objects.py | 53 +++++++---- fast64_internal/sm64/tools/properties.py | 9 +- 6 files changed, 125 insertions(+), 63 deletions(-) diff --git a/fast64_internal/sm64/animation/properties.py b/fast64_internal/sm64/animation/properties.py index a11ce61f1..7d3b99df0 100644 --- a/fast64_internal/sm64/animation/properties.py +++ b/fast64_internal/sm64/animation/properties.py @@ -883,7 +883,7 @@ class SM64_AnimProperties(PropertyGroup): importing: PointerProperty(type=SM64_AnimImportProperties) - def upgrade_old_props(self, scene: Scene): + def upgrade_version_0(self, scene: Scene): self.importing.upgrade_old_props(scene) # Export @@ -931,10 +931,13 @@ def upgrade_old_props(self, scene: Scene): self.version = 1 - def upgrade_changed_props(self, scene): - if self.version != self.cur_version: - self.upgrade_old_props(scene) - self.version = SM64_AnimProperties.cur_version + def upgrade_scene(self, scene: Scene): + if self.version < self.cur_version: + self.upgrade_version_0(scene) + self.set_to_newest_version() + + def set_to_newest_version(self): + self.version = self.cur_version class SM64_ArmatureAnimProperties(PropertyGroup): diff --git a/fast64_internal/sm64/custom_cmd/properties.py b/fast64_internal/sm64/custom_cmd/properties.py index bc8916278..e0101e101 100644 --- a/fast64_internal/sm64/custom_cmd/properties.py +++ b/fast64_internal/sm64/custom_cmd/properties.py @@ -735,6 +735,7 @@ def custom_cmd_change_preset(self: "SM64_CustomCmdProperties", context: Context) class SM64_CustomCmdProperties(PropertyGroup): version: IntProperty(name="SM64_CustomCmdProperties Version", default=0) + cur_version = 1 tab: BoolProperty(default=False) preset: EnumProperty(items=get_custom_cmd_preset_enum, update=custom_cmd_change_preset) @@ -826,8 +827,11 @@ class SM64_CustomCmdProperties(PropertyGroup): def preset_hash(self): return str(hash(str(self.to_dict("PRESET_EDIT", include_defaults=False).items()))) + def set_to_newest_version(self): + self.version = self.cur_version + def upgrade_object(self, obj: Object): - if self.version != 0: + if self.version == self.cur_version: return found_cmd, arg = upgrade_old_prop(self, "str_cmd", obj, "customGeoCommand"), get_first_set_prop( obj, "customGeoCommandArgs" @@ -840,7 +844,7 @@ def upgrade_object(self, obj: Object): self.args[-1].parameter = arg def upgrade_bone(self, bone: Bone): - if self.version != 0: + if self.version == self.cur_version: return upgrade_old_prop(self, "str_cmd", self, "custom_geo_cmd_macro") args = get_first_set_prop(self, "custom_geo_cmd_args") diff --git a/fast64_internal/sm64/settings/properties.py b/fast64_internal/sm64/settings/properties.py index cb9a57c5d..753d03f4b 100644 --- a/fast64_internal/sm64/settings/properties.py +++ b/fast64_internal/sm64/settings/properties.py @@ -135,8 +135,9 @@ def gfx_write_method(self): return GfxMatWriteMethod.WriteAll if self.write_all else GfxMatWriteMethod.WriteDifferingAndRevert - @staticmethod - def upgrade_changed_props(): + def upgrade_scene(self, scene: bpy.types.Scene): + if self.version >= self.cur_version: + return old_scene_props_to_new = { "importRom": "import_rom", "exportRom": "export_rom", @@ -157,44 +158,51 @@ def upgrade_changed_props(): "non_decomp_level": {"levelCustomExport"}, "export_header_type": {"geoExportHeaderType", "colExportHeaderType", "animExportHeaderType"}, "custom_include_directory": {"geoTexDir"}, - "binary_level": {"levelAnimExport"}, # as the others binary props get carried over to here we need to update the cur_version again } binary_level_names = {"levelAnimExport", "colExportLevel", "levelDLExport", "levelGeoExport"} old_custom_props = {"animCustomExport", "colCustomExport", "geoCustomExport", "DLCustomExport"} + + self.address_converter.upgrade_scene(scene) + self.animation.upgrade_scene(scene) + upgrade_old_prop( + self, + "export_type", + scene, + { + "animExportType", + "colExportType", + "DLExportType", + "geoExportType", + }, + ) + for old, new in old_scene_props_to_new.items(): + upgrade_old_prop(self, new, scene, old) + upgrade_old_prop(self, "show_importing_menus", self, "showImportingMenus") + + combined_props: SM64_CombinedObjectProperties = self.combined_export + for new, old in old_export_props_to_new.items(): + upgrade_old_prop(combined_props, new, scene, old) + + insertable_directory = get_first_set_prop(scene, "animInsertableBinaryPath") + if insertable_directory is not None: # Ignores file name + combined_props.insertable_directory = os.path.split(insertable_directory)[1] + + if get_first_set_prop(combined_props, old_custom_props): + combined_props.export_header_type = "Custom" + upgrade_old_prop(combined_props, "level_name", scene, binary_level_names, old_enum=OLD_BINARY_LEVEL_ENUMS) + self.set_to_newest_version() + + @staticmethod + def upgrade_changed_props(): for scene in bpy.data.scenes: sm64_props: SM64_Properties = scene.fast64.sm64 - sm64_props.address_converter.upgrade_changed_props(scene) - sm64_props.animation.upgrade_changed_props(scene) - if sm64_props.version == SM64_Properties.cur_version: - continue - upgrade_old_prop( - sm64_props, - "export_type", - scene, - { - "animExportType", - "colExportType", - "DLExportType", - "geoExportType", - }, - ) - for old, new in old_scene_props_to_new.items(): - upgrade_old_prop(sm64_props, new, scene, old) - upgrade_old_prop(sm64_props, "show_importing_menus", sm64_props, "showImportingMenus") - - combined_props: SM64_CombinedObjectProperties = sm64_props.combined_export - for new, old in old_export_props_to_new.items(): - upgrade_old_prop(combined_props, new, scene, old) - - insertable_directory = get_first_set_prop(scene, "animInsertableBinaryPath") - if insertable_directory is not None: # Ignores file name - combined_props.insertable_directory = os.path.split(insertable_directory)[1] - - if get_first_set_prop(combined_props, old_custom_props): - combined_props.export_header_type = "Custom" - upgrade_old_prop(combined_props, "level_name", scene, binary_level_names, old_enum=OLD_BINARY_LEVEL_ENUMS) - sm64_props.version = SM64_Properties.cur_version + sm64_props.upgrade_scene(scene) + + def set_to_newest_version(self): + self.version = self.cur_version + self.address_converter.set_to_newest_version() + self.animation.set_to_newest_version() def to_repo_settings(self): data = {} @@ -267,6 +275,13 @@ def draw_props(self, layout: UILayout, show_repo_settings: bool = True): import_rom_ui_warnings(col, self.import_rom) +def depsgraph_handler(_scene: bpy.types.Scene, depsgraph: bpy.types.Depsgraph): + for update in depsgraph.updates: + id = update.id + if isinstance(id, bpy.types.Scene): + id.fast64.sm64.set_to_newest_version() + + classes = (SM64_Properties,) @@ -274,7 +289,12 @@ def settings_props_register(): for cls in classes: register_class(cls) + bpy.app.handlers.depsgraph_update_post.append(depsgraph_handler) + def settings_props_unregister(): for cls in reversed(classes): unregister_class(cls) + + while depsgraph_handler in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(depsgraph_handler) diff --git a/fast64_internal/sm64/sm64_geolayout_bone.py b/fast64_internal/sm64/sm64_geolayout_bone.py index 9bb807453..1931a53e3 100644 --- a/fast64_internal/sm64/sm64_geolayout_bone.py +++ b/fast64_internal/sm64/sm64_geolayout_bone.py @@ -454,7 +454,6 @@ def getSwitchOptionBone(switchArmature): class SM64_BoneProperties(PropertyGroup): - version: IntProperty(name="SM64_BoneProperties Version", default=0) custom: PointerProperty(type=SM64_CustomCmdProperties) revert_previous_mat: BoolProperty(name="Revert Previous Material", default=False) revert_after_mat: BoolProperty( @@ -467,6 +466,9 @@ class SM64_BoneProperties(PropertyGroup): def upgrade_bone(self, bone): self.custom.upgrade_bone(bone) + def set_to_newest_version(self): + self.custom.set_to_newest_version() + @staticmethod def upgrade_changed_props(): for obj in bpy.data.objects: @@ -475,6 +477,14 @@ def upgrade_changed_props(): bone.fast64.sm64.upgrade_bone(bone) +def depsgraph_handler(_scene: bpy.types.Scene, depsgraph: bpy.types.Depsgraph): + for update in depsgraph.updates: + id = update.id + if isinstance(id, bpy.types.Armature): + for bone in id.bones: + bone.fast64.sm64.set_to_newest_version() + + sm64_bone_classes = ( AddSwitchOption, RemoveSwitchOption, @@ -570,6 +580,8 @@ def sm64_bone_register(): # Used during object duplication on export Object.original_name = StringProperty() + bpy.app.handlers.depsgraph_update_post.append(depsgraph_handler) + def sm64_bone_unregister(): for cls in reversed(sm64_bone_classes): @@ -602,3 +614,6 @@ def sm64_bone_unregister(): del Object.render_range del Object.scaleFromGeolayout + + while depsgraph_handler in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(depsgraph_handler) diff --git a/fast64_internal/sm64/sm64_objects.py b/fast64_internal/sm64/sm64_objects.py index ecf4a5a88..443d8f2d4 100644 --- a/fast64_internal/sm64/sm64_objects.py +++ b/fast64_internal/sm64/sm64_objects.py @@ -2840,11 +2840,9 @@ class SM64_GeoASMProperties(bpy.types.PropertyGroup): name="Geo ASM Param", default="0", description="Function parameter. (Binary exporting will cast to int)" ) - @staticmethod - def upgrade_object(obj: bpy.types.Object): - geo_asm = obj.fast64.sm64.geo_asm - upgrade_old_prop(geo_asm, "func", obj, {"geoASMFunc", "geo_func"}) - upgrade_old_prop(geo_asm, "param", obj, {"geoASMParam", "func_param"}) + def upgrade_object(self, obj: bpy.types.Object): + upgrade_old_prop(self, "func", obj, {"geoASMFunc", "geo_func"}) + upgrade_old_prop(self, "param", obj, {"geoASMParam", "func_param"}) class SM64_AreaProperties(bpy.types.PropertyGroup): @@ -2896,20 +2894,17 @@ class SM64_GameObjectProperties(bpy.types.PropertyGroup): bparam3: bpy.props.StringProperty(name="Behavior Param 3", description="Third Behavior Param", default="") bparam4: bpy.props.StringProperty(name="Behavior Param 4", description="Fourth Behavior Param", default="") - @staticmethod - def upgrade_object(obj): - game_object: SM64_GameObjectProperties = obj.fast64.sm64.game_object - - upgrade_old_prop(game_object, "bparams", obj, "sm64_obj_bparam") + def upgrade_object(self, obj: bpy.types.Object): + upgrade_old_prop(self, "bparams", obj, "sm64_obj_bparam") # get combined bparams, if they arent the default value then return because they have been set - combined_bparams = game_object.get_combined_bparams() + combined_bparams = self.get_combined_bparams() if combined_bparams != DEFAULT_BEHAVIOR_PARAMS: return # If bparams arent the default bparams, disable `use_individual_params` - if game_object.bparams != DEFAULT_BEHAVIOR_PARAMS: - game_object.use_individual_params = False + if self.bparams != DEFAULT_BEHAVIOR_PARAMS: + self.use_individual_params = False def get_combined_bparams(self): params = [self.bparam1, self.bparam2, self.bparam3, self.bparam4] @@ -3020,15 +3015,30 @@ class SM64_ObjectProperties(bpy.types.PropertyGroup): animation: bpy.props.PointerProperty(type=SM64_ArmatureAnimProperties) + def upgrade_obj(self, obj: bpy.types.Object): + if self.version == 0: + self.geo_asm.upgrade_object(obj) + if self.version < 3: + self.game_object.upgrade_object(obj) + if self.version < 4: + self.custom.upgrade_object(obj) + self.set_to_newest_version() + @staticmethod def upgrade_changed_props(): for obj in bpy.data.objects: - if obj.fast64.sm64.version == 0: - SM64_GeoASMProperties.upgrade_object(obj) - if obj.fast64.sm64.version < 3: - SM64_GameObjectProperties.upgrade_object(obj) - obj.fast64.sm64.custom.upgrade_object(obj) - obj.fast64.sm64.version = SM64_ObjectProperties.cur_version + obj.fast64.sm64.upgrade_obj(obj) + + def set_to_newest_version(self): + self.version = self.cur_version + self.custom.set_to_newest_version() + + +def depsgraph_handler(_scene: bpy.types.Scene, depsgraph: bpy.types.Depsgraph): + for update in depsgraph.updates: + id = update.id + if isinstance(id, bpy.types.Object): + id.fast64.sm64.set_to_newest_version() sm64_obj_classes = ( @@ -3244,6 +3254,8 @@ def sm64_obj_register(): bpy.types.Object.enableRoomSwitch = bpy.props.BoolProperty(name="Enable Room System") + bpy.app.handlers.depsgraph_update_post.append(depsgraph_handler) + def sm64_obj_unregister(): del bpy.types.Object.sm64_model_enum @@ -3321,6 +3333,9 @@ def sm64_obj_unregister(): del bpy.types.Object.switchParam del bpy.types.Object.enableRoomSwitch + while depsgraph_handler in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(depsgraph_handler) + for cls in reversed(sm64_obj_classes): unregister_class(cls) diff --git a/fast64_internal/sm64/tools/properties.py b/fast64_internal/sm64/tools/properties.py index b64690c59..300267d78 100644 --- a/fast64_internal/sm64/tools/properties.py +++ b/fast64_internal/sm64/tools/properties.py @@ -21,10 +21,15 @@ class SM64_AddrConvProperties(PropertyGroup): level: EnumProperty(items=enumLevelNames, name="Level", default="castle_inside") clipboard: BoolProperty(name="Copy to Clipboard", default=True) - def upgrade_changed_props(self, scene: Scene): + def upgrade_scene(self, scene: Scene): + if self.version >= self.cur_version: + return upgrade_old_prop(self, "address", scene, "convertibleAddr", fix_forced_base_16=True) upgrade_old_prop(self, "level", scene, "level") - self.version = SM64_AddrConvProperties.cur_version + self.set_to_newest_version() + + def set_to_newest_version(self): + self.version = self.cur_version def draw_props(self, layout: UILayout, import_rom: PathLike = None): col = layout.column()