MVP checks / mvp (push) Canceled after 0s
Add shared Rust/WASM physics, worker meshing and diagnostics, 64-chunk full-height streaming, atlas texture support, and baseline world import. Document the current implementation and include the supplied in-game lobby screenshot.
130 lines
8.0 KiB
Markdown
130 lines
8.0 KiB
Markdown
# Block and sky lighting
|
|
|
|
Light-emitting block states illuminate their surroundings. The renderer computes
|
|
two independent integer fields, **block light** and **sky light**, each ranging
|
|
from 0 to 15. These fields determine the brightness of terrain, transparent
|
|
surfaces and moving avatars/entities. Emissive surfaces themselves remain bright.
|
|
|
|
## Rules and reference data
|
|
|
|
Emission comes from the server catalog's state-specific `light` value. An ordinary
|
|
torch emits 14, a sea lantern 15, and an unlit lamp emits 0. State changes,
|
|
installation and removal trigger a new calculation. Overlapping lights select
|
|
the strongest level; they do not add their levels together.
|
|
|
|
Block light travels to the six adjacent cells and loses at least one level at
|
|
each transition. Target material dampening and the combined source/target
|
|
occlusion faces can reduce or stop transmission. The solver uses actual face
|
|
rectangle unions, including complementary slab and stair shapes, rather than
|
|
treating every collision box as an opaque cube. Light can travel around an open
|
|
doorway with attenuation along that path.
|
|
|
|
Open vertical sky has level 15. Unobstructed downward travel through clear cells
|
|
keeps 15; other propagation loses at least one level. Roofs, material dampening
|
|
and face occlusion interrupt direct skylight. A sealed room therefore has sky
|
|
level 0 even during the daytime scene, while a light source inside it
|
|
continues to illuminate the room.
|
|
|
|
`client/light-properties.json` contains measured Java 26.2 dampening and effective
|
|
light-occlusion shapes for all **32,366 states**, with 60 deduplicated shapes.
|
|
The loader resolves either the catalog's `minecraft_id` or state strings,
|
|
including partial properties with original defaults. These IDs are independent
|
|
of Shacraft's runtime material IDs. Examples include transparent glass (0),
|
|
tinted glass (15), water/leaves/ordinary ice (1), and packed/blue ice (15).
|
|
Metadata is about light transmission, which can differ from collision geometry.
|
|
|
|
The data and 78 directional crossing measurements come from the pinned original
|
|
executable through an independently authored probe. The propagation tests agree
|
|
with those crossings. This is evidence for the measured properties and local
|
|
transitions, not execution of an entire original Minecraft world-light engine.
|
|
|
|
```sh
|
|
python3 scripts/measure_lighting.py --java /path/to/java25/bin/java
|
|
node --test client/tests/light-properties.test.js client/tests/block-light.test.js
|
|
```
|
|
|
|
The probe checks the official bundle and extracted executable hashes. No original
|
|
runtime or proprietary source code is distributed. The compact runtime metadata
|
|
is about 150 KB before HTTP compression.
|
|
|
|
## Rendering and updates
|
|
|
|
`terrain-controller.js` maintains one persistent `terrain-worker.js` instance.
|
|
The worker owns the section-indexed block map, material light properties, texture
|
|
metadata and bounded light fields. A normal streamed view has 196,608 cells;
|
|
the field buffers total about 1.18 MB. Initial blocks are packed in short main-thread
|
|
time slices and transferred as `Int32Array` records `(x, y, z, block)`. Subsequent
|
|
messages carry numeric block deltas, section unloads and changed definitions,
|
|
instead of repeatedly cloning the full loaded block map.
|
|
|
|
`terrain-state.js` computes fields through `block-light.js`, compares old/new
|
|
light values around section neighborhoods, and builds affected static geometry
|
|
through `mesh-geometry.js`. Propagation, light comparison, AO sampling, face culling
|
|
and mesh generation therefore all run in the worker. Vertex samples blend
|
|
accessible neighbors outside each face and avoid sampling through solid corners.
|
|
The worker keeps its field arrays and transfers copies for moving actors to
|
|
sample on the main thread. Epoch/version checks reject obsolete results; dirty
|
|
section notifications from skipped intermediate fields are retained so a later
|
|
result cannot leave an old visible mesh with stale lighting.
|
|
|
|
Material definitions also invalidate geometry independently of light values.
|
|
The worker compares transmitted material signatures, finds loaded sections using
|
|
changed definitions, and refreshes those sections and their AO/culling neighbors.
|
|
This replaces a retained placeholder when its real opaque material arrives even
|
|
if the light field is unchanged. Repeated identical definitions do not trigger
|
|
extra remeshing; pending material changes are combined across coalesced updates.
|
|
|
|
The main thread receives transferable opaque/translucent vertex arrays. It
|
|
uploads them in steps of at most 64 KiB with a 2 ms scheduling budget per frame,
|
|
retaining the old visible section until both replacements are complete. The
|
|
section object keeps its identity when its GPU handles are replaced. There is
|
|
one mesh request in flight and at most two completed results waiting for upload.
|
|
GPU allocation and individual driver calls cannot be preempted; the scheduling
|
|
budget does not guarantee that every frame finishes in 2 ms.
|
|
|
|
The browser's world map is also indexed by section, allowing validated chunk
|
|
transitions to update entering/departing blocks without reparsing the entire
|
|
retained volume. If the terrain worker is unavailable or fails at runtime, the
|
|
renderer keeps existing visible meshes and falls back to main-thread terrain
|
|
meshing plus the former `BlockLightController`. That controller can use its own
|
|
light worker, or calculate light on the main thread when workers are unavailable.
|
|
Fallback mode can therefore pause rendering during heavy calculations.
|
|
|
|
Canonical levels remain scalar. Warm torch light, cool sea-lantern light and
|
|
other source tints are a Shacraft visual treatment of those levels. They are not
|
|
a claim that vanilla Java lighting stores RGB light. The shader converts the
|
|
field to linear irradiance and combines it with the existing sun shadows and
|
|
ambient occlusion. At block/sky level 0 only a small visibility floor remains;
|
|
the daytime sky is not reflected or fogged brightly through a sealed room.
|
|
|
|
F3 shows block/sky levels at the camera, source count and build status, plus
|
|
worker/fallback mode, pending geometry, packet-preparation time, worker mesh-build
|
|
time, geometry-upload time and recent frame-time p95/maximum. Canvas attributes
|
|
`data-terrain-mode`, `data-terrain-prepare-ms`, `data-mesh-build-ms`, and
|
|
`data-mesh-upload-ms` distinguish background work from main-thread upload work.
|
|
The enclosed room in `/tests/renderer-smoke.html` supports source on/off/color changes
|
|
and removal/restoration of a partition, with explicit canonical readings.
|
|
|
|
The game opens at night by default. The lighting button in the game menu switches
|
|
between day and night and remembers the choice in this browser. Night uses a
|
|
dark sky, stars, moonlight and matching fog/water reflections. This changes sky
|
|
illumination in the shader only: propagated block/sky levels, lamp strength and
|
|
terrain meshes remain unchanged. The setting is local; there is no synchronized
|
|
server day/night cycle yet. The renderer fixture keeps its daytime default.
|
|
|
|
## Boundaries
|
|
|
|
Lighting is computed for the client's loaded view, with an exposed sky boundary
|
|
above that volume and closed unknown side/bottom boundaries. It does not yet
|
|
receive a full-world authoritative sky heightmap, so a roof above the loaded
|
|
vertical range can require more world context. Rebuilding a field after an edit
|
|
has a short worker/update delay. Colored tints and display brightness are visual
|
|
choices; global illumination and ray-traced point-light shadows are not used.
|
|
|
|
The fields are currently renderer state. This change does not add light-dependent
|
|
mob spawning, crop growth, redstone updates, or other absent gameplay systems.
|
|
|
|
## Procedural world columns
|
|
|
|
In the v2 world stream, the server supplies full-height opaque column maxima. These seed the top of the local skylight volume and preserve dark caves when the roof is outside the vertical view. Roof edits update the column; unloading a horizontal column releases its data. Sky rendering follows the camera sky exposure so the unloaded underground horizon stays dark. The active light field remains bounded to the streamed window; this is not global propagation over the entire world. See [world streaming](WORLD_STREAMING.md).
|