added chunk generating/saving system

Commit d3e02c9e4e27 · mgecumshooter · 2026-10-18 11:58 +0300

Changeset
d3e02c9e4e277d4b09e1cf5672f53f9768695c63

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -29,4 +29,8 @@
 neorun.json
 
 
-*~
\ No newline at end of file
+*~
+
+saves
+./saves
+/saves
\ No newline at end of file
diff --git a/src/chunk/mod.rs b/src/chunk/mod.rs
--- a/src/chunk/mod.rs
+++ b/src/chunk/mod.rs
@@ -5,9 +5,11 @@
     // core::*,
     // consts::*,
 };
+use std::fs;
 
 // internal deps
-use crate::voxel::{ self, Voxel, };
+use crate::voxel::{ self, Voxel, VOXEL_SIZE };
+use crate::util::*;
 
 
 // constants
@@ -68,7 +70,35 @@
 
     // returns global voxel position by its chunk index
     pub fn global_voxel_pos(&self, idx: usize) -> Vector3 {
-        self.chunk_pos + self.voxel_pos(idx)
+        self.chunk_pos * CHUNK_SIZE as f32 + self.voxel_pos(idx)
+    }
+
+    pub fn get_chunk_aabb(&self) -> Aabb {
+	Aabb {
+	    min: self.chunk_pos * CHUNK_SIZE as f32,
+	    max: self.chunk_pos * CHUNK_SIZE as f32 + Vector3::new
+		(CHUNK_SIZE as f32, CHUNK_SIZE as f32, CHUNK_SIZE as f32),
+	}
+    }
+
+    pub fn get_voxel_global_aabb(&self, idx: usize) -> Aabb {
+	Aabb {
+	    min: self.global_voxel_pos(idx),
+	    max: self.global_voxel_pos(idx) + Vector3::new
+		(VOXEL_SIZE as f32, VOXEL_SIZE as f32, VOXEL_SIZE as f32),
+	}
+    }
+
+    pub fn get_voxel_pos_glb(&self, x: i32, y: i32, z: i32) -> (i32, i32, i32) {
+	let chunk_x: i32;
+	let chunk_y: i32;
+	let chunk_z: i32;
+
+	chunk_x = (x as f32 - self.chunk_pos.x * CHUNK_SIZE as f32) as i32;
+	chunk_y = (y as f32 - self.chunk_pos.y * CHUNK_SIZE as f32) as i32;
+	chunk_z = (z as f32 - self.chunk_pos.z * CHUNK_SIZE as f32) as i32;
+
+	(chunk_x, chunk_y, chunk_z)
     }
 
     // detects, whether voxel is exoped or not
@@ -84,7 +114,8 @@
             let ny = y as isize + dy;
             let nz = z as isize + dz;
 
-            if nx < 0 || ny < 0 || nz < 0 || nx >= CHUNK_SIZE as isize || ny >= CHUNK_SIZE as isize || nz >= CHUNK_SIZE as isize {
+            if nx < 0 || ny < 0 || nz < 0 || nx >= CHUNK_SIZE as isize ||
+		ny >= CHUNK_SIZE as isize || nz >= CHUNK_SIZE as isize {
                 return true;
             }
 
@@ -107,7 +138,7 @@
 
                     if self.is_exposed(x, y, z) {
                         ctx.draw_cube(
-                            self.chunk_pos + Vector3::new(x as f32, y as f32, z as f32), 
+                            self.chunk_pos * CHUNK_SIZE as f32 + Vector3::new(x as f32, y as f32, z as f32), 
                             voxel::VOXEL_SIZE as f32, 
                             voxel::VOXEL_SIZE as f32, 
                             voxel::VOXEL_SIZE as f32, 
@@ -119,4 +150,48 @@
         }
         self
     }
+
+    pub fn save(&self) {
+	let dir = "saves";
+	let _ = fs::create_dir_all(dir);
+	let path = format!("{}/chunk_{}_{}_{}.bin", dir,
+			   self.chunk_pos.x as i32,
+			   self.chunk_pos.y as i32,
+			   self.chunk_pos.z as i32,
+	);
+
+	let mut bytes = Vec::with_capacity(self.voxels.len());
+	for v in &self.voxels {
+	    bytes.push(match v {
+		Voxel::Air => 0u8,
+		Voxel::Stone => 1u8,
+	    });
+	}
+
+	let _ = fs::write(path, bytes);
+    }
+
+    pub fn load_from_disk(pos: Vector3) -> Option<Self> {
+	let path = format!("saves/chunk_{}_{}_{}.bin",
+			   pos.x as i32, pos.y as i32, pos.z as i32,
+	);
+	if let Ok(bytes) = fs::read(path) {
+	    if bytes.len() == CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE {
+		let mut voxels = [Voxel::Air; CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE];
+		for (i, &b) in bytes.iter().enumerate() {
+		    voxels[i] = match b {
+			1u8 => Voxel::Stone,
+			_ => Voxel::Air,
+		    };
+		}
+		return Some(Self{
+		    chunk_pos: pos,
+		    voxels,
+		    dirty: false,
+		    mesh: None,
+		});
+	    }
+	}
+	None
+    }
 }
