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"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -232,6 +238,8 @@ dependencies = [
|
|||
"ash-window",
|
||||
"raw-window-handle",
|
||||
"renderer",
|
||||
"serde_json",
|
||||
"shared",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"winit",
|
||||
|
|
@ -381,6 +389,12 @@ dependencies = [
|
|||
"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]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
|
|
@ -460,6 +474,12 @@ dependencies = [
|
|||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.29.3"
|
||||
|
|
@ -504,6 +524,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.22.4"
|
||||
|
|
@ -699,6 +725,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
|
|
@ -708,6 +745,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "num_enum"
|
||||
version = "0.7.6"
|
||||
|
|
@ -1103,6 +1149,30 @@ version = "5.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.6.2"
|
||||
|
|
@ -1163,7 +1233,7 @@ dependencies = [
|
|||
"ash",
|
||||
"ash-window",
|
||||
"bytemuck",
|
||||
"glam",
|
||||
"glam 0.29.3",
|
||||
"gpu-allocator",
|
||||
"raw-window-handle",
|
||||
"thiserror 2.0.18",
|
||||
|
|
@ -1256,6 +1326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1278,10 +1349,25 @@ dependencies = [
|
|||
"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]]
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"shared",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
|
@ -1298,6 +1384,13 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"fastrand",
|
||||
"glam 0.27.0",
|
||||
"noise",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
|
|
@ -2135,3 +2228,9 @@ dependencies = [
|
|||
"quote",
|
||||
"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
|
||||
|
||||
[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
|
||||
//! the client and the server.
|
||||
|
||||
/// Adds two numbers together.
|
||||
///
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
pub mod world;
|
||||
pub mod generator;
|
||||
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