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")