diff --git a/korman/exporter/decal.py b/korman/exporter/decal.py index 33461a1..d6120b2 100644 --- a/korman/exporter/decal.py +++ b/korman/exporter/decal.py @@ -15,6 +15,7 @@ import bpy from collections import defaultdict +import itertools from PyHSPlasma import * import weakref @@ -34,7 +35,8 @@ def _get_footprint_class(exporter, name, vs): class DecalConverter: _decal_lookup = { - "footprint": _get_footprint_class, + "footprint_dry": _get_footprint_class, + "footprint_wet": _get_footprint_class, "puddle": _get_puddle_class, "ripple": lambda e, name, vs: plDynaRippleVSMgr if vs else plDynaRippleMgr, } @@ -42,6 +44,7 @@ class DecalConverter: def __init__(self, exporter): self._decal_managers = defaultdict(list) self._exporter = weakref.ref(exporter) + self._notifies = defaultdict(set) def add_dynamic_decal_receiver(self, so, decal_name): # One decal manager in Blender can map to many Plasma decal managers. @@ -61,6 +64,15 @@ class DecalConverter: if key.location == so_loc and getattr(decal_mgr, "waveSet", None) == waveset: decal_mgr.addTarget(so_key) + # HACKAGE: Add the wet/dirty notifes now that we know about all the decal managers. + notify_names = self._notifies[decal_name] + notify_keys = itertools.chain.from_iterable((self._decal_managers[i] for i in notify_names)) + for notify_key in notify_keys: + for i in (i.object for i in decal_mgrs): + i.addNotify(notify_key) + # Don't need to do that again. + del self._notifies[decal_name] + def export_active_print_shape(self, print_shape, decal_name): decal_mgrs = self._decal_managers.get(decal_name) if decal_mgrs is None: @@ -106,7 +118,7 @@ class DecalConverter: self._decal_managers[decal_name].append(decal_mgr.key) # Certain decals are required to be squares - if decal_type in {"footprint", "wake"}: + if decal_type in {"footprint_dry", "footprint_wet", "wake"}: length, width = decal.length / 100.0, decal.width / 100.0 else: length = max(decal.length, decal.width) / 100.0 @@ -129,7 +141,7 @@ class DecalConverter: decal_mgr.scale = hsVector3(length, width, 1.0) # Hardwired calculations from PlasmaMAX - if decal_type in {"footprint", "bullet"}: + if decal_type in {"footprint_dry", "footprint_wet", "bullet"}: decal_mgr.rampEnd = 0.1 decal_mgr.decayStart = decal.life_span - (decal.life_span * 0.25) decal_mgr.lifeSpan = decal.life_span @@ -141,6 +153,15 @@ class DecalConverter: else: raise RuntimeError() + # While any decal manager can be wet/dry, it really makes the most sense to only + # expose wet footprints. In the future, we could expose the plDynaDecalEnableMsg + # to nodes for advanced hacking. + decal_mgr.waitOnEnable = decal_type == "footprint_wet" + if decal_type in {"puddle", "ripple"}: + decal_mgr.wetLength = decal.wet_time + self._notifies[decal_name].update((i.name for i in decal.wet_managers + if i.enabled and i.name != decal_name)) + # UV Animations are hardcoded in PlasmaMAX. Any reason why we should expose this? # I can't think of any presently... Note testing the final instance instead of the # artist setting in case that gets overridden (puddle -> ripple) diff --git a/korman/properties/prop_scene.py b/korman/properties/prop_scene.py index 61b66a2..81cb2c5 100644 --- a/korman/properties/prop_scene.py +++ b/korman/properties/prop_scene.py @@ -42,6 +42,16 @@ class PlasmaBakePass(bpy.types.PropertyGroup): default=((True,) * _NUM_RENDER_LAYERS)) +class PlasmaWetDecalRef(bpy.types.PropertyGroup): + enabled = BoolProperty(name="Enabled", + default=True, + options=set()) + + name = StringProperty(name="Decal Name", + description="Wet decal manager", + options=set()) + + class PlasmaDecalManager(bpy.types.PropertyGroup): def _get_display_name(self): return self.name @@ -53,6 +63,10 @@ class PlasmaDecalManager(bpy.types.PropertyGroup): for j in itertools.chain(decal_receive.managers, decal_print.managers): if j.name == prev_value: j.name = value + for i in bpy.context.scene.plasma_scene.decal_managers: + for j in i.wet_managers: + if j.name == prev_value: + j.name = value self.name = value name = StringProperty(name="Decal Name", @@ -64,10 +78,11 @@ class PlasmaDecalManager(bpy.types.PropertyGroup): decal_type = EnumProperty(name="Decal Type", description="", - items=[("footprint", "Footprint", ""), + items=[("footprint_dry", "Footprint (Dry)", ""), + ("footprint_wet", "Footprint (Wet)", ""), ("puddle", "Water Ripple (Shallow)", ""), ("ripple", "Water Ripple (Deep)", "")], - default="footprint", + default="footprint_dry", options=set()) image = PointerProperty(name="Image", description="", @@ -102,6 +117,15 @@ class PlasmaDecalManager(bpy.types.PropertyGroup): subtype="TIME", unit="TIME", min=0.0, soft_max=300.0, default=30.0, options=set()) + wet_time = FloatProperty(name="Wet Time", + description="How long the decal print shapes stay wet after losing contact with this surface", + subtype="TIME", unit="TIME", + min=0.0, soft_max=300.0, default=10.0, + options=set()) + + # Footprints to wet-ize + wet_managers = CollectionProperty(type=PlasmaWetDecalRef) + active_wet_index = IntProperty(options={"HIDDEN"}) class PlasmaScene(bpy.types.PropertyGroup): diff --git a/korman/ui/ui_scene.py b/korman/ui/ui_scene.py index efcac8e..dc2d4c0 100644 --- a/korman/ui/ui_scene.py +++ b/korman/ui/ui_scene.py @@ -32,6 +32,15 @@ class DecalManagerListUI(bpy.types.UIList): layout.prop(item, "display_name", emboss=False, text="") +class WetManagerListUI(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0): + if item.name: + layout.label(item.name) + layout.prop(item, "enabled", text="") + else: + layout.label("[Empty]") + + class PlasmaDecalManagersPanel(SceneButtonsPanel, bpy.types.Panel): bl_label = "Plasma Decal Managers" @@ -68,5 +77,24 @@ class PlasmaDecalManagersPanel(SceneButtonsPanel, bpy.types.Panel): col.label("Draw Settings:") col.prop(decal_mgr, "intensity") sub = col.row() - sub.active = decal_mgr.decal_type in {"footprint", "bullet", "torpedo"} + sub.active = decal_mgr.decal_type in {"footprint_dry", "footprint_wet", "bullet", "torpedo"} sub.prop(decal_mgr, "life_span") + sub = col.row() + sub.active = decal_mgr.decal_type in {"puddle", "ripple"} + sub.prop(decal_mgr, "wet_time") + + if decal_mgr.decal_type in {"puddle", "ripple"}: + box.separator() + box.label("Wet Footprints:") + ui_list.draw_list(box, "WetManagerListUI", "scene", decal_mgr, "wet_managers", + "active_wet_index", rows=2, maxrows=3) + try: + wet_ref = decal_mgr.wet_managers[decal_mgr.active_wet_index] + except: + pass + else: + wet_mgr = next((i for i in scene.decal_managers if i.name == wet_ref.name), None) + box.alert = getattr(wet_mgr, "decal_type", None) == "footprint_wet" + box.prop_search(wet_ref, "name", scene, "decal_managers", icon="NONE") + if wet_ref.name == decal_mgr.name: + box.label(text="Circular reference", icon="ERROR")