ship(O): Phase O — DatPath Unification — SHIPPED
ONE thing touches the DATs. WB code lives in our repo:
- src/AcDream.Core/Rendering/Wb/ — pure helpers (5 files, ~782 LOC)
- src/AcDream.App/Rendering/Wb/ — GL infra + mesh pipeline (~27 files, ~7K LOC)
Project references to WorldBuilder.Shared + Chorizite.OpenGLSDLBackend
dropped from AcDream.App.csproj and AcDream.Core.csproj.
references/WorldBuilder/ remains in-tree as read-reference only.
DefaultDatReaderWriter eliminated; DatCollection is the only dat reader.
WbMeshAdapter consumes our DatCollection via DatCollectionAdapter
(O-D7 fallback adapter; ObjectMeshManager has 26 _dats.X call sites,
exceeding the 20 refactor threshold).
Visual side-by-side passed: Holtburg town, inn interior, dungeon all
render identically to pre-O.
Doc updates:
- CLAUDE.md: rewrote WB integration cribs to point at extracted code.
Code Structure Rules rule 2 updated to remove stale seam names.
"Currently working toward" flipped from Phase O to M1.5 resumption.
- docs/architecture/worldbuilder-inventory.md: Phase O banner added.
Status/integration model updated to post-O ownership. Workflow
section updated to reference our extracted tree, not WB project ref.
- docs/plans/2026-04-11-roadmap.md: Phase O moved to shipped table.
Phase O "ahead" block collapsed to SHIPPED note. M1.5 block updated
to ACTIVE (Phase O shipped; resuming from 2026-05-20 baseline).
- docs/plans/2026-05-12-milestones.md: M1.5 heading updated to ACTIVE;
Phase O ship writeup prepended to the M1.5 block.
Phase O ship closes Tasks O-T1..O-T7 shipped across this session.
Specs + audit + plan: docs/superpowers/{specs,plans}/2026-05-21-phase-o-*
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
57ee19968c
commit
2256006cb7
4 changed files with 162 additions and 140 deletions
144
CLAUDE.md
144
CLAUDE.md
|
|
@ -25,49 +25,62 @@ single source of truth for how the client is structured. All work must
|
|||
align with this document. When the architecture doc and reality diverge,
|
||||
update one or the other — never leave them out of sync.
|
||||
|
||||
**WorldBuilder is acdream's rendering + dat-handling base, integrated
|
||||
as of Phase N.4 ship (2026-05-08).** WB's `ObjectMeshManager` is the
|
||||
production mesh pipeline; `WbMeshAdapter` is the seam; `WbDrawDispatcher`
|
||||
is the production draw path (default-on, see `WbFoundationFlag`). Before
|
||||
re-implementing any AC-specific rendering or dat-handling algorithm,
|
||||
**read `docs/architecture/worldbuilder-inventory.md` FIRST**. If
|
||||
WorldBuilder has it, port from WorldBuilder (or call into our fork via
|
||||
the adapter), not from retail decomp. WorldBuilder is MIT-licensed,
|
||||
verified to render the world correctly, and uses the same Silk.NET
|
||||
stack we target. Re-porting from retail decomp when WB already has a
|
||||
tested port is how subtle bugs (the scenery edge-vertex bug, the
|
||||
triangle-Z bug) keep slipping in. Retail decomp remains the oracle for
|
||||
network, physics, animation, movement, UI, plugin, audio, chat — see
|
||||
the inventory doc's 🔴 list for the full scope of "we still write this
|
||||
ourselves".
|
||||
**WorldBuilder code lives in our tree as of Phase O (shipped 2026-05-21).**
|
||||
Phase N.4 (2026-05-08) adopted WB's rendering + dat-handling base as a
|
||||
project reference. Phase O (2026-05-21) extracted the ~33 files / ~7.7K LOC
|
||||
we actually use into our own namespaces and dropped the two external project
|
||||
references. `DatCollection` is now the **only** dat reader in process —
|
||||
`DefaultDatReaderWriter` is gone. `references/WorldBuilder/` remains in-tree
|
||||
as a read-reference (MIT-licensed; grep it freely), but nothing in
|
||||
`src/AcDream.*` references it as a project dependency.
|
||||
|
||||
**WB integration cribs:**
|
||||
- `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs` — single seam over WB's
|
||||
`ObjectMeshManager`. Owns the WB pipeline, drains its staged-upload
|
||||
queue per frame via `Tick()`, populates `AcSurfaceMetadataTable` with
|
||||
per-batch translucency / luminosity / fog metadata.
|
||||
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — production draw
|
||||
path. Groups all visible (entity, batch) pairs, single-uploads the
|
||||
matrix buffer, fires one `glDrawElementsInstancedBaseVertexBaseInstance`
|
||||
per group with `BaseInstance` pointing at the slice. Per-entity
|
||||
frustum cull, opaque front-to-back sort, palette-hash memoization.
|
||||
- `src/AcDream.App/Rendering/Wb/LandblockSpawnAdapter.cs` /
|
||||
`EntitySpawnAdapter.cs` — bridge spawn lifecycle to WB ref-counts.
|
||||
Atlas tier (procedural) goes via Landblock; per-instance tier
|
||||
**Where the extracted code lives (post-Phase O):**
|
||||
- `src/AcDream.Core/Rendering/Wb/` — pure dat/mesh helpers (5 files, ~782 LOC):
|
||||
`TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`,
|
||||
`TextureHelpers`. No GL dependency; safe to use from Core.
|
||||
- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline (~27 files,
|
||||
~7K LOC): `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`,
|
||||
`LandblockSpawnAdapter`, `EntitySpawnAdapter`, `TextureCache`,
|
||||
`GlobalMeshBuffer`, shader infrastructure, and the EnvCell/portal/scenery/
|
||||
terrain-blending pipeline classes.
|
||||
|
||||
Before re-implementing any AC-specific rendering or dat-handling algorithm,
|
||||
**read `docs/architecture/worldbuilder-inventory.md` FIRST**. The inventory
|
||||
describes what we extracted (now in our tree) and what we still write ourselves.
|
||||
Re-porting from retail decomp when we already have a tested port is how subtle
|
||||
bugs (the scenery edge-vertex bug, the triangle-Z bug) keep slipping in. Retail
|
||||
decomp remains the oracle for network, physics, animation, movement, UI, plugin,
|
||||
audio, chat — see the inventory doc's 🔴 list.
|
||||
|
||||
**WB rendering cribs (all paths now in `src/AcDream.App/Rendering/Wb/`):**
|
||||
- `WbMeshAdapter.cs` — single seam over `ObjectMeshManager`. Owns the mesh
|
||||
pipeline, drains its staged-upload queue per frame via `Tick()`, populates
|
||||
`AcSurfaceMetadataTable` with per-batch translucency / luminosity / fog
|
||||
metadata. Consumes `DatCollection` via `DatCollectionAdapter` (O-D7 fallback
|
||||
path; `ObjectMeshManager` has 26 internal `_dats.X` call sites that exceed
|
||||
the inline-swap threshold — the adapter bridges our `IDatCollection` to the
|
||||
`IDatReaderWriter` interface WB's internals expect).
|
||||
- `WbDrawDispatcher.cs` — production draw path. Groups all visible (entity,
|
||||
batch) pairs, single-uploads the matrix buffer, fires one
|
||||
`glDrawElementsInstancedBaseVertexBaseInstance` per group with `BaseInstance`
|
||||
pointing at the slice. Per-entity frustum cull, opaque front-to-back sort,
|
||||
palette-hash memoization.
|
||||
- `LandblockSpawnAdapter.cs` / `EntitySpawnAdapter.cs` — bridge spawn lifecycle
|
||||
to ref-counts. Atlas tier (procedural) goes via Landblock; per-instance tier
|
||||
(server-spawned, palette/texture overrides) goes via Entity.
|
||||
- **Modern path is mandatory as of N.5 ship amendment (2026-05-08).**
|
||||
`WbFoundationFlag`, `InstancedMeshRenderer`, and `StaticMeshRenderer`
|
||||
are deleted. Missing `GL_ARB_bindless_texture` or
|
||||
`GL_ARB_shader_draw_parameters` throws `NotSupportedException` at
|
||||
startup. There is no legacy fallback.
|
||||
- **WB's modern rendering path** (GL 4.3 + bindless) packs every mesh
|
||||
- **The modern rendering path** (GL 4.3 + bindless) packs every mesh
|
||||
into a single global VAO/VBO/IBO. Each batch references its slice
|
||||
via `FirstIndex` (offset into IBO) + `BaseVertex` (offset into VBO).
|
||||
Honor those offsets when issuing draws — `DrawElementsInstanced`
|
||||
with `indices=0` will draw every entity's first triangle from the
|
||||
global mesh, not the per-batch range. (This is exactly the
|
||||
exploded-character bug we hit during Task 26.)
|
||||
- **WB's `ObjectRenderBatch.SurfaceId` is unset** — the actual surface
|
||||
- **`ObjectRenderBatch.SurfaceId` is unset** — the actual surface
|
||||
id lives in `batch.Key.SurfaceId` (the `TextureKey` struct).
|
||||
- **`ObjectMeshManager.IncrementRefCount` only bumps a counter** — it
|
||||
does NOT trigger mesh loading. You must explicitly call
|
||||
|
|
@ -83,14 +96,14 @@ ourselves".
|
|||
Two `glMultiDrawElementsIndirect` calls per frame, one per pass.
|
||||
Total ~12-15 GL calls per frame for entity rendering regardless of
|
||||
scene complexity.
|
||||
- **`TextureCache` requires `BindlessSupport`** for the WB modern path.
|
||||
- **`TextureCache` requires `BindlessSupport`** for the modern path.
|
||||
Three `Bindless`-suffixed `GetOrUpload*` methods return 64-bit handles
|
||||
made resident at upload time, backed by parallel Texture2DArray uploads
|
||||
(`UploadRgba8AsLayer1Array`). The legacy `uint`-returning methods stay
|
||||
for Sky / Terrain / Debug / particle paths that still sample via
|
||||
`sampler2D`. After N.6 retires legacy renderers, the legacy upload path
|
||||
+ caches can be deleted.
|
||||
- **Translucency model is two-pass alpha-test** (matches WB), not
|
||||
- **Translucency model is two-pass alpha-test** (matches original WB), not
|
||||
per-blend-mode subpasses. Opaque pass discards `α<0.95`; transparent
|
||||
pass discards `α≥0.95` AND `α<0.05`. Native `Additive` blend renders
|
||||
as alpha-blend on GfxObj surfaces — falsifiable; if a magic-content
|
||||
|
|
@ -103,8 +116,8 @@ ourselves".
|
|||
extend `InstanceData` stride 64→80 bytes, add the field, mix into
|
||||
fragment color in `mesh_modern.frag`. ~30 min when the time comes.
|
||||
- `src/AcDream.App/Rendering/TerrainModernRenderer.cs` — terrain dispatcher
|
||||
on N.5's modern primitives. Mirrors WB's `TerrainRenderManager` pattern
|
||||
(single global VBO/EBO + slot allocator + `glMultiDrawElementsIndirect`)
|
||||
on N.5's modern primitives. Mirrors the original WB `TerrainRenderManager`
|
||||
pattern (single global VBO/EBO + slot allocator + `glMultiDrawElementsIndirect`)
|
||||
but driven by acdream's `LandblockMesh.Build` so retail's `FSplitNESW`
|
||||
formula is preserved (issue #51 resolved). Atlas handles bound via the
|
||||
uvec2 + `sampler2DArray(handle)` constructor pattern (NOT the direct
|
||||
|
|
@ -189,14 +202,16 @@ pursuing live in [`docs/architecture/code-structure.md`](docs/architecture/code-
|
|||
as part of the change.
|
||||
|
||||
2. **`AcDream.Core` must not depend on the window / GL / backend
|
||||
projects, except via documented interop seams.** The only
|
||||
currently-allowed seams are `WorldBuilder.Shared` (stateless helpers:
|
||||
`TerrainUtils`, `TerrainEntry`, `RegionInfo`) and
|
||||
`Chorizite.OpenGLSDLBackend.Lib` (stateless helpers only:
|
||||
`SceneryHelpers`, `TextureHelpers`). New Core code that needs a GL
|
||||
surface must define an interface in Core and let `AcDream.App`
|
||||
implement it — never the reverse. If you need to add a project
|
||||
reference to Core, the change must come with an inventory-doc
|
||||
projects, except via documented interop seams.** As of Phase O
|
||||
(2026-05-21), the only allowed seams are the extracted helpers in
|
||||
`src/AcDream.Core/Rendering/Wb/` (`TerrainUtils`, `TerrainEntry`,
|
||||
`RegionInfo`, `SceneryHelpers`, `TextureHelpers` — stateless, no GL).
|
||||
The former `WorldBuilder.Shared` and `Chorizite.OpenGLSDLBackend.Lib`
|
||||
project references are gone; their code now lives in our tree at those
|
||||
paths. New Core code that needs a GL surface must define an interface
|
||||
in Core and let `AcDream.App` implement it — never the reverse. If you
|
||||
need to add a project reference to Core, the change must come with an
|
||||
inventory-doc
|
||||
update explaining why.
|
||||
|
||||
3. **UI panels target `AcDream.UI.Abstractions` only.** No panel may
|
||||
|
|
@ -698,34 +713,29 @@ inn door, click NPC, pick up item. Freeze list active — M1's phases
|
|||
are off-limits until M7 polish. Writeup at top of M1 block in
|
||||
`docs/plans/2026-05-12-milestones.md`.
|
||||
|
||||
**Currently working toward: Phase O — DatPath Unification** (active as
|
||||
of 2026-05-21, by user direction — pre-empts M1.5). Tagline: "ONE
|
||||
thing touches the DATs." We currently run two dat readers in process
|
||||
(our `DatCollection` + WorldBuilder's `DefaultDatReaderWriter`); this
|
||||
phase extracts the WB pieces we actually use into
|
||||
`src/AcDream.Core/Rendering/Wb/`, swaps their dat dependency to our
|
||||
`DatCollection`, and drops the `WorldBuilder.Shared` +
|
||||
`Chorizite.OpenGLSDLBackend` project references. WB stays in
|
||||
`references/` as a read-reference, not as a project dependency.
|
||||
Verbatim copy first, no "improvements" while extracting. Spec:
|
||||
**Phase O — DatPath Unification — SHIPPED 2026-05-21.** ONE thing
|
||||
touches the DATs. ~33 WB files (~7.7K LOC) extracted into
|
||||
`src/AcDream.{Core,App}/Rendering/Wb/`. Project references to
|
||||
`WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` dropped.
|
||||
`DefaultDatReaderWriter` eliminated; `DatCollection` is the only dat
|
||||
reader. `WbMeshAdapter` consumes it via `DatCollectionAdapter`
|
||||
(O-D7 fallback; 26 `_dats.*` call sites exceeded the inline-swap
|
||||
threshold). `references/WorldBuilder/` stays in-tree as read-reference.
|
||||
Visual side-by-side passed: Holtburg town, inn interior, dungeon all
|
||||
render identically to pre-O. Spec:
|
||||
[`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md).
|
||||
Estimated 7-8 working days, ships as one merge window.
|
||||
|
||||
**M1.5 — PAUSED for Phase O.** Indoor walking work is held at the
|
||||
2026-05-20 baseline (described below) and resumes once Phase O lands.
|
||||
M1.5's Phase A6/A7 don't touch dat infrastructure, so no rework will
|
||||
be needed. Demo scenario: enter the Holtburg Sewer through the
|
||||
in-town portal, navigate through (walls block, stairs work, items
|
||||
block, lighting reads correctly), exit back to town. Phases to ship
|
||||
after Phase O: A6 (Indoor physics fidelity, cdb-driven) + A7 (Indoor
|
||||
lighting fidelity, RenderDoc + retail-decomp driven). Issues in
|
||||
scope: #80, #81, #83, #88, #90 (workaround removal), L-indoor,
|
||||
**Currently working toward: M1.5 — Indoor world feels right** (resumed
|
||||
from 2026-05-20 baseline after Phase O ship). Demo scenario: enter the
|
||||
Holtburg Sewer through the in-town portal, navigate through (walls
|
||||
block, stairs work, items block, lighting reads correctly), exit back
|
||||
to town. Phases: A6 (Indoor physics fidelity, cdb-driven) + A7
|
||||
(Indoor lighting fidelity, RenderDoc + retail-decomp driven). Issues
|
||||
in scope: #80, #81, #83, #88, #90 (workaround removal), L-indoor,
|
||||
L-spotlight, stairs, 2nd-floor, cellar, and the
|
||||
`TryFindIndoorWalkablePlane` synthesis removal. **M2 ("Kill a
|
||||
drudge") is deferred until M1.5 lands** — drudges live in dungeons
|
||||
and the M2 demo target requires solid indoor navigation. Full M1.5
|
||||
writeup at the corresponding block in
|
||||
`docs/plans/2026-05-12-milestones.md`.
|
||||
`TryFindIndoorWalkablePlane` synthesis removal. **M2 ("Kill a drudge")
|
||||
is deferred until M1.5 lands.** Full M1.5 writeup at the corresponding
|
||||
block in `docs/plans/2026-05-12-milestones.md`.
|
||||
|
||||
**Today's pre-M1.5 baseline (2026-05-20).** Five surgical fixes
|
||||
shipped to close the user-reported "logged in inside the inn, ran
|
||||
|
|
|
|||
|
|
@ -1,35 +1,55 @@
|
|||
# WorldBuilder Inventory — what we take, adapt, or leave
|
||||
# WorldBuilder Inventory — what we extracted, adapted, or left behind
|
||||
|
||||
**Status:** load-bearing reference. As of 2026-05-08 acdream's strategy is
|
||||
to **rely heavily on WorldBuilder** for rendering and dat-handling rather
|
||||
than re-port retail algorithms ourselves. WorldBuilder is MIT-licensed, is
|
||||
verified by visual inspection to render the AC world correctly (terrain,
|
||||
scenery, slabs, dungeons, slopes, particles), and uses the same Silk.NET
|
||||
+ .NET stack we already target.
|
||||
> **Phase O shipped 2026-05-21.** The ~33 WB files we actually use have
|
||||
> been extracted into our tree. `references/WorldBuilder/` stays as a
|
||||
> **read-reference only** — nothing in `src/AcDream.*` references it as a
|
||||
> project dependency. `DatCollection` is now the only dat reader in process.
|
||||
>
|
||||
> Use this document to:
|
||||
> 1. Know **where our extracted code lives** (look for the "Extracted to"
|
||||
> column / notes in each section below).
|
||||
> 2. Know **what WB still has** that we haven't needed yet — grep
|
||||
> `references/WorldBuilder/` if you ever need to add something.
|
||||
> 3. Know **what WB never had** (the 🔴 list) — those are always ours.
|
||||
|
||||
**Integration model:** **fork upstream WorldBuilder** at
|
||||
`github.com/Chorizite/WorldBuilder`, depend on our fork, delete editor-only
|
||||
code, expose hooks for our network state to feed scene data in. Sync with
|
||||
upstream via merge so we inherit fixes. This document tells you, before
|
||||
you write code, whether the thing you're about to port already exists in
|
||||
WorldBuilder.
|
||||
**Pre-O status (archived for context):** As of Phase N.4 (2026-05-08)
|
||||
acdream relied heavily on WorldBuilder as a project reference for rendering
|
||||
and dat-handling. WorldBuilder is MIT-licensed, verified by visual inspection
|
||||
to render the AC world correctly (terrain, scenery, slabs, dungeons, slopes,
|
||||
particles), and uses the same Silk.NET + .NET stack we target.
|
||||
|
||||
**Workflow change:** Before re-implementing any AC-specific rendering or
|
||||
dat-handling algorithm, **check this inventory first**. If WorldBuilder
|
||||
has it, port from WorldBuilder (or call into our fork once it's wired
|
||||
up), not from retail decomp. Retail decomp remains the oracle for things
|
||||
WorldBuilder lacks — animation, motion, physics collision, networking.
|
||||
**Post-O integration model:** Extracted WB code lives in two locations in
|
||||
our tree (see CLAUDE.md for the full breakdown):
|
||||
- `src/AcDream.Core/Rendering/Wb/` — pure helpers (no GL): `TerrainUtils`,
|
||||
`TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers`.
|
||||
- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline:
|
||||
`ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`, texture cache,
|
||||
shader infra, EnvCell/portal/scenery/terrain-blending pipeline classes.
|
||||
|
||||
`DatCollectionAdapter` bridges our `IDatCollection` to the `IDatReaderWriter`
|
||||
interface WB's internals expect (O-D7 fallback; `ObjectMeshManager` has 26
|
||||
internal `_dats.*` call sites — above the 20-site inline-swap threshold).
|
||||
|
||||
**Workflow:** Before re-implementing any AC-specific rendering or dat-handling
|
||||
algorithm, **check this inventory first**. If we already extracted it (🟢
|
||||
sections), it's in `src/AcDream.App/Rendering/Wb/` — use our copy. If WB has
|
||||
it but we haven't extracted it yet, grep `references/WorldBuilder/` and extract
|
||||
as needed. Retail decomp remains the oracle for things WB never had (🔴 list).
|
||||
|
||||
Attribution: WorldBuilder is MIT-licensed. `NOTICE.md` includes WB attribution.
|
||||
|
||||
---
|
||||
|
||||
## Repo layout (as of cloned snapshot under `references/WorldBuilder/`)
|
||||
## Read-reference layout (under `references/WorldBuilder/`, not project-referenced)
|
||||
|
||||
- **`Chorizite.OpenGLSDLBackend/`** — full OpenGL renderer (Silk.NET).
|
||||
- **`Chorizite.OpenGLSDLBackend/`** — full OpenGL renderer (Silk.NET). The
|
||||
components we use are extracted into `src/AcDream.App/Rendering/Wb/`.
|
||||
- **`WorldBuilder.Shared/`** — data models, dat parsers, landscape module.
|
||||
- **`WorldBuilder/`** — Avalonia desktop app shell (NOT taken).
|
||||
- **`WorldBuilder.{Windows,Linux,Mac}/`** — platform entry points (NOT taken).
|
||||
- **`WorldBuilder.Server/`** — collab editing backend (NOT taken).
|
||||
- **`Tests/` + `WorldBuilder.Shared.Benchmarks/`** — test harness (study).
|
||||
The helpers we use are extracted into `src/AcDream.Core/Rendering/Wb/`.
|
||||
- **`WorldBuilder/`** — Avalonia desktop app shell (not taken).
|
||||
- **`WorldBuilder.{Windows,Linux,Mac}/`** — platform entry points (not taken).
|
||||
- **`WorldBuilder.Server/`** — collab editing backend (not taken).
|
||||
- **`Tests/` + `WorldBuilder.Shared.Benchmarks/`** — test harness (study only).
|
||||
|
||||
**Upstream NuGet dependencies** (these stay as NuGet packages, we don't
|
||||
vendor them):
|
||||
|
|
@ -234,17 +254,28 @@ WorldBuilder is a dat editor; it does not have:
|
|||
|
||||
---
|
||||
|
||||
## What this means for the workflow
|
||||
## What this means for the workflow (post-Phase O)
|
||||
|
||||
The CLAUDE.md "grep named → decompile → verify → port" workflow stays
|
||||
the rule for everything in the 🔴 list (network, physics, animation,
|
||||
movement, UI, plugin, audio, chat). For anything in 🟢, the new rule is:
|
||||
**check this inventory FIRST**. If WB has it, port from WB. Re-porting
|
||||
from retail decomp when WB already has a tested port is no longer
|
||||
appropriate — that's how we got the scenery edge-vertex bug.
|
||||
movement, UI, plugin, audio, chat).
|
||||
|
||||
When the inventory says "take wholesale or adapt" and we discover a
|
||||
behavior mismatch with retail (rare — WB is verified), the resolution
|
||||
is: reconcile WB ↔ retail decomp ↔ holtburger ↔ ACE ↔ ACViewer (the
|
||||
existing reference hierarchy in CLAUDE.md). WorldBuilder ranks at the
|
||||
top of that hierarchy for anything 🟢.
|
||||
For anything in 🟢 that we've already extracted: **the code is in our
|
||||
tree at `src/AcDream.{Core,App}/Rendering/Wb/`**. Read it there — don't
|
||||
grep `references/WorldBuilder/` unless you want to compare against the
|
||||
original. Re-porting from retail decomp when we already have a tested
|
||||
port is still how we'd get the scenery edge-vertex bug back.
|
||||
|
||||
For anything in 🟢 that we have NOT yet extracted: grep
|
||||
`references/WorldBuilder/` to find the source, then extract it using the
|
||||
Phase O pattern (verbatim copy → adapt constructor to accept
|
||||
`IDatCollection` via `DatCollectionAdapter` where needed → add to
|
||||
`src/AcDream.App/Rendering/Wb/`). Do NOT add a new project reference back
|
||||
to `WorldBuilder.Shared` or `Chorizite.OpenGLSDLBackend` — Phase O
|
||||
permanently removed those.
|
||||
|
||||
When we discover a behavior mismatch with retail (rare — the extracted
|
||||
code is the same as the original), the resolution is: reconcile extracted
|
||||
code ↔ retail decomp ↔ holtburger ↔ ACE ↔ ACViewer (the existing
|
||||
reference hierarchy in CLAUDE.md). Our extracted code ranks at the top
|
||||
of that hierarchy for anything 🟢.
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
| Indoor walking Phase 1 — BSP cluster (partial) | 2026-05-19. Probe + WorldPicker cell-BSP occlusion (#86 closed) + CellId promotion via AABB containment (partial #84 fix). Seven commits across 5 phases: `18a2e28` plan, `27d7de1` Phase A `[indoor-bsp]` probe + toggle, `3764867` Phase B CellBspRayOccluder in WorldPicker, `4e308d5` Phase B screen-rect tests, `c19d6fb` Phase D AABB containment + L.2e bare-low-byte fix, `fda6af7` Phase E `[cell-cache]` diagnostic, `1f11ba9` Phase E extended AABB/bsphere/poly-count fields. **#86 closed** (picker occlusion). **#84 partially closed** (spawn-in-building stuck-above-floor resolved; threshold/doorway walls remain open under #87). **#85 open** (wall pass-through root cause confirmed as same as #84 remaining symptom — CellId doesn't stay promoted during outdoor→indoor walking). **#87 filed** (portal-based indoor cell tracking — retail-faithful follow-up). `[indoor-bsp]` + `[cell-cache]` probes stay in place as scaffolding for the follow-up phase. Handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](../research/2026-05-19-cluster-a-shipped-handoff.md). Plan: [`docs/superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md`](../superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md). | Tests ✓ |
|
||||
| Indoor walking Phase 2 — Portal-based cell tracking | 2026-05-19. Portal-graph traversal replaces Phase D's AABB containment. Six commits: `1969c55` CellBSP+Portals wired into CellPhysics; `aad6976` CellTransit.FindCellList + FindTransitCellsSphere + AddAllOutsideCells + ResolveCellId rename; `069534a` BuildingPhysics + CheckBuildingTransit for outdoor→indoor entry; `702b30a` code-review polish; `3ffe1e4` pass foot-sphere center to ResolveCellId (critical fix — was passing CheckPos instead of GlobalSphere[0].Origin, causing PointInsideCellBsp to return false at floor level); `eb0f772` TryFindIndoorWalkablePlane synthesizes walkable plane from cell floor poly so the resolver doesn't fall through to outdoor SampleTerrainWalkable. **Closes #87, #85, and the wall-pass-through portion of #84 (fully closes #84).** Files #88 (indoor static object vibration — pre-existing) and #89 (BSPQuery.SphereIntersectsCellBsp — approximation in CheckBuildingTransit). `[cell-transit]`, `[indoor-bsp]`, `[check-bldg]`, `[cell-cache]` probes stay in place. Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](../research/2026-05-19-indoor-walking-phase2-shipped-handoff.md). | Live ✓ |
|
||||
| Indoor walking Phase A4 — Multi-cell BSP iteration | 2026-05-20. Ports retail's `CTransition::check_other_cells` (`acclient_2013_pseudo_c.txt:272717-272798`). After the primary cell's BSP returns OK, every other cell the foot-sphere overlaps is queried via `BSPQuery.FindCollisions`. Halt on first Collided/Adjusted/Slid; Slid clears the contact-plane fields. Three commits land the slices: `e6369e2` `CellTransit.FindCellSet` overload (refactor `FindCellList` to expose the candidate set); `493c5e5` `Transition.CheckOtherCells` + `ApplyOtherCellResult` combine helper; `691493e` (orig `967d065`, then `3add110` revert, then this reapply) wires `CheckOtherCells` into `FindEnvCollisions`. 10 new unit tests; 1139 + 8 baseline maintained. **Visual verification surfaced a separate, pre-existing M2 blocker:** at the Holtburg inn doorway the CellId ping-pongs between outdoor `0xA9B40022` and indoor vestibule `0xA9B40164` rapidly because indoor BSP push-back exits the indoor CellBSP volume → ResolveCellId reclassifies as outdoor → wall checks bypassed on outdoor ticks. Bug reproduces fully with A4 reverted (`launch-revert2.log`), confirming A4 is not the cause. A4 is correct and tested but **dormant in practice** until the ping-pong is fixed. Handoff: [`docs/research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md`](../research/2026-05-20-phase-a4-shipped-cell-pingpong-finding.md). Spec: [`docs/superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md`](../superpowers/specs/2026-05-20-phase-a4-multi-cell-bsp-design.md). Plan: [`docs/superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md`](../superpowers/plans/2026-05-20-phase-a4-multi-cell-bsp.md). | Live ✓ (dormant pending cell-tracking fix) |
|
||||
| Phase O — DatPath Unification | 2026-05-21. ONE thing touches the DATs. Extracted ~33 WB files / ~7.7K LOC into `src/AcDream.{Core,App}/Rendering/Wb/`. Dropped project references to `WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` from `AcDream.App.csproj` and `AcDream.Core.csproj`. `DefaultDatReaderWriter` eliminated; `DatCollection` is the only dat reader. `WbMeshAdapter` consumes it via `DatCollectionAdapter` (O-D7 fallback; `ObjectMeshManager` has 26 internal `_dats.*` call sites exceeding the 20-site inline-swap threshold). `references/WorldBuilder/` stays in-tree as read-reference. **Visual side-by-side passed**: Holtburg town, inn interior, dungeon all render identically to pre-O. `NOTICE.md` includes WB MIT attribution. Spec: [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md). Audit: [`docs/superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md`](../superpowers/specs/2026-05-21-phase-o-t1-wb-audit.md). Plan: [`docs/superpowers/plans/2026-05-21-phase-o-plan.md`](../superpowers/plans/2026-05-21-phase-o-plan.md). | Live ✓ |
|
||||
|
||||
Plus polish that doesn't get its own phase number:
|
||||
- FlyCamera default speed lowered + Shift-to-boost
|
||||
|
|
@ -85,48 +86,17 @@ Plus polish that doesn't get its own phase number:
|
|||
|
||||
## Phases ahead — agreed order
|
||||
|
||||
### Phase O — DatPath Unification (ACTIVE — pre-empts M1.5 by user direction 2026-05-21)
|
||||
### Phase O — DatPath Unification — SHIPPED 2026-05-21
|
||||
|
||||
**Tagline:** ONE thing touches the DATs.
|
||||
|
||||
**Filed:** 2026-05-21. **Status:** active. **Spec:** [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md).
|
||||
**Filed:** 2026-05-21. **Status:** SHIPPED. **Spec:** [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md).
|
||||
|
||||
**Problem:** As of Phase N.4 (2026-05-08) we run TWO dat readers in
|
||||
process — our `DatCollection` and WorldBuilder's
|
||||
`DefaultDatReaderWriter` (constructed inside `WbMeshAdapter.cs:79`).
|
||||
Both open the same four `.dat` files independently. Costs:
|
||||
~50-100 MB duplicated index cache, double seeks on shared blocks, and
|
||||
the cross-check awkwardness in `WbMeshAdapter.cs:224-229`. AC community
|
||||
feedback flagged WB's dat-touching as "built for tools, not runtime";
|
||||
the double-reader is the most concrete manifestation.
|
||||
See the shipped-table entry above for the full summary. Phase O is
|
||||
complete; M1.5 (indoor walking, paused for Phase O) resumes from
|
||||
the 2026-05-20 baseline.
|
||||
|
||||
**Approach:** Extract the WB pieces we actually use (mesh pipeline,
|
||||
texture decode, GL state, scenery, terrain blending, EnvCell/portal
|
||||
decode — roughly 3-5K LOC) into `src/AcDream.Core/Rendering/Wb/`.
|
||||
Verbatim copy first, no "improvements" while extracting. Adapt
|
||||
constructors to consume our `DatCollection` instead of
|
||||
`IDatReaderWriter`. Drop the two project references
|
||||
(`WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend`) at the end.
|
||||
WB is MIT-licensed; attribute in `NOTICE.md`. Keep `references/WorldBuilder/`
|
||||
in-tree for read-reference (not as a project dependency).
|
||||
|
||||
**Why pre-empt M1.5:** User direction 2026-05-21 — we want this
|
||||
foundational decoupling done before further milestone work compounds
|
||||
the WB dependency. M1.5's Phase A6/A7 indoor work doesn't touch dat
|
||||
infrastructure, so it can resume cleanly after Phase O lands.
|
||||
|
||||
**Tasks** (full breakdown in spec §5; ~7-8 working days):
|
||||
- **O-T1** — WB call-graph audit. Produce the exact "must extract" list.
|
||||
- **O-T2** — Create `src/AcDream.Core/Rendering/Wb/` + `NOTICE.md` attribution.
|
||||
- **O-T3** — Extract texture / GL infrastructure (no dat dependency, low risk).
|
||||
- **O-T4** — Extract mesh pipeline (`ObjectMeshManager` + types) + `DatCollection` swap.
|
||||
- **O-T5** — Extract scenery + terrain blending pipelines + `DatCollection` swap.
|
||||
- **O-T6** — Extract EnvCell + portal renderers + `DatCollection` swap.
|
||||
- **O-T7** — Drop the two project references. Drop `_wbDats` and cross-check code.
|
||||
- **O-T8** — Verification gate: three-scene visual side-by-side with main (Holtburg, inn, dungeon).
|
||||
- **O-T9** — Ship. Update CLAUDE.md + `worldbuilder-inventory.md` to reflect new ownership.
|
||||
|
||||
**Acceptance** (full list in spec §6):
|
||||
**Acceptance met** (full list in spec §6):
|
||||
- `dotnet build` + `dotnet test` green; ~1147 test count maintained.
|
||||
- Zero `using WorldBuilder.*` or `Chorizite.OpenGLSDLBackend.*` in `src/AcDream.*`.
|
||||
- `DefaultDatReaderWriter` referenced in zero places in our source.
|
||||
|
|
@ -141,7 +111,7 @@ copy + swap to `DatCollection` only. Refactors in follow-up phases.
|
|||
**After Phase O ships:** M1.5 resumes from its 2026-05-20 baseline
|
||||
with no code changes lost — M1.5 doesn't touch WB-extracted territory.
|
||||
|
||||
### Milestone M1.5 — "Indoor world feels right" (PAUSED for Phase O — see above)
|
||||
### Milestone M1.5 — "Indoor world feels right" (ACTIVE — Phase O shipped; resuming from 2026-05-20 baseline)
|
||||
|
||||
The current top of the work order. Two phases (A6 + A7) inside one
|
||||
milestone. M2 ("kill a drudge") is deferred until M1.5 lands —
|
||||
|
|
|
|||
|
|
@ -185,7 +185,18 @@ close range and the player sees "You pick up the X." in chat.
|
|||
|
||||
---
|
||||
|
||||
### M1.5 — "Indoor world feels right" — 🔵 NEXT (active, opened 2026-05-20)
|
||||
### M1.5 — "Indoor world feels right" — 🔵 ACTIVE (resumed 2026-05-21 after Phase O ship)
|
||||
|
||||
**Phase O — DatPath Unification — shipped 2026-05-21.** ONE thing
|
||||
touches the DATs. ~33 WB files (~7.7K LOC) extracted into
|
||||
`src/AcDream.{Core,App}/Rendering/Wb/`; project references to
|
||||
`WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` dropped;
|
||||
`DefaultDatReaderWriter` eliminated. Visual side-by-side passed at
|
||||
Holtburg town, inn interior, and dungeon. Phase O pre-empted M1.5
|
||||
per user direction 2026-05-21; M1.5 now resumes from the 2026-05-20
|
||||
baseline with no code changes lost — Phase O did not touch dat-loading
|
||||
infrastructure for physics or collision, only the rendering pipeline.
|
||||
M1.5's planned phases (A6 + A7) are unaffected.
|
||||
|
||||
**Demo scenario:** Enter the Holtburg Sewer dungeon through the in-town entry
|
||||
portal. Navigate to the end (5–7 rooms with stairs + a multi-Z chamber).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue