Carry each billboard particle's resident texture handle in the instance ABI so stable retail alpha ordering can batch mixed textures without rebinding and redrawing each short texture run. Keep DAT blend transitions as the only billboard draw boundary. The connected 0xC95B stress scene improved from 6 FPS / 171 ms to about 153 FPS / 6.6 ms at the same roughly 21,000 visible entities. Release build succeeds and all 5,916 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
458 lines
24 KiB
Markdown
458 lines
24 KiB
Markdown
# WorldBuilder Inventory — what we extracted, adapted, or left behind
|
||
|
||
> **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.
|
||
|
||
**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.
|
||
|
||
**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.
|
||
|
||
**EnvCell streaming seam:** `EnvCellLandblockBuildBuilder` is an acdream-owned
|
||
adapter around the extracted WB rendering path. One streaming job privately builds
|
||
the complete portal-cell + shell-placement payload, and `EnvCellRenderer.CommitLandblock`
|
||
publishes that completed snapshot on the render thread. WB's geometry-id arithmetic
|
||
and mesh preparation remain unchanged; the transaction wrapper exists because
|
||
acdream streams asynchronously while the WB editor's manager owned its own loading
|
||
loop. Do not reintroduce worker-side `RegisterCell` calls or shared pending cell
|
||
collections.
|
||
|
||
`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).
|
||
|
||
**MP1a (2026-07-05): CPU mesh-extraction half moved to `AcDream.Content`.**
|
||
The GL-free portion of the former `ObjectMeshManager` — dat read → polygon
|
||
walk → vertex/index build → inline BCn/palette texture decode →
|
||
`ObjectMeshData` — is now `MeshExtractor` in a new `src/AcDream.Content/`
|
||
assembly (no Silk.NET dependency), so the MP1b bake tool can run the exact
|
||
same extraction code offline without an OpenGL context. This was a
|
||
mechanical, verbatim move (namespace + visibility only) per
|
||
`docs/superpowers/plans/2026-07-05-mp1a-content-extraction.md` — no
|
||
behavior change, no divergence-register row.
|
||
|
||
- `src/AcDream.Content/MeshExtractor.cs` — the `Prepare*` family
|
||
(`PrepareMeshData`, `PrepareSetupMeshData`, `PrepareGfxObjMeshData`,
|
||
`PrepareEnvCellMeshData`, `PrepareCellStructMeshData`,
|
||
`PrepareCellStructEdgeLineData`) + private helpers (`CollectParts`,
|
||
`CollectEmittersFromScript`, `ComputeBounds`, `BuildPolygonIndices`,
|
||
`BuildCellStructPolygonIndices`) and the decoded-texture cache /
|
||
`ThreadLocal<BcDecoder>` that back them.
|
||
- `src/AcDream.Content/ObjectMeshData.cs` — the CPU-side boundary records:
|
||
`VertexPositionNormalTexture`, `StagedEmitter`, `ObjectMeshData`,
|
||
`MeshBatchData`, `TextureBatchData`.
|
||
- `src/AcDream.Content/TextureKey.cs` — the atlas dedup key, lifted out of
|
||
the GL-owning `TextureAtlasManager` (which stays in App and now
|
||
references the lifted struct).
|
||
- `src/AcDream.Content/UploadFormats.cs` — Content-owned
|
||
`UploadPixelFormat`/`UploadPixelType` enums carried by
|
||
`MeshBatchData`/`TextureBatchData` instead of
|
||
`Silk.NET.OpenGL.PixelFormat`/`PixelType` (Content must stay
|
||
Silk.NET-free — the bake tool must not ship GL binaries). Underlying
|
||
values are the GL ABI constants, numerically identical to the Silk.NET
|
||
members; App casts at its single upload boundary (the `AddTexture` call
|
||
in `UploadGfxObjMeshData`) via a lifted nullable enum conversion —
|
||
value- and null-preserving.
|
||
- `src/AcDream.Content/IDatReaderWriter.cs`, `EdgeLineBuilder.cs` — GL-free
|
||
dependencies of the extractor, moved (namespace-only) alongside it.
|
||
- **Side-stage sink seam:** `CollectEmittersFromScript` pre-loads particle
|
||
GfxObj meshes mid-extraction and, pre-MP1a, enqueued them directly onto
|
||
`ObjectMeshManager._stagedMeshData`. The extractor now takes an
|
||
`Action<ObjectMeshData>? sideStagedSink` constructor parameter; App wires
|
||
it to `_stagedMeshData.Enqueue`, preserving the original
|
||
immediate-enqueue semantics exactly — including on a mid-`Prepare*`
|
||
throw (preloads staged before a malformed-dat texture-decode exception
|
||
survive, as they always did). The MP1b bake tool passes its own
|
||
collector.
|
||
- **Stays in `src/AcDream.App/Rendering/Wb/`:** `ObjectMeshManager` (now a
|
||
thin wrapper owning the staged-queue/worker-pool/Dispose-quiesce lifecycle
|
||
and all GL upload — constructs one `MeshExtractor` and delegates every
|
||
former `Prepare*` call site to it), `ObjectRenderData`/`ObjectRenderBatch`
|
||
(hold a GL `TextureAtlasManager` field), `TextureAtlasManager`,
|
||
`DatCollectionAdapter` (concrete `DatCollection`-backed implementation of
|
||
the now-Content-namespaced `IDatReaderWriter`), `GeometryUtils` (used only
|
||
by App-side raycasting, not by extraction), `AcSurfaceMetadata`/
|
||
`AcSurfaceMetadataTable` (not on the extraction path), `Building.cs`
|
||
(explicitly out of scope).
|
||
- `AcDream.Core` is untouched; `AcDream.Content` references `AcDream.Core`
|
||
(for `TextureHelpers`, `Sphere`/`BoundingBox` via `Chorizite.Core.Lib`);
|
||
`AcDream.App` references `AcDream.Content`. `AcDream.Core` does NOT
|
||
reference `AcDream.Content` (one-way dependency, per Code Structure Rule 2).
|
||
- Reason: MP1 (`docs/superpowers/specs/2026-07-05-modern-pipeline-design.md`
|
||
§6.1) — the bake tool needs the identical mesh/texture extraction code
|
||
running with no GL context, so baked pak output and live-client output stay
|
||
byte-identical.
|
||
|
||
**Retail VFX hook compatibility seam (2026-07-14).** Chorizite.DatReaderWriter
|
||
2.1.7 models `CreateBlockingParticleHook` as the common hook header only, while
|
||
retail inherits the complete `CreateParticleHook` payload. The narrow readers in
|
||
`src/AcDream.Content/Vfx/` read raw bytes through the existing `DatCollection`
|
||
database, delegate every ordinary hook to the package, and substitute only the
|
||
retail blocking-particle shape. Both live animation playback and PhysicsScript
|
||
loading use those cached readers; `MeshExtractor` uses the same PhysicsScript
|
||
loader when preloading emitter meshes. This is not a second DAT reader:
|
||
`DatCollection` remains the sole database owner and access path. Retail anchors:
|
||
`CreateBlockingParticleHook::Execute` `0x00526EF0` and
|
||
`ParticleManager::CreateBlockingParticleEmitter` `0x0051B8A0`.
|
||
|
||
**Retail particle visibility/degradation seam (2026-07-17).** WorldBuilder's
|
||
particle simulator remains a useful DAT-integrator and batching reference, but
|
||
it does not carry the live retail client's `CObjCell::IsInView` degradation
|
||
path. `EmitterDescRegistry` now resolves the hardware particle GfxObj and its
|
||
ordered `GfxObjDegradeInfo` entries through the same sole `DatCollection`;
|
||
`ParticleSystem` ports the retail finite/infinite degraded branches; App feeds
|
||
the unified PView interior set plus the landscape renderer's independently
|
||
computed outdoor landcell set through a focused
|
||
`ParticleVisibilityController`. Examination and dedicated-pass emitters carry
|
||
an explicit bypass policy; missing/portal world views are empty rather than
|
||
fail-open.
|
||
No WorldBuilder dependency or second DAT access layer was introduced. Retail
|
||
anchors: `CPhysicsPart::GetMaxDegradeDistance` `0x0050D510`,
|
||
`GfxObjDegradeInfo::get_max_degrade_distance` `0x0051E2D0`,
|
||
`CPhysicsObj::ShouldDrawParticles` `0x0050FE60`, and
|
||
`ParticleEmitter::UpdateParticles` `0x0051D180`.
|
||
|
||
**Retail shared world-alpha seam (2026-07-18).** WorldBuilder's editor
|
||
renderers classify translucent mesh batches correctly, but they have no live
|
||
retail `CPartCell`/`CShadowPart` list and render particles in a separate
|
||
batcher. `src/AcDream.App/Rendering/RetailAlphaQueue.cs` is therefore an
|
||
acdream-owned runtime seam above the extracted mesh pipeline. During the main
|
||
world frame, `WbDrawDispatcher` and `ParticleRenderer` submit transparent
|
||
GfxObj subsets and scene particles into one stable far-to-near stream keyed by
|
||
the transformed DAT `SortCenter`; only adjacent compatible entries may batch.
|
||
Billboard particle textures are resident bindless `sampler2DArray` handles in
|
||
the per-instance vertex ABI, so different textures preserve that sorted order
|
||
inside one instanced draw; only a DAT blend-mode boundary splits the run. This
|
||
keeps dense particle fields from becoming one GL draw per alternating texture.
|
||
`RetailPViewRenderer` drains the landscape scope before the optional depth
|
||
clear, and `GameWindow` drains the final scope before private viewports/UI.
|
||
Sky and sealed off-screen render targets remain independent. No DAT reader,
|
||
mesh decoder, or second scene graph was introduced. Retail anchors:
|
||
`CPhysicsPart::UpdateViewerDistance` `0x0050E030`,
|
||
`RenderDeviceD3D::DrawObjCellForDummies` `0x005A0760`,
|
||
`CShadowPart::insertion_sort` `0x006B5130`,
|
||
`D3DPolyRender::AddMeshToAlphaList` `0x0059C230`, and
|
||
`D3DPolyRender::FlushAlphaList` `0x0059D2E0`. The modern per-cell-order and
|
||
EnvCell-shell residual is tracked explicitly as AP-34.
|
||
|
||
**Retail portal-space viewport adapter (2026-07-15).**
|
||
`src/AcDream.App/Rendering/PortalTunnelPresentation.cs` uses the extracted
|
||
Setup/GfxObj mesh pipeline and mandatory `WbDrawDispatcher` for retail's
|
||
synthetic CreatureMode tunnel object. The adapter does not duplicate mesh or
|
||
DAT decoding: it resolves the two client-enum assets through `DatCollection`,
|
||
uses the shared `RetailAnimationLoader`, registers Setup part refs through
|
||
`WbMeshAdapter`, and submits the animated `SetupMesh` through the existing
|
||
dispatcher. It clears world clip routing and point lights for the private scene
|
||
before installing retail's distant light. The lifecycle, camera, animation,
|
||
and draw ordering are acdream-owned ports of `gmSmartBoxUI`; WorldBuilder never
|
||
implemented this UI viewport.
|
||
|
||
**Portal destination render-readiness seam (2026-07-16).**
|
||
`GpuWorldState.IsRenderReady` does not treat dictionary publication as a draw
|
||
barrier. `LandblockSpawnAdapter` retains the complete required-id set for each
|
||
landblock: atlas-tier GfxObjs plus the synthetic geometry ids prepared by the
|
||
independent EnvCell shell pipeline. `WbMeshAdapter.IsRenderDataReady` opens the
|
||
gate only after `ObjectMeshManager` has real GPU render data. Its bounded CPU
|
||
cache retains texture payloads and stages a missing GPU object through a
|
||
deduplicated upload queue on cache hit, covering eviction and revisit churn.
|
||
GPU upload is deliberately not an ownership acquire: atlas GfxObjs use their
|
||
landblock/entity pins, while synthetic EnvCell geometry uses a no-generic-decode
|
||
pin balanced from each landblock snapshot. A late upload after all owners have
|
||
released enters the evictable LRU instead of resurrecting a reference. After
|
||
publication pins the synthetic ids, the controller replays their immutable
|
||
environment/cell-structure/surface preparation descriptors; if a formerly
|
||
unowned mesh was evicted between worker scheduling and publication, this
|
||
schema-aware replay re-stages it without a generic GfxObj lookup.
|
||
Near-to-Far demotion is a separate App transaction: it releases EnvCell
|
||
rendering and landblock mesh pins while retaining the terrain slot. Core's
|
||
matching physics demotion preserves the terrain surface but removes indoor
|
||
cells, portals, buildings, and static shadow registrations.
|
||
|
||
**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.
|
||
|
||
---
|
||
|
||
## Read-reference layout (under `references/WorldBuilder/`, not project-referenced)
|
||
|
||
- **`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.
|
||
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):
|
||
|
||
| Package | Version | Purpose |
|
||
|---|---|---|
|
||
| `Chorizite.Core` | 0.0.18 | Plugin framework — contains `Chorizite.Core.Lib.BoundingBox`, `Chorizite.Core.Render.*` interfaces used by every render manager |
|
||
| `Chorizite.DatReaderWriter` | 2.1.x | dat parsing (we already use 2.1.7) |
|
||
| `Chorizite.DatReaderWriter.Extensions` | 1.1.x | extra dat helpers |
|
||
| `BCnEncoder.Net` | 2.2.x | DXT decode (we already use) |
|
||
| `SixLabors.ImageSharp` | 3.1.x | image loading |
|
||
| `Silk.NET.OpenGL` + `Silk.NET.SDL` | 2.23.x | GL + windowing (we use Silk's own windowing, they use SDL) |
|
||
| `MP3Sharp` | 1.0.5 | MP3 decode |
|
||
|
||
---
|
||
|
||
## 🟢 RENDERING — take wholesale or adapt
|
||
|
||
These are what makes WB "perfect". Anything in this section, we should
|
||
use from WB rather than re-implement.
|
||
|
||
### Terrain
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `TerrainRenderManager` | Full pipeline (per-chunk GPU buffers, draw orchestration) |
|
||
| `LandSurfaceManager` | Texture blending atlas (palCode, alpha masks, road overlays) |
|
||
| `TerrainGeometryGenerator` | Heightmap → mesh, normals, OnRoad, GetHeight, GetNormal |
|
||
| `TerrainChunk` | 16×16 landblock chunk geometry |
|
||
| `TextureAtlasManager` | Texture atlas builder |
|
||
| `VertexLandscape` | Terrain vertex format |
|
||
|
||
**Modern terrain adapter:** acdream's bindless path uses `TerrainAtlas` plus
|
||
`TerrainModernRenderer` rather than WB's draw manager, but retains
|
||
`LandSurfaceManager`'s layer-indexed `TerrainTex.TexTiling` contract. The
|
||
36-entry table is uploaded to `terrain_modern.frag`; base, overlay, and road
|
||
layers each use their owning repeat count while alpha masks stay at cell scale.
|
||
Retail `TexMerge::CopyAndTile` (`0x00503580`) and `TexMerge::Merge`
|
||
(`0x005038C0`) are the behavior oracle; see
|
||
`docs/research/2026-07-13-retail-terrain-texture-tiling-pseudocode.md`.
|
||
|
||
### Scenery (procedural placement: trees, bushes, rocks, fences)
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `SceneryRenderManager` | Generate + render per-vertex scenery |
|
||
| `SceneryHelpers` | Displace / RotateObj / ScaleObj / ObjAlign / CheckSlope |
|
||
| `SceneryInstance` | Per-spawn instance data |
|
||
|
||
acdream's streamed projection assigns generated instances local runtime IDs
|
||
through `AcDream.Core.World.ProceduralSceneryIdAllocator`. The namespace is
|
||
`0x8XXYYIII` (full X/Y bytes plus a 12-bit counter); bit 31 remains the stable
|
||
scenery classification seam. This is projection identity, not placement
|
||
behavior. The former 8-bit counter rejected dense retail-DAT landblocks before
|
||
their render transaction could publish (#218).
|
||
|
||
### Static objects (buildings, slabs, props — Setup + GfxObj + ObjDesc)
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `StaticObjectRenderManager` | Master pipeline for static objects |
|
||
| `ObjectRenderManagerBase` + `BaseObjectRenderManager` | Common render base |
|
||
| `ObjectMeshManager` | Mesh extraction from Setup/GfxObj, ObjDesc application |
|
||
|
||
### Dungeons / interiors
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `EnvCellRenderManager` | Dungeon interior cell geometry |
|
||
| `PortalRenderManager` | Portal traversal / visibility |
|
||
|
||
### Sky + atmosphere
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `SkyboxRenderManager` | Skybox rendering |
|
||
| `ParticleEmitterRenderer` + `ParticleBatcher` + `ActiveParticleEmitter` | Particle systems (sky particles, weather, magic) |
|
||
|
||
### Visibility / culling
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `VisibilityManager` + `VisibilitySnapshot` | Frustum + cell visibility |
|
||
| `Frustum` | Frustum-cull math |
|
||
|
||
### Other rendering helpers
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `MinimapRenderer` | Top-down minimap |
|
||
| `GlobalMeshBuffer` | Shared GPU mesh buffer |
|
||
| `GpuResourceManager` | GPU resource lifecycle |
|
||
| `InstanceData` | Instanced draw data |
|
||
| `TextureHelpers` | INDEX16, P8, BGRA, DXT decode + alpha (canonical port) |
|
||
| `DebugRenderer` + `DebugRendererLineDrawer` + `EdgeLineBuilder` | Debug primitives |
|
||
|
||
### Shaders (22 total)
|
||
|
||
Located at `Chorizite.OpenGLSDLBackend/Shaders/`:
|
||
|
||
`Landscape.{vert,frag}` · `StaticObject.{vert,frag}` · `StaticObjectModern.{vert,frag}` · `Particle.{vert,frag}` · `PortalStencil.{vert,frag}` · `Outline.{vert,frag}` · `Simple3D.{vert,frag}` · `InstancedLine.{vert,frag}` · `Text.{vert,frag}` · `UI.{vert,frag}` · `Gizmo.{vert,frag}` (editor-only)
|
||
|
||
---
|
||
|
||
## 🟢 LOW-LEVEL GL / FRAMEWORK — take or replace with our own
|
||
|
||
Either take WB's wrappers wholesale, or keep our own and adapt the
|
||
render managers to use ours. These wrappers are stateless or
|
||
near-stateless and are the easiest to swap.
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `OpenGLGraphicsDevice` | Silk.NET.OpenGL wrapper |
|
||
| `OpenGLRenderer` | Render orchestration |
|
||
| `GLSLShader` | Shader compile/link/uniforms |
|
||
| `GLHelpers` + `GLStateScope` | GL state utility |
|
||
| `ManagedGLFrameBuffer` / `ManagedGLIndexBuffer` / `ManagedGLTexture` / `ManagedGLTextureArray` / `ManagedGLUniformBuffer` / `ManagedGLVertexArray` / `ManagedGLVertexBuffer` | GL resource wrappers |
|
||
| `TextureParameters` | Sampler config |
|
||
| `GpuMemoryTracker` | Memory tracking |
|
||
| `Camera2D` / `Camera3D` / `CameraBase` / `ICamera` / `CameraController` | Camera primitives |
|
||
| `GameScene` + `SingleObjectScene` + `SceneData` + `ModernRenderData` + `RenderPass` | Scene / pass structures |
|
||
|
||
---
|
||
|
||
## 🟢 GEOMETRY / MATH UTILS — take wholesale
|
||
|
||
| Component | File |
|
||
|---|---|
|
||
| `TerrainUtils` (OnRoad, GetNormal, GetHeight, GetRoad, palCode) | `WorldBuilder.Shared/Modules/Landscape/Lib/TerrainUtils.cs` |
|
||
| `TerrainCacheManager` | `…/Lib/TerrainCacheManager.cs` |
|
||
| `TerrainRaycast` | `…/Lib/TerrainRaycast.cs` |
|
||
| `GeometryUtils` | `WorldBuilder.Shared/Lib/GeometryUtils.cs` |
|
||
| `RaycastingUtils` (ray-vs-sphere/AABB/triangle) | `WorldBuilder.Shared/Lib/RaycastingUtils.cs` |
|
||
| `DoubleNumerics` (double-precision Vector/Matrix) | `WorldBuilder.Shared/Lib/DoubleNumerics.cs` |
|
||
| `DatUtils` | `WorldBuilder.Shared/Lib/DatUtils.cs` |
|
||
| `BoundingBoxExtensions` | `Chorizite.OpenGLSDLBackend/Lib/BoundingBoxExtensions.cs` |
|
||
|
||
---
|
||
|
||
## 🟢 DATA MODELS — take selectively
|
||
|
||
| Component | What it does |
|
||
|---|---|
|
||
| `RegionInfo` | Landblock metadata wrapper (LandblockSizeInUnits, CellSizeInUnits, etc.) |
|
||
| `TerrainEntry` | Per-vertex terrain (Type/Scenery/Road/Height) |
|
||
| `MergedLandblock` | Merged dat data |
|
||
| `CellSplitDirection` | SW-NE vs NE-SW |
|
||
| `Cell` | Generic cell wrapper |
|
||
| `ObjectId` | Object identifier |
|
||
| `Position` | World position |
|
||
| `ACEnums` | AC-specific enums |
|
||
| `WbBuildingPortal` / `WbCellPortal` | Portal structures |
|
||
| `BuildingObject` | Building data |
|
||
|
||
---
|
||
|
||
## 🟡 EDITOR-ONLY — leave behind / delete in fork
|
||
|
||
These exist for the editor experience and have no place in a game
|
||
client. Delete in fork.
|
||
|
||
- **`Modules/Landscape/Tools/*`** — `BrushTool`, `BucketFillTool`,
|
||
`RoadLineTool`, `RoadVertexTool`, `InspectorTool`,
|
||
`ObjectManipulationTool`, `Gizmo*` (DragHandler, HitTester, Renderer,
|
||
State), `TexturePainting*`, `SceneRaycaster`,
|
||
`LandscapeBrush`, `LandscapeToolBase`, `LandscapeToolContext`,
|
||
`IToolSettingsProvider`, `ILandscapeBrush`, `ILandscapeEditorService`,
|
||
`ILandscapeRaycastService`, `ILandscapeTool`, `ITexturePaintingTool`
|
||
- **`Modules/Landscape/Commands/*`** — undo/redo command pattern for
|
||
editor (Add/Delete/Move/Rename/Reorder/etc.)
|
||
- **`LandscapeDocument` + `LandscapeLayer` + `LandscapeLayerGroup` + `LandscapeChunk` + `LandscapeLayerChunk` + `LandscapeLayerBase`** — editor document model
|
||
- **`Modules/Landscape/Models/TerrainPatch*` + `LandblockChangedEventArgs`** — editor mutation events
|
||
- **`Modules/Landscape/Services/ILandscapeCacheService` + `ILandscapeDataProvider` + `ILandscapeObjectService` + impls** — editor data flow
|
||
- **All `Migrations/*`** — SQLite schema migrations (project file format)
|
||
- **`Repositories/*`** + **`Services/*`** — project storage, dat repository, AceDb, SignalR sync, document manager, undo stack, world coordinates, keyword DB, project migration, semantic kernel AI helpers
|
||
- **`Hubs/*`** — collaborative editing via SignalR
|
||
- **`StaticObject` (editor model)** — replace with our own scene-state data model fed from network
|
||
- **`BackendGizmoDrawer` + `GizmoRenderer`** — editor gizmos
|
||
- **`ProjectStructures, IProject, Project`** — editor project files
|
||
- **`KeyBinding`** — editor input binding
|
||
- **`ViewportInputEvent[Extensions]`** — editor viewport input
|
||
- **`EditorState`** — editor state container
|
||
|
||
---
|
||
|
||
## 🟡 AUDIO / FONT — we already have alternatives
|
||
|
||
Keep ours; don't take theirs.
|
||
|
||
- **`AudioPlaybackEngine`** — uses MP3Sharp. We have OpenAL.
|
||
- **`FontRenderer`** — uses ImageSharp. We have BitmapFont/StbTrueTypeSharp + ImGui.
|
||
|
||
---
|
||
|
||
## 🔴 NOT IN WORLDBUILDER — port from retail decomp ourselves
|
||
|
||
WorldBuilder is a dat editor; it does not have:
|
||
|
||
- **Network protocol** — UDP framing, ISAAC, packet codec, ACE message
|
||
layer (we have this; oracle is `references/holtburger`)
|
||
- **Physics** — collision (CPhysicsObj transitions, BSP queries, sphere
|
||
sweeps), step-up, walkable validation (we have partial; oracle is the
|
||
retail decomp at `docs/research/named-retail/`)
|
||
- **Animation** — motion sequencer, cycle/non-cycle parts, animation
|
||
frame interpolation (we have this; oracle is retail decomp)
|
||
- **Movement** — local player WASD → MoveToState wire, remote-entity
|
||
motion via UpdateMotion + dead-reckoning (we have this; oracle is
|
||
`references/holtburger` + retail decomp)
|
||
- **Game UI** — chat, vitals, inventory, spell book, allegiance, options
|
||
(we have this; ImGui-based today, custom-toolkit later)
|
||
- **Plugin API** — `IGameState`, `IEvents`, `IActions`, `IPacketPipeline`,
|
||
`IOverlay` (we have this — acdream-unique)
|
||
- **Game events** — combat, allegiance, spell casting, quest events
|
||
(we have this; oracle is ACE for opcodes + retail for client behavior)
|
||
- **Audio** — OpenAL pipeline, sound triggers (we have this)
|
||
- **TurbineChat** + **slash commands** (we have this)
|
||
- **Login + character selection flow** (we have this)
|
||
- **World-object mouse selection** — WorldBuilder supplies mesh/DAT access but
|
||
no retail client picker. Our narrow `RetailSelectionGeometryCache` reuses
|
||
`DatCollection` to expose each GfxObj drawing-BSP root sphere and visual
|
||
polygons; `WbDrawDispatcher` supplies the normal draw's current part
|
||
transforms to the named-retail selection accumulator.
|
||
|
||
---
|
||
|
||
## 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 🟢 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 🟢.
|