Compare commits
8 Commits
b263bdd77e
...
c64343f574
Author | SHA1 | Date |
---|---|---|
Jarrod Doyle | c64343f574 | |
Jarrod Doyle | 19882dcff2 | |
Jarrod Doyle | 175be69b6d | |
Jarrod Doyle | 1282c6be19 | |
Jarrod Doyle | 926497b2c2 | |
Jarrod Doyle | 94b8af0f7f | |
Jarrod Doyle | 025f4d0508 | |
Jarrod Doyle | 50f3c1e9e1 |
|
@ -0,0 +1,83 @@
|
||||||
|
using System.Numerics;
|
||||||
|
using KeepersCompound.LGS.Database.Chunks;
|
||||||
|
|
||||||
|
namespace KeepersCompound.Lightmapper;
|
||||||
|
|
||||||
|
public class Light
|
||||||
|
{
|
||||||
|
public Vector3 Position;
|
||||||
|
public Vector3 Color;
|
||||||
|
public float InnerRadius;
|
||||||
|
public float Radius;
|
||||||
|
public float R2;
|
||||||
|
|
||||||
|
public bool Spotlight;
|
||||||
|
public Vector3 SpotlightDir;
|
||||||
|
public float SpotlightInnerAngle;
|
||||||
|
public float SpotlightOuterAngle;
|
||||||
|
|
||||||
|
public int ObjId;
|
||||||
|
public int LightTableIndex;
|
||||||
|
public bool Anim;
|
||||||
|
|
||||||
|
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 float StrengthAtPoint(Vector3 point, Plane plane)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,38 +8,6 @@ namespace KeepersCompound.Lightmapper;
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
// Super simple for now
|
|
||||||
private record Light
|
|
||||||
{
|
|
||||||
public Vector3 position;
|
|
||||||
public Vector3 color;
|
|
||||||
public float innerRadius;
|
|
||||||
public float radius;
|
|
||||||
public float r2;
|
|
||||||
|
|
||||||
public bool spotlight;
|
|
||||||
public Vector3 spotlightDir;
|
|
||||||
public float spotlightInnerAngle;
|
|
||||||
public float spotlightOuterAngle;
|
|
||||||
|
|
||||||
public int objId;
|
|
||||||
public int lightTableIndex;
|
|
||||||
public bool anim;
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Timing.Reset();
|
Timing.Reset();
|
||||||
|
@ -135,8 +103,8 @@ class Program
|
||||||
{
|
{
|
||||||
// We need to update the object property so it knows its mapping range
|
// We need to update the object property so it knows its mapping range
|
||||||
// TODO: Handle nulls
|
// TODO: Handle nulls
|
||||||
var light = lights.Find(l => l.anim && l.lightTableIndex == lightIdx);
|
var light = lights.Find(l => l.Anim && l.LightTableIndex == lightIdx);
|
||||||
var prop = animLightChunk.properties.Find(p => p.objectId == light.objId);
|
var prop = animLightChunk.properties.Find(p => p.objectId == light.ObjId);
|
||||||
prop.LightTableLightIndex = lightIdx;
|
prop.LightTableLightIndex = lightIdx;
|
||||||
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
|
prop.LightTableMapIndex = (ushort)worldRep.LightingTable.AnimMapCount;
|
||||||
prop.CellsReached = (ushort)animCellMaps.Count;
|
prop.CellsReached = (ushort)animCellMaps.Count;
|
||||||
|
@ -194,22 +162,15 @@ class Program
|
||||||
var sz = brush.size;
|
var sz = brush.size;
|
||||||
var light = new Light
|
var light = new Light
|
||||||
{
|
{
|
||||||
position = brush.position,
|
Position = brush.position,
|
||||||
color = Utils.HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
|
Color = Utils.HsbToRgb(sz.Y, sz.Z, Math.Min(sz.X, 255.0f)),
|
||||||
radius = float.MaxValue,
|
Radius = float.MaxValue,
|
||||||
r2 = float.MaxValue,
|
R2 = float.MaxValue,
|
||||||
lightTableIndex = lightTable.LightCount,
|
LightTableIndex = lightTable.LightCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
lights.Add(light);
|
lights.Add(light);
|
||||||
lightTable.AddLight(new WorldRep.LightTable.LightData
|
lightTable.AddLight(light.ToLightData(32.0f));
|
||||||
{
|
|
||||||
Location = light.position,
|
|
||||||
Direction = light.spotlightDir,
|
|
||||||
Color = light.color / 32.0f, // TODO: This is based on light_scale config var
|
|
||||||
InnerAngle = -1.0f,
|
|
||||||
Radius = 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessObjectLight(
|
private static void ProcessObjectLight(
|
||||||
|
@ -231,9 +192,9 @@ class Program
|
||||||
|
|
||||||
var baseLight = new Light
|
var baseLight = new Light
|
||||||
{
|
{
|
||||||
position = brush.position,
|
Position = brush.position,
|
||||||
spotlightDir = -Vector3.UnitZ,
|
SpotlightDir = -Vector3.UnitZ,
|
||||||
spotlightInnerAngle = -1.0f,
|
SpotlightInnerAngle = -1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (propModelName != null)
|
if (propModelName != null)
|
||||||
|
@ -246,11 +207,11 @@ class Program
|
||||||
var model = new ModelFile(modelPath);
|
var model = new ModelFile(modelPath);
|
||||||
if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot))
|
if (model.TryGetVhot(ModelFile.VhotId.LightPosition, out var vhot))
|
||||||
{
|
{
|
||||||
baseLight.position += vhot.Position;
|
baseLight.Position += vhot.Position;
|
||||||
}
|
}
|
||||||
if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot))
|
if (model.TryGetVhot(ModelFile.VhotId.LightDirection, out vhot))
|
||||||
{
|
{
|
||||||
baseLight.spotlightDir = vhot.Position;
|
baseLight.SpotlightDir = vhot.Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,31 +224,31 @@ class Program
|
||||||
rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y));
|
rot *= Matrix4x4.CreateRotationY(float.DegreesToRadians(brush.angle.Y));
|
||||||
rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z));
|
rot *= Matrix4x4.CreateRotationZ(float.DegreesToRadians(brush.angle.Z));
|
||||||
|
|
||||||
baseLight.spotlight = true;
|
baseLight.Spotlight = true;
|
||||||
baseLight.spotlightDir = Vector3.Transform(baseLight.spotlightDir, rot);
|
baseLight.SpotlightDir = Vector3.Transform(baseLight.SpotlightDir, rot);
|
||||||
baseLight.spotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle));
|
baseLight.SpotlightInnerAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.InnerAngle));
|
||||||
baseLight.spotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle));
|
baseLight.SpotlightOuterAngle = (float)Math.Cos(float.DegreesToRadians(propSpotlight.OuterAngle));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propLight != null)
|
if (propLight != null)
|
||||||
{
|
{
|
||||||
var light = new Light
|
var light = new Light
|
||||||
{
|
{
|
||||||
position = baseLight.position + propLight.Offset,
|
Position = baseLight.Position + propLight.Offset,
|
||||||
color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
|
Color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propLight.Brightness),
|
||||||
innerRadius = propLight.InnerRadius,
|
InnerRadius = propLight.InnerRadius,
|
||||||
radius = propLight.Radius,
|
Radius = propLight.Radius,
|
||||||
r2 = propLight.Radius * propLight.Radius,
|
R2 = propLight.Radius * propLight.Radius,
|
||||||
spotlight = baseLight.spotlight,
|
Spotlight = baseLight.Spotlight,
|
||||||
spotlightDir = baseLight.spotlightDir,
|
SpotlightDir = baseLight.SpotlightDir,
|
||||||
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
SpotlightInnerAngle = baseLight.SpotlightInnerAngle,
|
||||||
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
SpotlightOuterAngle = baseLight.SpotlightOuterAngle,
|
||||||
lightTableIndex = lightTable.LightCount,
|
LightTableIndex = lightTable.LightCount,
|
||||||
};
|
};
|
||||||
if (propLight.Radius == 0)
|
if (propLight.Radius == 0)
|
||||||
{
|
{
|
||||||
light.radius = float.MaxValue;
|
light.Radius = float.MaxValue;
|
||||||
light.r2 = float.MaxValue;
|
light.R2 = float.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
lights.Add(light);
|
lights.Add(light);
|
||||||
|
@ -301,23 +262,23 @@ class Program
|
||||||
|
|
||||||
var light = new Light
|
var light = new Light
|
||||||
{
|
{
|
||||||
position = baseLight.position + propAnimLight.Offset,
|
Position = baseLight.Position + propAnimLight.Offset,
|
||||||
color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
|
Color = Utils.HsbToRgb(propLightColor.Hue, propLightColor.Saturation, propAnimLight.MaxBrightness),
|
||||||
innerRadius = propAnimLight.InnerRadius,
|
InnerRadius = propAnimLight.InnerRadius,
|
||||||
radius = propAnimLight.Radius,
|
Radius = propAnimLight.Radius,
|
||||||
r2 = propAnimLight.Radius * propAnimLight.Radius,
|
R2 = propAnimLight.Radius * propAnimLight.Radius,
|
||||||
spotlight = baseLight.spotlight,
|
Spotlight = baseLight.Spotlight,
|
||||||
spotlightDir = baseLight.spotlightDir,
|
SpotlightDir = baseLight.SpotlightDir,
|
||||||
spotlightInnerAngle = baseLight.spotlightInnerAngle,
|
SpotlightInnerAngle = baseLight.SpotlightInnerAngle,
|
||||||
spotlightOuterAngle = baseLight.spotlightOuterAngle,
|
SpotlightOuterAngle = baseLight.SpotlightOuterAngle,
|
||||||
anim = true,
|
Anim = true,
|
||||||
objId = id,
|
ObjId = id,
|
||||||
lightTableIndex = propAnimLight.LightTableLightIndex,
|
LightTableIndex = propAnimLight.LightTableLightIndex,
|
||||||
};
|
};
|
||||||
if (propAnimLight.Radius == 0)
|
if (propAnimLight.Radius == 0)
|
||||||
{
|
{
|
||||||
light.radius = float.MaxValue;
|
light.Radius = float.MaxValue;
|
||||||
light.r2 = float.MaxValue;
|
light.R2 = float.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
lights.Add(light);
|
lights.Add(light);
|
||||||
|
@ -459,7 +420,7 @@ class Program
|
||||||
// Check if plane normal is facing towards the light
|
// Check if plane normal is facing towards the light
|
||||||
// If it's not then we're never going to be (directly) lit by this
|
// If it's not then we're never going to be (directly) lit by this
|
||||||
// light.
|
// light.
|
||||||
var centerDirection = renderPoly.Center - light.position;
|
var centerDirection = renderPoly.Center - light.Position;
|
||||||
if (Vector3.Dot(plane.Normal, centerDirection) >= 0)
|
if (Vector3.Dot(plane.Normal, centerDirection) >= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -468,15 +429,15 @@ class Program
|
||||||
// If there aren't *any* points on the plane that are in range of the light
|
// If there aren't *any* points on the plane that are in range of the light
|
||||||
// then none of the lightmap points will be so we can discard.
|
// then none of the lightmap points will be so we can discard.
|
||||||
// The more compact a map is the less effective this is
|
// The more compact a map is the less effective this is
|
||||||
var planeDist = MathUtils.DistanceFromPlane(plane, light.position);
|
var planeDist = MathUtils.DistanceFromPlane(plane, light.Position);
|
||||||
if (planeDist > light.radius)
|
if (planeDist > light.Radius)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the poly of the lightmap doesn't intersect the light radius then
|
// If the poly of the lightmap doesn't intersect the light radius then
|
||||||
// none of the lightmap points will so we can discard.
|
// none of the lightmap points will so we can discard.
|
||||||
if (!MathUtils.Intersects(new MathUtils.Sphere(light.position, light.radius), aabb))
|
if (!MathUtils.Intersects(new MathUtils.Sphere(light.Position, light.Radius), aabb))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -488,60 +449,65 @@ class Program
|
||||||
var pos = topLeft;
|
var pos = topLeft;
|
||||||
pos += x * 0.25f * renderPoly.TextureVectors.Item1;
|
pos += x * 0.25f * renderPoly.TextureVectors.Item1;
|
||||||
pos += y * 0.25f * renderPoly.TextureVectors.Item2;
|
pos += y * 0.25f * renderPoly.TextureVectors.Item2;
|
||||||
|
|
||||||
// We need to clip the point to slightly inside of the poly
|
// Embree has robustness issues when hitting poly edges which
|
||||||
// to avoid three problems:
|
// results in false misses. To alleviate this we pre-push everything
|
||||||
// 1. Darkened spots from lightmap pixels who's center is outside
|
// slightly towards the center of the poly.
|
||||||
// of the polygon but is partially contained in the polygon
|
var centerOffset = renderPoly.Center - pos;
|
||||||
// 2. Darkened spots from linear filtering of points outside of the
|
if (centerOffset.LengthSquared() > MathUtils.Epsilon)
|
||||||
|
{
|
||||||
|
pos += Vector3.Normalize(centerOffset) * MathUtils.Epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't see our target point from the center of the poly
|
||||||
|
// then it's outside the world. We need to clip the point to slightly
|
||||||
|
// inside the poly and retrace to avoid three problems:
|
||||||
|
// 1. Darkened spots from lightmap pixels whose center is outside
|
||||||
|
// the polygon but is partially contained in the polygon
|
||||||
|
// 2. Darkened spots from linear filtering of points outside the
|
||||||
// polygon which have missed
|
// polygon which have missed
|
||||||
// 3. Darkened spots where centers are on the exact edge of a poly
|
// 3. Darkened spots where centers are on the exact edge of a poly
|
||||||
// which can sometimes cause Embree to miss casts
|
// which can sometimes cause Embree to miss casts
|
||||||
var p2d = planeMapper.MapTo2d(pos);
|
var inPoly = TraceRay(scene, pos, renderPoly.Center + plane.Normal * 0.25f);
|
||||||
p2d = MathUtils.ClipPointToPoly2d(p2d, v2ds);
|
if (!inPoly)
|
||||||
pos = planeMapper.MapTo3d(p2d);
|
{
|
||||||
|
var p2d = planeMapper.MapTo2d(pos);
|
||||||
|
p2d = MathUtils.ClipPointToPoly2d(p2d, v2ds);
|
||||||
|
pos = planeMapper.MapTo3d(p2d);
|
||||||
|
}
|
||||||
|
|
||||||
// If we're out of range there's no point casting a ray
|
// If we're out of range there's no point casting a ray
|
||||||
// There's probably a better way to discard the entire lightmap
|
// There's probably a better way to discard the entire lightmap
|
||||||
// if we're massively out of range
|
// if we're massively out of range
|
||||||
var direction = pos - light.position;
|
if ((pos - light.Position).LengthSquared() > light.R2)
|
||||||
if (direction.LengthSquared() > light.r2)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cast from the light to the pixel because the light has
|
// We cast from the light to the pixel because the light has
|
||||||
// no mesh in the scene to hit
|
// no mesh in the scene to hit
|
||||||
var hitResult = scene.Trace(new Ray
|
var hit = TraceRay(scene, light.Position, pos);
|
||||||
{
|
|
||||||
Origin = light.position,
|
|
||||||
Direction = Vector3.Normalize(direction),
|
|
||||||
});
|
|
||||||
|
|
||||||
// cheeky epsilon
|
|
||||||
// TODO: Some pixels aren't hitting and I'm not sure why
|
|
||||||
var hit = hitResult && Math.Abs(hitResult.Distance - direction.Length()) < MathUtils.Epsilon;
|
|
||||||
if (hit)
|
if (hit)
|
||||||
{
|
{
|
||||||
// If we're an anim light there's a lot of stuff we need to update
|
// If we're an anim light there's a lot of stuff we need to update
|
||||||
// Firstly we need to add the light to the cells anim light palette
|
// Firstly we need to add the light to the cells anim light palette
|
||||||
// Secondly we need to set the appropriate bit of the lightmap's
|
// Secondly we need to set the appropriate bit of the lightmap's
|
||||||
// bitmask. Finally we need to check if the lightmap needs another layer
|
// bitmask. Finally we need to check if the lightmap needs another layer
|
||||||
if (light.anim)
|
if (light.Anim)
|
||||||
{
|
{
|
||||||
// TODO: Don't recalculate this for every point lol
|
// TODO: Don't recalculate this for every point lol
|
||||||
var paletteIdx = cell.AnimLights.IndexOf((ushort)light.lightTableIndex);
|
var paletteIdx = cell.AnimLights.IndexOf((ushort)light.LightTableIndex);
|
||||||
if (paletteIdx == -1)
|
if (paletteIdx == -1)
|
||||||
{
|
{
|
||||||
paletteIdx = cell.AnimLightCount;
|
paletteIdx = cell.AnimLightCount;
|
||||||
cell.AnimLightCount++;
|
cell.AnimLightCount++;
|
||||||
cell.AnimLights.Add((ushort)light.lightTableIndex);
|
cell.AnimLights.Add((ushort)light.LightTableIndex);
|
||||||
}
|
}
|
||||||
info.AnimLightBitmask |= 1u << paletteIdx;
|
info.AnimLightBitmask |= 1u << paletteIdx;
|
||||||
layer = paletteIdx + 1;
|
layer = paletteIdx + 1;
|
||||||
}
|
}
|
||||||
var strength = CalculateLightStrengthAtPoint(light, pos, plane);
|
var strength = light.StrengthAtPoint(pos, plane);
|
||||||
lightmap.AddLight(layer, x, y, light.color, strength, hdr);
|
lightmap.AddLight(layer, x, y, light.Color, strength, hdr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,6 +517,17 @@ class Program
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TraceRay(Raytracer scene, Vector3 origin, Vector3 target)
|
||||||
|
{
|
||||||
|
var direction = target - origin;
|
||||||
|
var hitResult = scene.Trace(new Ray
|
||||||
|
{
|
||||||
|
Origin = origin,
|
||||||
|
Direction = Vector3.Normalize(direction),
|
||||||
|
});
|
||||||
|
return hitResult && Math.Abs(hitResult.Distance - direction.Length()) < MathUtils.Epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetCellLightIndices(WorldRep wr, Light[] lights)
|
private static void SetCellLightIndices(WorldRep wr, Light[] lights)
|
||||||
{
|
{
|
||||||
|
@ -576,61 +553,13 @@ class Program
|
||||||
var cellSphere = new MathUtils.Sphere(cell.SphereCenter, cell.SphereRadius);
|
var cellSphere = new MathUtils.Sphere(cell.SphereCenter, cell.SphereRadius);
|
||||||
foreach (var light in lights)
|
foreach (var light in lights)
|
||||||
{
|
{
|
||||||
if (MathUtils.Intersects(cellSphere, new MathUtils.Sphere(light.position, light.radius)))
|
if (MathUtils.Intersects(cellSphere, new MathUtils.Sphere(light.Position, light.Radius)))
|
||||||
{
|
{
|
||||||
cell.LightIndexCount++;
|
cell.LightIndexCount++;
|
||||||
cell.LightIndices.Add((ushort)light.lightTableIndex);
|
cell.LightIndices.Add((ushort)light.LightTableIndex);
|
||||||
cell.LightIndices[0]++;
|
cell.LightIndices[0]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float CalculateLightStrengthAtPoint(Light light, Vector3 point, Plane plane)
|
|
||||||
{
|
|
||||||
// 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 = light.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 (light.innerRadius != 0 && len > light.innerRadius)
|
|
||||||
{
|
|
||||||
strength *= (light.radius - len) / (light.radius - light.innerRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (light.spotlight)
|
|
||||||
{
|
|
||||||
var spotAngle = Vector3.Dot(-Vector3.Normalize(dir), light.spotlightDir);
|
|
||||||
var inner = light.spotlightInnerAngle;
|
|
||||||
var outer = light.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue