spec(rendering): Phase N WorldBuilder migration design + N.1 scenery
Adds two design docs and a roadmap entry for the strategic shift from "port retail rendering algorithms ourselves" to "depend on a fork of Chorizite/WorldBuilder for rendering + dat-handling." - docs/superpowers/specs/2026-05-08-phase-n-worldbuilder-migration-design.md — parent design: integration model (fork + submodule), 10 sub-phases (N.0 setup through N.10 GL consolidation), strangler-fig phasing with per-phase feature flags, retail-decomp boundary clarified for what WB does NOT cover (network, physics, animation, motion, UI, plugin, audio, chat). - docs/superpowers/specs/2026-05-08-phase-n1-scenery-via-wb-helpers-design.md — N.1 detailed design: replace IsOnRoad / DisplaceObject / slope-normal calc / rotation / scale inside SceneryGenerator.Generate() with calls to WB's SceneryHelpers + TerrainUtils. Keep data flow, ScenerySpawn shape, and renderer integration. Add small LandBlock → TerrainEntry[] adapter. Feature flag ACDREAM_USE_WB_SCENERY=1. - docs/plans/2026-04-11-roadmap.md — Phase N entry added between Phase M and Phase J. Lists all 10 sub-phases with effort estimates. Fork already created at https://github.com/eriknihlen/WorldBuilder. N.0 setup (replace references/WorldBuilder/ snapshot with submodule, add project references, build green) is the next implementation step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b210be126
commit
8a06fce7a5
3 changed files with 501 additions and 0 deletions
|
|
@ -498,6 +498,93 @@ before porting.
|
|||
|
||||
---
|
||||
|
||||
### Phase N — WorldBuilder Rendering Migration
|
||||
|
||||
**Goal:** Stop re-porting AC-specific rendering / dat-handling
|
||||
algorithms. Depend on a fork of `Chorizite/WorldBuilder` (MIT) for
|
||||
terrain, scenery, static objects, EnvCells, portals, sky, particles,
|
||||
texture decoding, mesh extraction, and visibility. Acdream keeps its
|
||||
own network, physics, animation, motion, UI, plugin, audio, chat
|
||||
layers (those aren't in WB).
|
||||
|
||||
**Why now (2026-05-08):** the scenery edge-vertex bug at landblock
|
||||
`0xA9B1` was the third subtle porting bug in a quarter (after the
|
||||
triangle-Z bug and the hover-over-terrain bug). Even when our code
|
||||
looked byte-identical to WB's, our output diverged. WB renders the
|
||||
world correctly; the cost of "we re-port retail algorithms" is now
|
||||
higher than "we depend on WB's tested port."
|
||||
|
||||
**Design + inventory:**
|
||||
|
||||
- `docs/architecture/worldbuilder-inventory.md` — full taxonomy of
|
||||
what WB has and what we keep porting ourselves.
|
||||
- `docs/superpowers/specs/2026-05-08-phase-n-worldbuilder-migration-design.md` —
|
||||
parent design doc.
|
||||
- `docs/superpowers/specs/2026-05-08-phase-n1-scenery-via-wb-helpers-design.md` —
|
||||
N.1 detailed design.
|
||||
|
||||
**Integration model:** fork at
|
||||
`https://github.com/eriknihlen/WorldBuilder` (already created), git
|
||||
submodule replacing `references/WorldBuilder/` snapshot, project
|
||||
references in our solution. Long-lived `acdream` branch in the fork
|
||||
for our deletions/additions; merge upstream `master` periodically.
|
||||
|
||||
**Sub-phases (strangler-fig with feature flags):**
|
||||
|
||||
- **N.0 — Setup.** Submodule + project references + build green. ~1-2 hrs.
|
||||
- **N.1 — Scenery algorithm calls.** Replace `IsOnRoad` /
|
||||
`DisplaceObject` / slope-normal calc / rotation / scale inside
|
||||
`SceneryGenerator.Generate()` with calls to WB's `SceneryHelpers` +
|
||||
`TerrainUtils`. Tiny adapter `LandBlock → TerrainEntry[]`. Keeps our
|
||||
data flow + `ScenerySpawn` shape. Feature flag
|
||||
`ACDREAM_USE_WB_SCENERY=1`. ~1-2 days.
|
||||
- **N.2 — Terrain math helpers.** Refactor `TerrainSurface.SampleZ` /
|
||||
`SampleNormal` / `SampleSurface` to call WB's `TerrainUtils.GetHeight`
|
||||
/ `GetNormal` internally. ~1-2 days.
|
||||
- **N.3 — Texture decoding.** Replace `TextureCache` decode pipeline
|
||||
with WB's `TextureHelpers`. ~2-3 days.
|
||||
- **N.4 — Object meshing.** Replace `SetupMesh.cs` + `GfxObjMesh.cs`
|
||||
with calls to WB's `ObjectMeshManager`. Character-appearance
|
||||
behaviors (CreaturePalette / GfxObjRemapping / HiddenParts) remain
|
||||
ours — ACME is the secondary oracle. ~1 week.
|
||||
- **N.5 — Terrain rendering.** Replace `TerrainChunkRenderer` +
|
||||
`TerrainAtlas` + `TerrainBlending` with WB's `TerrainRenderManager` +
|
||||
`LandSurfaceManager` + `TerrainGeometryGenerator`. ~2 weeks.
|
||||
- **N.6 — Static objects rendering.** Replace `StaticMeshRenderer` +
|
||||
`InstancedMeshRenderer` with WB's `StaticObjectRenderManager`.
|
||||
~2 weeks.
|
||||
- **N.7 — EnvCells / dungeons.** Replace EnvCell rendering with WB's
|
||||
`EnvCellRenderManager` + `PortalRenderManager`. ~2 weeks.
|
||||
- **N.8 — Sky + particles.** Replace sky rendering + particle pipeline
|
||||
(#36 / C.1 work) with WB's `SkyboxRenderManager` +
|
||||
`ParticleEmitterRenderer`. ~1 week.
|
||||
- **N.9 — Visibility / culling.** Replace `CellVisibility` +
|
||||
`FrustumCuller` with WB's `VisibilityManager`. ~3-5 days.
|
||||
- **N.10 — GL infrastructure consolidation (optional).** Replace our
|
||||
`Shader` / `TextureCache` / `SamplerCache` plumbing with WB's
|
||||
`ManagedGL*` wrappers + `OpenGLGraphicsDevice`. ~1 week.
|
||||
|
||||
**Estimated calendar:** 2-3 months. Engineering effort: 6-8 weeks.
|
||||
|
||||
**Each sub-phase:**
|
||||
- Ships behind `ACDREAM_USE_WB_<NAME>=1` flag.
|
||||
- Has its own conformance test (side-by-side against existing path).
|
||||
- Visual verification before flag becomes default-on.
|
||||
- Old code deleted after default-on lands cleanly.
|
||||
|
||||
**N.2-N.10 detailed specs are NOT yet written** — each gets its own
|
||||
brainstorm + spec when we reach it.
|
||||
|
||||
**Acceptance:**
|
||||
- All 10 sub-phases shipped, feature flags removed, old rendering code
|
||||
paths deleted.
|
||||
- Visual verification at Holtburg + Foundry statue + a representative
|
||||
dungeon shows no regression vs Phase C.1.
|
||||
- WB upstream merges into our `acdream` branch are clean (or have
|
||||
documented conflict-resolution patterns).
|
||||
|
||||
---
|
||||
|
||||
### Phase J — Long-tail (deferred / low-priority)
|
||||
|
||||
Not detailed here; each gets its own brainstorm when it becomes relevant.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,223 @@
|
|||
# Phase N — WorldBuilder Rendering Migration: Design
|
||||
|
||||
**Date:** 2026-05-08
|
||||
**Status:** Design complete, awaiting plan generation for N.1.
|
||||
|
||||
## Goal
|
||||
|
||||
Stop re-porting AC-specific rendering and dat-handling algorithms from
|
||||
retail decomp. Instead, depend on a fork of WorldBuilder
|
||||
(`github.com/Chorizite/WorldBuilder`, MIT) for terrain, scenery, static
|
||||
objects, EnvCells, portals, sky, particles, texture decoding, mesh
|
||||
extraction, and visibility / culling. Acdream keeps its own network,
|
||||
physics, animation, motion, UI, plugin, audio, and chat layers — those
|
||||
are not in WorldBuilder.
|
||||
|
||||
## Why
|
||||
|
||||
acdream has accumulated a recurring pattern of subtle porting bugs in
|
||||
its own rendering algorithms (the latest: a tree near the road at
|
||||
landblock `0xA9B1` that retail and WorldBuilder do not show but our
|
||||
re-port did, despite the algorithm code looking byte-identical to
|
||||
WorldBuilder's). The triangle-Z bug, the hover-over-terrain bug, and
|
||||
the edge-vertex spawn bug are all in the same family: small porting
|
||||
errors that survive surface-level review.
|
||||
|
||||
WorldBuilder is verified by visual inspection to render the AC world
|
||||
correctly. It uses the same Silk.NET + .NET stack we already target.
|
||||
It is MIT-licensed. It has fewer subtle bugs because its developers
|
||||
have run it against the entire client_cell + client_portal dat content
|
||||
and fixed everything users have reported.
|
||||
|
||||
The cost of "we re-port retail algorithms ourselves" is now higher than
|
||||
the cost of "we depend on someone else's tested port and inherit their
|
||||
fixes." Migrating the rendering+dat layer to WorldBuilder is the
|
||||
right call.
|
||||
|
||||
## Inventory reference
|
||||
|
||||
The full taxonomy of "what WorldBuilder has, what we keep porting
|
||||
ourselves" lives at
|
||||
[`docs/architecture/worldbuilder-inventory.md`](../../architecture/worldbuilder-inventory.md).
|
||||
Before re-implementing any rendering or dat-handling algorithm, **check
|
||||
the inventory first**. CLAUDE.md is updated to enforce this.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Integration model
|
||||
|
||||
**Fork upstream WorldBuilder, depend on the fork via git submodule.**
|
||||
|
||||
- Fork: `https://github.com/eriknihlen/WorldBuilder` (already created;
|
||||
upstream: `Chorizite/WorldBuilder`).
|
||||
- Long-lived branch in fork: `acdream`. Upstream `master` merges into
|
||||
`acdream` periodically; our acdream-specific changes (delete editor
|
||||
files, expose hooks for our scene state) live on `acdream`.
|
||||
- The current read-only snapshot at `references/WorldBuilder/` is
|
||||
**replaced** by a git submodule pointing at the fork's `acdream`
|
||||
branch. Existing CLAUDE.md path references and research docs that
|
||||
cite `references/WorldBuilder/...` keep working.
|
||||
- Our solution adds two `<ProjectReference>`s:
|
||||
- `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Chorizite.OpenGLSDLBackend.csproj`
|
||||
- `references/WorldBuilder/WorldBuilder.Shared/WorldBuilder.Shared.csproj`
|
||||
- Transitive NuGet dependencies (`Chorizite.Core`,
|
||||
`Chorizite.DatReaderWriter.Extensions`, `BCnEncoder.Net`,
|
||||
`SixLabors.ImageSharp`, `Silk.NET.SDL`, `MP3Sharp`) flow through.
|
||||
- Editor-only files in WorldBuilder (Modules/Landscape/{Tools,
|
||||
Commands, Services, Migrations, Hubs}, LandscapeDocument, etc.) stay
|
||||
in the fork's source tree but are simply not referenced by acdream.
|
||||
They impose no runtime cost. We can prune later if upstream stays
|
||||
well-organized.
|
||||
|
||||
### Phasing — strangler fig, subsystem by subsystem
|
||||
|
||||
Each sub-phase is independently shippable behind a feature flag
|
||||
(`ACDREAM_USE_WB_<NAME>=1`). After visual verification the flag becomes
|
||||
default-on, then is removed and the old code is deleted. This gives us
|
||||
a one-line revert if a phase regresses.
|
||||
|
||||
| # | Sub-phase | Effort | Risk |
|
||||
|---|---|---|---|
|
||||
| **N.0** | Submodule + project references + build green | 1-2 hrs | Low |
|
||||
| **N.1** | Scenery algorithm calls | 1-2 days | Low |
|
||||
| **N.2** | Terrain math helpers | 1-2 days | Low |
|
||||
| **N.3** | Texture decoding | 2-3 days | Medium |
|
||||
| **N.4** | Object meshing (Setup/GfxObj) | 1 week | Medium |
|
||||
| **N.5** | Terrain rendering (full pipeline) | 2 weeks | High |
|
||||
| **N.6** | Static objects rendering | 2 weeks | High |
|
||||
| **N.7** | EnvCells / dungeons | 2 weeks | High |
|
||||
| **N.8** | Sky + particles | 1 week | Medium |
|
||||
| **N.9** | Visibility / culling | 3-5 days | Medium |
|
||||
| **N.10** | GL infrastructure consolidation (optional) | 1 week | Medium |
|
||||
|
||||
Total estimated calendar: 2-3 months. Engineering effort: 6-8 weeks.
|
||||
|
||||
### What WorldBuilder does NOT cover (keep porting from retail decomp)
|
||||
|
||||
- Network protocol (UDP, ISAAC, ACE messages) — keep ours
|
||||
- Physics: collision, BSP queries, sphere sweeps, walkable validation
|
||||
— keep ours (partial), continue porting from retail decomp
|
||||
- Animation: motion sequencer, cycle/non-cycle parts — keep ours
|
||||
- Movement: WASD → MoveToState wire, remote-entity motion via
|
||||
UpdateMotion + dead-reckoning — keep ours
|
||||
- Game UI: chat, vitals, inventory, spell book — keep ours (ImGui
|
||||
today, custom-toolkit later)
|
||||
- Plugin API: IGameState, IEvents, IActions, IPacketPipeline,
|
||||
IOverlay — keep ours (acdream-unique)
|
||||
- Game events: combat, allegiance, spell casting — keep ours
|
||||
- Audio (OpenAL pipeline) — keep ours
|
||||
- TurbineChat + slash commands — keep ours
|
||||
- Login + character selection flow — keep ours
|
||||
|
||||
Per CLAUDE.md update, these still follow the
|
||||
"grep named → decompile → verify → port" workflow against retail decomp
|
||||
at `docs/research/named-retail/`.
|
||||
|
||||
### Network reference posture
|
||||
|
||||
`references/Chorizite.ACProtocol/` (separate Chorizite repo) remains
|
||||
the Primary Oracle for protocol field order and packed-dword
|
||||
conventions per CLAUDE.md's reference table. No fork needed there. We
|
||||
will lean on it harder during future network-conformance phases (Phase
|
||||
M is already on the roadmap for that).
|
||||
|
||||
## Components
|
||||
|
||||
### N.0 — Setup (must land before N.1)
|
||||
|
||||
**Files / actions:**
|
||||
- Remove `references/WorldBuilder/` from working tree (it's currently a
|
||||
checked-in snapshot). Add it back as a submodule pointing at
|
||||
`git@github.com:eriknihlen/WorldBuilder.git` tracking the `acdream`
|
||||
branch (created off `master`).
|
||||
- Add `<ProjectReference>` entries in
|
||||
`src/AcDream.Core/AcDream.Core.csproj` and
|
||||
`src/AcDream.App/AcDream.App.csproj` for the two WB projects.
|
||||
- Update `.gitmodules` to reflect the new submodule.
|
||||
- Verify `dotnet build` and `dotnet test` are green.
|
||||
- Commit.
|
||||
|
||||
**Done criteria:**
|
||||
- `git submodule status` shows `references/WorldBuilder` at the fork's
|
||||
`acdream` HEAD.
|
||||
- Solution builds clean with no new warnings.
|
||||
- Existing 870+ tests still pass.
|
||||
|
||||
### N.1 — Scenery algorithm calls
|
||||
|
||||
See companion design doc:
|
||||
[`2026-05-08-phase-n1-scenery-via-wb-helpers-design.md`](2026-05-08-phase-n1-scenery-via-wb-helpers-design.md).
|
||||
|
||||
Brief: replace the algorithm guts inside `SceneryGenerator.Generate()`
|
||||
with calls to WB's `SceneryHelpers` (Displace, RotateObj, ScaleObj,
|
||||
ObjAlign, CheckSlope) and `TerrainUtils` (OnRoad, GetNormal). Keep our
|
||||
data flow, our `ScenerySpawn` shape, our renderer integration. Add a
|
||||
small adapter `LandBlock → TerrainEntry[]`.
|
||||
|
||||
### N.2-N.10 — separately brainstormed when we get there
|
||||
|
||||
Each sub-phase will get its own brainstorm + spec when we reach it.
|
||||
Estimating ahead is unreliable for the bigger phases (N.5, N.6, N.7);
|
||||
we'll know more after N.1 ships and we have hands-on experience with
|
||||
the WB integration.
|
||||
|
||||
## Risks
|
||||
|
||||
1. **Chorizite.Core dependency footprint.** Each render manager we
|
||||
take pulls in `Chorizite.Core.Lib` and `Chorizite.Core.Render`.
|
||||
Mitigation: take the NuGet dep, don't try to strip it. Risk is
|
||||
mostly cosmetic (an extra package).
|
||||
|
||||
2. **WB's data-flow is editor-shaped.** `LandscapeDocument`,
|
||||
`LandscapeChunk`, etc. are editor concepts. Mitigation: write small
|
||||
adapters that produce the editor-shaped data from our dat reads.
|
||||
Phase N.1 is intentionally chosen to avoid this — we use only the
|
||||
stateless helpers, not the full `SceneryRenderManager`. Larger
|
||||
phases (N.5+) will need real adapter layers.
|
||||
|
||||
3. **Upstream divergence.** WB's `master` will keep moving. Mitigation:
|
||||
merge upstream `master` into our `acdream` branch periodically (at
|
||||
minimum, before each new phase starts). Our acdream-specific
|
||||
changes are isolated to deletions and additions on the `acdream`
|
||||
branch, which merges cleanly with upstream most of the time.
|
||||
|
||||
4. **Behaviors WB doesn't have.** WB is a dat editor; some
|
||||
in-game-only behaviors (creature appearance via CreaturePalette /
|
||||
GfxObjRemapping / HiddenParts) aren't in WB and we'll still need to
|
||||
handle them ourselves at the integration boundary. Mitigation:
|
||||
ACME's `StaticObjectManager.cs` covers these and is documented in
|
||||
CLAUDE.md as the secondary oracle for character appearance.
|
||||
|
||||
5. **Visual regression during migration.** Mitigation: feature flag
|
||||
per phase. Visual verification at known-good locations (Holtburg,
|
||||
Foundry statue, dungeon entrances) before flag becomes default-on.
|
||||
|
||||
## Testing
|
||||
|
||||
- **N.0:** existing 870+ tests stay green; `dotnet build` clean.
|
||||
- **N.1:** new conformance test that runs both our `SceneryGenerator`
|
||||
and a parallel call into WB's helpers against the same fixture data,
|
||||
asserts identical spawn list. Visual verification at landblock
|
||||
`0xA9B1` — the offending tree should be gone, Issue #49's missing
|
||||
scenery should still be visible.
|
||||
- **N.2-N.10:** each phase will define its own conformance and visual
|
||||
verification criteria when brainstormed.
|
||||
|
||||
## Documentation impact
|
||||
|
||||
- [x] `docs/architecture/worldbuilder-inventory.md` — created.
|
||||
- [x] `CLAUDE.md` — updated with new posture (top-level rule + reference
|
||||
table + per-domain oracle hierarchy).
|
||||
- [ ] `docs/plans/2026-04-11-roadmap.md` — add Phase N entry alongside
|
||||
L, M, etc. (this happens in the same commit as the spec).
|
||||
- [ ] `docs/architecture/acdream-architecture.md` — needs an
|
||||
acknowledging note that the rendering layer is now WB-backed; can
|
||||
follow in a later commit, not blocking.
|
||||
|
||||
## Out of scope for this design
|
||||
|
||||
- Phase N.2-N.10 detailed scope (each gets own brainstorm).
|
||||
- Network conformance work (separate Phase M).
|
||||
- Animation, physics, motion ports (continue against retail decomp,
|
||||
not WB).
|
||||
- UI, plugin, chat work (separate phases, not affected).
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
# Phase N.1 — Scenery via WorldBuilder Helpers: Design
|
||||
|
||||
**Date:** 2026-05-08
|
||||
**Parent design:** [`2026-05-08-phase-n-worldbuilder-migration-design.md`](2026-05-08-phase-n-worldbuilder-migration-design.md)
|
||||
**Status:** Design complete, awaiting plan generation.
|
||||
|
||||
## Goal
|
||||
|
||||
Replace the algorithm guts of `SceneryGenerator.Generate()` with calls
|
||||
to WorldBuilder's stateless `SceneryHelpers` and `TerrainUtils`. Keep
|
||||
our data flow, our `ScenerySpawn` shape, and our renderer integration
|
||||
unchanged.
|
||||
|
||||
## Why scenery first
|
||||
|
||||
1. **Active bug source.** Issues #48, #49 are scenery-related; the
|
||||
investigation in this session uncovered another (the road-edge tree
|
||||
at `0xA9B1`) we couldn't easily root-cause despite our code looking
|
||||
identical to WB's.
|
||||
2. **Smallest coherent slice.** Scenery placement uses only stateless
|
||||
helpers from WB (Displace, OnRoad, GetNormal, CheckSlope, RotateObj,
|
||||
ScaleObj). No need to take WB's `SceneryRenderManager`, no need for
|
||||
editor-shaped data flow.
|
||||
3. **Proves the integration pattern.** Phase N.0 wires up the
|
||||
submodule + project references. N.1 uses them with a tiny surface
|
||||
area. If something is wrong with the dependency model, we discover
|
||||
it cheaply.
|
||||
|
||||
## Architecture
|
||||
|
||||
### What changes
|
||||
|
||||
`src/AcDream.Core/World/SceneryGenerator.cs`:
|
||||
- Remove our private `IsOnRoad(LandBlock, float, float)` helper.
|
||||
- Remove our private `DisplaceObject(ObjectDesc, uint, uint, uint)` helper.
|
||||
- Remove the `RoadHalfWidth` constant.
|
||||
- Replace inline algorithm calls with WB equivalents (see table below).
|
||||
|
||||
New file `src/AcDream.Core/World/WbSceneryAdapter.cs` (or similar
|
||||
location — TBD during implementation):
|
||||
- Helper `BuildTerrainEntries(LandBlock block) → TerrainEntry[]`
|
||||
converting our `DatReaderWriter.DBObjs.LandBlock` (the dat type) into
|
||||
the `TerrainEntry[]` shape WB's `TerrainUtils` expects (9×9 grid,
|
||||
Type/Scenery/Road/Height fields per vertex).
|
||||
- Helper for `RegionInfo` if needed (small wrapper over our
|
||||
`Region` dat).
|
||||
|
||||
### Algorithm-call substitution table
|
||||
|
||||
| Today (ours) | Phase N.1 (WB) |
|
||||
|---|---|
|
||||
| `IsRoadVertex(raw)` (kept; small util) | unchanged — small predicate, no benefit to swap |
|
||||
| `IsOnRoad(block, lx, ly)` | `TerrainUtils.OnRoad(new Vector3(lx, ly, 0), terrainEntries)` |
|
||||
| `DisplaceObject(obj, gx, gy, j)` | `SceneryHelpers.Displace(obj, gx, gy, j)` |
|
||||
| Slope normal: `TerrainSurface.SampleNormalZFromHeightmap(...)` | `TerrainUtils.GetNormal(region, terrainEntries, lbX, lbY, lbOffset).Z` |
|
||||
| Slope check: `nz < obj.MinSlope \|\| nz > obj.MaxSlope` | `SceneryHelpers.CheckSlope(obj, normal.Z)` (returns bool) |
|
||||
| Rotation logic (`AFrame::set_heading` reproduction) | `SceneryHelpers.RotateObj(obj, gx, gy, j, localPos)` (returns Quaternion) |
|
||||
| Scale logic (LCG + Pow + clamp) | `SceneryHelpers.ScaleObj(obj, gx, gy, j)` (returns float) |
|
||||
|
||||
### What does NOT change
|
||||
|
||||
- The 9×9 vertex loop (`for (x = 0; x < 9; x++) for (y = 0; y < 9; y++)`).
|
||||
- Scene selection hash.
|
||||
- Frequency roll.
|
||||
- `obj.WeenieObj != 0` skip (weenie entries are dynamic spawns).
|
||||
- Bounds check `lx, ly ∈ [0, 192)`.
|
||||
- Per-spawn building check using our `buildingCells` HashSet.
|
||||
- `BaseLoc.Z` offset application.
|
||||
- `ScenerySpawn` record shape returned to the renderer.
|
||||
- `Generate()` method signature — same parameters, same return type.
|
||||
|
||||
### What about `obj_within_block`?
|
||||
|
||||
We attempted this during the bug investigation but it's too aggressive
|
||||
when applied with the model's actual sorting sphere radius (rejects
|
||||
trees that should be there). WB also doesn't apply it. The retail
|
||||
behavior we couldn't reproduce stays unreproduced for now — we accept
|
||||
that as a known minor cosmetic discrepancy and move on. The point of
|
||||
N.1 is matching WB's behavior, not retail's. If WB and retail
|
||||
disagree, that's a WB-upstream problem to file separately.
|
||||
|
||||
## Components
|
||||
|
||||
### Files modified
|
||||
|
||||
- `src/AcDream.Core/World/SceneryGenerator.cs` — algorithm-call swap.
|
||||
- `src/AcDream.Core/AcDream.Core.csproj` — already has WB project ref
|
||||
from N.0.
|
||||
|
||||
### Files added
|
||||
|
||||
- `src/AcDream.Core/World/WbSceneryAdapter.cs` — `LandBlock →
|
||||
TerrainEntry[]` and any other small adapters needed.
|
||||
- `tests/AcDream.Core.Tests/World/SceneryGeneratorWbConformanceTests.cs`
|
||||
— side-by-side test asserting our generator's output equals what
|
||||
comes out when the same algorithms are called via WB directly.
|
||||
|
||||
### Files deleted (eventually, after flag is on by default)
|
||||
|
||||
- The deleted helpers in `SceneryGenerator.cs` mentioned above.
|
||||
|
||||
### Feature flag
|
||||
|
||||
Phase 1 of the rollout: `ACDREAM_USE_WB_SCENERY=1` (default off — old
|
||||
path runs). When the env var is set, the new WB-backed path runs.
|
||||
|
||||
Phase 2 (after visual verification at Holtburg / `0xA9B1`): flag
|
||||
default-on. Old path can still be reached via
|
||||
`ACDREAM_USE_WB_SCENERY=0`.
|
||||
|
||||
Phase 3 (one or two sessions later, after no regressions): delete the
|
||||
flag and the old code paths entirely.
|
||||
|
||||
## Done criteria
|
||||
|
||||
1. `dotnet build` green with no new warnings.
|
||||
2. All existing tests pass (870+).
|
||||
3. New conformance test passes: `SceneryGeneratorWbConformanceTests`
|
||||
runs both code paths against fixture LandBlock data and asserts
|
||||
identical spawn lists (same ObjectId, same LocalPosition within
|
||||
1e-4, same Rotation within 1e-4, same Scale within 1e-4).
|
||||
4. Visual verification at landblock `0xA9B1` (Holtburg area):
|
||||
- The offending tree near the road that retail/WB do not show is
|
||||
**gone** in our render.
|
||||
- Issue #49's previously missing scenery (the tree from the 9×9
|
||||
loop expansion) is **still visible**.
|
||||
- No new visual regressions in surrounding landblocks during a
|
||||
brief flight around Holtburg.
|
||||
5. Issue #49 stays closed; no new issues filed.
|
||||
|
||||
## Risks (Phase-N.1-specific)
|
||||
|
||||
1. **`TerrainEntry` field semantics.** WB packs Type/Scenery/Road/
|
||||
Height into the `TerrainEntry` struct in a specific format. Getting
|
||||
the adapter wrong means OnRoad / scenery selection produces
|
||||
different results than ours. Mitigation: read
|
||||
`WorldBuilder.Shared/Modules/Landscape/Models/TerrainEntry.cs`
|
||||
carefully; cross-check against WB's `TerrainUtils.GetRoad` /
|
||||
`GetTerrainEntryForCell` to confirm field encoding.
|
||||
2. **`RegionInfo` dependencies.** WB's `TerrainUtils.GetNormal` takes
|
||||
a `RegionInfo` parameter. We need to either build a minimal
|
||||
`RegionInfo` from our `Region` dat or call WB's normal calc
|
||||
differently. Mitigation: investigate during implementation; expect
|
||||
this is a small wrapper.
|
||||
3. **`obj.MaxScale / obj.MinScale` divide-by-zero.** Our code checks
|
||||
`if (obj.MinScale == obj.MaxScale)` first; WB's `ScaleObj` does the
|
||||
same per-line review of `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/SceneryHelpers.cs:42-51`. Should be a non-issue.
|
||||
4. **Rotation quaternion convention.** Our rotation produces
|
||||
`headingQuat * baseLoc.Orientation`. WB's `RotateObj` calls
|
||||
`SetHeading` which does its own composition. Need to confirm the
|
||||
resulting quaternion is the same convention our renderer expects.
|
||||
Mitigation: the conformance test catches this if it's wrong.
|
||||
|
||||
## Testing
|
||||
|
||||
### Conformance test (new)
|
||||
|
||||
`SceneryGeneratorWbConformanceTests`:
|
||||
- Construct a synthetic `LandBlock` with known terrain data.
|
||||
- Run `SceneryGenerator.Generate(...)` with `ACDREAM_USE_WB_SCENERY=0`
|
||||
and again with `=1`.
|
||||
- Assert spawn counts equal.
|
||||
- Assert each spawn's ObjectId, LocalPosition (within 1e-4), Rotation
|
||||
(within 1e-4 per component), Scale (within 1e-4) are equal.
|
||||
|
||||
### Existing tests
|
||||
|
||||
`SceneryGeneratorTests` covers: road-vertex predicate, edge-vertex
|
||||
displacement bounds, interior-vertex displacement bounds. These tests
|
||||
exercise our internal helpers (`IsRoadVertex`, `DisplaceObject`).
|
||||
After N.1, the `DisplaceObject` test must be either deleted (if we
|
||||
delete the helper) or replaced (if we keep `IsRoadVertex` as a small
|
||||
predicate — it's only one bit-test).
|
||||
|
||||
### Visual verification
|
||||
|
||||
User runs the client against ACE locally:
|
||||
- Navigate to landblock `0xA9B1` (Holtburg). Verify offending tree
|
||||
near road is gone.
|
||||
- Confirm Issue #49's tree is still visible.
|
||||
- Fly around Holtburg, scan visible scenery for any obvious
|
||||
regression.
|
||||
|
||||
## Out of scope for N.1
|
||||
|
||||
- Replacing our `SceneryRenderManager` (we don't have one — we have
|
||||
`SceneryGenerator` producing `ScenerySpawn[]` and the renderer
|
||||
consuming it directly). N.1 only touches the generator.
|
||||
- Replacing our terrain math helpers (that's N.2).
|
||||
- Replacing the static-object renderer (that's N.6).
|
||||
- Anything in N.2-N.10.
|
||||
Loading…
Add table
Add a link
Reference in a new issue