Skip to content
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ The private Yarn workspace root contains the public library and its consumers. U

**Entities** (`src/entity/`): `Entities.getEntity` loads a versioned `entity-models` file. By default it selects `main` plus the dataset `passes` that have no `when` label or whose label is in `options.when` (`getPassList` lists them); `layer`/`layers` select exactly the named layers in order, without passes. `textures` overrides are keyed like `EntityModel.layers`, where a further draw of the same geometry is `<layer>#<n>` from 2. `getLayerList` lists available names. Each draw uses the material for its dataset `render` mode (`Materials.createEntityCanvasMaterial`), cached per texture, mode, and tint; modes that vanilla culls get no inward faces. `EntityObject` `tints` maps pass tint labels to colours. `energy_swirl` and `breeze_wind` materials are owned by their object: `Ticker` advances `EntityObject.age` and the texture offset while the object has a parent; removal pauses it and reattachment resumes it. `EntityObject.getLayerGroup` and layer-scoped `getGroupByName`/`getMeshByName` expose each layer’s visibility and poses. Parts form a nested `Object3D` hierarchy with radian poses, inherited texture dimensions, cube growth, and mirrored box UVs. The entity root applies the dataset's `transform` (vanilla's renderer operations for the default state: living entities face +Z with their feet at the origin); models without one get scale `(-1, -1, 1)`. Nested child nodes hold this transform, one per operation, so every operation order is exact and caller position, rotation, and scale apply on top of it. `flip: true` forces that plain flip and `flip: false` keeps raw model space. Without an explicit texture key, use dataset `textureLocation` first, then the resolver (model path, a same-named texture in its directory, then variant data); `getBlock` and `getBlockList` are deprecated aliases. `Entities.getDatasetRoot` loads geometry, lists, animations, and the block index from the nearest published release when the exact dataset version is missing; textures keep the requested root. `src/bedrock/` contains type declarations only.

