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>
Climate (Core) layers a continuous seasonal-plus-daily temperature and the
current season over the Calendar, registered via context.UseClimate. A new
Lighting module (Graphics) adds DayNight: a daylight factor and ambient
color from the calendar's time of day, pushed into the renderer's new
AmbientLight (multiplied into world-space sprites only, so the scene
darkens at night while screen-space overlays stay readable) and exposed as
a sampleable SampleAt for the simulation. Both deterministic and GPU-free,
covered by tests; docs updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add MrGameEng.AI utility-AI module; format codebase with CSharpier
New MrGameEng.AI module (ResponseCurve, Consideration, UtilityAction,
UtilityAi selector, Blackboard) plus CSharpier formatting applied across
the whole engine. Documents the CSharpier convention in CLAUDE.md.
@
Test projects become executables (OutputType=Exe) as required by v3.
Satisfy the new xUnit1051 analyzer by passing TestContext cancellation
tokens in the generator compilation test.
Five optimizations measured on the 100k-entity stress scene (Release,
vsync off): 103 FPS baseline -> 297 FPS.
- Sprite submission and vertex building run on all cores above
Renderer2DOptions.ParallelThreshold (default 8192). Work is sliced
into 4096-entity segments: a Friflo chunk holds a whole archetype,
so per-chunk parallelism degenerates to one thread. Segments merge
in deterministic order, preserving radix sort stability.
- Vertex buffer is ring-written with SetDataOptions.NoOverwrite
(GPU buffer 2x frame size); Discard only on wrap-around.
- Texture2DRegion precomputes UVs - four float divisions per sprite
per frame removed.
- Renderer2D exposes per-phase timings (submit/sort/build/upload/draw),
shown in the sample HUD - all further optimization is data-driven.
- Sample BounceSystem parallelized the same segmented way.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SpriteBatcher now sorts with a stable LSD radix sort: equal-key sprites
keep submission order across frames (no flicker) and passes over digits
identical in all keys are skipped, making the common single-layer case
nearly free. Hot paths avoid per-sprite trig and square roots: SinCos is
skipped for unrotated sprites and the culling radius comes from the
region's precomputed diagonal.
Stress scene (100k entities, ~61k on screen, Release): 103 -> 124 FPS.
Sample now runs with VSync off to show real frame rates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>