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>
13 KiB
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 insrc/AcDream.*references it as a project dependency.DatCollectionis now the only dat reader in process.Use this document to:
- Know where our extracted code lives (look for the "Extracted to" column / notes in each section below).
- Know what WB still has that we haven't needed yet — grep
references/WorldBuilder/if you ever need to add something.- 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.
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.
Read-reference layout (under references/WorldBuilder/, not project-referenced)
Chorizite.OpenGLSDLBackend/— full OpenGL renderer (Silk.NET). The components we use are extracted intosrc/AcDream.App/Rendering/Wb/.WorldBuilder.Shared/— data models, dat parsers, landscape module. The helpers we use are extracted intosrc/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 |
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 |
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,ITexturePaintingToolModules/Landscape/Commands/*— undo/redo command pattern for editor (Add/Delete/Move/Rename/Reorder/etc.)LandscapeDocument+LandscapeLayer+LandscapeLayerGroup+LandscapeChunk+LandscapeLayerChunk+LandscapeLayerBase— editor document modelModules/Landscape/Models/TerrainPatch*+LandblockChangedEventArgs— editor mutation eventsModules/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 helpersHubs/*— collaborative editing via SignalRStaticObject(editor model) — replace with our own scene-state data model fed from networkBackendGizmoDrawer+GizmoRenderer— editor gizmosProjectStructures, IProject, Project— editor project filesKeyBinding— editor input bindingViewportInputEvent[Extensions]— editor viewport inputEditorState— 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)
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 🟢.