implemented raycast

Commit 830ace00d928 · mgecumshooter · 2026-10-23 11:04 +0300

Changeset
830ace00d928f01eddb4c5ca1e0b2bc1f5e80602

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/src/chunk/mod.rs b/src/chunk/mod.rs
--- a/src/chunk/mod.rs
+++ b/src/chunk/mod.rs
@@ -173,6 +173,7 @@
     }
     
     pub fn generate_mesh(&mut self) {
+	self.meshes.clear();
         let mut vertices: Vec<Vector3> = Vec::new();
         let mut normals: Vec<Vector3> = Vec::new();
         let mut indices: Vec<u16> = Vec::new();
diff --git a/src/main.rs b/src/main.rs
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,6 +14,7 @@
 use player::Player;
 use util::*;
 use world::*;
+use voxel::*;
 
 fn main() {
     let (mut rl, rl_thd) = rlb::init().size(800, 600).title("ESOTERA").vsync().build();
@@ -73,6 +74,42 @@
 
 	let fps = rl.get_fps();
 
+	let screen_x1 = (rl.get_screen_width() / 2) - 10;
+	let screen_x2 = (rl.get_screen_width() / 2) + 10;
+	let screen_y1 = rl.get_screen_height() / 2 - 10;
+	let screen_y2 = rl.get_screen_height() / 2 + 10;
+	let screen_x = rl.get_screen_width() / 2;
+	let screen_y = rl.get_screen_height() / 2;
+
+	if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) {
+	    let (voxel, _normal) = util::raycast(plr.get_eye_pos(), plr.get_look_dir(), 16f32, &world);
+	    match voxel {
+		Some(vox) => {
+		    let gx = vox.x as i32;
+		    let gy = vox.y as i32;
+		    let gz = vox.z as i32;
+
+		    let chunk_coord = (
+			gx.div_euclid(CHUNK_SIZE as i32),
+			gy.div_euclid(CHUNK_SIZE as i32),
+			gz.div_euclid(CHUNK_SIZE as i32),
+		    );
+
+		    if let Some(chunk) = world.get_mut(chunk_coord) {
+			let local_x = gx.rem_euclid(CHUNK_SIZE as i32) as usize;
+			let local_y = gy.rem_euclid(CHUNK_SIZE as i32) as usize;
+			let local_z = gz.rem_euclid(CHUNK_SIZE as i32) as usize;
+
+			let idx = chunk.voxel_idx(local_x, local_y, local_z);
+			chunk.voxels[idx] = Voxel::from_kind(VoxelKind::Air);
+			chunk.dirty = true; // иначе меш не перестроится
+		    }
+		}
+		None => {}
+	    }
+	    
+	}
+	
         if rend_tex.width() != rl.get_render_width() || rend_tex.height() != rl.get_render_height()
         {
             unsafe { rlb::ffi::rlUnloadTexture(normal_tex_id) };
@@ -152,6 +189,20 @@
 		&format!("FPS: {}", fps),
 		0, 20, 20, Color::WHITE,
 	    );
+	    d.draw_line(
+		screen_x1,
+		screen_y,
+		screen_x2,
+		screen_y,
+		Color::WHITE
+	    );
+	    d.draw_line(
+		screen_x,
+		screen_y1,
+		screen_x,
+		screen_y2,
+		Color::WHITE
+	    );
         });
     }
     if let Err(e) = std::fs::remove_dir_all("saves") {
diff --git a/src/player/mod.rs b/src/player/mod.rs
--- a/src/player/mod.rs
+++ b/src/player/mod.rs
@@ -149,9 +149,16 @@
     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 click_this_frame =
+            rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT)
+            || rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_RIGHT)
+            || rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_MIDDLE);
+
+	if !click_this_frame {
+	    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;
diff --git a/src/util/mod.rs b/src/util/mod.rs
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -1,5 +1,8 @@
 use super::rlb;
 use rlb::prelude::*;
+use crate::voxel::*;
+use crate::chunk::*;
+use crate::world::*;
 
 pub struct Cube {
     pub width: f32,
@@ -34,6 +37,101 @@
     }
 }
 