**World** (`src/world/`): `MineRenderWorld` is an early prototype with sparse chunks keyed by signed coordinates. Cubic 16³ `Chunk`s store `ChunkData` palettes with immutable states and recycled unused entries, 4,096 `Uint16` state indices, and per-cell NBT. Render handles and stable `BlockInfo` views remain separate; `.block` returns detached snapshots. Block coordinates are integers; chunk-local coordinates are 0–15. World and owned-chunk setters refresh `cullface` masks after edits, including across chunk boundaries; only opaque full-cube models occlude neighbors. The versioned `blockCulling` dataset lists vanilla's further face rules, applied in both rendering modes: glass, ice, slime and similar blocks hide faces toward the same block, mangrove roots toward the same block above or below, and connected panes or bars toward each other. Leaves keep their faces, as vanilla's cutout leaves do; data without `blockCulling` applies no such rule. `new MineRenderWorld(scene, { sectionMeshing: true })` merges models of any shape, including cutout, translucent, multipart, and animated models, plus fluid surfaces into per-section meshes with separate solid, translucent, and fluid atlas pages (at most 2048×2048 pixels by default; `maxAtlasSize`), with one mesh per page. Page selection uses texture alpha (`hasTranslucency`), so plain glass and panes use the solid/cutout page where vanilla uses the translucent layer. Translucent quads within a section are not depth-sorted. Per-position flower offsets remain unsupported. Only block entities keep individual objects; only opaque full cubes occlude neighbors. Animated atlases occupy their own pages and redraw on frame changes. Fluid surfaces use an 18×18×18 cell neighborhood and are built in the section worker when available. The section worker entry must stay free of three, DOM, and Node imports, so pure geometry code lives in `SectionGeometry.ts` and `FluidQuads.ts`. Merged `BlockInfo.object` values are `undefined`; edit through world/chunk setters. Replacing, removing, or clearing blocks releases their instance slots. `await world.unloadChunkColumn(x, z)` removes every section at absolute chunk-column coordinates and refreshes neighboring faces and fluids. `await world.unloadChunkColumns([{ x, z }, ...])` validates, copies, and deduplicates a group before removal, then refreshes surviving faces, fluids, lighting, AO, and biome colors once. `await world.setBlockVisibleAt(position, visible)` preserves block data and reveals neighboring faces in both rendering modes; replacement or removal resets visibility. Without an executor, parsed columns copy each section's palette into the chunk, resolve each palette entry once, and fill cells in one synchronous pass; `placeMultiBlock` groups blocks per chunk and resolves each distinct state once per group, with weighted variants chosen per cell and yields after about 8 ms of work. Passing a `BatchedExecutor` keeps the previous per-position batches, order at repeated positions, and wait-for-active-batch failure semantics. Bulk placement applies saved properties before model creation and flushes neighbor culling once, including after partial failure; single-block edits update neighbors immediately. Parsed column placement and unloading mark whole sections and neighboring sections' shared boundary cells dirty; structure placement and single edits queue chunk-local indices, and the culling drain recomputes masks in a flat pass per section and rebuilds each changed section once through `SectionMesh.buildAsync`, which runs the pure `buildSectionGeometry` (`SectionGeometry.ts`, typed arrays in and out) in the browser section worker when `Env.provider.createWorker` returns one and synchronously otherwise; a rebuild superseded by a newer rebuild or `clear()` is disposed. `BatchedExecutor.submit` returns a task promise; `stop()` rejects queued and future tasks while active tasks finish. Idle executors hold no timer; caller-provided executors remain caller-owned. Saved biomes supply grass, foliage, dry-foliage, and per-cell water colors in both rendering modes. Saved lighting, corner ambient occlusion, and biome blending are opt-in; light propagation, exact partial-shape ambient occlusion, and LOD remain unsupported. The demos in `apps/web/` exercise `MineRenderWorld`.
**World** (`src/world/`): `MineRenderWorld` is an early prototype with sparse chunks keyed by signed coordinates. Cubic 16³ `Chunk`s store `ChunkData` palettes with immutable states and recycled unused entries, 4,096 `Uint16` state indices, and per-cell NBT. Render handles and stable `BlockInfo` views remain separate; `.block` returns detached snapshots. Block coordinates are integers; chunk-local coordinates are 0–15. World and owned-chunk setters refresh `cullface` masks after edits, including across chunk boundaries; only opaque full-cube models occlude neighbors. The versioned `blockCulling` dataset lists vanilla's further face rules, applied in both rendering modes: glass, ice, slime and similar blocks hide faces toward the same block, mangrove roots toward the same block above or below, and connected panes or bars toward each other. Leaves keep their faces, as vanilla's cutout leaves do; data without `blockCulling` applies no such rule. `new MineRenderWorld(scene, { sectionMeshing: true })` merges models of any shape, including cutout, translucent, multipart, and animated models, plus fluid surfaces into per-section meshes with separate solid, translucent, and fluid atlas pages (at most 2048×2048 pixels by default; `maxAtlasSize`), with one mesh per page. Page selection uses texture alpha (`hasTranslucency`), so plain glass and panes use the solid/cutout page where vanilla uses the translucent layer. Transparent model and fluid pages draw their quads back to front: perspective views sort by distance from the camera and re-sort when it enters another block, as vanilla does; orthographic views sort by view depth and re-sort when the view direction changes. Sorting stays within each atlas page; intersecting surfaces can still blend incorrectly, and Three.js sorts the meshes for different pages or sections as whole objects. Per-position flower offsets remain unsupported. Only block entities keep individual objects; only opaque full cubes occlude neighbors. Animated atlases occupy their own pages and redraw on frame changes. Fluid surfaces use an 18×18×18 cell neighborhood and are built in the section worker when available. The section worker entry must stay free of three, DOM, and Node imports, so pure geometry code lives in `SectionGeometry.ts` and `FluidQuads.ts`. Merged `BlockInfo.object` values are `undefined`; edit through world/chunk setters. Replacing, removing, or clearing blocks releases their instance slots. `await world.unloadChunkColumn(x, z)` removes every section at absolute chunk-column coordinates and refreshes neighboring faces and fluids. `await world.unloadChunkColumns([{ x, z }, ...])` validates, copies, and deduplicates a group before removal, then refreshes surviving faces, fluids, lighting, AO, and biome colors once. `await world.setBlockVisibleAt(position, visible)` preserves block data and reveals neighboring faces in both rendering modes; replacement or removal resets visibility. Without an executor, parsed columns copy each section's palette into the chunk, resolve each palette entry once, and fill cells in one synchronous pass; `placeMultiBlock` groups blocks per chunk and resolves each distinct state once per group, with weighted variants chosen per cell and yields after about 8 ms of work. Passing a `BatchedExecutor` keeps the previous per-position batches, order at repeated positions, and wait-for-active-batch failure semantics. Bulk placement applies saved properties before model creation and flushes neighbor culling once, including after partial failure; single-block edits update neighbors immediately. Parsed column placement and unloading mark whole sections and neighboring sections' shared boundary cells dirty; structure placement and single edits queue chunk-local indices, and the culling drain recomputes masks in a flat pass per section and rebuilds each changed section once through `SectionMesh.buildAsync`, which runs the pure `buildSectionGeometry` (`SectionGeometry.ts`, typed arrays in and out) in the browser section worker when `Env.provider.createWorker` returns one and synchronously otherwise; a rebuild superseded by a newer rebuild or `clear()` is disposed. `BatchedExecutor.submit` returns a task promise; `stop()` rejects queued and future tasks while active tasks finish. Idle executors hold no timer; caller-provided executors remain caller-owned. Saved biomes supply grass, foliage, dry-foliage, and per-cell water colors in both rendering modes. Saved lighting, corner ambient occlusion, and biome blending are opt-in; light propagation, exact partial-shape ambient occlusion, and LOD remain unsupported. The demos in `apps/web/` exercise `MineRenderWorld`.

