From d8a0bf8fc27d63eb9c9f1cb47c66333e41a76233 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Thu, 8 Dec 2022 21:33:30 +0200 Subject: [PATCH] feat: added perlin noise chunk generation --- .vscode/launch.json | 47 ++++++++++++++++ Cargo.lock | 97 +++++++++++++++++++++++++++++++--- Cargo.toml | 1 + src/game.rs | 77 +++++++++++---------------- src/game/camera.rs | 10 +++- src/game/chunk.rs | 4 +- src/terrain2d.rs | 28 +++++----- src/terrain2d/chunk2d.rs | 47 +++++++++++----- src/terrain2d/terrain_gen2d.rs | 46 ++++++++++++++++ 9 files changed, 275 insertions(+), 82 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/terrain2d/terrain_gen2d.rs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ea1672b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [{ + "type": "lldb", + "request": "launch", + "name": "Debug executable 'kuilu'", + "cargo": { + "args": [ + "build", + "--bin=kuilu", + "--package=kuilu" + ], + "filter": { + "name": "kuilu", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "LD_LIBRARY_PATH": "${workspaceFolder}/target/debug/deps:${env:HOME}/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib", + } + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'kuilu'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=kuilu", + "--package=kuilu" + ], + "filter": { + "name": "kuilu", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "LD_LIBRARY_PATH": "${workspaceFolder}/target/debug/deps:${env:HOME}/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib", + } + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 22a781f..564875d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,7 +36,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom", + "getrandom 0.2.7", "once_cell", "version_check", ] @@ -48,7 +48,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.7", "once_cell", "version_check", ] @@ -879,7 +879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7473635355a99fcf7181091a2ac11df03561706b1696cb0cc72e4ddd010571" dependencies = [ "ahash 0.7.6", - "getrandom", + "getrandom 0.2.7", "hashbrown", "instant", "tracing", @@ -1632,6 +1632,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.7" @@ -1641,7 +1652,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2062,6 +2073,7 @@ dependencies = [ "bevy-inspector-egui", "bevy_rapier2d", "lazy_static", + "noise", ] [[package]] @@ -2245,7 +2257,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -2437,6 +2449,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "noise" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba869e17168793186c10ca82c7079a4ffdeac4f1a7d9e755b9491c028180e40" +dependencies = [ + "num-traits", + "rand", + "rand_xorshift", +] + [[package]] name = "nom" version = "7.1.1" @@ -2796,6 +2819,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "pretty-type-name" version = "1.0.0" @@ -2843,6 +2872,56 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b" +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +dependencies = [ + "rand_core", +] + [[package]] name = "range-alloc" version = "0.1.2" @@ -3472,7 +3551,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ - "getrandom", + "getrandom 0.2.7", "serde", ] @@ -3511,6 +3590,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 63d3671..56856fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ bevy = { version = "0.9.0", features = ["dynamic"] } bevy-inspector-egui = "0.14.0" bevy_rapier2d = { path = "../bevy_rapier/bevy_rapier2d" } lazy_static = "1.4.0" +noise = "0.8.2" # Enable a small amount of optimization in debug mode [profile.dev] diff --git a/src/game.rs b/src/game.rs index 16a3104..ed73bd4 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,13 +1,17 @@ -use bevy::prelude::*; +use bevy::{input::mouse::MouseWheel, prelude::*}; use bevy_inspector_egui::*; use bevy_rapier2d::prelude::*; use crate::{ - terrain2d::{Chunk2D, Terrain2D, Terrain2DPlugin}, + terrain2d::{Chunk2D, Terrain2D, Terrain2DPlugin, TerrainGen2D}, util::Vector2I, }; -use self::{camera::GameCameraPlugin, kinematic::KinematicPlugin, player::PlayerPlugin}; +use self::{ + camera::{CameraFollow, GameCameraPlugin}, + kinematic::KinematicPlugin, + player::PlayerPlugin, +}; pub mod camera; pub mod chunk; @@ -24,58 +28,39 @@ pub fn init() { .add_plugin(GameCameraPlugin) .add_plugin(Terrain2DPlugin) // .add_plugin(PlayerPlugin) - // .add_startup_system(setup_debug_ground) .add_startup_system(setup_debug_terrain) + .add_startup_system(setup_debug_camera) + .add_system(debug_controls) .run(); } -fn setup_debug_terrain(mut terrain: ResMut) { - for y in 0..32 { - for x in 0..8 { - terrain.add_chunk(Vector2I { x, y }, Chunk2D::new_circle()); +fn debug_controls( + mut query: Query<&mut Transform, With>, + mut events: EventReader, +) { + for event in events.iter() { + for mut transform in query.iter_mut() { + transform.translation += Vec3::new(0.0, event.y, 0.0) * 30.0; } } } -fn setup_debug_ground(mut commands: Commands) { - // Static ground +fn setup_debug_camera(mut commands: Commands) { commands - .spawn(()) - .insert(Name::new("Ground")) - .insert(Collider::cuboid(40.0, 25.0)) - .insert(SpriteBundle { - sprite: Sprite { - color: Color::rgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(80.0, 50.0)), - ..default() - }, - transform: Transform::from_xyz(-100.0, -250.0, 0.0), - ..default() - }); - commands - .spawn(()) - .insert(Name::new("Ground")) - .insert(Collider::cuboid(40.0, 25.0)) - .insert(SpriteBundle { - sprite: Sprite { - color: Color::rgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(80.0, 50.0)), - ..default() - }, - transform: Transform::from_xyz(100.0, -200.0, 0.0), - ..default() - }); - commands - .spawn(()) - .insert(Name::new("Ground")) - .insert(Collider::cuboid(100.0, 25.0)) - .insert(SpriteBundle { - sprite: Sprite { - color: Color::rgb(0.25, 0.25, 0.75), - custom_size: Some(Vec2::new(200.0, 50.0)), - ..default() - }, - transform: Transform::from_xyz(0.0, -300.0, 0.0), + .spawn(TransformBundle::default()) + .insert(Name::new("Debug Camera")) + .insert(CameraFollow { + movement: camera::FollowMovement::Smooth(10.0), ..default() }); } + +fn setup_debug_terrain(mut terrain: ResMut) { + let terrain_gen = TerrainGen2D::new(432678); + for y in 0..32 { + for x in 0..8 { + let position = Vector2I { x, y }; + terrain.add_chunk(position, terrain_gen.gen_chunk(&position)); + } + } +} diff --git a/src/game/camera.rs b/src/game/camera.rs index 29ec458..b05523e 100644 --- a/src/game/camera.rs +++ b/src/game/camera.rs @@ -1,4 +1,7 @@ -use bevy::{prelude::*, render::camera::{ScalingMode, WindowOrigin}}; +use bevy::{ + prelude::*, + render::camera::{ScalingMode, WindowOrigin}, +}; use bevy_inspector_egui::{Inspectable, RegisterInspectable}; use crate::util::{move_towards_vec3, vec3_lerp}; @@ -42,6 +45,11 @@ fn camera_setup(mut commands: Commands) { window_origin: WindowOrigin::BottomLeft, ..default() }, + camera_2d: Camera2d { + clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Custom( + Color::rgb(0.0, 0.0, 0.0), + ), + }, ..default() }, )); diff --git a/src/game/chunk.rs b/src/game/chunk.rs index ee8d57f..26bd244 100644 --- a/src/game/chunk.rs +++ b/src/game/chunk.rs @@ -1,13 +1,13 @@ use bevy::prelude::*; -use crate::terrain2d::ChunkIndex; +use crate::terrain2d::Chunk2DIndex; pub struct ChunkPlugin {} #[derive(Reflect, Component, Default)] #[reflect(Component)] pub struct Chunk { - pub index: ChunkIndex, + pub index: Chunk2DIndex, } #[derive(Bundle)] diff --git a/src/terrain2d.rs b/src/terrain2d.rs index 62518d5..006e7d3 100644 --- a/src/terrain2d.rs +++ b/src/terrain2d.rs @@ -6,9 +6,11 @@ use std::collections::{ use bevy::prelude::*; mod chunk2d; +mod terrain_gen2d; mod texel2d; pub use chunk2d::*; +pub use terrain_gen2d::*; pub use texel2d::*; use crate::util::{math::*, Vector2I}; @@ -17,7 +19,7 @@ pub struct Terrain2DPlugin; impl Plugin for Terrain2DPlugin { fn build(&self, app: &mut App) { - app.register_type::() + app.register_type::() .insert_resource(Terrain2D::new()) .add_event::() .add_system(emit_terrain_events) @@ -41,14 +43,14 @@ fn emit_terrain_events( } pub enum TerrainEvent { - ChunkAdded(ChunkIndex), - ChunkRemoved(ChunkIndex), - TexelsUpdated(ChunkIndex, ChunkRect), + ChunkAdded(Chunk2DIndex), + ChunkRemoved(Chunk2DIndex), + TexelsUpdated(Chunk2DIndex, ChunkRect), } #[derive(Default, Resource)] pub struct Terrain2D { - chunk_map: HashMap, + chunk_map: HashMap, events: Vec, } @@ -60,29 +62,29 @@ impl Terrain2D { } } - pub fn add_chunk(&mut self, index: ChunkIndex, chunk: Chunk2D) { + pub fn add_chunk(&mut self, index: Chunk2DIndex, chunk: Chunk2D) { self.chunk_map.insert(index, chunk); self.events.push(TerrainEvent::ChunkAdded(index)) } - pub fn remove_chunk(&mut self, index: ChunkIndex) { + pub fn remove_chunk(&mut self, index: Chunk2DIndex) { self.events.push(TerrainEvent::ChunkRemoved(index)); self.chunk_map.remove(&index); } - pub fn chunk_iter(&self) -> Iter { + pub fn chunk_iter(&self) -> Iter { self.chunk_map.iter() } - pub fn chunk_iter_mut(&mut self) -> IterMut { + pub fn chunk_iter_mut(&mut self) -> IterMut { self.chunk_map.iter_mut() } - pub fn index_to_chunk(&self, index: &ChunkIndex) -> Option<&Chunk2D> { + pub fn index_to_chunk(&self, index: &Chunk2DIndex) -> Option<&Chunk2D> { self.chunk_map.get(index) } - pub fn index_to_chunk_mut(&mut self, index: &ChunkIndex) -> Option<&mut Chunk2D> { + pub fn index_to_chunk_mut(&mut self, index: &Chunk2DIndex) -> Option<&mut Chunk2D> { self.chunk_map.get_mut(index) } @@ -146,13 +148,13 @@ pub fn global_to_local(position: &Vector2I) -> Vector2I { } } -pub fn global_to_chunk_index(position: &Vector2I) -> ChunkIndex { +pub fn global_to_chunk_index(position: &Vector2I) -> Chunk2DIndex { Vector2I { x: wrapping_quotient(position.x, Chunk2D::SIZE.x), y: wrapping_quotient(position.y, Chunk2D::SIZE.y), } } -pub fn chunk_index_to_global(chunk_pos: &ChunkIndex) -> Vector2I { +pub fn chunk_index_to_global(chunk_pos: &Chunk2DIndex) -> Vector2I { *chunk_pos * Chunk2D::SIZE } diff --git a/src/terrain2d/chunk2d.rs b/src/terrain2d/chunk2d.rs index 4520502..666bda9 100644 --- a/src/terrain2d/chunk2d.rs +++ b/src/terrain2d/chunk2d.rs @@ -2,31 +2,37 @@ use std::collections::HashMap; use super::{local_to_texel_index, Terrain2D, TerrainEvent, Texel2D, TexelID, NEIGHBOUR_INDEX_MAP}; use crate::util::Vector2I; -use bevy::{prelude::*, render::{render_resource::Extent3d, texture::ImageSampler}}; +use bevy::{ + prelude::*, + render::{render_resource::Extent3d, texture::ImageSampler}, +}; use lazy_static::lazy_static; lazy_static! { pub static ref COLOR_MAP: HashMap = { let mut map = HashMap::new(); - map.insert(0, [0x20, 0x20, 0x20, 0xff]); - map.insert(1, [0xff, 0xff, 0xff, 0xff]); + map.insert(0, [0x03, 0x03, 0x03, 0xff]); + // map.insert(1, [0x47, 0x8e, 0x48, 0xff]); + map.insert(1, [0x9e, 0x7f, 0x63, 0xff]); + map.insert(2, [0x38, 0x32, 0x2d, 0xff]); + map.insert(3, [0x1e, 0x1e, 0x1e, 0xff]); map }; } #[derive(Reflect, Component, Default)] #[reflect(Component)] -pub struct Chunk2DIndex { - pub index: ChunkIndex, +pub struct Chunk2DHandler { + pub index: Chunk2DIndex, } #[derive(Bundle, Default)] pub struct ChunkBundle { - pub chunk: Chunk2DIndex, + pub chunk: Chunk2DHandler, pub sprite_bundle: SpriteBundle, } -pub type ChunkIndex = Vector2I; +pub type Chunk2DIndex = Vector2I; #[derive(Clone, Copy)] pub struct ChunkRect { @@ -106,6 +112,19 @@ impl Chunk2D { [Texel2D::default(); Self::SIZE_X * Self::SIZE_Y] } + pub fn xy_vec() -> Vec { + let mut result = Vec::with_capacity(Self::SIZE_X * Self::SIZE_Y); + for y in 0..Self::SIZE_Y { + for x in 0..Self::SIZE_X { + result.push(Vector2I { + x: x as i32, + y: y as i32, + }); + } + } + result + } + pub fn mark_all_dirty(&mut self) { self.dirty_rect = Some(ChunkRect { min: Vector2I::ZERO, @@ -170,7 +189,7 @@ pub fn chunk_spawner( mut terrain_events: EventReader, mut images: ResMut>, terrain: Res, - chunk_query: Query<(Entity, &Chunk2DIndex)>, + chunk_query: Query<(Entity, &Chunk2DHandler)>, ) { for terrain_event in terrain_events.iter() { match terrain_event { @@ -213,16 +232,16 @@ pub fn chunk_spawner( let pos = Vec2::from(*chunk_index * Chunk2D::SIZE); commands .spawn(ChunkBundle { - chunk: Chunk2DIndex { + chunk: Chunk2DHandler { index: *chunk_index, }, sprite_bundle: SpriteBundle { sprite: Sprite { - color: Color::rgb( - 0.25 + (chunk_index.x % 4) as f32 * 0.25, - 0.25 + (chunk_index.y % 4) as f32 * 0.25, - 0.75, - ), + // color: Color::rgb( + // (chunk_index.x % 8) as f32 / 7.0, + // (chunk_index.y % 8) as f32 / 7.0, + // 1.0, + // ), custom_size: Some(Vec2::from(Chunk2D::SIZE)), anchor: bevy::sprite::Anchor::BottomLeft, ..default() diff --git a/src/terrain2d/terrain_gen2d.rs b/src/terrain2d/terrain_gen2d.rs new file mode 100644 index 0000000..9ba4b2e --- /dev/null +++ b/src/terrain2d/terrain_gen2d.rs @@ -0,0 +1,46 @@ +use noise::{NoiseFn, PerlinSurflet}; + +use super::{chunk_index_to_global, Chunk2D, Chunk2DIndex}; + +pub struct TerrainGen2D { + pub seed: u32, + noise: PerlinSurflet, +} + +impl TerrainGen2D { + const NOISE_SCALE: f64 = 1.0; + + pub fn new(seed: u32) -> TerrainGen2D { + let noise = PerlinSurflet::new(seed); + TerrainGen2D { noise, seed } + } + + pub fn gen_chunk(&self, position: &Chunk2DIndex) -> Chunk2D { + let mut chunk = Chunk2D::new(); + for local in Chunk2D::xy_vec().iter() { + let global = chunk_index_to_global(position) + *local; + + let x = global.x as f64 * Self::NOISE_SCALE; + let y = global.y as f64 * Self::NOISE_SCALE; + + let mut value = 0.5; + value += self.noise.get([x / 115.0, y / 1.25 / 115.0]); + value += self.noise.get([x / 77.0, y / 77.0]) * 0.3; + value += self.noise.get([x / 17.0, y / 17.0]) * 0.05; + + let mut id = 0; + if value > 0.35 { + id = 1; + } + if value > 0.42 { + id = 2; + } + if value > 0.9 { + id = 3; + } + + chunk.set_texel(&local, id); + } + chunk + } +}