<# .SYNOPSIS Campaign V self-differential pixel gate. Captures deterministic offline screenshots and optionally compares them against a baseline capture. .DESCRIPTION Every Campaign V renderer-port slice must prove it changed no pixels. The connected lifecycle gate needs a live ACE server and the user's attention; this gate needs neither. The client is launched WITHOUT ACDREAM_LIVE, so it renders the world straight from the DATs, no session is created, and no ACE state can be disturbed. Coverage: terrain, terrain blending, scenery, static world meshes, sky and fog, and the full retained UI (vitals, spell bar, toolbar, chat, radar). It does NOT cover EnvCell interiors, particles, or the paperdoll viewport — those still need the connected route, so slices touching them (V4e, V4g) must additionally be gated by the user. Determinism levers, all three forced here rather than left to the ambient environment, because a capture that inherits any of them is not comparable against one that did not: * ACDREAM_DAY_GROUP -- the sky day group, so weather and the keyframe do not drift between runs. * ACDREAM_WORLD_TIME -- the Dereth day fraction. This moves the SUN, so it moves every lit surface, not just the sky. Slice V7 measured 22.3% of the frame differing between two captures 45 s apart at one stop on one backend with this unpinned. * ACDREAM_SKY_PHASE_SECONDS -- the cloud sheet's UV scroll, which runs on wall time and is independent of the world clock by design. ACDREAM_MSAA_SAMPLES is forced to 0 because multisample resolve positions are explicitly unspecified across implementations, and at 4x they swamp the threshold ninety-fold with two drivers' sample patterns. See plan section 5.5.16. A fixed warm-up lets streaming settle before the frame is captured. .PARAMETER Out Directory to write this capture into. Created if absent, emptied if present. .PARAMETER Baseline Optional. A directory from a previous capture. When supplied, every PNG is compared against its namesake and the script fails on any mismatch. .PARAMETER WarmupMs Milliseconds to let the world stream and settle before capturing. Default 12000, which reliably reaches a fully populated view on the reference machine. .PARAMETER DayGroup Sky day-group index to pin. Default 0. .PARAMETER Tolerance Per-channel absolute difference allowed. Default 2, matching the project's pinned screenshot rule. .PARAMETER MaxDifferentFraction Fraction of differing pixels allowed. Default 0.001, likewise pinned. .PARAMETER WorldDayFraction Dereth day fraction pinned on the launch, in [0, 1). Default 0.5 -- noon, the brightest and flattest lighting the day has. .PARAMETER SkyPhaseSeconds Elapsed-seconds value the sky's UV scroll is pinned to. Default 0, the cloud sheet's authored origin. .PARAMETER MsaaSamples MSAA sample count forced on the launch. Default 0 -- off, which is what plan section 2's pixel-acceptance contract specifies. Pass -1 to leave the quality preset alone. .PARAMETER MaskTopPixels Height in pixels of the sky and treeline band excluded from comparison. Default 280. This is not a fudge factor, and slice V10 re-measured it rather than assuming the V7 clock pins had retired it. Two captures at the SAME commit once differed in 0.29% of pixels, essentially all of it in the top rows, because the sky legitimately animates: clouds scroll on wall time and the Dereth clock moves the sun, and therefore every lit surface. Pinning both clocks -- which this script now does -- shrinks that but does NOT remove it. Measured at V10, two launches of the same binary with all four levers pinned: whole frame, GL vs GL 1,011 px of 921,600 (1.10e-03) whole frame, VK vs VK 482 px of 921,600 (5.23e-04) top 280 masked, GL vs GL 10 px of 563,200 (1.78e-05) top 280 masked, VK vs VK 8 px of 563,200 (1.42e-05) So the band still moves between launches on both backends -- enough that an unmasked self-differential on GL fails the 0.001 threshold on noise alone -- and everything below it is bit-stable to about ten pixels. Masking the band is what makes the rest a strict identity check instead of forcing a loose tolerance that would hide real regressions everywhere else. Consequence, unchanged: this gate does NOT cover sky rendering. Do NOT reach for a LARGER value to make a failing gate pass: plan section 7.1 rule 2. Its legitimate use is diagnostic -- reporting a masked/unmasked split so a known, registered band (for instance AD-46's treeline) can be quantified separately from the rest of the frame. .PARAMETER OrbitDistanceMeters Diagnostic initial orbit-camera distance in metres. Zero keeps acdream's normal default. .PARAMETER OrbitYawDegrees Diagnostic initial orbit-camera heading in degrees. Omit to keep acdream's normal default. .PARAMETER OrbitPitchDegrees Diagnostic initial orbit-camera elevation in degrees. Omit to keep acdream's normal default. A shallow positive angle can put a low sun in frame for ray and volumetric-shaft captures. .PARAMETER BuildingDetailTextures Retail Building Detail Textures preference written into the isolated settings (#226). Default $true (the product default). $false captures the pack-off frame without the building/EnvCell detail replay, which is how Campaign VM slice VM0 isolates that parity change from the rest. .PARAMETER Uncapped Disable both VSync and the normal refresh-rate software limiter. Omit for the capped product cadence. This is a diagnostic measurement mode only. .PARAMETER SkipBuild Skip the Release build (use when the caller already built). .PARAMETER ExeOverride Run this AcDream.App.exe instead of the repository's Release build, with the SAME isolated settings, scene and capture path. Campaign VM VM7 used it to compare the final binary against a reference binary built in a throwaway worktree (VM0's base+normals) without mixing tool versions — VM0 recorded that captures from two different tool versions are not comparable (isolated-settings fieldOfView). Implies -SkipBuild. .PARAMETER DebuggerScript Launch the client UNDER cdb (x64 Windows Debugger, -G -o -lines -cf