diff --git a/src/main.rs b/src/main.rs
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,20 +1,19 @@
 // external  deps
 use sola_raylib as rlb;
-use rlb::{
-    prelude::*,
-    // core::*,
-    // consts::*,
-};
-
+use rlb::prelude::*;
 
 // modules declarations
 mod chunk;
 mod voxel;
+mod player;
+mod util;
+mod world;
 
 // internal modules
 use chunk::*;
-
-
+use player::Player;
+use util::Cube;
+use world::*;
 
 
 fn main() {
@@ -35,31 +34,52 @@
     rl.disable_cursor();
 
 
-
     // shader stuff
     let mut shader = rl.load_shader(&rl_thd, Some("shaders/vert.glsl"), Some("shaders/frag.glsl")).unwrap();
     let mut post_shader = rl.load_shader(&rl_thd, None, Some("shaders/post.glsl")).unwrap();
-    let mut rend_tex = rl.load_render_texture(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
+    let mut rend_tex = rl.load_render_texture
+	(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
 
     
     // chunks
-    let test_chunk = Chunk::new(Vector3::zero());
+    // let test_chunk = Chunk::new(Vector3::zero());
+
+    // let test2 = [&test_chunk];
+
+    let mut world = World::new(2);
+
+    let mut plr = Player::new(
+	Vector3::new(0f32, 10f32, 0f32),
+	Cube::new(
+	    Vector3::new(5f32, 10f32, 5f32),
+	),
+    );
 
 
     // main game loop
     while !rl.window_should_close() {
+	world.update_around_player(plr.get_eye_pos());
+
+	let active_chunks: Vec<&Chunk> = world.chunks.values().collect();
+	plr.update(&rl, &active_chunks);
+	plr.apply_to_camera(&mut cam);
+
+	
         if rend_tex.width() != rl.get_render_width() || rend_tex.height() != rl.get_render_height() {
-            rend_tex = rl.load_render_texture(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
+            rend_tex = rl.load_render_texture
+		(&rl_thd, rl.get_render_width() as u32, rl.get_render_height() as u32).unwrap();
         }
 
-        rl.update_camera(&mut cam, CameraMode::CAMERA_FIRST_PERSON);
-        rl.draw(&rl_thd, |mut d| {
+	rl.draw(&rl_thd, |mut d| {
             d.draw_texture_mode(&rl_thd, &mut rend_tex, |mut tx| {
                 tx.clear_background(Color::new(30, 30, 30, 255));
                 tx.draw_mode3D(cam, |mut d3, _| {
                     d3.draw_grid(100, 1f32);
                     d3.draw_shader_mode(&mut shader, |mut shd| {
-                        test_chunk.draw(&mut shd);
+                        // test_chunk.draw(&mut shd);
+			for chunk in world.chunks.values() {
+			    chunk.draw(&mut shd);
+			}
                     });
                 });
             });
@@ -71,7 +91,17 @@
                     height: -rend_tex.texture().height as f32
                 }, Vector2::zero(), Color::WHITE);
             });
+	    d.draw_text(
+		&format!("x: {:.2}, y: {:.2}, z: {:.2}",
+			 plr.get_eye_pos().x,
+			 plr.get_eye_pos().y,
+			 plr.get_eye_pos().z,
+		),
+		0,
+		0,
+		20,
+		Color::WHITE
+	    );
         });
     }
 }
-
diff --git a/src/player/mod.rs b/src/player/mod.rs
new file mode 100644
--- /dev/null
+++ b/src/player/mod.rs
@@ -0,0 +1,169 @@
+use super::rlb;
+use rlb::prelude::*;
+
+use crate::util::*;
+use crate::voxel::*;
+use crate::chunk::Chunk;
+
+const MOVE_SPEED: f32 = 5f32;
+const MOUSE_SENS: f32 = 0.05f32;
+const PITCH_LIMIT: f32 = 89f32;
+
+pub enum Axis {
+    X,
+    Y,
+    Z,
+}
+
+pub struct Player {
+    body: Cube,
+    pos: Vector3,
+    yaw: f32,
+    pitch: f32,
+}
+
+impl Player {
+    pub fn new(pos: Vector3, body: Cube) -> Self {
+	Self {
+	    pos: pos,
+	    body: body,
+	    yaw: 0f32,
+	    pitch: 0f32,
+	}
+    }
+
+    pub fn get_aabb(&self) -> Aabb {
+	Aabb {
+	    min: self.pos,
+	    max: Vector3::new(self.body.width, self.body.height, self.body.length) + self.pos,
+	}
+    }
+    
+    pub fn get_feet_pos(&self) -> Vector3 {
+	self.get_aabb().min + Vector3::new(self.body.width * 0.5f32, 0f32, self.body.length * 0.5f32)
+    }
+
+    pub fn get_eye_pos(&self) -> Vector3 {
+	self.get_feet_pos() + Vector3::new(0f32, self.body.height * 0.9, 0f32)
+    }
+
+    pub fn get_look_dir(&self) -> Vector3 {
+	let y = self.yaw.to_radians();
+	let p = self.pitch.to_radians();
+
+	Vector3::new(
+	    p.cos() * y.sin(),
+	    p.sin(),
+	    -p.cos() * y.cos(),
+	)
+    }
+
+    pub fn move_axis (&mut self, velocity: f32, axis: Axis, world: &Vec<Aabb>) {
+	if velocity == 0f32 {
+	    return;
+	}
+
+	match axis {
+	    Axis::X => self.pos.x += velocity,
+		Axis::Y => self.pos.y += velocity,
+		Axis::Z => self.pos.z += velocity,
+	}
+	
+	let plr = self.get_aabb();
+
+	for voxel in world {
+	    if plr.overlaps(&voxel) {
+		match axis {
+		    Axis::X => {
+			if velocity > 0f32 {
+			    self.pos.x = voxel.min.x - self.body.width;
+			} else {
+			    self.pos.x = voxel.max.x;
+			}
+		    }
+		    Axis::Y => {
+			if velocity > 0f32 {
+			    self.pos.y = voxel.min.y - self.body.width;
+			} else {
+			    self.pos.y = voxel.max.y;
+			}
+		    }
+		    Axis::Z => {
+			if velocity > 0f32 {
+			    self.pos.z = voxel.min.z - self.body.width;
+			} else {
+			    self.pos.z = voxel.max.z;
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    pub fn get_surroundings(&self, world: &[&Chunk]) -> Vec<Aabb> {
+	let plr_aabb = self.get_aabb();
+	let mut obstacles = Vec::new();
+
+	let min_x = (plr_aabb.min.x.floor() as isize) - 1;
+	let max_x = (plr_aabb.max.x.ceil() as isize) + 1;
+	
+	let min_y = (plr_aabb.min.y.floor() as isize) - 1;
+	let max_y = (plr_aabb.max.y.ceil() as isize) + 1;
+	
+	let min_z = (plr_aabb.min.z.floor() as isize) - 1;
+	let max_z = (plr_aabb.max.z.ceil() as isize) + 1;
+
+	for chunk in world {
+	    for x in min_x..=max_x {
+		for y in min_y..=max_y {
+		    for z in min_z..=max_z {
+			let (chk_x, chk_y, chk_z) = chunk.get_voxel_pos_glb(x as i32, y as i32, z as i32);
+			if let Some(Voxel::Stone) = chunk.get_voxel
+			    (chk_x as usize, chk_y as usize, chk_z as usize) {
+			    let idx = chunk.voxel_idx(chk_x as usize, chk_y as usize, chk_z as usize);
+			    obstacles.push(chunk.get_voxel_global_aabb(idx));
+			}
+		    }
+		}
+	    }
+	}
+	obstacles
+    }
+
+    pub fn update(&mut self, rl: &RaylibHandle, world: &[&Chunk]) {
+	let mouse_delta = rl.get_mouse_delta();
+
+	self.yaw += mouse_delta.x * MOUSE_SENS;
+	self.pitch -= mouse_delta.y * MOUSE_SENS;
+	self.pitch = self.pitch.clamp(-PITCH_LIMIT, PITCH_LIMIT);
+	
+	let forward =
+	    (rl.is_key_down(KeyboardKey::KEY_W) as i8 - rl.is_key_down(KeyboardKey::KEY_S) as i8) as f32;
+	let sideways =
+	    (rl.is_key_down(KeyboardKey::KEY_D) as i8 - rl.is_key_down(KeyboardKey::KEY_A) as i8) as f32;
+
+	let y = self.yaw.to_radians();
+	let fwd = Vector3::new(y.sin(), 0f32, -y.cos());
+	let sdw = Vector3::new(y.cos(), 0f32, y.sin());
+
+	let mut wish = fwd * forward + sdw * sideways;
+	
+	let len = (wish.x * wish.x + wish.z * wish.z).sqrt();
+	if len > 0f32 {
+	    wish.x /= len;
+	    wish.z /= len;
+	}
+
+	let obstacles = self.get_surroundings(world);
+	
+	let dt = rl.get_frame_time();
+	self.move_axis(wish.x * MOVE_SPEED * dt, Axis::X, &obstacles);
+	self.move_axis(wish.z * MOVE_SPEED * dt, Axis::Z, &obstacles);
+    }
+
+    pub fn apply_to_camera (&self, cam: &mut Camera3D) {
+	let eye = self.get_eye_pos();
+	cam.position = eye;
+	cam.target = eye + self.get_look_dir();
+    }
+}
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1,32 @@
+use super::rlb;
+use rlb::prelude::*;
+
+pub struct Cube {
+    pub width: f32,
+    pub length: f32,
+    pub height: f32,
+}
+
+impl Cube {
+    pub fn new(params: Vector3) -> Self {
+	Self {
+	    width: params.x,
+	    height: params.y,
+	    length: params.z,
+	}
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct Aabb {
+    pub min: Vector3,
+    pub max: Vector3,
+}
+
+impl Aabb {
+    pub fn overlaps (&self, other: &Aabb) -> bool {
+	self.min.x < other.max.x && self.max.x > other.min.x &&
+	    self.min.y < other.max.y && self.max.y > other.min.y &&
+	    self.min.z < other.max.z && self.max.z > other.min.z
+    }
+}
diff --git a/src/world/mod.rs b/src/world/mod.rs
new file mode 100644
--- /dev/null
+++ b/src/world/mod.rs
@@ -0,0 +1,64 @@
+use std::collections::HashMap;
+use crate::chunk::{Chunk, CHUNK_SIZE};
+use super::rlb::prelude::*;
+
+pub struct World {
+    pub chunks: HashMap<(i32, i32, i32), Chunk>,
+    pub render_distance: i32,
+}
+
+impl World {
+    pub fn new(render: i32) -> Self {
+	Self {
+	    chunks: HashMap::new(),
+	    render_distance: render,
+	}
+    }
+
+    pub fn update_around_player(&mut self, player_pos: Vector3) {
+	let p_chunk_x = (player_pos.x / CHUNK_SIZE as f32).floor() as i32;
+	let p_chunk_y = (player_pos.y / CHUNK_SIZE as f32).floor() as i32;
+	let p_chunk_z = (player_pos.z / CHUNK_SIZE as f32).floor() as i32;
+
+	let mut needed_chunks = Vec::new();
+
+	let r = self.render_distance;
+	for x in -r..=r {
+	    for y in -r..=r {
+		for z in -r..=r {
+		    if (x*x + y*y + z*z) <= (r*r) {
+			let chunk_coords = (p_chunk_x + x,
+					    p_chunk_y + y,
+					    p_chunk_z + z
+			);
+			needed_chunks.push(chunk_coords);
+
+			if !self.chunks.contains_key(&chunk_coords) {
+			    let pos_v3 = Vector3::new(chunk_coords.0 as f32,
+						      chunk_coords.1 as f32,
+						      chunk_coords.2 as f32,
+			    );
+			    let chunk = Chunk::load_from_disk(pos_v3).unwrap_or_else(|| {
+				Chunk::new(pos_v3)
+			    });
+
+			    self.chunks.insert(chunk_coords, chunk);
+			}
+		    }
+		}
+	    }
+	}
+
+	let mut to_unload = Vec::new();
+	for &coords in self.chunks.keys() {
+	    if !needed_chunks.contains(&coords) {
+		to_unload.push(coords);
+	    }
+	}
+	for coords in to_unload {
+	    if let Some(chunk) = self.chunks.remove(&coords) {
+		chunk.save();
+	    }
+	}
+    }
+}