`world.raycast(raycaster)` returns the nearest visible rendered block: integer `position`, detached `block` data, and scene-space `point`, `normal`, and `distance`. It resolves merged geometry through per-quad cell indices and individual geometry through owned model/instance references. Hidden blocks and unrelated scene objects are excluded; transparent texture pixels still participate in geometric picking.

Expand Down
2 changes: 2 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Regenerate and publish the `1.21.11` entity dataset with the procedural clips fr
### 11. World subsystem redesign for scale — high (the V2 differentiator)
Immediate fixes: ~~fix `getChunkAt` to use `Map.get(key)`~~; ~~remove the 4×4×4 bound and negative-coordinate rejection~~; ~~remove hardcoded debug wireframes~~; ~~fix `BatchedExecutor`'s missing setInterval delay + add `stop()`~~; ~~place structures and chunks in bounded batches with one final neighbor-culling pass~~. Then the redesign: ~~palette + typed-array section storage~~, ~~opt-in merged meshes and bounded atlas pages per chunk section for models of any shape, including cutout, translucent, multipart, and animated models, plus fluid surfaces~~ (block entities retain per-block objects), ~~neighbor face culling via model `cullface` against opaque full cubes~~ (vanilla's same-block and connected-block rules from the `blockCulling` dataset also hide shared faces; partial-shape rules remain), ~~explicit chunk load/unload~~, ~~`await world.unloadChunkColumns(positions)` removes columns together and refreshes surviving neighbors once; the streamer requires this method, which `MineRenderWorld` provides, for eviction and disposal~~, ~~per-block visibility without deleting block data~~, ~~camera-driven streaming~~. ~~Opt-in baked per-vertex ambient occlusion from opaque full cubes, with runtime toggling.~~ Exact partial-shape ambient occlusion, chunk-level visibility management, and LOD remain. Section meshes already have bounds for Three.js frustum culling.

~~Sort transparent model and fluid quads back to front within each atlas page.~~ Intersecting surfaces and quad ordering across pages or sections remain approximate.

~~Preserve modern Anvil biome palettes during import and placement, including air-only sections, with world-coordinate lookup.~~ ~~Apply biome colors to grass, foliage, dry foliage, and water in both rendering modes, including merged fluid sections, resource-pack colormaps, and dark forest/swamp grass modifiers.~~ ~~Add opt-in biome color blending in section meshes with a runtime radius, cached color averaging, and border refreshes after biome edits or streaming.~~ Older numeric biome arrays and world-seed-based biome sampling remain unsupported. ~~Import saved sky/block light and apply opt-in flat lighting to blocks and fluids in section meshes.~~ ~~Add opt-in ambient occlusion to axis-aligned boundary block faces in section meshes, honoring model opt-outs and refreshing edited or streamed neighbors.~~ Exact partial-shape ambient occlusion and light propagation remain. Measure real-world decoding, placement, frame stalls, and retained memory before choosing further worker or LOD work. The [world-streaming benchmark](./apps/web/benchmarks/README.md) records fixed local-save routes with cold and warm asset passes, operation timings, frame-callback gaps, and JavaScript heap samples.

~~Seed weighted blockstate alternatives by world position so unloading and reloading a chunk preserves its appearance.~~ Individual blocks and section meshes share vanilla-identical weighted selection from absolute block coordinates. Standalone previews remain random unless given `variantPosition`.
Expand Down
2 changes: 2 additions & 0 deletions packages/minerender/reference/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Calling `removeFromScene()` or `dispose()` on an instance reference releases its

With `sectionMeshing: true`, [MineRenderWorld](/api/index/classes/MineRenderWorld) merges eligible blocks into section meshes. A merged block has no individual `BlockInfo.object`. Edit it through the world or chunk setters so geometry and neighbor culling update together.

Translucent blocks and water in a section mesh draw back to front. Perspective views sort them by distance from the camera and re-sort when the camera enters another block; orthographic views sort by view depth. Separate section meshes rely on Three.js sorting the meshes as whole objects, and intersecting surfaces can still blend incorrectly.

Block model elements with `shade: false` skip directional face shading in separate, merged, and instanced models, including world section meshes. Tints, saved world light, and ambient occlusion still apply. Item previews keep directional shading because vanilla's item renderer ignores the flag. Omitted or `true` values retain directional shading; GUI items with `gui_light: "front"` disable it for the whole model.

World placement selects weighted block models from absolute block coordinates, so unloading and reloading preserves their appearance in both rendering modes. Standalone `scene.addBlock` previews remain random unless you supply `variantPosition: [x, y, z]` in block units. The position is copied at construction; moving the object does not change its selection. The pick is vanilla-identical for the same coordinates and ordered weights. Position-based selection rejects total weights above 2,147,483,647.
Expand Down
Loading
Loading