From e307c36a7d20253ff17f028d0aad7549f4e013b2 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 22 Sep 2018 20:56:20 -0400 Subject: [PATCH] Fix #119 This adds the ability to copy and paste all modifiers (or a single modifier) to another object. --- korman/operators/op_modifier.py | 98 +++++++++++++++++++++++++++++++++ korman/properties/prop_scene.py | 6 ++ korman/ui/ui_modifiers.py | 15 ++++- 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/korman/operators/op_modifier.py b/korman/operators/op_modifier.py index 82292c2..5dc65c7 100644 --- a/korman/operators/op_modifier.py +++ b/korman/operators/op_modifier.py @@ -32,6 +32,15 @@ def _fetch_modifiers(): return items class ModifierOperator: + def _get_modifier(self, context): + if self.active_modifier == -1: + return None + pl_mods = context.object.plasma_modifiers.modifiers + pl_mod = next((i for i in pl_mods if self.active_modifier == i.display_order), None) + if pl_mod is None: + raise IndexError(self.active_modifier) + return pl_mod + @classmethod def poll(cls, context): return context.scene.render.engine == "PLASMA_GAME" @@ -63,6 +72,95 @@ class ModifierAddOperator(ModifierOperator, bpy.types.Operator): return {"FINISHED"} +class ModifierCopyOperator(ModifierOperator, bpy.types.Operator): + bl_idname = "object.plasma_modifier_copy" + bl_label = "Copy Modifiers" + bl_description = "Copy Modifiers from an Object" + + active_modifier = IntProperty(name="Modifier Display Order", + default=-1, + options={"HIDDEN"}) + + def execute(self, context): + pl_scene = context.scene.plasma_scene + pl_scene.modifier_copy_object = context.object + pl_mod = self._get_modifier(context) + if pl_mod is None: + pl_scene.property_unset("modifier_copy_id") + else: + pl_scene.modifier_copy_id = pl_mod.pl_id + return {"FINISHED"} + + +class ModifierPasteOperator(ModifierOperator, bpy.types.Operator): + bl_idname = "object.plasma_modifier_paste" + bl_label = "Paste Modifier" + bl_description = "Paste Modifier(s) to another Object" + + def execute(self, context): + pl_scene = context.scene.plasma_scene + if not pl_scene.is_property_set("modifier_copy_object"): + raise RuntimeError() + + dst_object, src_object = context.object, pl_scene.modifier_copy_object + pl_mod_id = pl_scene.modifier_copy_id + + if pl_mod_id: + self._paste_modifier(src_object, dst_object, pl_mod_id) + else: + for mod_cls in modifiers.PlasmaModifierProperties.__subclasses__(): + self._paste_modifier(src_object, dst_object, mod_cls.pl_id) + return {"FINISHED"} + + def _paste_modifier(self, src_object, dst_object, pl_mod_id): + src_mod = getattr(src_object.plasma_modifiers, pl_mod_id) + dst_mod = getattr(dst_object.plasma_modifiers, pl_mod_id) + + if not src_mod.enabled: + return + + # The modifier index needs to be refigured, otherwise, bad things will happen + # when the user tries to use the modifier operators again. + mod_id = dst_object.plasma_modifiers.determine_next_id() + + # NOTE: Usage of keys vs items is intentional because the value returned by items may + # not be accepted as a valid type for assignment. Sounds like a blender bug IMO. + for i in src_mod.rna_type.properties: + self._paste_property(src_mod, dst_mod, i) + + # See above, ensure no id collisions + dst_mod.display_order = mod_id + + def _paste_property(self, src, dst, prop): + prop_name = prop.identifier + + # Old properties? Discard their asses. + if not hasattr(dst, prop_name): + return + + # Collection properties must be manually copied... + if prop.type == "COLLECTION": + dst_prop, src_prop = getattr(dst, prop_name), getattr(src, prop_name) + dst_prop.clear() + for src_item in src_prop: + dst_item = dst_prop.add() + for item_prop in src_item.rna_type.properties: + self._paste_property(src_item, dst_item, item_prop) + else: + try: + if src.is_property_set(prop_name): + setattr(dst, prop_name, getattr(src, prop_name)) + else: + dst.property_unset(prop_name) + except AttributeError: + pass + + @classmethod + def poll(cls, context): + pl_scene = context.scene.plasma_scene + return super().poll(context) and pl_scene.is_property_set("modifier_copy_object") + + class ModifierRemoveOperator(ModifierOperator, bpy.types.Operator): bl_idname = "object.plasma_modifier_remove" bl_label = "Remove Modifier" diff --git a/korman/properties/prop_scene.py b/korman/properties/prop_scene.py index 074df1e..36d0675 100644 --- a/korman/properties/prop_scene.py +++ b/korman/properties/prop_scene.py @@ -44,3 +44,9 @@ class PlasmaBakePass(bpy.types.PropertyGroup): class PlasmaScene(bpy.types.PropertyGroup): bake_passes = CollectionProperty(type=PlasmaBakePass) active_pass_index = IntProperty(options={"HIDDEN"}) + + modifier_copy_object = PointerProperty(name="INTERNAL: Object to copy modifiers from", + options={"HIDDEN", "SKIP_SAVE"}, + type=bpy.types.Object) + modifier_copy_id = StringProperty(name="INTERNAL: Modifier to copy from", + options={"HIDDEN", "SKIP_SAVE"}) diff --git a/korman/ui/ui_modifiers.py b/korman/ui/ui_modifiers.py index 30e8afe..49a7a77 100644 --- a/korman/ui/ui_modifiers.py +++ b/korman/ui/ui_modifiers.py @@ -48,7 +48,9 @@ class PlasmaModifiersPanel(ModifierButtonsPanel, bpy.types.Panel): # Any items following that are members of that category, of course... # ... I hope that my rambling has helped somebody understand more about the undocumented mess # that is Blender Python. - layout.operator_menu_enum("object.plasma_modifier_add", "types") + row = layout.row(align=True) + row.operator_menu_enum("object.plasma_modifier_add", "types") + row.menu("PlasmaModifiersSpecialMenu", icon="DOWNARROW_HLT", text="") # First, let's sort the list of modifiers based on their display order # We don't do this sort in the property itself because this is really just a UI hint. @@ -77,6 +79,7 @@ class PlasmaModifiersPanel(ModifierButtonsPanel, bpy.types.Panel): row.operator("object.plasma_modifier_move_up", text="", icon="TRIA_UP").active_modifier = modifier.display_order row.operator("object.plasma_modifier_move_down", text="", icon="TRIA_DOWN").active_modifier = modifier.display_order + row.operator("object.plasma_modifier_copy", text="", icon="COPYDOWN").active_modifier = modifier.display_order row.operator("object.plasma_modifier_reset", text="", icon="FILE_REFRESH").active_modifier = modifier.display_order row.operator("object.plasma_modifier_remove", text="", icon="X").active_modifier = modifier.display_order @@ -84,3 +87,13 @@ class PlasmaModifiersPanel(ModifierButtonsPanel, bpy.types.Panel): # by whatever insanity is in the modifier module. modifier modifier modifier... # MODDDDDDDDIFIIIIEEEERRRRRRRR!!! return layout + + +class PlasmaModifiersSpecialMenu(ModifierButtonsPanel, bpy.types.Menu): + bl_label = "Plasma Modifier Specials" + + def draw(self, context): + layout = self.layout + + layout.operator("object.plasma_modifier_copy", icon="COPYDOWN", text="Copy Modifiers").active_modifier = -1 + layout.operator("object.plasma_modifier_paste", icon="PASTEDOWN", text="Paste Modifier(s)")