Added raycasting (needs testing) and basic character terrain collision

Former-commit-id: be6bfacfd28e777a64d8157fce129f8072e20b38
This commit is contained in:
Joshua Barretto 2019-04-23 23:48:31 +01:00
parent 61afb98869
commit f210de09df
10 changed files with 133 additions and 33 deletions

View File

@ -194,10 +194,10 @@ impl Client {
{ {
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32)); let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
for i in chunk_pos.x - 2..chunk_pos.x + 2 { for i in chunk_pos.x - 3..chunk_pos.x + 3 {
for j in chunk_pos.y - 2..chunk_pos.y + 2 { for j in chunk_pos.y - 3..chunk_pos.y + 3 {
for k in 0..1 { for k in 0..1 {
let key = chunk_pos + Vec3::new(i, j, k); let key = Vec3::new(i, j, k);
if self.state.terrain().get_key(key).is_none() if self.state.terrain().get_key(key).is_none()
&& !self.pending_chunks.contains(&key) && !self.pending_chunks.contains(&key)
{ {

View File

@ -16,6 +16,8 @@ pub mod terrain;
pub mod util; pub mod util;
pub mod volumes; pub mod volumes;
pub mod vol; pub mod vol;
pub mod ray;
// TODO: unignore the code here, for some reason it refuses to compile here while has no problems copy-pasted elsewhere // TODO: unignore the code here, for some reason it refuses to compile here while has no problems copy-pasted elsewhere
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client /// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client
/// # Examples /// # Examples

View File

@ -179,7 +179,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
} }
// Try getting messages from the send channel // Try getting messages from the send channel
for _ in 0..10 { for _ in 0..100 {
match send_rx.try_recv() { match send_rx.try_recv() {
Ok(send_msg) => { Ok(send_msg) => {
// Serialize message // Serialize message
@ -209,7 +209,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
} }
// Try sending bytes through the TCP stream // Try sending bytes through the TCP stream
for _ in 0..10 { for _ in 0..100 {
//println!("HERE! Outgoing len: {}", outgoing_chunks.len()); //println!("HERE! Outgoing len: {}", outgoing_chunks.len());
match outgoing_chunks.pop_front() { match outgoing_chunks.pop_front() {
Some(chunk) => match stream.write_all(&chunk) { Some(chunk) => match stream.write_all(&chunk) {
@ -230,7 +230,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
} }
// Try receiving bytes from the TCP stream // Try receiving bytes from the TCP stream
for _ in 0..10 { for _ in 0..100 {
let mut buf = [0; 1024]; let mut buf = [0; 1024];
match stream.read(&mut buf) { match stream.read(&mut buf) {
@ -245,7 +245,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
} }
// Try turning bytes into messages // Try turning bytes into messages
for _ in 0..10 { for _ in 0..100 {
match incoming_buf.get(0..8) { match incoming_buf.get(0..8) {
Some(len_bytes) => { Some(len_bytes) => {
let len = usize::from_le_bytes(<[u8; 8]>::try_from(len_bytes).unwrap()); // Can't fail let len = usize::from_le_bytes(<[u8; 8]>::try_from(len_bytes).unwrap()); // Can't fail

77
common/src/ray.rs Normal file
View File

@ -0,0 +1,77 @@
use vek::*;
use crate::vol::{
Vox,
ReadVol,
};
pub trait RayUntil<V: Vox> = FnMut(&V) -> bool;
pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>> {
vol: &'a V,
from: Vec3<f32>,
to: Vec3<f32>,
until: F,
max_iter: usize,
}
impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
pub fn new(vol: &'a V, from: Vec3<f32>, to: Vec3<f32>, until: F) -> Self {
Self {
vol,
from,
to,
until,
max_iter: 100,
}
}
pub fn until<G: RayUntil<V::Vox>>(self, g: G) -> Ray<'a, V, G> {
Ray {
vol: self.vol,
from: self.from,
to: self.to,
until: g,
max_iter: self.max_iter,
}
}
pub fn max_iter(mut self, max_iter: usize) -> Self {
self.max_iter = max_iter;
self
}
pub fn cast(mut self) -> (f32, Result<&'a V::Vox, V::Err>) {
// TODO: Fully test this!
const PLANCK: f32 = 0.001;
let mut dist = 0.0;
let dir = (self.to - self.from).normalized();
let mut pos = self.from;
let mut ipos = pos.map(|e| e as i32);
for _ in 0..self.max_iter {
pos = dir * dist;
ipos = pos.map(|e| e as i32);
match self.vol
.get(ipos)
.map(|vox| (vox, (self.until)(vox)))
{
Ok((vox, true)) => return (dist, Ok(vox)),
Ok((_, false)) => {},
Err(err) => return (dist, Err(err)),
}
let deltas = (
dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) -
pos.map(|e| e.fract())
) / dir;
dist += deltas.reduce(f32::min).max(PLANCK);
}
(dist, self.vol.get(ipos))
}
}

View File

@ -1,25 +1,42 @@
// Library use vek::*;
use specs::{Join, Read, ReadStorage, System, WriteStorage}; use specs::{Join, Read, ReadStorage, System, WriteStorage, ReadExpect};
// Crate
use crate::{ use crate::{
comp::phys::{Pos, Vel}, comp::phys::{Pos, Vel},
state::DeltaTime, state::DeltaTime,
terrain::TerrainMap,
vol::{Vox, ReadVol},
}; };
// Basic ECS physics system // Basic ECS physics system
pub struct Sys; pub struct Sys;
const GRAVITY: f32 = 9.81;
impl<'a> System<'a> for Sys { impl<'a> System<'a> for Sys {
type SystemData = ( type SystemData = (
WriteStorage<'a, Pos>, ReadExpect<'a, TerrainMap>,
ReadStorage<'a, Vel>,
Read<'a, DeltaTime>, Read<'a, DeltaTime>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
); );
fn run(&mut self, (mut positions, velocities, dt): Self::SystemData) { fn run(&mut self, (terrain, dt, mut positions, mut velocities): Self::SystemData) {
(&mut positions, &velocities) for (pos, vel) in (&mut positions, &mut velocities).join() {
.join() // this can be parallelized with par_join() // Gravity
.for_each(|(pos, vel)| pos.0 += vel.0 * dt.0 as f32); vel.0.z -= GRAVITY * dt.0 as f32;
// Movement
pos.0 += vel.0 * dt.0 as f32;
// Basic collision with terrain
while terrain
.get(pos.0.map(|e| e as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
{
pos.0.z += 0.05;
vel.0.z = 0.0;
}
}
} }
} }

View File

@ -1,5 +1,5 @@
// Library
use vek::*; use vek::*;
use crate::ray::{Ray, RayUntil};
/// A voxel /// A voxel
pub trait Vox { pub trait Vox {
@ -64,6 +64,12 @@ pub trait ReadVol: BaseVol {
/// Get a reference to the voxel at the provided position in the volume. /// Get a reference to the voxel at the provided position in the volume.
#[inline(always)] #[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>; fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err>;
fn ray(&self, from: Vec3<f32>, to: Vec3<f32>) -> Ray<Self, fn(&Self::Vox) -> bool>
where Self: Sized
{
Ray::new(self, from, to, |vox| !vox.is_empty())
}
} }
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data. /// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.

View File

@ -112,7 +112,7 @@ impl Server {
self.state self.state
.ecs_mut() .ecs_mut()
.create_entity_synced() .create_entity_synced()
.with(comp::phys::Pos(Vec3::zero())) .with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)))
.with(comp::phys::Vel(Vec3::zero())) .with(comp::phys::Vel(Vec3::zero()))
.with(comp::phys::Dir(Vec3::unit_y())) .with(comp::phys::Dir(Vec3::unit_y()))
.with(character) .with(character)
@ -125,7 +125,7 @@ impl Server {
character: comp::Character, character: comp::Character,
) { ) {
state.write_component(entity, character); state.write_component(entity, character);
state.write_component(entity, comp::phys::Pos(Vec3::zero())); state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
state.write_component(entity, comp::phys::Vel(Vec3::zero())); state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Dir(Vec3::unit_y())); state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
// Make sure everything is accepted // Make sure everything is accepted
@ -182,7 +182,6 @@ impl Server {
// Fetch any generated `TerrainChunk`s and insert them into the terrain // Fetch any generated `TerrainChunk`s and insert them into the terrain
// Also, send the chunk data to anybody that is close by // Also, send the chunk data to anybody that is close by
for (key, chunk) in self.chunk_rx.try_iter() { for (key, chunk) in self.chunk_rx.try_iter() {
println!("Generation finished {:?}", key);
// Send the chunk to all nearby players // Send the chunk to all nearby players
for (entity, player, pos) in ( for (entity, player, pos) in (
&self.state.ecs().entities(), &self.state.ecs().entities(),
@ -194,7 +193,6 @@ impl Server {
// TODO: Distance check // TODO: Distance check
// if self.state.terrain().key_pos(key) // if self.state.terrain().key_pos(key)
println!("Send to player {:?}", key);
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate { self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
key, key,
chunk: Box::new(chunk.clone()), chunk: Box::new(chunk.clone()),

View File

@ -67,7 +67,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(), -Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(),
-Vec3::unit_y(), -Vec3::unit_y(),
Vec3::unit_z(), Vec3::unit_z(),
-Vec3::unit_x(), -Vec3::unit_x(),
@ -80,7 +80,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_x(), -Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_x(),
Vec3::unit_y(), Vec3::unit_y(),
Vec3::unit_z(), Vec3::unit_z(),
Vec3::unit_x(), Vec3::unit_x(),
@ -93,7 +93,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32), -Vec3::one() + pos.map(|e| e as f32),
Vec3::unit_x(), Vec3::unit_x(),
Vec3::unit_z(), Vec3::unit_z(),
-Vec3::unit_y(), -Vec3::unit_y(),
@ -106,7 +106,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(), -Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_y(),
Vec3::unit_z(), Vec3::unit_z(),
Vec3::unit_x(), Vec3::unit_x(),
Vec3::unit_y(), Vec3::unit_y(),
@ -119,7 +119,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32), -Vec3::one() + pos.map(|e| e as f32),
Vec3::unit_y(), Vec3::unit_y(),
Vec3::unit_x(), Vec3::unit_x(),
-Vec3::unit_z(), -Vec3::unit_z(),
@ -132,7 +132,7 @@ impl<M> Meshable for Dyna<Block, M> {
.unwrap_or(true) .unwrap_or(true)
{ {
mesh.push_quad(create_quad( mesh.push_quad(create_quad(
Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_z(), -Vec3::one() + pos.map(|e| e as f32) + Vec3::unit_z(),
Vec3::unit_x(), Vec3::unit_x(),
Vec3::unit_y(), Vec3::unit_y(),
Vec3::unit_z(), Vec3::unit_z(),

View File

@ -106,9 +106,9 @@ impl Terrain {
if client.state().terrain().get_key(pos).is_some() { if client.state().terrain().get_key(pos).is_some() {
match self.mesh_todo.iter_mut().find(|todo| todo.pos == pos) { match self.mesh_todo.iter_mut().find(|todo| todo.pos == pos) {
Some(todo) => todo.started_tick = current_tick, //Some(todo) => todo.started_tick = current_tick,
// The chunk it's queued yet, add it to the queue // The chunk it's queued yet, add it to the queue
None => self.mesh_todo.push_back(ChunkMeshState { _ /* None */ => self.mesh_todo.push_back(ChunkMeshState {
pos, pos,
started_tick: current_tick, started_tick: current_tick,
active_worker: false, active_worker: false,

View File

@ -40,8 +40,8 @@ impl World {
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32); let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
let wposf = wpos.map(|e| e as f64); let wposf = wpos.map(|e| e as f64);
let freq = 1.0 / 32.0; let freq = 1.0 / 64.0;
let ampl = 16.0; let ampl = 12.0;
let offs = 16.0; let offs = 16.0;
let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl + offs; let height = perlin_nz.get(Vec2::from(wposf * freq).into_array()) * ampl + offs;
@ -49,7 +49,7 @@ impl World {
if wposf.z < height - 1.0 { if wposf.z < height - 1.0 {
stone stone
} else { } else {
sand grass
} }
} else { } else {
air air