Files
Emil c7e86663d8
MVP checks / mvp (push) Canceled after 0s
Expand voxel gameplay, lighting, full-height streaming and world imports
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.
2026-09-17 02:10:53 +03:00

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).