CI / build-test (push) Successful in 1m14s
Extend the Lighting module with a per-cell lightmap. LightmapBuilder (pure, tested) fills an ambient base, shades occluder cells, and adds point lights that attenuate with distance and are blocked by occluders between source and cell (grid-traced soft shadows). Lightmap holds the grid plus a greyscale texture (Upload) and a bilinear SampleAt for the simulation; a PointLight component places lights in the world. LightmapSystem rebuilds the grid a few times a second (ambient from the day/night cycle with a night floor, occluders from a game-supplied grid, point lights from the ECS); LightmapRenderSystem multiplies the lightmap over the world after the sprite flush and under the HUD. Wired via scene.UseLighting(...), sampled through Lighting.SampleAt. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
2.0 KiB
C#
70 lines
2.0 KiB
C#
using MrGameEng.Lighting;
|
|
using Xunit;
|
|
|
|
namespace MrGameEng.Lighting.Tests;
|
|
|
|
public class LightmapBuilderTests
|
|
{
|
|
private static float[] Build(
|
|
int width,
|
|
int height,
|
|
float ambient,
|
|
bool[] occluders,
|
|
params LightSample[] lights
|
|
)
|
|
{
|
|
var light = new float[width * height];
|
|
LightmapBuilder.Build(light, width, height, ambient, occluders, lights);
|
|
return light;
|
|
}
|
|
|
|
[Fact]
|
|
public void Ambient_WithoutOccluders_IsUniform()
|
|
{
|
|
var light = Build(4, 4, 0.6f, new bool[16]);
|
|
Assert.All(light, v => Assert.Equal(0.6f, v, 5));
|
|
}
|
|
|
|
[Fact]
|
|
public void OccluderCell_IsDarkerThanOpenCell()
|
|
{
|
|
var occ = new bool[9];
|
|
occ[4] = true; // centre cell shaded
|
|
var light = Build(3, 3, 0.8f, occ);
|
|
Assert.True(light[4] < light[0]);
|
|
Assert.Equal(0.8f * LightmapBuilder.OccluderShade, light[4], 5);
|
|
}
|
|
|
|
[Fact]
|
|
public void PointLight_IsBrighterNearSourceAndFadesToRadius()
|
|
{
|
|
var light = Build(7, 1, 0f, new bool[7], new LightSample(0, 0, 6f, 1f));
|
|
Assert.True(light[0] > light[2] && light[2] > light[5]);
|
|
Assert.InRange(light[6], 0f, 0.01f); // на радиусе свет угасает
|
|
}
|
|
|
|
[Fact]
|
|
public void Occluder_CastsShadowBehindIt()
|
|
{
|
|
var occ = new bool[7];
|
|
occ[3] = true; // препятствие между источником (0) и дальними клетками
|
|
var light = Build(7, 1, 0f, occ, new LightSample(0, 0, 10f, 1f));
|
|
|
|
Assert.True(light[1] > 0f); // перед препятствием — освещено
|
|
Assert.Equal(0f, light[5], 5); // за препятствием — тень
|
|
}
|
|
|
|
[Fact]
|
|
public void Values_StayWithinUnitRange()
|
|
{
|
|
var light = Build(
|
|
5,
|
|
5,
|
|
0.5f,
|
|
new bool[25],
|
|
new LightSample(2, 2, 4f, 2f) // яркий свет — проверяем клампинг
|
|
);
|
|
Assert.All(light, v => Assert.InRange(v, 0f, 1f));
|
|
}
|
|
}
|