From c43cc5425f6afdbb5542ee6c607e1daea37be3ab Mon Sep 17 00:00:00 2001 From: Spectre Date: Tue, 11 Feb 2025 09:30:51 +0100 Subject: [PATCH] starting procedural generation --- Cargo.toml | 1 + TODO.md | 69 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 TODO.md diff --git a/Cargo.toml b/Cargo.toml index 80d8dda..493ff53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +noise = "0.9.0" diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..3d3302e --- /dev/null +++ b/TODO.md @@ -0,0 +1,69 @@ +# TODO List for a Minecraft-Like Terrain Generator in Rust + +## 1. Define Objectives and Requirements +- **Features:** + - Global terrain generation (overall elevation) + - Local detail (surface variations) + - Biome transitions and optionally cave systems +- **Technical Constraints:** + - Chunk dimensions (e.g., 16×16 blocks horizontally with a fixed vertical height) + - Memory management (chunk caching, on-demand generation) + - Server integration (protocol, networking, etc.) +- **Output Format:** + - How to represent the world (e.g., a 3D array of block types) + - Block types (Air, Grass, Dirt, Stone, Water, etc.) + +## 2. Research and Select Noise Algorithms +- Study noise algorithms such as Perlin and Simplex. +- Understand Fractal Brownian Motion (fBm) to combine multiple octaves. +- Define parameters like frequency, amplitude, persistence, number of octaves, and scaling factors. + +## 3. Set Up Your Rust Project +- Create a new project with Cargo. +- Add necessary dependencies in `Cargo.toml` (e.g., the `noise` crate). +- Set up version control (Git). + +## 4. Implement Basic Noise Generation +- Write a simple prototype to generate noise values. +- Use a scaling factor to avoid sampling only on integer coordinates. +- Test with a fixed seed for reproducibility. + +## 5. Implement Fractal Brownian Motion (fBm) +- Create a function to combine multiple noise octaves. +- Adjust parameters (octaves, persistence, etc.) and test the results. + +## 6. Map Noise to Terrain Height +- Convert normalized noise values (e.g., from -1 to 1) to block heights. +- Define a mapping strategy (for example, scaling to a maximum height). + +## 7. Design the Chunk Data Structure +- Decide on chunk dimensions (e.g., 16×16×128). +- Create a simple structure to represent blocks (using enums or similar). + +## 8. Generate Chunks Based on Noise +- For each (x, z) coordinate in a chunk: + - Calculate the noise value. + - Map it to a terrain height. + - Fill in blocks based on the height (e.g., surface, sub-surface, stone). +- Keep the code modular and avoid overcomplicating early on. + +## 9. Test and Visualize the Generated Terrain +- Write unit tests for noise functions and terrain mapping. +- Create a simple visualization (e.g., a 2D height map printed to the console or exporting data for external tools). +- Verify that parameter adjustments produce the expected variations. + +## 10. Integrate the Generator into Your Server Architecture +- Implement on-demand chunk generation as the player moves. +- Cache generated chunks (in memory or on disk) to avoid re-computation. +- Consider multithreading or asynchronous processing for parallel generation. + +## 11. Optimize and Refine +- Profile the terrain generation for performance bottlenecks. +- Fine-tune noise parameters and mapping logic. +- Plan future enhancements (biomes, caves, advanced block types). + +## 12. Document and Maintain the Codebase +- Document your functions, parameters, and overall architecture. +- Use version control to track changes and manage iterative improvements. +- Keep your code modular for easy future enhancements. + diff --git a/src/main.rs b/src/main.rs index e7a11a9..1fa79c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,80 @@ -fn main() { - println!("Hello, world!"); +// Example Code + +use noise::{NoiseFn, Perlin}; +#[derive(Debug, Clone, Copy)] +enum BlockType { + Air, + Grass, + Dirt, + Stone, + Water, +} +const CHUNK_SIZE: usize = 16; +const CHUNK_HEIGHT: usize = 128; + +type Chunk = Vec>>; + +fn main() { + let perlin = Perlin::new(42); + let scale = 0.1; + let x = 10.0 * scale; + let y = 20.0 * scale; + let noise_value = perlin.get([x, y]); + println!("Noise value: {}", noise_value); +} +fn fbm(perlin: &Perlin, x: f64, y: f64, octaves: u32, persistence: f64) -> f64 { + let mut total = 0.0; + let mut amplitude = 1.0; + let mut frequency = 1.0; + let mut max_value = 0.0; + + for _ in 0..octaves { + total += perlin.get([x * frequency, y * frequency]) * amplitude; + max_value += amplitude; + amplitude *= persistence; + frequency *= 2.0; + } + total / max_value +} + +fn map_noise_to_height(noise_value: f64) -> usize { + let normalized = (noise_value + 1.0) / 2.0; // map from [-1, 1] to [0, 1] + let max_height = 128; + (normalized * max_height as f64) as usize +} +fn generate_chunk(perlin: &Perlin, chunk_x: i32, chunk_z: i32) -> Chunk { + let mut chunk = vec![vec![vec![BlockType::Air; CHUNK_HEIGHT]; CHUNK_SIZE]; CHUNK_SIZE]; + let scale = 0.1; + + for local_x in 0..CHUNK_SIZE { + for local_z in 0..CHUNK_SIZE { + let world_x = chunk_x * CHUNK_SIZE as i32 + local_x as i32; + let world_z = chunk_z * CHUNK_SIZE as i32 + local_z as i32; + let noise_value = fbm( + perlin, + world_x as f64 * scale, + world_z as f64 * scale, + 4, + 0.5, + ); + let height = map_noise_to_height(noise_value); + + for y in 0..CHUNK_HEIGHT { + if y > height { + chunk[local_x][local_z][y] = if y < 64 { + BlockType::Water + } else { + BlockType::Air + }; + } else if y == height { + chunk[local_x][local_z][y] = BlockType::Grass; + } else if y > height.saturating_sub(3) { + chunk[local_x][local_z][y] = BlockType::Dirt; + } else { + chunk[local_x][local_z][y] = BlockType::Stone; + } + } + } + } + chunk }