+pub fn raycast(
+    origin: Vector3,
+    direction: Vector3,
+    max_distance: f32,
+    world: &World,
+) -> (Option<Vector3>, Option<Vector3>) {
+    let mut current_cell = [
+        origin.x.floor() as i32,
+        origin.y.floor() as i32,
+        origin.z.floor() as i32,
+    ];
+
+    let step = [
+        if direction.x > 0.0 { 1 } else if direction.x < 0.0 { -1 } else { 0 },
+        if direction.y > 0.0 { 1 } else if direction.y < 0.0 { -1 } else { 0 },
+        if direction.z > 0.0 { 1 } else if direction.z < 0.0 { -1 } else { 0 },
+    ];
+
+    let mut t_max = [f32::INFINITY; 3];
+    let mut t_delta = [f32::INFINITY; 3];
+
+    for i in 0..3 {
+        let origin_axis = match i { 0 => origin.x, 1 => origin.y, _ => origin.z };
+        let dir_axis = match i { 0 => direction.x, 1 => direction.y, _ => direction.z };
+
+        if dir_axis == 0.0 {
+            continue;
+        }
+
+        let next_boundary = if dir_axis > 0.0 {
+            current_cell[i] as f32 + 1.0
+        } else {
+            current_cell[i] as f32
+        };
+
+        t_max[i] = (next_boundary - origin_axis) / dir_axis;
+        t_delta[i] = 1.0 / dir_axis.abs();
+    }
+
+    let mut normal = [0i32, 0, 0];
+
+    if let Some((pos, norm)) = check_cell(current_cell, normal, world) {
+        return (Some(pos), Some(norm));
+    }
+
+    loop {
+        let (axis, t) = if t_max[0] < t_max[1] && t_max[0] < t_max[2] {
+            (0, t_max[0])
+        } else if t_max[1] < t_max[2] {
+            (1, t_max[1])
+        } else {
+            (2, t_max[2])
+        };
+
+        if t > max_distance {
+            return (None, None);
+        }
+
+        current_cell[axis] += step[axis];
+        t_max[axis] += t_delta[axis];
+
+        normal = [0, 0, 0];
+        normal[axis] = -step[axis];
+
+        if let Some((pos, norm)) = check_cell(current_cell, normal, world) {
+            return (Some(pos), Some(norm));
+        }
+    }
+}
+
+fn check_cell(cell: [i32; 3], normal: [i32; 3], world: &World) -> Option<(Vector3, Vector3)> {
+    let chunk_coord = (
+        cell[0].div_euclid(CHUNK_SIZE as i32),
+        cell[1].div_euclid(CHUNK_SIZE as i32),
+        cell[2].div_euclid(CHUNK_SIZE as i32),
+    );
+
+    let chunk = world.get(chunk_coord)?;
+
+    let local_x = cell[0].rem_euclid(CHUNK_SIZE as i32) as usize;
+    let local_y = cell[1].rem_euclid(CHUNK_SIZE as i32) as usize;
+    let local_z = cell[2].rem_euclid(CHUNK_SIZE as i32) as usize;
+
+    let voxel = chunk.get_voxel(local_x, local_y, local_z)?;
+
+    if voxel.kind == VoxelKind::Air {
+        return None;
+    }
+
+    Some((
+        Vector3::new(cell[0] as f32, cell[1] as f32, cell[2] as f32),
+        Vector3::new(normal[0] as f32, normal[1] as f32, normal[2] as f32),
+    ))
+}
+
 // had to do this ;(
 pub fn load_depth_tex(width: i32, height: i32) -> (RenderTexture2D, u32) {
     unsafe {
diff --git a/src/world/mod.rs b/src/world/mod.rs
--- a/src/world/mod.rs
+++ b/src/world/mod.rs
@@ -8,6 +8,13 @@
 }
 
 impl World {
+    pub fn get(&self, coords: (i32, i32, i32)) -> Option<&Chunk> {
+	self.chunks.get(&coords)
+    }
+    pub fn get_mut(&mut self, coords: (i32, i32, i32)) -> Option<&mut Chunk> {
+	self.chunks.get_mut(&coords)
+    }
+    
     pub fn new(render: i32) -> Self {
 	Self {
 	    chunks: HashMap::new(),