Skip to content

Render negative-Y sections on 1.18+ worlds - #488

Open
u9g wants to merge 2 commits into
PrismarineJS:masterfrom
u9g:render-negative-y-sections
Open

Render negative-Y sections on 1.18+ worlds#488
u9g wants to merge 2 commits into
PrismarineJS:masterfrom
u9g:render-negative-y-sections

Conversation

@u9g

@u9g u9g commented Sep 2, 2026

Copy link
Copy Markdown
Member

Revives #485 as a standalone change (the original was stacked on #484; this one is based on master and depends on nothing else).

Problem

A camera below y=0 renders nothing but the clear color — a mineflayer bot standing on the surface of any 1.18+ overworld, or anywhere in a deepslate cave, shows a uniform sky-blue frame even with the surrounding chunks fully loaded.

Cause

Three spots hardcode the pre-1.18 world range [0, 256):

  • worldrenderer.js dirty-section loops in addColumn/removeColumn iterate y = 0..240, so sections below y=0 are never queued for meshing.
  • worker.js indexes chunk.sections[Math.floor(y / 16)], but 1.18+ ChunkColumn indexes from minY (-64): sections[(pos.y - minY) >> 4]. For y<0 this resolves to sections[-1] = undefined and the section is skipped.
  • models.js culls faces against a hardcoded y < 0 instead of the world's minY, so every face on a below-zero surface is culled.

Fix

  • Derive minY/worldHeight from the version's chunk implementation in setVersion (defaults 0/256, so pre-1.18 behavior is unchanged) and iterate the real range.
  • Index worker sections by (y - (chunk.minY ?? 0)) / 16.
  • Expose minY on the worker's World wrapper and gate models.js culling on world.minY ?? 0.

Verification

Confirmed live: a mineflayer bot on a Paper 1.21.8 server standing at y=-60 rendered a blank sky before and a full grass-plains first-person view after (headless-gl + this branch). setVersion('1.16.5')/'1.12.2' still resolve to { minY: 0, worldHeight: 256 }, so pre-1.18 is untouched.

u9g added 2 commits September 6, 2026 10:19
A camera below y=0 renders nothing but the clear color: a bot standing on
the surface of any 1.18+ world (surface ~y=-60 in the overworld's lower
range, or anywhere in a deepslate cave) shows a uniform sky-blue frame with
the surrounding chunks fully loaded.

Three spots hardcode the pre-1.18 world range [0, 256):
- worldrenderer.js dirty-section loops in addColumn/removeColumn iterate
  y=0..240, so sections below y=0 are never queued for meshing
- worker.js indexes chunk.sections[floor(y/16)], but 1.18+ ChunkColumn
  indexes from minY (-64): sections[(y - minY) >> 4]; y<0 resolves to
  sections[-1] = undefined and the section is skipped
- models.js culls faces against a hardcoded y<0 instead of the world's minY

Derive minY/worldHeight from the version's chunk implementation (defaults
0/256, so pre-1.18 is unchanged), iterate the real range, index worker
sections by (y - minY)/16, and gate models.js culling on world.minY.

Revives PrismarineJS#485 (closed); no longer stacked on the 26.1 PR.
…enderer

Requiring prismarine-chunk from worldrenderer.js pulled all of
minecraft-data into the browser index bundle, OOMing webpack in CI.
The node-side loadChunk producers already have the column, so they
send minY/worldHeight along with the chunk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant