merge: bring main (A7 lighting Fix A–D + UN-7 + #140 Fix D) into the D.5 branch

Integrates main's 19 commits (A7 outdoor/indoor torch lighting Fix A/B/C/D,
GlobalLightPacker, shader updates, UN-7) under the D.5 toolbar/item-model stack
(D.5.1/D.5.2/D.5.4/D.5.3a). Auto-merged cleanly except docs/ISSUES.md.

Conflict resolved: both lineages used #140 for different issues. Kept main's
#140 = "A7 Fix D" (resolved); renumbered the toolbar/selected-object issue to
#141 (note added; this branch's commits/spec still reference #140 — immutable).
The register auto-merged (AP-46 cites file:line, not #140; UN-7 keeps #140=Fix D).

Build + full suite green on the merged tree (2,713 passed / 4 skipped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-20 12:01:20 +02:00
commit 31d7ffd253
27 changed files with 2327 additions and 103 deletions

View file

@ -0,0 +1,42 @@
using AcDream.App.Rendering.Wb;
using Xunit;
namespace AcDream.App.Tests.Rendering.Wb;
/// <summary>
/// A7 Fix D round 2 — pins retail's <c>useSunlight</c> gate for per-object torch
/// lighting (<c>WbDrawDispatcher.IndoorObjectReceivesTorches</c>). Retail enables
/// the static wall-torches on an object ONLY in the indoor stage
/// (<c>DrawMeshInternal</c> 0x0059f398: <c>if (useSunlight == 0) minimize_object_lighting()</c>),
/// so OUTDOOR objects — building exterior shells (null ParentCellId) and outdoor
/// scenery (land sub-cell 0x0001..0x00FF) — get the sun, never torches. Only
/// EnvCell-parented (indoor, low word &gt;= 0x0100) objects receive torches.
/// </summary>
public sealed class WbDrawDispatcherTorchGateTests
{
[Fact]
public void BuildingShell_NullParent_IsOutdoor_NoTorches()
{
// Building exterior shells are top-level landblock stabs with no
// ParentCellId (LandblockLoader sets BuildingShellAnchorCellId, not Parent).
Assert.False(WbDrawDispatcher.IndoorObjectReceivesTorches(null));
}
[Theory]
[InlineData(0xA9B4_0001u)] // outdoor land sub-cell
[InlineData(0xA9B4_0020u)] // outdoor land sub-cell
[InlineData(0xA9B4_0040u)] // last outdoor land sub-cell (0x40)
public void OutdoorLandCell_NoTorches(uint parentCellId)
{
Assert.False(WbDrawDispatcher.IndoorObjectReceivesTorches(parentCellId));
}
[Theory]
[InlineData(0xA9B4_0100u)] // first EnvCell
[InlineData(0xA9B4_0164u)] // interior EnvCell
[InlineData(0x0007_0143u)] // dungeon EnvCell
public void IndoorEnvCell_GetsTorches(uint parentCellId)
{
Assert.True(WbDrawDispatcher.IndoorObjectReceivesTorches(parentCellId));
}
}