ThiefLightmapper/KeepersCompound.Lightmapper/Light.cs

124 lines
3.9 KiB
C#

using System.Numerics;
using KeepersCompound.LGS.Database.Chunks;
namespace KeepersCompound.Lightmapper;
public class Light
{
public Vector3 Position;
public Vector3 Color;
public float Brightness;
public float InnerRadius;
public float Radius;
public float R2;
public bool QuadLit;
public bool Spotlight;
public Vector3 SpotlightDir;
public float SpotlightInnerAngle;
public float SpotlightOuterAngle;
public int ObjId;
public int LightTableIndex;
public bool Anim;
public bool Dynamic;
public WorldRep.LightTable.LightData ToLightData(float lightScale)
{
return new WorldRep.LightTable.LightData
{
Location = Position,
Direction = SpotlightDir,
Color = Color / lightScale,
InnerAngle = SpotlightInnerAngle,
OuterAngle = SpotlightOuterAngle,
Radius = Radius == float.MaxValue ? 0 : Radius,
};
}
public void FixRadius()
{
if (Radius == 0)
{
Radius = float.MaxValue;
R2 = float.MaxValue;
}
}
public void ApplyTransforms(
Vector3 vhotLightPos,
Vector3 vhotLightDir,
Matrix4x4 translate,
Matrix4x4 rotate,
Matrix4x4 scale)
{
Position = Vector3.Transform(Position, rotate) + Vector3.Transform(vhotLightPos, scale * rotate * translate);
SpotlightDir = Vector3.Normalize(Vector3.Transform(vhotLightDir, scale * rotate));
}
public float StrengthAtPoint(Vector3 point, Plane plane, uint lightCutoff)
{
// Calculate light strength at a given point. As far as I can tell
// this is exact to Dark (I'm a genius??). It's just an inverse distance
// falloff with diffuse angle, except we have to scale the length.
var dir = Position - point;
var angle = Vector3.Dot(Vector3.Normalize(dir), plane.Normal);
var len = dir.Length();
var slen = len / 4.0f;
var strength = (angle + 1.0f) / slen;
// Inner radius starts a linear falloff to 0 at the radius
if (InnerRadius != 0 && len > InnerRadius)
{
strength *= (Radius - len) / (Radius - InnerRadius);
}
// Anim lights have a (configurable) minimum light cutoff. This is checked before
// spotlight multipliers are applied so we don't cutoff the spot radius falloff.
if (Anim && strength * Brightness < lightCutoff)
{
return 0f;
}
// This is basically the same as how inner radius works. It just applies
// a linear falloff to 0 between the inner angle and outer angle.
if (Spotlight)
{
var spotAngle = Vector3.Dot(-Vector3.Normalize(dir), SpotlightDir);
var inner = SpotlightInnerAngle;
var outer = SpotlightOuterAngle;
// In an improperly configured spotlight inner and outer angles might be the
// same. So to avoid division by zero (and some clamping) we explicitly handle
// some cases
float spotlightMultiplier;
if (spotAngle >= inner)
{
spotlightMultiplier = 1.0f;
}
else if (spotAngle <= outer)
{
spotlightMultiplier = 0.0f;
}
else
{
spotlightMultiplier = (spotAngle - outer) / (inner - outer);
}
strength *= spotlightMultiplier;
}
return strength;
}
public float CalculateMaxRadius(float minLightCutoff)
{
// TODO: Should it be ceiling'd? Do we need to care about hdr? (I don't think so)
var radius = 8 * Brightness / minLightCutoff;
return radius;
// 2 / (x / 4.0f) = minLightCutoff;
// 2 / minLightCutOff = x / 4.0f;
// x = 8 * rgb / minLightCutOff;
}
}