diff --git a/korman/operators/op_mesh.py b/korman/operators/op_mesh.py index 0917589..ecf60df 100644 --- a/korman/operators/op_mesh.py +++ b/korman/operators/op_mesh.py @@ -96,7 +96,7 @@ class PlasmaAddFlareOperator(PlasmaMeshOperator, bpy.types.Operator): bpyscene.objects.active = flare_root # Enable VFM on Empty - bpy.ops.object.plasma_modifier_add(types="viewfacemod") + flare_root.plasma_modifiers.viewfacemod.enabled = True flare_root.plasma_modifiers.viewfacemod.preset_options = "Sprite" # Create a textured Plane @@ -416,8 +416,8 @@ class PlasmaAddLadderMeshOperator(PlasmaMeshOperator, bpy.types.Operator): origin_to_bottom(upper_rgn) upper_rgn.rotation_euler[2] = math.radians(90.0) - bpy.ops.object.plasma_modifier_add(types="laddermod") laddermod = upper_rgn.plasma_modifiers.laddermod + laddermod.enabled = True laddermod.is_enabled = self.lower_entry_enabled laddermod.num_loops = (self.ladder_height - 6) / 2 laddermod.direction = "DOWN" @@ -457,8 +457,8 @@ class PlasmaAddLadderMeshOperator(PlasmaMeshOperator, bpy.types.Operator): origin_to_bottom(lower_rgn) lower_rgn.rotation_euler[2] = math.radians(-90.0) - bpy.ops.object.plasma_modifier_add(types="laddermod") laddermod = lower_rgn.plasma_modifiers.laddermod + laddermod.enabled = True laddermod.is_enabled = self.lower_entry_enabled laddermod.num_loops = (self.ladder_height - 6) / 2 laddermod.direction = "UP" @@ -604,8 +604,8 @@ class PlasmaAddLinkingBookMeshOperator(PlasmaMeshOperator, bpy.types.Operator): # Add Linking Book modifier bpyscene.objects.active = panel_root panel_root.select = True - bpy.ops.object.plasma_modifier_add(types="linkingbookmod") lbmod = panel_root.plasma_modifiers.linkingbookmod + lbmod.enabled = True lbmod.clickable_region = clk_rgn lbmod.seek_point = seek_point lbmod.anim_type = self.link_anim_type diff --git a/korman/operators/op_modifier.py b/korman/operators/op_modifier.py index 8cc0c1b..ce5df53 100644 --- a/korman/operators/op_modifier.py +++ b/korman/operators/op_modifier.py @@ -13,11 +13,15 @@ # You should have received a copy of the GNU General Public License # along with Korman. If not, see . +from __future__ import annotations + import bpy from bpy.props import * + +import itertools import time +from typing import * -from ..ordered_set import OrderedSet from ..properties import modifiers def _fetch_modifiers(): @@ -32,7 +36,7 @@ def _fetch_modifiers(): return items class ModifierOperator: - def _get_modifier(self, context): + def _get_modifier(self, context) -> modifiers.PlasmaModifierProperties: if self.active_modifier == -1: return None pl_mods = context.object.plasma_modifiers.modifiers @@ -51,24 +55,16 @@ class ModifierAddOperator(ModifierOperator, bpy.types.Operator): bl_label = "Add Modifier" bl_description = "Adds a Plasma Modifier" - types = EnumProperty(name="Modifier Type", - description="The type of modifier we add to the list", - items=_fetch_modifiers()) + types = EnumProperty( + name="Modifier Type", + description="The type of modifier we add to the list", + items=_fetch_modifiers() + ) def execute(self, context): - plmods = context.object.plasma_modifiers - myType = self.types - theMod = getattr(plmods, myType) - - theMod.display_order = plmods.determine_next_id() - theMod.created() - - # Determine if this modifier has any dependencies and make sure they're enabled - deps = getattr(theMod, "pl_depends", set()) - for dep in deps: - depMod = getattr(plmods, dep) - if not depMod.enabled: - bpy.ops.object.plasma_modifier_add(types=dep) + modifier = getattr(context.object.plasma_modifiers, self.types) + modifier.enabled = True + return {"FINISHED"} @@ -195,61 +191,36 @@ class ModifierRemoveOperator(ModifierOperator, bpy.types.Operator): bl_label = "Remove Modifier" bl_description = "Removes this Plasma Modifier" - active_modifier = IntProperty(name="Modifier Display Order", - default=-1, - options={"HIDDEN"}) - - mods2delete = CollectionProperty(type=modifiers.PlasmaModifierSpec, options=set()) + active_modifier = IntProperty( + name="Modifier Display Order", + default=-1, + options={"HIDDEN"} + ) def draw(self, context): layout = self.layout - mods = context.object.plasma_modifiers + pl_mods = context.object.plasma_modifiers + mod = self._get_modifier(context) + mods_to_delete = itertools.chain( + (getattr(pl_mods, i) for i in mod.get_dependents()), + [mod] + ) layout.label("This action will remove the following modifiers:") layout = layout.column_flow(align=True) - for i in self.mods2delete: - mod = getattr(mods, i.name) - layout.label(" {}".format(mod.bl_label), icon=getattr(mod, "bl_icon", "NONE")) + for mod in filter(lambda x: x.enabled, mods_to_delete): + layout.label(f" {mod.bl_label}", icon=getattr(mod, "bl_icon", "NONE")) def execute(self, context): - want2delete = set((i.name for i in self.mods2delete)) - mods = sorted(context.object.plasma_modifiers.modifiers, key=lambda x: x.display_order) - subtract = 0 - - for mod in mods: - if mod.pl_id in want2delete: - mod.display_order = -1 - mod.destroyed() - subtract += 1 - else: - mod.display_order -= subtract + self._get_modifier(context).enabled = False return {"FINISHED"} def invoke(self, context, event): - mods = context.object.plasma_modifiers - self.mods2delete.clear() - - want2delete = OrderedSet() - if self.active_modifier == -1: - want2delete.update((i.pl_id for i in context.object.plasma_modifiers.modifiers)) - else: - want2delete.add(self._get_modifier(context).pl_id) - - # Here's the rub - # When we start, we should have just one modifier in want2delete - # HOWEVER, the mod may have dependencies, which in turn may have more deps - # So we collect them into the list... you dig? - for i in want2delete: - for mod in modifiers.PlasmaModifierProperties.__subclasses__(): - if not getattr(mods, mod.pl_id).enabled: - continue - if i in getattr(mod, "pl_depends", set()): - want2delete.add(mod.pl_id) - for i in want2delete: - mod = self.mods2delete.add() - mod.name = i - - if len(want2delete) == 1: + has_dependents = any( + getattr(context.object.plasma_modifiers, i).enabled + for i in self._get_modifier(context).get_dependents() + ) + if not has_dependents: return self.execute(context) else: return context.window_manager.invoke_props_dialog(self) diff --git a/korman/properties/modifiers/base.py b/korman/properties/modifiers/base.py index 8cc24fa..1994484 100644 --- a/korman/properties/modifiers/base.py +++ b/korman/properties/modifiers/base.py @@ -17,7 +17,8 @@ import bpy from bpy.props import * import abc -from typing import Any, Dict, Generator, Optional +import itertools +from typing import Any, Dict, FrozenSet, Optional class PlasmaModifierProperties(bpy.types.PropertyGroup): @property @@ -45,15 +46,37 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup): def draw_no_defer(self): """Disallow geometry being sorted into a blending span""" return False - + @property def draw_late(self): return False @property - def enabled(self): + def enabled(self) -> bool: return self.display_order >= 0 + @enabled.setter + def enabled(self, value: bool) -> None: + plmods = self.id_data.plasma_modifiers + if value and self.enabled is False: + self.display_order = plmods.determine_next_id() + self.created() + + # Determine if this modifier has any dependencies and make sure they're enabled + for dep in getattr(self, "pl_depends", set()): + getattr(plmods, dep).enabled = True + elif not value and self.enabled is True: + mods_to_delete = frozenset(itertools.chain([self.pl_id], self.get_dependents())) + enabled_mods = sorted(self.id_data.plasma_modifiers.modifiers, key=lambda x: x.display_order) + subtract = 0 + for modifier in enabled_mods: + if modifier.pl_id in mods_to_delete: + modifier.display_order = -1 + modifier.destroyed() + subtract += 1 + else: + modifier.display_order -= subtract + def export(self, exporter, bo, so): """This is the main phase of the modifier export where most, if not all, PRP objects should be generated. No new Blender objects should be created unless their lifespan is constrained @@ -75,6 +98,16 @@ class PlasmaModifierProperties(bpy.types.PropertyGroup): """Indicates that the geometry's faces should be sorted by the engine""" return False + @classmethod + def get_dependents(cls) -> FrozenSet[str]: + """Returns the set of modifiers that depend on this modifier being active.""" + deps = set() + for i in PlasmaModifierProperties.__subclasses__(): + if cls.pl_id in getattr(i, "pl_depends", []): + deps.add(i.pl_id) + deps.update(i.get_dependents()) + return frozenset(deps) + def harvest_actors(self): return () diff --git a/korman/properties/modifiers/sound.py b/korman/properties/modifiers/sound.py index 4409d53..63b174f 100644 --- a/korman/properties/modifiers/sound.py +++ b/korman/properties/modifiers/sound.py @@ -556,8 +556,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties): emitter_obj.plasma_modifiers.modifiers ) for i in bad_mods: - # HACK: You can't set the enabled property. - i.display_order = -1 + i.enabled = False # But only 3D stereo sounds! soundemit_mod = emitter_obj.plasma_modifiers.soundemit @@ -630,7 +629,7 @@ class PlasmaSoundEmitter(PlasmaModifierProperties): if not my_anim_groups: group = bo.plasma_modifiers.animation_group if not group.enabled: - toggle.track(group, "display_order", sum((1 for i in bo.plasma_modifiers.modifiers))) + toggle.track(group, "enabled", True) for i in group.children: toggle.track(i, "enabled", False) my_anim_groups.append(group)