3 Commits

Author SHA1 Message Date
c1f240dd32 windows build script, runtime impl 2026-04-15 10:54:58 +03:30
ae7d82a542 readme update 2026-04-15 10:54:08 +03:30
a1acd221a0 readme 2026-04-14 21:20:36 +03:30
4 changed files with 296 additions and 320 deletions

370
README.md
View File

@@ -4,87 +4,94 @@
<img src="./logo-cropped.svg" alt="FastVoxel logo" width="180" /> <img src="./logo-cropped.svg" alt="FastVoxel logo" width="180" />
</p> </p>
**FastVoxel** is a **Voxel Engine** for Godot written as a Rust GDExtension. The goal is basically: generate chunks quickly, keep voxel storage lightweight, and build meshes at runtime for blocky worlds with textured materials. **FastVoxel** is a voxel engine for Godot written in Rust using GDExtension.
Main goals are:
This repo contains the engine/GDExtension side of the project. - Generate chunks fast enough (It's already faster than minecraft, but not because of my good code, it's actually minecraft's fault)
- store voxels using almost no memory (because RAM prices blah blah blah.. and also I enjoy suffering so why not spend 1k hours optimizing this thing?),
- and build meshes at runtime for blocky worlds with textured materials.
It's currently being refactored constantly, so some internal structure may change, but the main ideas and APIs are stable enough to explain here. (or Are They?) This repo contains the engine side of the project.
Yes, things are constantly being refactored.
And yes, I still have no idea what I'm doing, so take that for granted and pray to the compiler.
## Highlights ## Highlights
- Rust-based Godot 4 GDExtension - Rust-based Godot 4 GDExtension
- chunked voxel mesh pipeline - Chunked voxel mesh pipeline
- bit-packed voxel storage (solid / air) - Bit-packed voxel storage (solid/air, liquid, whatever)
- procedural terrain using `fastnoise-lite` - Procedural terrain via `fastnoise-lite`
- chunk streaming around the player - Chunk streaming around the player
- runtime cube meshing with texture atlas support - Runtime cube meshing with texture atlas support (up to 1024x1024)
- Godot editor resources for config + voxel registry - Godot editor resources for config + voxel registry
## Soon: ## Soon
- Multi-Threaded chunk meshing and world generation using a `Work Stealing` Thread pool with divideandconquer parallelism. - Multi-threaded chunk meshing & generation using a **work stealing** thread pool with divideandconquer parallelism.
(Some C dev just segfaulted reading that.)
## Screenshots ## Screenshots
<p align="center"> <p align="center">
<img src="docs/screenshots/colors.png" alt="Albedo Vertex color" width="960" /> <img src="docs/screenshots/colors.png" width="960" />
</p> </p>
<p align="center"> <p align="center">
<img src="docs/screenshots/heightmap-noise.png" alt="Heightmap Surface Noise" width="960" /> <img src="docs/screenshots/heightmap-noise.png" width="960" />
</p> </p>
<p align="center"> <p align="center">
<img src="docs/screenshots/normal-map.png" alt="Albedo Texture + Normal map" width="960" /> <img src="docs/screenshots/normal-map.png" width="960" />
</p> </p>
Right now the repo only contains branding assets. I haven't added gameplay screenshots yet. The repo mostly contains branding assets. More gameplay screenshots coming soon, assuming I stop rewriting the engine every Tuesday.
If you want to add screenshots that show directly on the README, the easiest thing is to just drop images somewhere like: To add your own: put them in
``` ```
docs/screenshots/WHATEVER.png docs/screenshots
``` ```
Then embed them in the README like: then embed with
```md ```
![WHATEVER](docs/screenshots/WHATEVER.png) ![image](docs/screenshots/my_face.png)
``` ```
## What FastVoxel Actually Does ## What FastVoxel Actually Does
The engine generates a voxel world using chunk columns. The engine generates a voxel world using chunk columns.
Rough pipeline looks like this: The surprisingly simple pipeline:
1. A `SurfaceGenerator` decides if a voxel is solid or not. 1. `SurfaceGenerator` decides if a voxel is solid.
2. A `ChunkColumn` holds a vertical stack of chunks. 2. `ChunkColumn` stacks chunks vertically.
3. Each `Chunk` stores voxel occupancy in a compact bit-packed format. 3. Each `Chunk` stores voxels in bitpacked `u32`s.
4. A `Mesher` converts visible voxel faces into triangle meshes. 4. `Mesher` converts only the exposed voxel faces into triangles.
5. A `Renderer` turns those meshes into Godot `MeshInstance3D` nodes. 5. `Renderer` throws those triangles into Godot's scene tree.
6. A world node updates loaded terrain as the player moves around. 6. The world updates chunks as the player moves around.
The idea is to keep generation, storage, and meshing fairly modular so different strategies can be swapped in later. Everything is modular because future "me", will absolutely regret today's design decisions and might decide to get back into the cave and rewrite the engine again.
## What FastVoxel "Doesn't" do ## What FastVoxel _Doesn't_ Do (Yet)
1. The engine doesn't use compute shaders or any kind of GPU accelerated meshing algorithem. 1. No compute shaders or GPU meshing. This is an OS-Thread party.
2. The engine doesn't bake per-vertex AO on greedy mesher (because it complicates things and I'm running on two brain cells at the moment) 2. No pervertex AO on the greedy mesher.
3. The engine doesn't cull the neighboring faces because it has no access to the data of adjacent chunks. (I'm working on it). (Because it's complicated and my last functioning brain cell is currently writing this.)
3. Doesn't cull neighboring chunk faces yet because chunks don't talk to each other.
(They're socially anxious.)
## Core Concepts ## Core Concepts
### Chunked world layout ### Chunked world layout
Terrain is divided into columns of chunks. Terrain = columns of chunks.
Current defaults: Defaults:
- `CHUNK_SIZE = 32` - chunk size: `32 × 32 × 32`
- each chunk = `32 × 32 × 32` voxels - chunks load/unload based on render distance (default: 8)
- columns stack multiple chunks vertically like a hamburger
- chunk loading/unloading happens around the player based on render distance
Relevant files: Relevant files:
@@ -94,291 +101,32 @@ Relevant files:
### Bit-packed voxel storage ### Bit-packed voxel storage
Instead of storing a struct per voxel, chunks store occupancy using packed `u32` blocks. since a voxel is represented by a single **bit**, a single `u32` can store the state of `32 voxels`. Instead of storing a big struct per voxel, we cram voxels into `u32`s.
right now a voxel is basically: Each voxel = **1 bit**:
- `0` -> air - `0` => air
- `1` -> solid - `1` => solid
This keeps memory usage low and makes lookups very cheap. A single `u32` stores 32 voxels (very memory-friendly).
For a 32 × 32 × 32 chunk, each horizontal row of 32 voxels fits into a single u32. Which means a layer of the chunk requires 32 u32s, and the entire chunk can be stored as a 32 × 32 array of u32s. Each u32 corresponds to one row of voxels along the X axis. A 32×32×32 chunk:
This compact memory layout works well for terrain meshing stage, where the main concern is to quickly check whether voxels are empty or solid. because we use a single bit to represent a voxel instead of a full struct or enum or whatever, the implementation is 32× more memory efficient (no sh- sherlock). - each row (32 voxels) = `u32`
- each layer = 32 rows = 32 `u32`s
- whole chunk = 32 × 32 = **1024 `u32`s**
Material differences are currently handled at the meshing/registry layer instead of inside the voxel storage itself. This layout is amazing for meshing because checking solid/air is basically:
### Surface generation
The default generator is `SimpleSurfaceGenerator`.
It uses `fastnoise-lite` to create a heightmap and then fills voxels from the bottom up to that height.
Important files:
- `src/generation/generator.rs`
- `src/generation/simple_heightmap.rs`
The generation system is trait-based so other terrain approaches can be plugged in later.
### Meshing
There are a few different meshing implementations right now.
- `CullingMesher` basic visible-face meshing
- `TexturedMesher` cube meshing with texture atlas support
- `BinaryGreedyMesher` more optimization-focused experiment
Relevant files:
- `src/meshing/mesher.rs`
- `src/meshing/textured_mesher.rs`
- `src/meshing/binary_greedy_mesher.rs`
The textured mesher is currently the most useful one if you're aiming for something Minecraft-like since it supports per-face UV lookup via a voxel registry.
### Godot integration
The engine exposes a few classes/resources to Godot via GDExtension.
Main ones:
- `WorldConfig` terrain + generation settings
- `VoxelRegistry` describes voxel types and atlas tiles
- `World` runtime node responsible for generation and updates
- `WorldPlugin` editor-side plugin hooks
Files:
- `src/editor/world_config.rs`
- `src/editor/voxel_registry.rs`
- `src/editor/world_node.rs`
- `src/editor/world_plugin.rs`
## Project Structure
``` ```
src/ bit = (row >> x) & 1
├── chunk/ # chunk data, columns, chunk manager, mesh buffers
├── editor/ # Godot resources, world node, editor plugin
├── generation/ # generator traits + procedural terrain generation
├── meshing/ # meshing strategies
├── rendering/ # converting mesh data to Godot meshes
├── terrain/ # higher-level terrain experiments / refactor work
└── voxel/ # voxel registry + earlier voxel abstractions
``` ```
## Build and Install Since we use 1 bit instead of a whole struct, one could argu the memory usage is ~32× lower.
Important, because RAM now costs $800 for 16 GB. that's like fifty bucks per gigabyte.
### Requirements Materials are handled at the meshing/registry layer, not in voxel storage.
You need:
- Rust toolchain
- a Godot 4 project set up for GDExtension
- optionally a sibling Godot project if you want to use the provided `build.sh`
### Build the extension
```
cargo build --release
```
### Helper build script
There's a small `build.sh` script that builds the Rust library and copies it into a Godot project.
By default it expects a project at:
```
../minekoloft
```
It copies the Linux shared library to:
```
../minekoloft/addons/fastvoxel/bin/linux
```
Run it with:
```
./build.sh
```
If your Godot project is somewhere else, just edit the `GODOT_PROJECT` path in the script.
## Using It in Godot
Typical workflow looks like this:
1. build the Rust extension
2. copy/install the addon into your Godot project
3. create a `WorldConfig` resource
4. create a `VoxelRegistry` resource
5. add a `World` node to a scene
6. assign the config and registry in the inspector
7. trigger terrain generation
8. call the update method each frame with the player position
### WorldConfig
`WorldConfig` stores generation settings like:
- `chunk_size`
- `render_distance`
- `worker_threads`
- `seed`
- `frequency`
- `terrain_height`
Default values in the code are roughly:
- chunk size: `Vector3i(32, 32, 32)`
- render distance: `8`
- worker threads: `4`
- seed: `1234`
- frequency: `0.03`
- terrain height: `6`
These are just meant for quick test worlds.
### VoxelRegistry
`VoxelRegistry` is a Godot `Resource` containing voxel model definitions.
Each `VoxelModel` can specify:
- voxel type
- atlas size
- tile index for each cube face
Faces supported:
- left
- right
- bottom
- top
- back
- front
The mesher uses this to assign UVs when building meshes.
### Runtime API
The intended `World` node API currently includes:
- `generate_terrain()`
- `regenerate_terrain()`
- `clear_terrain()`
- `update_from_gdscript(player_world_pos: Vector3)`
Example usage in GDScript:
```gdscript
@onready var world = $World
@onready var player = $Player
func _ready() -> void:
world.generate_terrain()
func _process(_delta: float) -> void:
world.update_from_gdscript(player.global_position)
```
## Engine-Level API Notes
### Generation trait
Terrain generation is abstracted behind `SurfaceGenerator`.
```rust
pub trait SurfaceGenerator: Send + Sync {
fn generate(&self, column: &mut ChunkColumn);
fn sample_voxel(&self, pos: Vector3i) -> bool;
}
```
This makes it easier to experiment with:
- different noise algorithms
- caves
- biome systems
- deterministic seeds
- threaded generation later on
### Mesher trait
Meshing also uses a trait interface.
```rust
pub trait Mesher: Send + Sync {
fn generate_mesh(&self, chunk: &Chunk, mesh: &mut ChunkMesh);
fn generate_mesh_with_registry(
&self,
chunk: &Chunk,
registry: &VoxelRegistry,
mesh: &mut ChunkMesh,
);
fn get_name(&self) -> &str;
}
```
Some meshers only need occupancy data, while others need material/registry info.
Keeping this separated makes it easier to experiment with meshing strategies without touching world management code.
### ChunkManager
`ChunkManager` is responsible for most of the runtime work:
- managing chunk column lifetimes
- generating terrain around the player
- unloading distant chunks
- triggering meshing
- submitting meshes to the renderer
- handing mesh instances back to the scene tree
So it basically sits between generation, meshing, and rendering.
## Current State
The project is mid-development and some systems are being refactored (mainly around the world/terrain layers).
But the overall direction is pretty clear:
- compact chunk storage
- trait-based generation
- pluggable meshing
- Godot editor resources
- runtime chunk streaming
## Why This README Exists
Mostly so the repo has an actual front page explaining:
- what the project is
- how it works
- how to build it
- where to start reading the code
Wiki pages tend to be less visible when someone first lands on the repository.
## Possible Future Work
Some things likely coming next:
- finishing the current terrain/world refactor
- adding real gameplay screenshots or gifs
- stabilizing the Godot-facing API
- multithreaded chunk generation **(GOD HELP)**
- more voxel/material data in storage
- improved greedy meshing and cross-chunk face handling
- a small example Godot project using the addon
## License ## License
This project is licensed under the MIT License. MIT License. Do whatever, just don't blame me.
See the `LICENSE` file for the full text.

View File

@@ -4,25 +4,25 @@
set -e set -e
# Configuration # Configuration
GODOT_PROJECT="../minekoloft" # relative path to Godot project GODOT_PROJECT="../minekoloft"
LIB_NAME="fastvoxel" # your library name LIB_NAME="fastvoxel"
DEST_LINUX="$GODOT_PROJECT/addons/fastvoxel/bin/linux" DEST_LINUX="$GODOT_PROJECT/addons/fastvoxel/bin/linux"
DEST_WINDOWS="$GODOT_PROJECT/addons/fastvoxel/bin/windows" DEST_WINDOWS="$GODOT_PROJECT/addons/fastvoxel/bin/windows"
# Build for Linux (native) # Build for Linux (native)
echo "Building for Linux (native)..." echo "Building for Linux (native)..."
cargo build --release cargo build --release --target x86_64-unknown-linux-gnu
# Copy the shared library # Copy the shared library
TMP="$DEST_LINUX/lib${LIB_NAME}.so.tmp" TMP="$DEST_LINUX/lib${LIB_NAME}.so.tmp"
cp "target/release/lib${LIB_NAME}.so" "$TMP" cp "target/x86_64-unknown-linux-gnu/release/lib${LIB_NAME}.so" "$TMP"
mv "$TMP" "$DEST_LINUX/lib${LIB_NAME}.so" mv "$TMP" "$DEST_LINUX/lib${LIB_NAME}.so"
# Optional: Cross compile for Windows (if needed) # Optional: Cross compile for Windows (if needed)
# Uncomment the following lines if you have the Windows target installed # Uncomment the following lines if you have the Windows target installed
# echo "Building for Windows (cross-compile)..." echo "Building for Windows (cross-compile)..."
# cargo build --release --target x86_64-pc-windows-gnu cargo build --release --target x86_64-pc-windows-gnu
# mkdir -p "$DEST_WINDOWS" mkdir -p "$DEST_WINDOWS"
# cp "target/x86_64-pc-windows-gnu/release/${LIB_NAME}.dll" "$DEST_WINDOWS/" cp "target/x86_64-pc-windows-gnu/release/${LIB_NAME}.dll" "$DEST_WINDOWS/"
# echo "Copied to $DEST_WINDOWS" echo "Copied to $DEST_WINDOWS"
echo "Done." echo "Done."

220
docs/INTRODUCTION.MD Normal file
View File

@@ -0,0 +1,220 @@
# 📄 Introduction into FastVoxel
## Surface generation
Default generator: `SimpleSurfaceGenerator`
It uses `fastnoise-lite` to create a heightmap, then fills voxel columns from the bottom up.
Files:
- `src/generation/generator.rs`
- `src/generation/simple_heightmap.rs`
Generation uses traits, so you can plug in whatever mumbo jumbo nonsense you want later: caves, biomes, multiverse, whatever.
## Meshing
Multiple meshers exist because one was simply not enough to satisfy my unhealthy obsession with "doing it right this time."
[engine development perfectionism disorder. there's no cure.]
- `CullingMesher` basic visible-face meshing
- `TexturedMesher` UV-aware cube meshing (Minecraft basically)
- `BinaryGreedyMesher` experimental optimization attempt
Files:
- `src/meshing/mesher.rs`
- `src/meshing/textured_mesher.rs`
- `src/meshing/binary_greedy_mesher.rs`
The textured mesher is currently the most practical one because it respects UVs and etc. simply because you can define the material for it.
It's not optimized though.
## Godot integration
The engine exposes several handy APIs for the editor:
- `WorldConfig` generation settings
- `VoxelRegistry` voxel definitions and atlas stuff
- `World` main terrain node
- `WorldPlugin` editor plugin hooks
Files:
- `src/editor/world_config.rs`
- `src/editor/voxel_registry.rs`
- `src/editor/world_node.rs`
- `src/editor/world_plugin.rs`
## Project Structure
```
src/
├── chunk/ # chunk data, columns, manager
├── editor/ # Godot resources + editor plugin
├── generation/ # terrain generators
├── meshing/ # meshing algorithems
├── rendering/ # mesh to Godot conversion
├── terrain/ # I'm not so sure
└── voxel/ # registry + older voxel logic
```
## Build and Install
Requirements:
- Rust toolchain
- Godot 4 project with GDExtension
- Optional sibling project for `build.sh` to work.
### Build the extension
```
cargo build --release
```
### Helper script
`build.sh` builds & copies the extension to:
```
../minekoloft/addons/fastvoxel/bin/linux
```
Run with:
```
./build.sh
```
If your project is elsewhere, edit the path.
## Using It in Godot
Typical use:
1. build the Rust extension
2. install addon in your Godot project
3. create `WorldConfig`
4. create `VoxelRegistry`
5. add `World` node
6. assign config + registry
7. generate terrain
8. Call the "update with player position" API every frame. Spam it. It returns early and won't yell at you.
### WorldConfig
Contains:
- chunk_size
- render_distance
- worker_threads
- seed
- frequency
- terrain_height
Defaults:
- chunk: `32,32,32`
- render dist: `8`
- threads: `4`
- seed: `1234`
- freq: `0.03`
- terrain height: `6`
### VoxelRegistry
Each voxel defines:
- type
- atlas size
- tile index per face (top, bottom, left, right, front, back)
Mesher uses this for UVs.
### Runtime API
World node provides:
- `generate_terrain()`
- `regenerate_terrain()`
- `clear_terrain()`
- `update_from_gdscript(player_world_pos)`
GDScript example:
```gdscript
@onready var world = $World
@onready var player = $Player
func _ready():
world.generate_terrain()
func _process(_delta):
world.update_from_gdscript(player.global_position)
```
## Engine-Level API Notes
### Generation trait
```rust
pub trait SurfaceGenerator: Send + Sync {
fn generate(&self, column: &mut ChunkColumn);
fn sample_voxel(&self, pos: Vector3i) -> bool;
}
```
### Mesher trait
```rust
pub trait Mesher: Send + Sync {
fn generate_mesh(&self, chunk: &Chunk, mesh: &mut ChunkMesh);
fn generate_mesh_with_registry(
&self,
chunk: &Chunk,
registry: &VoxelRegistry,
mesh: &mut ChunkMesh,
);
fn get_name(&self) -> &str;
}
```
### ChunkManager
Handles:
- chunk lifetime
- generation around player
- unloading distant chunks
- meshing
- sending meshes to renderer
- attaching instances to scene
Basically the project's overworked intern.
## Current State
Mid-development, active refactors, might panic on unwrap.
Direction is clear though:
- compact chunk storage
- trait-based generation
- modular meshing
- Godot resource workflow
- runtime streaming
## Why This Document Exists
Because why not? If you actually wanted to read the code, you wouldn't be here.
## Future Work
- finish terrain/world refactor
- more screenshots
- stable API for godot
- multithreaded chunk gen (**GOD HELP**)
- Better Godot material integration for voxel meshes (so things look less like programmer art).
- fix the greedy meshing
- sample Godot project

View File

@@ -2,6 +2,14 @@ use std::collections::HashMap;
use crate::{chunk::Chunk, runtime::types::WorldConfig}; use crate::{chunk::Chunk, runtime::types::WorldConfig};
pub enum RuntimeState {
Idle,
ReadingStorage,
Generating,
Meshing,
SavingToStorage,
}
pub struct Runtime { pub struct Runtime {
chunks: HashMap<(i32, i32), Chunk>, chunks: HashMap<(i32, i32), Chunk>,
// renderer: Renderer, // renderer: Renderer,