feat(shared): define base voxel world types and procedural generation
This commit is contained in:
parent
38077c980d
commit
4724e91281
101
Cargo.lock
generated
101
Cargo.lock
generated
|
|
@ -115,6 +115,12 @@ version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -232,6 +238,8 @@ dependencies = [
|
||||||
"ash-window",
|
"ash-window",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"renderer",
|
"renderer",
|
||||||
|
"serde_json",
|
||||||
|
"shared",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"winit",
|
"winit",
|
||||||
|
|
@ -381,6 +389,12 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
@ -460,6 +474,12 @@ dependencies = [
|
||||||
"wasip2",
|
"wasip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glam"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.29.3"
|
version = "0.29.3"
|
||||||
|
|
@ -504,6 +524,12 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.22.4"
|
version = "0.22.4"
|
||||||
|
|
@ -699,6 +725,17 @@ dependencies = [
|
||||||
"jni-sys 0.3.1",
|
"jni-sys 0.3.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "noise"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6da45c8333f2e152fc665d78a380be060eb84fad8ca4c9f7ac8ca29216cff0cc"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
"rand_xorshift",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.50.3"
|
version = "0.50.3"
|
||||||
|
|
@ -708,6 +745,15 @@ dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
|
@ -1103,6 +1149,30 @@ version = "5.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
|
@ -1163,7 +1233,7 @@ dependencies = [
|
||||||
"ash",
|
"ash",
|
||||||
"ash-window",
|
"ash-window",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"glam",
|
"glam 0.29.3",
|
||||||
"gpu-allocator",
|
"gpu-allocator",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
|
@ -1256,6 +1326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1278,10 +1349,25 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "server"
|
name = "server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"serde_json",
|
||||||
|
"shared",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
@ -1298,6 +1384,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shared"
|
name = "shared"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"fastrand",
|
||||||
|
"glam 0.27.0",
|
||||||
|
"noise",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
|
|
@ -2135,3 +2228,9 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,8 @@ edition = "2024"
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bytemuck = { version = "1.15", features = ["derive"] }
|
||||||
|
fastrand = "2.4.1"
|
||||||
|
glam = "0.27"
|
||||||
|
noise = "0.9"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
||||||
81
crates/shared/src/generator.rs
Normal file
81
crates/shared/src/generator.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
//! Procedural generation logic for the voxel world.
|
||||||
|
|
||||||
|
use noise::{NoiseFn, Perlin};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::world::{BlockId, CHUNK_SIZE, Chunk, ChunkPos};
|
||||||
|
|
||||||
|
/// Configuration parameters for deterministic world generation.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct WorldGenConfig {
|
||||||
|
/// The base height around which terrain features are generated.
|
||||||
|
pub base_height: u32,
|
||||||
|
/// The scale factor applied to the noise coordinates. Smaller values create wider features.
|
||||||
|
pub noise_scale: f64,
|
||||||
|
/// The block identifier used for the top layer of the terrain.
|
||||||
|
pub surface_block: BlockId,
|
||||||
|
/// The block identifier used for the layers immediately below the surface.
|
||||||
|
pub subsurface_block: BlockId,
|
||||||
|
/// The block identifier used for deep underground layers.
|
||||||
|
pub stone_block: BlockId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A deterministic terrain generator that produces voxel chunks.
|
||||||
|
pub struct VoxelGenerator {
|
||||||
|
/// The configuration parameters guiding the generation.
|
||||||
|
pub config: WorldGenConfig,
|
||||||
|
noise: Perlin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoxelGenerator {
|
||||||
|
/// Initializes a new voxel generator with the specified configuration and seed.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(config: WorldGenConfig, seed: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
noise: Perlin::new(seed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a complete voxel chunk for the specified position.
|
||||||
|
#[must_use]
|
||||||
|
#[allow(clippy::cast_precision_loss, clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
|
||||||
|
pub fn generate_chunk(&self, pos: ChunkPos) -> Chunk {
|
||||||
|
let mut chunk = Chunk::default();
|
||||||
|
|
||||||
|
for x in 0..CHUNK_SIZE {
|
||||||
|
for z in 0..CHUNK_SIZE {
|
||||||
|
let global_x = (pos.x * CHUNK_SIZE as i32) + x as i32;
|
||||||
|
let global_z = (pos.z * CHUNK_SIZE as i32) + z as i32;
|
||||||
|
|
||||||
|
let noise_val = self.noise.get([
|
||||||
|
f64::from(global_x) * self.config.noise_scale,
|
||||||
|
f64::from(global_z) * self.config.noise_scale,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let amplitude = 15.0;
|
||||||
|
|
||||||
|
let target_height = self.config.base_height as i32 + (noise_val * amplitude) as i32;
|
||||||
|
|
||||||
|
for y in 0..CHUNK_SIZE {
|
||||||
|
let global_y = (pos.y * CHUNK_SIZE as i32) + y as i32;
|
||||||
|
|
||||||
|
let block = if global_y > target_height {
|
||||||
|
BlockId::AIR
|
||||||
|
} else if global_y == target_height {
|
||||||
|
self.config.surface_block
|
||||||
|
} else if global_y > target_height - 3 {
|
||||||
|
self.config.subsurface_block
|
||||||
|
} else {
|
||||||
|
self.config.stone_block
|
||||||
|
};
|
||||||
|
|
||||||
|
if block != BlockId::AIR {
|
||||||
|
chunk.set(x, y, z, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,20 +3,5 @@
|
||||||
//! This crate contains data structures and constants that are used by both
|
//! This crate contains data structures and constants that are used by both
|
||||||
//! the client and the server.
|
//! the client and the server.
|
||||||
|
|
||||||
/// Adds two numbers together.
|
pub mod world;
|
||||||
///
|
pub mod generator;
|
||||||
/// This is a placeholder function for the initial crate setup.
|
|
||||||
pub fn add(left: u64, right: u64) -> u64 {
|
|
||||||
left + right
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
75
crates/shared/src/world.rs
Normal file
75
crates/shared/src/world.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
//! Core data structures representing the voxel world.
|
||||||
|
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// The size of a chunk along one axis in blocks.
|
||||||
|
pub const CHUNK_SIZE: usize = 32;
|
||||||
|
/// The total number of blocks within a single chunk.
|
||||||
|
pub const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
|
||||||
|
/// A unique identifier representing a type of block in the world.
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Pod, Zeroable,
|
||||||
|
)]
|
||||||
|
pub struct BlockId(pub u16);
|
||||||
|
|
||||||
|
impl BlockId {
|
||||||
|
/// The block identifier representing empty space.
|
||||||
|
pub const AIR: BlockId = BlockId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A spatial volume containing voxel data.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Chunk {
|
||||||
|
/// The array of blocks contained within the chunk.
|
||||||
|
pub blocks: Box<[BlockId]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
/// Calculates the one-dimensional index for a three-dimensional block coordinate.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn index(x: usize, y: usize, z: usize) -> usize {
|
||||||
|
(x * CHUNK_SIZE * CHUNK_SIZE) + (z * CHUNK_SIZE) + y
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the block identifier at the specified coordinates.
|
||||||
|
#[must_use]
|
||||||
|
pub fn get(&self, x: usize, y: usize, z: usize) -> BlockId {
|
||||||
|
self.blocks[Self::index(x, y, z)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the block identifier at the specified coordinates.
|
||||||
|
pub fn set(&mut self, x: usize, y: usize, z: usize, block: BlockId) {
|
||||||
|
self.blocks[Self::index(x, y, z)] = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chunk {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
blocks: vec![BlockId::AIR; CHUNK_VOLUME].into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The three-dimensional spatial coordinates of a chunk in the world.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct ChunkPos {
|
||||||
|
/// The X coordinate of the chunk.
|
||||||
|
pub x: i32,
|
||||||
|
/// The Y coordinate of the chunk.
|
||||||
|
pub y: i32,
|
||||||
|
/// The Z coordinate of the chunk.
|
||||||
|
pub z: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChunkPos {
|
||||||
|
/// Initializes a new chunk position.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
||||||
|
Self { x, y, z }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue