diff --git a/korman/exporter/animation.py b/korman/exporter/animation.py index c32532a..0d932bd 100644 --- a/korman/exporter/animation.py +++ b/korman/exporter/animation.py @@ -59,6 +59,8 @@ class AnimationConverter: applicators.extend(self._convert_lamp_color_animation(bo.name, data_fcurves, lamp)) if isinstance(lamp, bpy.types.SpotLamp): applicators.extend(self._convert_spot_lamp_animation(bo.name, data_fcurves, lamp)) + if isinstance(lamp, bpy.types.PointLamp): + applicators.extend(self._convert_omni_lamp_animation(bo.name, data_fcurves, lamp)) # Check to make sure we have some valid animation applicators before proceeding. if not any(applicators): @@ -145,6 +147,61 @@ class AnimationConverter: applicator.channel = channel yield applicator + def _convert_omni_lamp_animation(self, name, fcurves, lamp): + energy_fcurve = next((i for i in fcurves if i.data_path == "energy"), None) + distance_fcurve = next((i for i in fcurves if i.data_path == "distance"), None) + if energy_fcurve is None and distance_fcurve is None: + return None + light_converter = self._exporter().light + intensity, atten_end = light_converter.convert_attenuation(lamp) + + # All types allow animating cutoff + if distance_fcurve is not None: + channel = plScalarControllerChannel() + channel.controller = self.make_scalar_leaf_controller(distance_fcurve, + lambda x: x * 2 if lamp.use_sphere else x) + applicator = plOmniCutoffApplicator() + applicator.channelName = name + applicator.channel = channel + yield applicator + + falloff = lamp.falloff_type + if falloff == "CONSTANT": + if energy_fcurve is not None: + self._exporter().report.warn("Constant attenuation cannot be animated in Plasma", ident=3) + elif falloff == "INVERSE_LINEAR": + def convert_linear_atten(distance, energy): + intens = abs(energy[0]) + atten_end = distance[0] * 2 if lamp.use_sphere else distance[0] + return light_converter.convert_attenuation_linear(intens, atten_end) + + keyframes = self._process_fcurves([distance_fcurve, energy_fcurve], convert_linear_atten, + {"distance": lamp.distance, "energy": lamp.energy}) + if keyframes: + channel = plScalarControllerChannel() + channel.controller = self._make_scalar_leaf_controller(keyframes, False) + applicator = plOmniApplicator() + applicator.channelName = name + applicator.channel = channel + yield applicator + elif falloff == "INVERSE_SQUARE": + def convert_quadratic_atten(distance, energy): + intens = abs(energy[0]) + atten_end = distance[0] * 2 if lamp.use_sphere else distance[0] + return light_converter.convert_attenuation_quadratic(intens, atten_end) + + keyframes = self._process_fcurves([distance_fcurve, energy_fcurve], convert_quadratic_atten, + {"distance": lamp.distance, "energy": lamp.energy}) + if keyframes: + channel = plScalarControllerChannel() + channel.controller = self._make_scalar_leaf_controller(keyframes, False) + applicator = plOmniSqApplicator() + applicator.channelName = name + applicator.channel = channel + yield applicator + else: + self._exporter().report.warn("Lamp Falloff '{}' animations are not supported".format(falloff), ident=3) + def _convert_sound_volume_animation(self, name, fcurves, soundemit): if not fcurves: return None diff --git a/korman/exporter/rtlight.py b/korman/exporter/rtlight.py index b37eef5..a253d27 100644 --- a/korman/exporter/rtlight.py +++ b/korman/exporter/rtlight.py @@ -40,11 +40,8 @@ class LightConverter: } def _convert_attenuation(self, bl, pl): - intens = bl.energy - if intens < 0: - intens = -intens - attenEnd = bl.distance * 2 if bl.use_sphere else bl.distance - + # If you change these calculations, be sure to update the AnimationConverter! + intens, attenEnd = self.convert_attenuation(bl) if bl.falloff_type == "CONSTANT": print(" Attenuation: No Falloff") pl.attenConst = intens @@ -54,18 +51,29 @@ class LightConverter: elif bl.falloff_type == "INVERSE_LINEAR": print(" Attenuation: Inverse Linear") pl.attenConst = 1.0 - pl.attenLinear = max(0.0, (intens * _FAR_POWER - 1.0) / attenEnd) + pl.attenLinear = self.convert_attenuation_linear(intens, attenEnd) pl.attenQuadratic = 0.0 pl.attenCutoff = attenEnd elif bl.falloff_type == "INVERSE_SQUARE": print(" Attenuation: Inverse Square") pl.attenConst = 1.0 pl.attenLinear = 0.0 - pl.attenQuadratic = max(0.0, (intens * _FAR_POWER - 1.0) / (attenEnd * attenEnd)) + pl.attenQuadratic = self.convert_attenuation_quadratic(intens, attenEnd) pl.attenCutoff = attenEnd else: raise BlenderOptionNotSupportedError(bl.falloff_type) + def convert_attenuation(self, lamp): + intens = abs(lamp.energy) + attenEnd = lamp.distance * 2 if lamp.use_sphere else lamp.distance + return (intens, attenEnd) + + def convert_attenuation_linear(self, intensity, end): + return max(0.0, (intensity * _FAR_POWER - 1.0) / end) + + def convert_attenuation_quadratic(self, intensity, end): + return max(0.0, (intensity * _FAR_POWER - 1.0) / pow(end, 2)) + def _convert_area_lamp(self, bl, pl): print(" [LimitedDirLightInfo '{}']".format(bl.name)) @@ -176,6 +184,11 @@ class LightConverter: if projectors: self._export_rt_projector(bo, pl_light, projectors) + # 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): + pl_light.setProperty(plLightInfo.kLPMovable, True) + # *Sigh* pl_light.sceneNode = self.mgr.get_scene_node(location=so.key.location) @@ -203,7 +216,6 @@ class LightConverter: else: state.miscFlags |= hsGMatState.kMiscOrthoProjection state.ZFlags |= hsGMatState.kZNoZWrite - pl_light.setProperty(plLightInfo.kLPMovable, True) pl_light.setProperty(plLightInfo.kLPCastShadows, False) if slot.blend_type == "ADD":