docs(plans): Campaign VM — VisualMaster plan + the Campaign AR review it closes
Adds the independent Campaign AR review (docs/research/2026-08-22-campaign-
ar-review.md): architecture, technical correctness, performance, coverage.
Findings F1-F8; F1 (default-path invariance proven only on a 2x2 recording-
device oracle) blocks merge; F2 (10 m/50 m detail fade has no retail anchor);
F3 (the port is retail's two-pass fallback; single-pass stage path
undetermined); F4 (post stack runs in gamma space); F5 (four overclaiming
sentences, one premise error the reviewer owns).
Campaign VM plans the closure: VM0 masked pixel + production perf A/B vs
6c79d35c; VM1 removes the fade (retail attenuates by mips); VM2 one cdb read
of m_caps.bCanDoSinglePassDetailing; VM3 linear-light post stack with a
numerically neutral preset; VM4 truthful docs; VM5 volumetric jitter; VM6
weather-driven foliage wind - procedural-scenery (bit-31 id namespace) cutout
subsets only, three motions (lean/branch/flutter) with gusts and per-tree
phase, shadows share the displacement include; VM7 closeout + merge.
Roadmap gains the Campaign VM pointer beside Campaign AR.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c51b07ef95
commit
4dbbe8fead
3 changed files with 638 additions and 0 deletions
|
|
@ -2072,6 +2072,8 @@ OpenGL ceiling; revisit macOS only if a supported graphics backend is chosen.
|
|||
|
||||
**Future / unscheduled — Campaign AR:** the opt-in [Atmospheric Rendering / Shader Packs campaign](2026-08-21-atmospheric-rendering.md) makes moving authored sun-and-moon directional shadows from trees, monsters, players, and buildings its Tier-2 headline while preserving acdream's current retail-faithful renderer as the default and leaving physics, collision, gameplay, and network behavior unchanged; the project owner assigned Campaign AR on 2026-08-22 without displacing active M4 gameplay work. The [celestial source contract](../research/2026-08-22-dereth-celestial-shadow-sources.md) selects sun, dominant moon, then secondary moon by rendered direction while retaining AC's single authored directional-energy channel; sun rays and volumetrics remain sun-only. The previously referenced #268 + TS-8 package is complete and retired. Stage 1's automated correctness, performance, lifetime, locked-restore, Release, evidence, documentation, and project-owner live gates completed on 2026-08-22 after the opt-in exposure correction. The [Stage 2 connected and closeout gate](../research/2026-08-22-atmospheric-stage2-connected-gate.md) now closes every available machine-local ACE, dense A/B, long-lifetime, graphical-package, shader, Release, and complete-test row. The implemented two-client gate proved that ACE rejects two concurrent characters on the one available account and is ready for separate observer credentials without recording secrets. A distinct-account remote-player row, unavailable physical GPU classes, and final project-owner pack-off/pack-on acceptance remain external gates; Campaign AR is not yet declared shipped.
|
||||
|
||||
**Planned, owner-directed — Campaign VM (VisualMaster):** the [VisualMaster campaign](2026-08-22-visualmaster-campaign.md) closes the [Campaign AR review](../research/2026-08-22-campaign-ar-review.md) findings with evidence (pack-off pixel/perf invariance vs `6c79d35c`, the un-anchored detail fade, a linear-light post stack, truthful closeouts, volumetric jitter) and adds weather-driven foliage wind behind the render pack. Named by the owner 2026-08-22; awaiting the owner's goal.
|
||||
|
||||
---
|
||||
|
||||
## Cross-cutting work tracked in parallel
|
||||
|
|
|
|||
382
docs/plans/2026-08-22-visualmaster-campaign.md
Normal file
382
docs/plans/2026-08-22-visualmaster-campaign.md
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
# Campaign VM — VisualMaster
|
||||
|
||||
**Date:** 2026-08-22 · **Status:** PLANNED — awaiting the owner's goal
|
||||
**Phase id:** **Campaign VM** — named by the project owner on 2026-08-22
|
||||
**Branch:** `claude/git-sync-status-5fb1d2` (= main + Campaign AR, `c51b07ef`)
|
||||
**Predecessor:** [Campaign AR](2026-08-21-atmospheric-rendering.md) and its
|
||||
[independent review](../research/2026-08-22-campaign-ar-review.md)
|
||||
**Scheduling:** owner-directed, like Campaign AR. Does not displace M4.
|
||||
|
||||
## Goal
|
||||
|
||||
Make Campaign AR **provably safe to merge** and then **finish the look**:
|
||||
close every review finding with evidence rather than assertion, put the
|
||||
Tier-1 post stack in linear light so its controls mean what they say, and add
|
||||
the one cheap, high-impact enhancement still missing from the discussed
|
||||
scope — **gentle, believable wind in Dereth's foliage**, driven by AC's own
|
||||
authored weather, opt-in through the same render-pack contract, with the
|
||||
default retail-faithful path untouched.
|
||||
|
||||
Campaign VM is shipped when both sides of the final matrix pass:
|
||||
|
||||
- **pack off:** pixel-identical to the pre-campaign build outside the two
|
||||
intended parity changes (terrain normals, building detail), with production
|
||||
CPU/GPU/allocation within the pre-campaign noise envelope; and
|
||||
- **pack on:** linear-light post stack, registered-or-removed detail fade,
|
||||
moving foliage that the owner judges "real, not a screensaver", shadows
|
||||
that move with the leaves, and all of it inside Campaign AR's declared
|
||||
budgets.
|
||||
|
||||
## Operating model
|
||||
|
||||
The launcher/AR cadence, unchanged:
|
||||
|
||||
- **Fable** plans, verifies claims against source/decomp, writes the
|
||||
closeouts, and drives the work order. Nothing in a closeout may claim more
|
||||
than its artifact shows — every "passes" names the command and the lane.
|
||||
- **Sonnet** implements each slice against the pinned contract below.
|
||||
- **Opus** reviews every slice with two lenses (architectural / retail-
|
||||
faithful) before its closeout; findings go through a fix round and a narrow
|
||||
re-review.
|
||||
- **The owner** is stopped for exactly three things: the cdb read on the
|
||||
retail client (VM2), the visual gates (VM3, VM6, VM7), and the merge.
|
||||
|
||||
Rules binding on every slice: no workarounds (CLAUDE.md), no guessed
|
||||
AC-specific constants — a number without a decomp/DAT anchor gets a register
|
||||
row in the same commit — and the pack-off path is the oracle: any slice that
|
||||
moves a pack-off pixel outside its declared mask is a bug, not a tuning.
|
||||
|
||||
## Slice ledger
|
||||
|
||||
| Slice | Closes | Gate |
|
||||
|---|---|---|
|
||||
| VM0 | F1 — default-path invariance | automated + Fable report |
|
||||
| VM1 | F2 — invented detail fade | automated |
|
||||
| VM2 | F3 — which retail detail path ran | **owner action** (launch retail) |
|
||||
| VM3 | F4 — linear-light post stack | automated + **owner visual** |
|
||||
| VM4 | F5 — overclaiming docs, incl. the reviewer's own | docs |
|
||||
| VM5 | F8 — volumetric banding; F7 filed | automated |
|
||||
| VM6 | Foliage wind | automated + **owner visual** |
|
||||
| VM7 | Closeout: full gates, register, roadmap, merge | **owner** |
|
||||
|
||||
Order is VM0 → VM1 → VM4 → VM5 → VM3 → VM6 → VM2 (whenever the owner has
|
||||
retail up) → VM7. VM0 goes first because everything after it is measured
|
||||
against the baseline it establishes.
|
||||
|
||||
---
|
||||
|
||||
## VM0 — Default-path invariance (F1)
|
||||
|
||||
**Why:** the only "pre-campaign oracle" is a 2×2 synthetic, one-draw
|
||||
recording-device fixture. `WbDrawDispatcher.Rhi.cs` was refactored underneath
|
||||
the default path. Nobody has compared real pack-off pixels or production
|
||||
performance against `6c79d35c`.
|
||||
|
||||
**Implementation**
|
||||
|
||||
1. Build `6c79d35c` in a throwaway worktree (Release). Capture with
|
||||
`tools/run-offline-pixel-gate.ps1 -Out artifacts/vm0/base` — the tool's
|
||||
own documented baseline mode.
|
||||
2. Capture HEAD pack-off twice with the same tool and camera set:
|
||||
`BuildingDetailTextures=false` (isolates the A2 normal change) and
|
||||
`=true` (the shipped default).
|
||||
3. Extend the tool's compare step with a **terrain mask** (same mechanism as
|
||||
the existing `sky-mask.png`): pixels whose depth/material belong to
|
||||
`terrain_modern` are excluded from the identity assertion and reported
|
||||
separately.
|
||||
4. Assert: HEAD(detail=false) vs base — **zero** differing pixels outside the
|
||||
terrain mask; inside the mask, differences are reported as a histogram
|
||||
(expected: low-amplitude shading deltas, no structural change).
|
||||
HEAD(detail=true) vs HEAD(detail=false) — differences confined to
|
||||
building/EnvCell surfaces (second mask from the detail replay's own
|
||||
command set).
|
||||
5. Production performance A/B, **no automation observer, no validation
|
||||
layers, uncapped Release**, at the CLAUDE.md production-profile camera and
|
||||
at pinned dense Arwic: base vs HEAD pack-off. Record CPU/GPU p50/p95/p99,
|
||||
FPS, `alloc_kb p50`, GC counts. The pre-campaign references are
|
||||
519.7 FPS / 1.869 ms CPU p50 (profile) and ~3.0/4.9 ms CPU p50/p95 (dense
|
||||
Caul, digest §render). Deltas must sit inside run-to-run noise; any
|
||||
allocation growth on the pack-off path is a regression to fix in this
|
||||
slice, not to file.
|
||||
6. Add the masked comparison as a repeatable tool mode
|
||||
(`-Baseline … -TerrainMask`) so VM3/VM6 can re-run it.
|
||||
|
||||
**Acceptance:** the two pixel assertions in step 4 hold; the perf A/B shows
|
||||
no default-path regression; the Fable report records the exact commands,
|
||||
commits and artifact paths. If a regression is found, it is fixed here and
|
||||
the report names the cause — no "accepted difference" without a diff that
|
||||
explains it.
|
||||
|
||||
## VM1 — Remove the invented detail fade (F2)
|
||||
|
||||
**Why:** `RetailDetailTextureContract.FullDetailDistanceMetres = 10` /
|
||||
`ZeroDetailDistanceMetres = 50` have no retail anchor. Retail's
|
||||
`DrawBuilding`/`DrawEnvCell`/`RenderMeshSubset`/`SetDetailSurfaceInternal`
|
||||
carry no distance term; attenuation is the LINEAR mip chain.
|
||||
|
||||
**Decision (Fable, retail-faithful rule):** remove the ramp. Mip averaging
|
||||
already converges the live category texture to its 1.033 mean factor.
|
||||
|
||||
**Implementation**
|
||||
|
||||
- Delete the two constants and `vDetailFade` from `mesh_detail.vert/.frag`
|
||||
(`mesh_detail.vert:83`); the fragment outputs `detail.rgba` unscaled.
|
||||
- Confirm the detail texture is uploaded with a full mip chain and sampled
|
||||
LINEAR/LINEAR/LINEAR, WRAP — that *is* retail's attenuation; test it.
|
||||
- Rename `RetailDetailTextureContract` members so nothing un-anchored is
|
||||
labelled retail; keep the blend-factor helper and its tests.
|
||||
- Update the #226 pseudocode note: strike the "10 m / 50 m" lines, add the
|
||||
decomp citations above, and state that attenuation is mip-driven.
|
||||
- If the owner later wants a ramp back, it returns as a **pack setting**
|
||||
(enhancement), never on the default path.
|
||||
|
||||
**Acceptance:** no reference to a distance fade remains in src or the note;
|
||||
VM0's masked comparison re-run shows the only change is on detail surfaces;
|
||||
at >50 m a building reads the same as at 60 m (mips), not a hard step.
|
||||
|
||||
## VM2 — Which detail path did retail hardware run? (F3, owner-gated)
|
||||
|
||||
**Why:** the port is of the two-pass framebuffer fallback. With
|
||||
`m_caps.bCanDoSinglePassDetailing`, retail used stage-1 texture ops
|
||||
(`PREMODULATE` + `BLENDCURRENTALPHA` — a lerp). The cap needs
|
||||
`D3DTEXOPCAPS_PREMODULATE` (0x0059f6c6), which consumer drivers rarely
|
||||
exposed — probable, not proven.
|
||||
|
||||
**Implementation:** one cdb script (`tools/cdb/vm2-detail-caps.cdb`): attach
|
||||
to the PDB-paired `acclient.exe`, `dt acclient!RenderDevice::render_device`
|
||||
→ `m_caps.bCanDoSinglePassDetailing`, `m_caps.bTexOpDotProduct3`, and the raw
|
||||
`m_D3DCaps.TextureOpCaps`; `qd`. Record the answer in the #226 note and the
|
||||
review doc.
|
||||
|
||||
- `bCanDoSinglePassDetailing == 0` → the port is the path players saw; close.
|
||||
- `== 1` → file the single-pass stage math as the correct target, register
|
||||
the current blend as a bounded divergence, and schedule the re-port as its
|
||||
own slice (it is a fragment-shader change only).
|
||||
|
||||
**Acceptance:** the value is recorded with the binary GUID and the date.
|
||||
|
||||
## VM3 — Linear-light post stack (F4)
|
||||
|
||||
**Why:** bloom threshold, ACES, Rec.709 luma, saturation and the 0.5
|
||||
contrast pivot all assume linear light; they receive gamma-encoded retail
|
||||
colours and the output is never re-encoded. `exposure = 0.80` is the
|
||||
compensation.
|
||||
|
||||
**Design**
|
||||
|
||||
- The main-world intermediate stays gamma-encoded `Rgba16Float` — the world
|
||||
pass and its alpha blending are retail's and must not change.
|
||||
- Every pack read of world colour decodes once: `acdreamDecode(c) = pow(c, 2.2)`
|
||||
(a named function in `atmospheric_common.glsl`; 2.2 is the retail-era
|
||||
display assumption, documented as such — not sRGB piecewise, which would
|
||||
imply a precision the source never had).
|
||||
- Bloom chain, sun-ray composite and volumetric composite operate in linear.
|
||||
- `atmospheric_filmic.frag`: exposure → ACES → grade → vignette in linear,
|
||||
then `acdreamEncode(c) = pow(c, 1/2.2)` to the UNORM swapchain.
|
||||
- Defaults re-tuned so the **neutral** preset reproduces the pack-off image
|
||||
within the VM0 tolerance when every effect is at its neutral value
|
||||
(exposure 1.0, bloom 0, tonemap mix 0, saturation 1, contrast 1,
|
||||
vignette 0) — the existing "every effect can be set to neutral" acceptance
|
||||
now actually holds numerically, and is asserted by a test.
|
||||
- Opinionated defaults (what the owner accepted at 0.80 exposure) are
|
||||
re-derived in linear and presented for the visual gate; expect exposure to
|
||||
return to ~1.0 and the bloom threshold to move up.
|
||||
|
||||
**Acceptance:** neutral preset ≡ pack-off within tolerance (new automated
|
||||
test on the recording RHI plus VM0's masked tool on a real capture); Stage-1
|
||||
luminance table re-captured; owner visual gate: "same look as accepted, no
|
||||
clipping, highlights roll off". Budget unchanged (two `pow` per pixel).
|
||||
|
||||
## VM4 — Truthful documents (F5)
|
||||
|
||||
Correct, in the AR plan and its reports, the four sentences the review
|
||||
named: the 2×2 oracle described as a production pin; the turning-hitch
|
||||
conclusion drawn under the automation observer; the "zero skips" headline
|
||||
(say "hermetic lanes"); and the Track A premise that `TerrainUtils.GetNormal`
|
||||
produced a faceted look. Also correct the reviewer's own findings doc §4
|
||||
(`2026-08-21-terrain-and-atmospheric-rendering-findings.md`): that function
|
||||
orients scenery; the render normals were already smooth; A2 replaced
|
||||
central-difference with retail's split-aware incident-face average.
|
||||
Add the VM0 numbers as the new baseline table. Docs only; no code.
|
||||
|
||||
## VM5 — Volumetric jitter, and file the shared transform buffer (F8, F7)
|
||||
|
||||
- `atmospheric_volumetric.frag`: interleaved-gradient-noise offset per pixel
|
||||
on the march start (`fract(52.9829189 * fract(dot(gl_FragCoord.xy,
|
||||
vec2(0.06711056, 0.00583715))))`), plus the existing quarter/half-res
|
||||
upsample. Automated: a fixed-camera capture's step-banding metric (row
|
||||
autocorrelation at the step period) drops below the current value;
|
||||
GPU delta < 0.02 ms.
|
||||
- File issue: the shadow pass uploads a second transform buffer; a future
|
||||
GPU-culling step should bind the main pass's instance SSBO. Not built here.
|
||||
|
||||
## VM6 — Foliage wind (the feature)
|
||||
|
||||
### What "real movement" means here
|
||||
|
||||
Trees do not wave like flags. Real foliage has **three motions at three
|
||||
speeds**: the whole tree leans slowly with the mean wind (seconds), branches
|
||||
swing at their own natural frequency (about one second), and leaves flutter
|
||||
fast and independently (fractions of a second). Gusts come and go over tens
|
||||
of seconds. Neighbouring trees are out of step. Trunk bases do not move.
|
||||
Anything that ignores one of these reads as "screensaver" — which is what
|
||||
the owner does not want.
|
||||
|
||||
### The rule for what sways (no guessing, data-driven)
|
||||
|
||||
- **Candidate set:** procedural scenery only — entity ids in the
|
||||
`0x8XXYYIII` namespace (`ProceduralSceneryIdAllocator`, bit 31). Landblock
|
||||
statics (fences, signposts, buildings), weenies, creatures and players never
|
||||
sway.
|
||||
- **Foliage subsets:** within a candidate, the **alpha-cutout material
|
||||
subsets** (leaves, fronds, bushes, grass tufts). Rocks have none and stay
|
||||
still; a tree's trunk is opaque and gets only the slow lean (below).
|
||||
- **Opaque subsets of a candidate that also owns cutout subsets** (trunks,
|
||||
branches) receive the slow lean only, scaled by height, never the flutter.
|
||||
- A pack may **exclude** object ids (`FoliageExclusions`, a list in the
|
||||
descriptor) for the rare scenery object that is cutout but not foliage.
|
||||
There is no include list: the rule is the rule.
|
||||
|
||||
This is a render-only classification flag per batch — bit 1 of
|
||||
`BatchData.flags` (`mesh_atmospheric.vert` already reads `flags`; bit 0 is
|
||||
the #226 built-mesh marker). The dispatcher sets it once at classification
|
||||
time from the entity id namespace and the subset's blend class; the pack-off
|
||||
`mesh_modern` pipelines never read it.
|
||||
|
||||
### The motion (vertex shader, pack variant only)
|
||||
|
||||
Applied in `mesh_atmospheric.vert` and in all six
|
||||
`directional_shadow_world_*` vertex shaders (constraint: **the shadow must
|
||||
move with the leaf** — the caster and receiver displacement are one shared
|
||||
include, `foliage_wind.glsl`, so they cannot drift apart).
|
||||
|
||||
Inputs: instance world origin `o` (translation column of
|
||||
`Instances[i].transform`), vertex world position `p`, `time` (from
|
||||
`uCameraAndTime.w`, already in the vertex stage), and the pack's wind block
|
||||
(new `uFoliageWind` in `atmospheric_common.glsl`):
|
||||
|
||||
```
|
||||
vec4 uFoliageWindDirection; // xy unit direction, z = mean strength [0..1], w = gust strength
|
||||
vec4 uFoliageWindParams; // x = lean amplitude m, y = branch amplitude m, z = flutter amplitude m, w = max height m
|
||||
```
|
||||
|
||||
All amplitudes are **metres at the canopy top**; they are pack settings with
|
||||
bounded ranges, not shader constants.
|
||||
|
||||
```
|
||||
h = clamp((p.z - o.z) / maxHeight, 0, 1) // 0 at the base, 1 at the top
|
||||
k = h * h // bend grows with height²: bases stay put
|
||||
ph = dot(o.xy, vec2(0.137, 0.291)) // per-tree phase from world position
|
||||
g = 0.5 + 0.5 * sin(0.05 * t + ph) // gust envelope, ~20 s
|
||||
+ 0.25 * sin(0.13 * t + 1.7 * ph)
|
||||
s = mean + gust * g // instantaneous strength
|
||||
|
||||
lean = k * leanAmp * s * (0.8 + 0.2 * sin(0.35 * t + ph)) // slow lean
|
||||
branch = k * branchAmp * s * sin(1.1 * t + ph + 2.0 * h) // ~1 Hz, phase runs up the tree
|
||||
flutter = h * flutterAmp * s * sin(6.0 * t + 7.0 * fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5453))
|
||||
|
||||
d = dir * (lean + branch) + perp(dir) * 0.35 * branch + vec2(flutter) * normalize(vec2(cos, sin) of vertex hash)
|
||||
p.xy += d
|
||||
p.z -= 0.5 * dot(d, d) / max(h * maxHeight, 0.5) // bend shortens, it does not stretch
|
||||
```
|
||||
|
||||
Opaque (trunk) subsets use `lean` only. Cutout subsets use all three. The
|
||||
vertex hash decorrelates leaves on the same tree; the instance phase
|
||||
decorrelates trees. The `p.z` term is the cheap length-preserving correction
|
||||
so a bent canopy sinks slightly instead of growing.
|
||||
|
||||
Normals are **not** rotated (Gouraud on a cutout leaf with flipped lighting
|
||||
would flicker); AC's flat-lit foliage does not need it.
|
||||
|
||||
### Weather drives it (AC owns the weather)
|
||||
|
||||
The pack's `AtmospherePolicyDeclaration` gains a `FoliageWind` table keyed by
|
||||
the same categorical `activeDayGroup` the rays already use:
|
||||
|
||||
| Day group | mean | gust |
|
||||
|---|---|---|
|
||||
| Clear | 0.25 | 0.15 |
|
||||
| Cloudy | 0.45 | 0.30 |
|
||||
| Overcast | 0.60 | 0.35 |
|
||||
| Rainy | 0.85 | 0.60 |
|
||||
|
||||
plus a global **Wind** slider (0–2×) and an **Off** setting. Transitions
|
||||
between day groups interpolate over the existing weather delta seconds
|
||||
(`uAtmosphereWeather.z`), so a weather change never snaps. Wind direction is
|
||||
a pack setting (default NE→SW, 225°); there is no authored retail wind
|
||||
direction to read, and the register row says so.
|
||||
|
||||
### Indoor, shadows, physics, picking
|
||||
|
||||
- Indoors (`!IsOutdoor`) wind is zero — EnvCells have no scenery anyway.
|
||||
- Shadow casters apply the identical displacement (shared include).
|
||||
- Physics is untouched by construction: the collision BSP is the trunk and
|
||||
nothing here touches Runtime or Core physics. The acceptance test asserts
|
||||
the Runtime/physics diff is empty.
|
||||
- World picking (`WorldPicker`) picks against the undisplaced mesh; a leaf
|
||||
may be up to `leanAmp + branchAmp` metres from its pick volume. That is
|
||||
accepted and registered (one row, "render-only foliage displacement");
|
||||
nobody picks leaves.
|
||||
|
||||
### Budget and quality scaling
|
||||
|
||||
- Cost: ~25 ALU per foliage vertex, zero CPU, zero submissions. Target:
|
||||
< 0.05 ms GPU at 1080p dense foliage route; measured with the AR matrix
|
||||
tooling (`tools/run-atmospheric-performance-matrix.ps1`).
|
||||
- Low preset: lean + branch only (no flutter). Medium/High: all three.
|
||||
|
||||
### Acceptance
|
||||
|
||||
- Automated: the classification flag is set only for bit-31 entities' cutout
|
||||
subsets (and lean-only for their opaque subsets); pack-off pipelines never
|
||||
read the flag and VM0's masked comparison is unchanged; the shared include
|
||||
is byte-identical between receiver and caster variants (test reads both
|
||||
SPIR-V inputs' source); weather table interpolates without discontinuity;
|
||||
budget row passes.
|
||||
- **Owner visual gate**, in this order: (1) Holtburg outskirts, Clear, noon —
|
||||
"barely moving, alive"; (2) same place, Rainy — "clearly windy, still not
|
||||
a flag"; (3) watch one tree for 30 s — gusts arrive and leave, neighbours
|
||||
out of step; (4) the tree's shadow on the ground moves with it; (5) walk
|
||||
indoors — nothing moves; (6) pack off — nothing moves. The owner's words
|
||||
decide; the numbers above are starting points to tune live.
|
||||
|
||||
## VM7 — Closeout and merge
|
||||
|
||||
- Full gates: `tools/run-release-gate.ps1` (hermetic lanes), the AR
|
||||
reference matrix re-run for the changed presets, VM0's masked comparison
|
||||
on the final binary, the connected lifecycle/reconnect route.
|
||||
- Register: rows for the wind direction constant, the render-only
|
||||
displacement vs picking, and whatever VM2 decides; TS-52 stays retired.
|
||||
- Roadmap: move Campaign VM to shipped; AR's "final owner gate" rows that
|
||||
VM0/VM3/VM6 satisfy are marked so in the AR plan.
|
||||
- Memory: a `project_visualmaster_campaign.md` digest entry (the wind rule,
|
||||
the linear-light decision, the VM0 oracle recipe).
|
||||
- Owner: final pack-off / pack-on visual matrix, then merge to main.
|
||||
|
||||
## What this campaign does NOT do
|
||||
|
||||
- It does not touch the retail-faithful default path except through VM0's
|
||||
evidence and VM1's removal of an un-anchored constant.
|
||||
- It does not add normal maps, PBR, SSAO, water, or a second weather system.
|
||||
- It does not move trunks' collision, walkability, or anything in Runtime.
|
||||
- It does not animate landblock statics, weenies, grass decals that are not
|
||||
scenery, or creatures.
|
||||
- It does not rebuild the shadow transform buffer (filed, VM5).
|
||||
- It does not re-port #226 to the single-pass path unless VM2 proves retail
|
||||
ran it.
|
||||
|
||||
## Proposed owner goal
|
||||
|
||||
> **Campaign VM — VisualMaster.** Drive
|
||||
> `docs/plans/2026-08-22-visualmaster-campaign.md` to shipped on branch
|
||||
> `claude/git-sync-status-5fb1d2`: VM0 proves the pack-off path is
|
||||
> pixel-identical to `6c79d35c` outside the terrain/detail masks with no
|
||||
> production perf or allocation regression; VM1 removes the un-anchored
|
||||
> detail fade; VM3 puts the post stack in linear light with a numerically
|
||||
> neutral preset; VM4 makes every document truthful; VM5 de-bands the
|
||||
> volumetrics; VM6 adds weather-driven foliage wind (scenery-only, cutout
|
||||
> subsets, three motions, shadows follow) behind the render pack. Fable plans
|
||||
> and verifies, Sonnet implements, Opus dual-lens reviews every slice. Stop
|
||||
> me only for: the retail cdb read (VM2), the three visual gates (VM3, VM6,
|
||||
> VM7), and the merge. Every closeout claim must name its command, lane and
|
||||
> artifact.
|
||||
Loading…
Add table
Add a link
Reference in a new issue