diag: ACDREAM_PROBE_LIGHT [light-detail] — per-light range/intensity/cone (#133 A7)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-13 20:55:14 +02:00
parent 167f05c4fa
commit 9e809bc661
2 changed files with 47 additions and 3 deletions

View file

@ -7641,7 +7641,8 @@ public sealed class GameWindow : IDisposable
sunIntensity: Lighting.Sun?.Intensity ?? 0f, sunIntensity: Lighting.Sun?.Intensity ?? 0f,
registeredLights: Lighting.RegisteredCount, registeredLights: Lighting.RegisteredCount,
activeLights: (int)ubo.CellAmbient.W, activeLights: (int)ubo.CellAmbient.W,
playerCellId: playerRoot?.CellId ?? 0u); playerCellId: playerRoot?.CellId ?? 0u,
lights: Lighting);
// Never cull the landblock the player is currently on. // Never cull the landblock the player is currently on.
uint? playerLb = null; uint? playerLb = null;

View file

@ -372,12 +372,25 @@ public static class RenderingDiagnostics
/// <summary> /// <summary>
/// #133 A7 — emit ONE rate-limited <c>[light]</c> line describing the /// #133 A7 — emit ONE rate-limited <c>[light]</c> line describing the
/// current scene-lighting state. Cheap no-op when /// current scene-lighting state, followed (when <paramref name="lights"/>
/// is supplied) by up to three <c>[light-detail]</c> lines for the nearest
/// ACTIVE point/spot lights. Cheap no-op when
/// <see cref="ProbeLightEnabled"/> is false; otherwise fires at most /// <see cref="ProbeLightEnabled"/> is false; otherwise fires at most
/// once per second. Pull the values from the spot where /// once per second. Pull the values from the spot where
/// <c>GameWindow.UpdateSunFromSky</c> set <c>Lighting.CurrentAmbient</c> /// <c>GameWindow.UpdateSunFromSky</c> set <c>Lighting.CurrentAmbient</c>
/// / <c>Lighting.Sun</c> and where <c>SceneLightingUbo.Build</c> computed /// / <c>Lighting.Sun</c> and where <c>SceneLightingUbo.Build</c> computed
/// the active-slot count. /// the active-slot count.
/// <para>
/// The <c>[light-detail]</c> lines are the answer to the "candle-spotlight"
/// question — they expose each torch's REAL dat-derived runtime values
/// (<c>range=</c> Falloff metres, <c>intensity=</c>, <c>cone=</c> radians,
/// <c>color=</c>, <c>distToViewer=</c>) so it is visible in launch.log
/// whether dungeon torches are tiny-range points or wide cones and at what
/// intensity — without a screenshot:
/// <code>
/// [light-detail] kind=Point range=&lt;Falloff m&gt; intensity=&lt;I&gt; cone=&lt;rad&gt; color=(r,g,b) distToViewer=&lt;m&gt;
/// </code>
/// </para>
/// </summary> /// </summary>
/// <param name="insideCell">The <c>playerInsideCell</c> value driving the indoor branch.</param> /// <param name="insideCell">The <c>playerInsideCell</c> value driving the indoor branch.</param>
/// <param name="ambientR">Cell ambient red (xyz of <c>uCellAmbient</c>).</param> /// <param name="ambientR">Cell ambient red (xyz of <c>uCellAmbient</c>).</param>
@ -387,12 +400,16 @@ public static class RenderingDiagnostics
/// <param name="registeredLights">Total point/spot lights registered with the LightManager.</param> /// <param name="registeredLights">Total point/spot lights registered with the LightManager.</param>
/// <param name="activeLights"><c>uCellAmbient.w</c> — shader active-slot count (includes the zeroed sun slot indoors).</param> /// <param name="activeLights"><c>uCellAmbient.w</c> — shader active-slot count (includes the zeroed sun slot indoors).</param>
/// <param name="playerCellId">The player's current cell id (0 if unresolved → outside).</param> /// <param name="playerCellId">The player's current cell id (0 if unresolved → outside).</param>
/// <param name="lights">The ticked <c>LightManager</c> (its <c>Active</c> list, sorted nearest-first by the
/// just-completed Tick). When non-null, drives the <c>[light-detail]</c> lines. Optional so existing call
/// sites / tests that only want the aggregate line keep compiling.</param>
public static void EmitLight(bool insideCell, public static void EmitLight(bool insideCell,
float ambientR, float ambientG, float ambientB, float ambientR, float ambientG, float ambientB,
float sunIntensity, float sunIntensity,
int registeredLights, int registeredLights,
int activeLights, int activeLights,
uint playerCellId) uint playerCellId,
AcDream.Core.Lighting.LightManager? lights = null)
{ {
if (!ProbeLightEnabled) return; if (!ProbeLightEnabled) return;
@ -406,6 +423,32 @@ public static class RenderingDiagnostics
"[light] insideCell={0} ambient=({1:0.###},{2:0.###},{3:0.###}) sun={4:0.###} registeredLights={5} activeLights={6} playerCell=0x{7:X8}", "[light] insideCell={0} ambient=({1:0.###},{2:0.###},{3:0.###}) sun={4:0.###} registeredLights={5} activeLights={6} playerCell=0x{7:X8}",
insideCell, ambientR, ambientG, ambientB, sunIntensity, insideCell, ambientR, ambientG, ambientB, sunIntensity,
registeredLights, activeLights, playerCellId)); registeredLights, activeLights, playerCellId));
// #133 A7 (2026-06-13) — per-light detail for the "spotlight bubble"
// question. Dump the actual runtime dat-derived values of the nearest
// ~3 ACTIVE point/spot lights so the real Falloff/Intensity/ConeAngle
// are visible in launch.log (are torch ranges 1m or 10m? points or
// spots? what intensity?). The sun (Directional, slot 0) is skipped —
// it carries no Range/cone meaning. DistSq is already cached by
// LightManager.Tick this frame, so the active list is sorted nearest-
// first; we just take the first few non-directional entries.
if (lights is null) return;
var active = lights.Active;
int shown = 0;
const int MaxDetail = 3;
for (int i = 0; i < active.Length && shown < MaxDetail; i++)
{
var ls = active[i];
if (ls is null) continue;
if (ls.Kind == AcDream.Core.Lighting.LightKind.Directional) continue;
float dist = ls.DistSq >= 0f ? MathF.Sqrt(ls.DistSq) : 0f;
Console.WriteLine(string.Format(ci,
"[light-detail] kind={0} range={1:0.###} intensity={2:0.###} cone={3:0.####} color=({4:0.###},{5:0.###},{6:0.###}) distToViewer={7:0.###}",
ls.Kind, ls.Range, ls.Intensity, ls.ConeAngle,
ls.ColorLinear.X, ls.ColorLinear.Y, ls.ColorLinear.Z, dist));
shown++;
}
} }
private static bool _probeEnvCellEnabled = private static bool _probeEnvCellEnabled =