From 5a9cd719f25a473d4e507323616b3af819d6c1bd Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 7 Jul 2016 20:44:51 -0400 Subject: [PATCH] More lighting improvements... Added the Render > Lighting modifier that lets one specify whether or not we should forcibly use RT lights. This allows us to change the light baking rules to allow the baking of nonanimated Plasma lights when this setting is disabled. The modifier also has text explaining what the lighting results should look like. --- korman/exporter/animation.py | 20 +-------- korman/exporter/convert.py | 2 +- korman/exporter/etlight.py | 20 ++++++--- korman/exporter/mesh.py | 7 +++ korman/exporter/rtlight.py | 2 +- korman/nodes/node_messages.py | 2 +- korman/properties/modifiers/anim.py | 2 +- korman/properties/modifiers/physics.py | 2 +- korman/properties/modifiers/render.py | 61 ++++++++++++++++++++++++++ korman/properties/prop_object.py | 22 ++++++++++ korman/ui/modifiers/render.py | 36 +++++++++++++++ 11 files changed, 146 insertions(+), 30 deletions(-) diff --git a/korman/exporter/animation.py b/korman/exporter/animation.py index 0d932bd..4698393 100644 --- a/korman/exporter/animation.py +++ b/korman/exporter/animation.py @@ -31,7 +31,7 @@ class AnimationConverter: return frame_num / self._bl_fps def convert_object_animations(self, bo, so): - if not self.is_animated(bo): + if not bo.plasma_object.has_animation_data: return def fetch_animation_data(id_data): @@ -308,24 +308,6 @@ class AnimationConverter: master = self._mgr.find_create_object(plAGMasterMod, so=so, bl=bo) return mod, master - def has_transform_animation(self, bo): - if bo.animation_data is not None: - if bo.animation_data.action is not None: - data_paths = frozenset((i.data_path for i in bo.animation_data.action.fcurves)) - return {"location", "rotation_euler", "scale"} & data_paths - return False - - def is_animated(self, bo): - if bo.animation_data is not None: - if bo.animation_data.action is not None: - return True - data = getattr(bo, "data", None) - if data is not None: - if data.animation_data is not None: - if data.animation_data.action is not None: - return True - return False - def make_matrix44_controller(self, fcurves, pos_path, scale_path, pos_default, scale_default): def convert_matrix_keyframe(**kwargs): pos = kwargs.get(pos_path) diff --git a/korman/exporter/convert.py b/korman/exporter/convert.py index 24763d2..1bd307b 100644 --- a/korman/exporter/convert.py +++ b/korman/exporter/convert.py @@ -259,7 +259,7 @@ class Exporter: return True if bo.name in self.actors: return True - if self.animation.has_transform_animation(bo): + if bo.plasma_object.has_transform_animation: return True for mod in bo.plasma_modifiers.modifiers: diff --git a/korman/exporter/etlight.py b/korman/exporter/etlight.py index 34d76d3..59ce33c 100644 --- a/korman/exporter/etlight.py +++ b/korman/exporter/etlight.py @@ -118,13 +118,14 @@ class LightBaker: # Return how many thingos we baked return sum(map(len, bake.values())) - def _generate_lightgroup(self, mesh, user_lg=None): + def _generate_lightgroup(self, bo, user_lg=None): """Makes a new light group for the baking process that excludes all Plasma RT lamps""" if user_lg is not None: user_lg = bpy.data.groups.get(user_lg) shouldibake = (user_lg is not None and bool(user_lg.objects)) + mesh = bo.data for material in mesh.materials: if material is None: # material is not assigned to this material... (why is this even a thing?) @@ -143,13 +144,20 @@ class LightBaker: source = lg.objects dest = bpy.data.groups.new("_LIGHTMAPGEN_{}".format(name)) - # Only use non-RT lights + # Rules: + # 1) No animated lights, period. + # 2) If we accept runtime lighting, no Plasma Objects + rtl_mod = bo.plasma_modifiers.lighting for obj in source: - if obj.plasma_object.enabled: + if obj.plasma_object.has_animation_data: + continue + if rtl_mod.rt_lights and obj.plasma_object.enabled: continue dest.objects.link(obj) shouldibake = True else: + # The aforementioned rules do not apply. You better hope you know WTF you are + # doing. I'm not going to help! dest = user_lg material.light_group = dest return shouldibake @@ -185,7 +193,7 @@ class LightBaker: bake[key].append(i) else: bake[key] = [i,] - elif not mods.water_basic.enabled: + elif mods.lighting.preshade: vcols = i.data.vertex_colors for j in _VERTEX_COLOR_LAYERS: if j in vcols: @@ -212,7 +220,7 @@ class LightBaker: uv_textures = mesh.uv_textures # Create a special light group for baking - if not self._generate_lightgroup(mesh, modifier.light_group): + if not self._generate_lightgroup(bo, modifier.light_group): return False # We need to ensure that we bake onto the "BlahObject_LIGHTMAPGEN" image @@ -285,7 +293,7 @@ class LightBaker: vcols = mesh.vertex_colors # Create a special light group for baking - if not self._generate_lightgroup(mesh): + if not self._generate_lightgroup(bo): return False # I have heard tale of some moar "No valid image to bake to" boogs if there is a really diff --git a/korman/exporter/mesh.py b/korman/exporter/mesh.py index 4dbefaa..e7e69f5 100644 --- a/korman/exporter/mesh.py +++ b/korman/exporter/mesh.py @@ -130,6 +130,13 @@ class MeshConverter: raise explosions.TooManyUVChannelsError(bo, bm) geospan.format = numUVWchans + # Begin total guesswork WRT flags + mods = bo.plasma_modifiers + if mods.lightmap.enabled: + geospan.props |= plGeometrySpan.kLiteVtxNonPreshaded + if mods.lighting.rt_lights: + geospan.props |= plGeometrySpan.kPropRunTimeLight + # Harvest lights permaLights, permaProjs = self._exporter().light.find_material_light_keys(bo, bm) for i in permaLights: diff --git a/korman/exporter/rtlight.py b/korman/exporter/rtlight.py index b343f81..76472ca 100644 --- a/korman/exporter/rtlight.py +++ b/korman/exporter/rtlight.py @@ -188,7 +188,7 @@ class LightConverter: # If the lamp has any sort of animation attached, then it needs to be marked movable. # Otherwise, Plasma may not use it for lighting. - if projectors or self._exporter().animation.is_animated(bo): + if projectors or bo.plasma_object.has_animation_data: pl_light.setProperty(plLightInfo.kLPMovable, True) # *Sigh* diff --git a/korman/nodes/node_messages.py b/korman/nodes/node_messages.py index 23abdf0..6fa9be0 100644 --- a/korman/nodes/node_messages.py +++ b/korman/nodes/node_messages.py @@ -174,7 +174,7 @@ class PlasmaAnimCmdMsgNode(PlasmaMessageNode, bpy.types.Node): if obj is None: self.raise_error("invalid object: '{}'".format(self.object_name)) if self.anim_type == "OBJECT": - if not exporter.animation.is_animated(obj): + if not obj.plasma_object.has_animation_data: self.raise_error("invalid animation") group = obj.plasma_modifiers.animation_group if group.enabled: diff --git a/korman/properties/modifiers/anim.py b/korman/properties/modifiers/anim.py index 7744c9e..320c184 100644 --- a/korman/properties/modifiers/anim.py +++ b/korman/properties/modifiers/anim.py @@ -112,7 +112,7 @@ class PlasmaAnimationGroupModifier(ActionModifier, PlasmaModifierProperties): active_child_index = IntProperty(options={"HIDDEN"}) def export(self, exporter, bo, so): - if not exporter.animation.is_animated(bo): + if not bo.plasma_object.has_animation_data: raise ExportError("'{}': Object is not animated".format(bo.name)) # The message forwarder is the guy that makes sure that everybody knows WTF is going on diff --git a/korman/properties/modifiers/physics.py b/korman/properties/modifiers/physics.py index c52c95e..aa983a3 100644 --- a/korman/properties/modifiers/physics.py +++ b/korman/properties/modifiers/physics.py @@ -105,7 +105,7 @@ class PlasmaCollider(PlasmaModifierProperties): def post_export(self, exporter, bo, so): test_bo = bo while test_bo is not None: - if exporter.animation.has_transform_animation(test_bo): + if test_bo.plasma_object.has_transform_animation: self._make_physical_movable(so) break test_bo = test_bo.parent diff --git a/korman/properties/modifiers/render.py b/korman/properties/modifiers/render.py index 8ea7c5f..74d1b2f 100644 --- a/korman/properties/modifiers/render.py +++ b/korman/properties/modifiers/render.py @@ -216,6 +216,67 @@ class PlasmaLightMapGen(PlasmaModifierProperties): def resolution(self): return int(self.quality) + +class PlasmaLightingMod(PlasmaModifierProperties): + pl_id = "lighting" + + bl_category = "Render" + bl_label = "Lighting" + bl_description = "Fine tune Plasma lighting settings" + + force_rt_lights = BoolProperty(name="Force RT Lighting", + description="Unleashes satan by forcing the engine to dynamically light this object", + default=True, + options=set()) + force_preshade = BoolProperty(name="Force Vertex Shading", + description="Ensures vertex lights are baked, even if illogical", + default=False, + options=set()) + + @property + def allow_preshade(self): + bo = self.id_data + if bo.plasma_modifiers.water_basic.enabled: + return False + if bo.plasma_modifiers.lightmap.enabled: + return False + return True + + def export(self, exporter, bo, so): + # Exposes no new keyed objects, mostly a hint to the ET light code + pass + + @property + def preshade(self): + bo = self.id_data + if self.allow_preshade: + if self.force_preshade: + return True + # RT lights means no preshading unless requested + if self.rt_lights: + return False + if not bo.plasma_object.has_transform_animation: + return True + return False + + @property + def rt_lights(self): + """Are RT lights forcibly enabled or do we otherwise want them?""" + return (self.enabled and self.force_rt_lights) or self.want_rt_lights + + @property + def want_rt_lights(self): + """Gets whether or not this object ought to be lit dynamically""" + bo = self.id_data + if bo.plasma_modifiers.lightmap.enabled: + return False + if bo.plasma_modifiers.water_basic.enabled: + return True + if bo.plasma_object.has_transform_animation: + return True + return False + + class PlasmaViewFaceMod(PlasmaModifierProperties): pl_id = "viewfacemod" diff --git a/korman/properties/prop_object.py b/korman/properties/prop_object.py index eb41c45..e8fa54b 100644 --- a/korman/properties/prop_object.py +++ b/korman/properties/prop_object.py @@ -52,6 +52,28 @@ class PlasmaObject(bpy.types.PropertyGroup): default=False, options={"HIDDEN"}) + @property + def has_animation_data(self): + bo = self.id_data + if bo.animation_data is not None: + if bo.animation_data.action is not None: + return True + data = getattr(bo, "data", None) + if data is not None: + if data.animation_data is not None: + if data.animation_data.action is not None: + return True + return False + + @property + def has_transform_animation(self): + bo = self.id_data + if bo.animation_data is not None: + if bo.animation_data.action is not None: + data_paths = frozenset((i.data_path for i in bo.animation_data.action.fcurves)) + return {"location", "rotation_euler", "scale"} & data_paths + return False + class PlasmaNet(bpy.types.PropertyGroup): manual_sdl = BoolProperty(name="Override SDL", diff --git a/korman/ui/modifiers/render.py b/korman/ui/modifiers/render.py index bfc4dde..a4474c2 100644 --- a/korman/ui/modifiers/render.py +++ b/korman/ui/modifiers/render.py @@ -45,6 +45,42 @@ def followmod(modifier, layout, context): if modifier.leader_type == "kFollowObject": layout.prop_search(modifier, "leader_object", bpy.data, "objects", icon="OUTLINER_OB_MESH") +def lighting(modifier, layout, context): + split = layout.split() + col = split.column() + col.prop(modifier, "force_rt_lights") + col = split.column() + col.active = modifier.allow_preshade + col.prop(modifier, "force_preshade") + layout.separator() + + lightmap = modifier.id_data.plasma_modifiers.lightmap + have_static_lights = lightmap.enabled or modifier.preshade + def yes_no(val): + return "Yes" if val else "No" + + col = layout.column(align=True) + col.label("Plasma Lighting Summary:") + if modifier.rt_lights and have_static_lights: + col.label(" You have unleashed Satan!", icon="GHOST_ENABLED") + else: + col.label(" Satan remains ensconced deep in the abyss...", icon="GHOST_ENABLED") + col.label("Animated lights will be cast at runtime.", icon="LAYER_USED") + col.label("Projection lights will be cast at runtime.", icon="LAYER_USED") + col.label("Specular lights will be cast to specular materials at runtime.", icon="LAYER_USED") + col.label("Other Plasma lights {} be cast at runtime.".format("will" if modifier.rt_lights else "will NOT"), + icon="LAYER_USED") + + if lightmap.enabled and lightmap.light_group: + col.label(" All '{}' lights will be baked to a lightmap".format(lightmap.light_group), + icon="LAYER_USED") + elif have_static_lights: + light_type = "Blender-only" if modifier.rt_lights else "unanimated" + map_type = "a lightmap" if lightmap.enabled else "vertex colors" + col.label("Other {} lights will be baked to {}.".format(light_type, map_type), icon="LAYER_USED") + else: + col.label("No static lights will be baked.", icon="LAYER_USED") + def lightmap(modifier, layout, context): layout.row(align=True).prop(modifier, "quality", expand=True) layout.prop(modifier, "render_layers", text="Active Render Layers")