Phase 12: texture atlas + UV-mapped 3D mesh textures

Fixes a real gap left over from Phase 11: mesh.rs's 3D mesher was still
using the hand-picked flat palette instead of texture-averaged colors.
Adds worker/src/atlas.rs to pack downloaded block textures into a single
PNG atlas + UV rect map, threads tile-relative UV and atlas-rect buffers
through the mesh binary format (v2, hard break — meshes are a
regenerable render cache), serves the atlas from MinIO via two new api
routes, and adds a custom Babylon shader that falls back to flat vertex
colors per-fragment for untextured quads. glTF export intentionally
stays vertex-color-only (documented reasoning in gltf-export.js) since
standard glTF materials can't express that same per-fragment fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tKdPZt78zbPUZMXWzKEKt
This commit is contained in:
2026-08-10 00:41:15 +02:00
parent f8216b6e77
commit 6ad8051eae
16 changed files with 843 additions and 44 deletions
+21
View File
@@ -27,6 +27,27 @@ pub fn set_texture_palette(palette: TexturePalette) {
let _ = TEXTURE_PALETTE.set(palette);
}
/// Read by `mesh.rs` so 3D section meshing's vertex-color fallback picks up the same
/// texture-averaged colors the 2D tile path already uses via `base_colors` — see this module's
/// doc comment above.
pub(crate) fn texture_palette() -> Option<&'static TexturePalette> {
TEXTURE_PALETTE.get()
}
/// Same `OnceLock`-once-at-startup pattern as `TEXTURE_PALETTE`, for the Phase 12 texture atlas
/// (see `atlas.rs`). `None` until `main.rs` successfully builds one (`ACCEPT_MINECRAFT_EULA` not
/// set, or the build failed) — `mesh.rs` falls back to a flat vertex color per quad whenever this
/// is `None` or the block's texture name has no atlas entry.
static TEXTURE_ATLAS: OnceLock<crate::atlas::TextureAtlas> = OnceLock::new();
pub fn set_texture_atlas(atlas: crate::atlas::TextureAtlas) {
let _ = TEXTURE_ATLAS.set(atlas);
}
pub(crate) fn texture_atlas() -> Option<&'static crate::atlas::TextureAtlas> {
TEXTURE_ATLAS.get()
}
/// One rendered column within a chunk, in chunk-local coordinates (0..16).
pub struct ColumnPixel {
pub local_x: u8,