From 98422678a385e445b4e7ab313fd0367915017f8d Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Thu, 21 Apr 2016 19:53:47 +0100 Subject: [PATCH] Add block targeting --- src/server/mod.rs | 12 +- src/server/target.rs | 256 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 src/server/target.rs diff --git a/src/server/mod.rs b/src/server/mod.rs index 4facb76..d556ec2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -27,7 +27,7 @@ use render; use settings::Stevenkey; use ecs; use entity; -use cgmath; +use cgmath::{self, Point}; use collision::Aabb; use types::Gamemode; use shared::{Axis, Position}; @@ -35,6 +35,7 @@ use format; mod sun; pub mod plugin_messages; +pub mod target; pub struct Server { username: String, @@ -74,6 +75,7 @@ pub struct Server { entity_tick_timer: f64, sun_model: Option, + target_info: target::Info, } pub struct PlayerInfo { @@ -293,6 +295,8 @@ impl Server { tick_timer: 0.0, entity_tick_timer: 0.0, sun_model: None, + + target_info: target::Info::new(), } } @@ -343,6 +347,12 @@ impl Server { } self.world.tick(&mut self.entities); + + if let Some((pos, bl)) = target::trace_ray(&self.world, 4.0, renderer.camera.pos.to_vec(), renderer.view_vector.cast(), target::test_block) { + self.target_info.update(renderer, pos, bl); + } else { + self.target_info.clear(renderer); + } } fn entity_tick(&mut self, renderer: &mut render::Renderer, delta: f64) { diff --git a/src/server/target.rs b/src/server/target.rs new file mode 100644 index 0000000..b7fdebd --- /dev/null +++ b/src/server/target.rs @@ -0,0 +1,256 @@ + +use world; +use world::block; +use shared::Position; +use cgmath; +use render; +use render::model; +use collision::{self, Aabb}; + +pub struct Info { + model: Option, + last_block: block::Block, + last_pos: Position, +} + +impl Info { + pub fn new() -> Info { + Info { + model: None, + last_block: block::Air{}, + last_pos: Position::new(0, 0, 0), + } + } + + pub fn clear(&mut self, renderer: &mut render::Renderer) { + self.last_block = block::Air{}; + if let Some(model) = self.model.take() { + renderer.model.remove_model(model); + } + } + + pub fn update(&mut self, renderer: &mut render::Renderer, pos: Position, bl: block::Block) { + if self.last_block == bl && self.last_pos == pos { + return; + } + self.last_block = bl; + self.last_pos = pos; + if let Some(model) = self.model.take() { + renderer.model.remove_model(model); + } + let mut parts = vec![]; + + const LINE_SIZE: f64 = 1.0 / 128.0; + let tex = render::Renderer::get_texture(renderer.get_textures_ref(), "steven:solid"); + + for bound in bl.get_collision_boxes() { + let bound = bound.add_v(cgmath::Vector3::new(pos.x as f64, pos.y as f64, pos.z as f64)); + for point in [ + (bound.min.x, bound.min.z), + (bound.min.x, bound.max.z), + (bound.max.x, bound.min.z), + (bound.max.x, bound.max.z), + ].into_iter() { + model::append_box(&mut parts, + (point.0-LINE_SIZE) as f32, (bound.min.y-LINE_SIZE) as f32, (point.1-LINE_SIZE) as f32, + (LINE_SIZE*2.0) as f32, ((bound.max.y-bound.min.y)+LINE_SIZE*2.0) as f32, (LINE_SIZE*2.0) as f32, + [ + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + ]); + } + + for point in [ + (bound.min.x, bound.min.z, bound.max.x, bound.min.z), + (bound.min.x, bound.max.z, bound.max.x, bound.max.z), + (bound.min.x, bound.min.z, bound.min.x, bound.max.z), + (bound.max.x, bound.min.z, bound.max.x, bound.max.z), + ].into_iter() { + model::append_box(&mut parts, + (point.0-LINE_SIZE) as f32, (bound.min.y-LINE_SIZE) as f32, (point.1-LINE_SIZE) as f32, + ((point.2-point.0)+(LINE_SIZE*2.0)) as f32, (LINE_SIZE*2.0) as f32, ((point.3-point.1)+(LINE_SIZE*2.0)) as f32, + [ + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + ]); + model::append_box(&mut parts, + (point.0-LINE_SIZE) as f32, (bound.max.y-LINE_SIZE) as f32, (point.1-LINE_SIZE) as f32, + ((point.2-point.0)+(LINE_SIZE*2.0)) as f32, (LINE_SIZE*2.0) as f32, ((point.3-point.1)+(LINE_SIZE*2.0)) as f32, + [ + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + Some(tex.clone()), + ]); + } + } + + for part in &mut parts { + part.r = 0; + part.g = 0; + part.b = 0; + } + + self.model = Some(renderer.model.create_model(model::DEFAULT, vec![parts])); + } +} + +pub fn test_block(world: &world::World, pos: Position, s: cgmath::Vector3, d: cgmath::Vector3) -> (bool, Option<(Position, block::Block)>) { + let block = world.get_block(pos); + for bound in block.get_collision_boxes() { + let bound = bound.add_v(cgmath::Vector3::new(pos.x as f64, pos.y as f64, pos.z as f64)); + if let Some(hit) = intersects_line(bound, s, d) { + // TODO: Face/cursor + let _ = hit; + return (true, Some((pos, block))); + } + } + (false, None) +} + +fn intersects_line(bound: collision::Aabb3, origin: cgmath::Vector3, dir: cgmath::Vector3) -> Option> { + const RIGHT: usize = 0; + const LEFT: usize = 1; + const MIDDLE: usize = 2; + let mut quadrant = [0, 0, 0]; + let mut candidate_plane = [0.0, 0.0, 0.0]; + let mut max_t = [0.0, 0.0, 0.0]; + let mut inside = true; + for i in 0 .. 3 { + if origin[i] < bound.min[i] { + quadrant[i] = LEFT; + candidate_plane[i] = bound.min[i]; + inside = false; + } else if origin[i] > bound.max[i] { + quadrant[i] = RIGHT; + candidate_plane[i] = bound.max[i]; + inside = false; + } else { + quadrant[i] = MIDDLE; + } + } + if inside { + return Some(origin); + } + + for i in 0 .. 3 { + if quadrant[i] != MIDDLE && dir[i] != 0.0 { + max_t[i] = (candidate_plane[i] - origin[i]) / dir[i]; + } + } + let mut which_plane = 0; + for i in 1 .. 3 { + if max_t[which_plane] < max_t[i] { + which_plane = i; + } + } + if max_t[which_plane] < 0.0 { + return None; + } + + let mut coord = cgmath::Vector3::new(0.0, 0.0, 0.0); + for i in 0 .. 3 { + if which_plane != i { + coord[i] = origin[i] + max_t[which_plane] * dir[i]; + if coord[i] < bound.min[i] || coord[i] > bound.max[i] { + return None; + } + } else { + coord[i] = candidate_plane[i]; + } + } + Some(coord) +} + +pub fn trace_ray(world: &world::World, max: f64, s: cgmath::Vector3, d: cgmath::Vector3, collide_func: F) -> Option + where F: Fn(&world::World, Position, cgmath::Vector3, cgmath::Vector3,) -> (bool, Option) { + struct Gen { + count: i32, + base: f64, + d: f64, + } + impl Gen { + fn new(start: f64, mut d: f64) -> Gen { + let base = if d > 0.0 { + (start.ceil() - start) / d + } else if d < 0.0 { + d = d.abs(); + (start - start.floor()) / d + } else { + 0.0 + }; + Gen { + count: 0, + base: base, + d: d, + } + } + + fn next(&mut self) -> f64 { + self.count += 1; + if self.d == 0.0 { + ::std::f64::INFINITY + } else { + self.base + ((self.count as f64 - 1.0) / self.d) + } + } + } + + let mut x_gen = Gen::new(s.x, d.x); + let mut y_gen = Gen::new(s.y, d.y); + let mut z_gen = Gen::new(s.z, d.z); + let mut next_nx = x_gen.next(); + let mut next_ny = y_gen.next(); + let mut next_nz = z_gen.next(); + + let mut x = s.x.floor() as i32; + let mut y = s.y.floor() as i32; + let mut z = s.z.floor() as i32; + + loop { + let (hit, ret) = collide_func(&world, Position::new(x, y, z), s, d); + if hit { + return ret; + } + let next_n = if next_nx <= next_ny { + if next_nx <= next_nz { + let old = next_nx; + next_nx = x_gen.next(); + x += d.x.signum() as i32; + old + } else { + let old = next_nz; + next_nz = z_gen.next(); + z += d.z.signum() as i32; + old + } + } else { + if next_ny <= next_nz { + let old = next_ny; + next_ny = y_gen.next(); + y += d.y.signum() as i32; + old + } else { + let old = next_nz; + next_nz = z_gen.next(); + z += d.z.signum() as i32; + old + } + }; + if next_n > max { + break; + } + } + + None +}