NyraCraft is an experimental voxel engine/game prototype written in Rust on top of wgpu + winit, with custom chunk streaming, LOD meshing, inventory/crafting, and data-driven block/item content.
- Procedural world generation (normal, flat, grid modes)
- Chunked voxel meshing and GPU rendering
- Runtime world editing (break/place blocks)
- Inventory + hotbar + crafting (2x2 player, 3x3 crafting table)
- Dropped items with physics, pickup, stacking, despawn
- Command/chat console (
/help,/items,/give) - Hot reload of textures/content (
F1 + R) - Debug overlays and runtime performance stats
- Rust 2024 edition
wgpu0.19winit0.29glamfor mathnoisefor terrain/cavesserde+serde_jsonfor content and recipesimagefor atlas/texture loading- Python 3 + Pillow (
PIL) for texture atlas generation script
- Rust toolchain + Cargo
- Python 3 available as
python3(orpython) - Pillow for atlas generation:
pip install pillowcargo run --release -- 12345Seed argument behavior:
- Number/text: deterministic normal world seed
FLAT: flat world modeGRID: flat world + debug marker columns- No argument: prompts in terminal for seed/mode (blank = random)
cargo testCurrent test set: 5 tests (worldgen + block/tool logic).
W/A/S/D: moveSpace: jump (or ascend in fly mode)Shift: sprintC: sneak (and descend in fly mode)Mouse: lookLeft Mouse: mine (hold for hardness-based breaking)Shift + Left Mouse: multi-break up to 12 matching nearby blocks (3x3x3 neighborhood)Right Mouse: place block / use crafting tableE: inventory1..9/ mouse wheel: hotbar selectQ: drop one item from selected hotbar slotEscape: pause menu/orT: open chat/command consoleF1: keybind overlayF3: detailed debug statsF1 + F: face debug colorsF1 + W: chunk wireframeF1 + P: pause/resume streamingF1 + V: pause/resume renderingF1 + R: hot reload atlases/contentF1 + M: toggle fly modeF1 + X: toggle fullscreenF1 + D: stats overlay toggle
/help/items/give <item> [count]
<item> accepts names/aliases and namespaced ids:
- blocks:
1:<block_id> - items:
2:<item_id>
src/
main.rs # app loop, input, streaming tick, worker setup
app/
bootstrap.rs # seed/mode parsing, atlas generation, CPU label
controls.rs # input helpers and keybind text
console.rs # chat/command parsing
dropped_items.rs # drop physics, merge, pickup, render data
menu.rs # pause menu layout/hit testing
streaming.rs # request queue, LOD policy, streaming/apply/caching
logger.rs # logs/latest.log + panic hook
world/
worldgen.rs # terrain, caves, biomes, trees, modes
mesher.rs # full/LOD meshing + greedy meshing + AO/light
blocks.rs # block/item registry, tool rules, drop rules
lightengine/ # sky visibility + face light
render/
gpu.rs # pipelines, chunk buffers, culling, UI rendering
mesh.rs # raw/packed vertex formats
texture.rs # atlas/colormap/texture upload
player/
movement.rs # movement, collision response, sneak edge-guard
block_edit.rs # break/place edits, dirty chunks, leaf decay
inventory.rs # inventory/hotbar/craft interaction and layout
crafting.rs # recipe loading + shaped/shapeless matcher
content/
blocks/*.json # block definitions
items/*.json # item definitions
recipes/*.json # crafting recipes
texturing/
atlas_gen.py # builds atlas_output/atls_blocks.png + atlas_items.png
- Initializes logger + panic hook (
logs/latest.log) - Resolves seed/mode and creates
WorldGen - Regenerates texture atlases only if sources changed
- Loads block/item/recipe registries
- Creates GPU device/pipelines
- Spawns:
- mesh worker threads (
available_parallelism/2, clamped1..=6) - cache writer thread for far-LOD mesh cache
- mesh worker threads (
- Fixed simulation tick every
50 ms(~20 TPS) - Frame pacing at ~
16 mstarget - Updates:
- player movement/physics
- dropped item simulation/pickup
- leaf decay and block edit remesh invalidation
- chunk stream requests + apply worker results
- adaptive budget tuning from live FPS/TPS
- Two chunk pipelines:
- raw chunk vertex path (near/high detail)
- packed far-vertex path (far LOD)
- Separate dynamic meshes for:
- dropped items
- block break overlay stages
- sun billboard
- UI pass draws hotbar/inventory/chat/keybind/stats/pause menu
- Namespace
1:n=> block ids - Namespace
2:n=> item ids - Item ids are internally
i8; namespaced2:nbecome negative ids to avoid collisions with block-backed items.
Defines:
id,register_name,aliases- hardness + required tool
- texture slots per face (
texture_slot_1basedortextures_slot_1based) - UV rotation per face
- transparency mode per face
- optional drop rules (
drop_item/drop_items)
Defines:
id,register_name,aliases- optional placeable block link
- atlas icon slot
- tool type, breaktime, durability, stack size
- optional edible metadata
Supports:
shapedshapeless- per-file recipe arrays
- recipe aliases/variants
- craft output preview + atomic consume on craft
This project already includes substantial optimization work, including LOD.
- Mesh modes in
world::mesher:FullSurfaceSidesSurfaceOnly
- Distance-based selection in
stream_tick - Step size scaling (
1/4/8/16/32/64) for farther chunks - LOD coverage logic avoids requesting lower quality if higher quality is already loaded/requested
- Full-detail chunks (
Full,step=1) use greedy face merging - Merges across planes with light bin constraints to reduce triangles/draw cost
- Far LOD can be packed to
PackedFarVertex(i16positions/uv, compact flags,u8color) - Greatly reduces far mesh memory and upload size
- Far packed chunks cached to disk:
target/mesh_cache/world_<world_id>/...
- Cache key includes coord + mode + step
- Binary format with magic header (
MSH3) - Only used for unedited chunks (dirty/override chunks bypass cache)
- Hot reload clears cache directory
- Three classes:
Edit(highest)NearFar
- Priority combines distance, view facing, and LOD quality
- Queue replacement logic keeps better/newer work and prevents stale tasks
- Per-tick request budget (
STREAM_REQUEST_TIME_BUDGET = 3 ms) - Per-tick apply budget (
APPLY_RESULTS_TIME_BUDGET = 4 ms) - Dirty fast lane when edited chunks are pending
- If FPS/TPS drop:
- lower request/apply/rebuild/pregen budgets
- reduce draw radius cap
- If performance recovers:
- budgets climb back up toward base values
- Chunks grouped into super-chunks of size
4x4x4chunks - One raw and one packed mesh buffer per super-chunk
- Slot-based in-place updates when capacity allows
- Rebuild only dirty super-chunks (distance-prioritized)
- Radius cull using draw distance
- Directional cull against camera forward + chunk bounding-sphere margin
- Extra horizon/underground cull for far terrain
- Visible set refreshed only when camera changes or periodic refresh needed
- Loaded chunk cap (
loaded_chunk_cap) - Mesh memory cap in MB (
mesh_memory_cap_mb) - Evicts farthest chunks when caps are exceeded
- Column max-height cache for stream planning
- Height/tree caches for block solidity queries during meshing
- CPU-side cached item atlas sampling for dropped item side tinting
- Dropped item caps + merge in-cell stacks
- Leaf decay BFS cap (
LEAF_DECAY_SEARCH_CAP) to avoid expensive canopy scans - Sneak edge-guard prevents accidental ledge stepping
Most tuning constants live in:
src/main.rs(radii, budgets, worker count, caps)src/app/streaming.rs(LOD policy, queue/apply budgets, request logic)src/world/worldgen.rs(terrain/cave/climate/tree parameters)src/world/mesher.rs(mesh mode behavior and AO/light work)
Notable current defaults:
CHUNK_SIZE = 18- world width in normal mode:
1000 x 1000chunks - base render radius:
512(dynamic with altitude) - base draw radius:
192 - LOD near/mid radii:
16 / 32chunks - mesh memory cap:
4096 MB
src/texturing/atlas_gen.py:
- scans
textures_blocks/andtextures_items/ - file names must start with numeric prefix (
<index>_name.pngor.tga) - packs 16x16 tiles into generated atlases:
src/texturing/atlas_output/atls_blocks.pngsrc/texturing/atlas_output/atlas_items.png
Atlases are regenerated automatically on startup only if source files are newer.
- No persistent world save/load for edited blocks yet (edits are runtime/session state)
- Single-player only
- No networking, entities/AI, or chunk disk persistence beyond far mesh cache
MIT (LICENSE)