mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added raycasting (needs testing) and basic character terrain collision
Former-commit-id: be6bfacfd28e777a64d8157fce129f8072e20b38
This commit is contained in:
parent
61afb98869
commit
f210de09df
@ -194,10 +194,10 @@ impl Client {
|
||||
{
|
||||
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 j in chunk_pos.y - 2..chunk_pos.y + 2 {
|
||||
for i in chunk_pos.x - 3..chunk_pos.x + 3 {
|
||||
for j in chunk_pos.y - 3..chunk_pos.y + 3 {
|
||||
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()
|
||||
&& !self.pending_chunks.contains(&key)
|
||||
{
|
||||
|
@ -16,6 +16,8 @@ pub mod terrain;
|
||||
pub mod util;
|
||||
pub mod volumes;
|
||||
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
|
||||
/// 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
|
||||
|
@ -179,7 +179,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
}
|
||||
|
||||
// Try getting messages from the send channel
|
||||
for _ in 0..10 {
|
||||
for _ in 0..100 {
|
||||
match send_rx.try_recv() {
|
||||
Ok(send_msg) => {
|
||||
// Serialize message
|
||||
@ -209,7 +209,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
}
|
||||
|
||||
// Try sending bytes through the TCP stream
|
||||
for _ in 0..10 {
|
||||
for _ in 0..100 {
|
||||
//println!("HERE! Outgoing len: {}", outgoing_chunks.len());
|
||||
match outgoing_chunks.pop_front() {
|
||||
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
|
||||
for _ in 0..10 {
|
||||
for _ in 0..100 {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
match stream.read(&mut buf) {
|
||||
@ -245,7 +245,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
}
|
||||
|
||||
// Try turning bytes into messages
|
||||
for _ in 0..10 {
|
||||
for _ in 0..100 {
|
||||
match incoming_buf.get(0..8) {
|
||||
Some(len_bytes) => {
|
||||
let len = usize::from_le_bytes(<[u8; 8]>::try_from(len_bytes).unwrap()); // Can't fail
|
||||
|
77
common/src/ray.rs
Normal file
77
common/src/ray.rs
Normal 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))
|
||||
}
|
||||
}
|
@ -1,25 +1,42 @@
|
||||
// Library
|
||||
use specs::{Join, Read, ReadStorage, System, WriteStorage};
|
||||
|
||||
// Crate
|
||||
use vek::*;
|
||||
use specs::{Join, Read, ReadStorage, System, WriteStorage, ReadExpect};
|
||||
use crate::{
|
||||
comp::phys::{Pos, Vel},
|
||||
state::DeltaTime,
|
||||
terrain::TerrainMap,
|
||||
vol::{Vox, ReadVol},
|
||||
};
|
||||
|
||||
// Basic ECS physics system
|
||||
pub struct Sys;
|
||||
|
||||
const GRAVITY: f32 = 9.81;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
WriteStorage<'a, Pos>,
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadExpect<'a, TerrainMap>,
|
||||
Read<'a, DeltaTime>,
|
||||
WriteStorage<'a, Pos>,
|
||||
WriteStorage<'a, Vel>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (mut positions, velocities, dt): Self::SystemData) {
|
||||
(&mut positions, &velocities)
|
||||
.join() // this can be parallelized with par_join()
|
||||
.for_each(|(pos, vel)| pos.0 += vel.0 * dt.0 as f32);
|
||||
fn run(&mut self, (terrain, dt, mut positions, mut velocities): Self::SystemData) {
|
||||
for (pos, vel) in (&mut positions, &mut velocities).join() {
|
||||
// Gravity
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Library
|
||||
use vek::*;
|
||||
use crate::ray::{Ray, RayUntil};
|
||||
|
||||
/// A voxel
|
||||
pub trait Vox {
|
||||
@ -64,6 +64,12 @@ pub trait ReadVol: BaseVol {
|
||||
/// Get a reference to the voxel at the provided position in the volume.
|
||||
#[inline(always)]
|
||||
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.
|
||||
|
@ -112,7 +112,7 @@ impl Server {
|
||||
self.state
|
||||
.ecs_mut()
|
||||
.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::Dir(Vec3::unit_y()))
|
||||
.with(character)
|
||||
@ -125,7 +125,7 @@ impl Server {
|
||||
character: comp::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::Dir(Vec3::unit_y()));
|
||||
// Make sure everything is accepted
|
||||
@ -182,7 +182,6 @@ impl Server {
|
||||
// Fetch any generated `TerrainChunk`s and insert them into the terrain
|
||||
// Also, send the chunk data to anybody that is close by
|
||||
for (key, chunk) in self.chunk_rx.try_iter() {
|
||||
println!("Generation finished {:?}", key);
|
||||
// Send the chunk to all nearby players
|
||||
for (entity, player, pos) in (
|
||||
&self.state.ecs().entities(),
|
||||
@ -194,7 +193,6 @@ impl Server {
|
||||
// TODO: Distance check
|
||||
// if self.state.terrain().key_pos(key)
|
||||
|
||||
println!("Send to player {:?}", key);
|
||||
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: Box::new(chunk.clone()),
|
||||
|
@ -67,7 +67,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_z(),
|
||||
-Vec3::unit_x(),
|
||||
@ -80,7 +80,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_z(),
|
||||
Vec3::unit_x(),
|
||||
@ -93,7 +93,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_z(),
|
||||
-Vec3::unit_y(),
|
||||
@ -106,7 +106,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_x(),
|
||||
Vec3::unit_y(),
|
||||
@ -119,7 +119,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_x(),
|
||||
-Vec3::unit_z(),
|
||||
@ -132,7 +132,7 @@ impl<M> Meshable for Dyna<Block, M> {
|
||||
.unwrap_or(true)
|
||||
{
|
||||
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_y(),
|
||||
Vec3::unit_z(),
|
||||
|
@ -106,9 +106,9 @@ impl Terrain {
|
||||
|
||||
if client.state().terrain().get_key(pos).is_some() {
|
||||
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
|
||||
None => self.mesh_todo.push_back(ChunkMeshState {
|
||||
_ /* None */ => self.mesh_todo.push_back(ChunkMeshState {
|
||||
pos,
|
||||
started_tick: current_tick,
|
||||
active_worker: false,
|
||||
|
@ -40,8 +40,8 @@ impl World {
|
||||
let wpos = lpos + chunk_pos * chunk.get_size().map(|e| e as i32);
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
let freq = 1.0 / 32.0;
|
||||
let ampl = 16.0;
|
||||
let freq = 1.0 / 64.0;
|
||||
let ampl = 12.0;
|
||||
let offs = 16.0;
|
||||
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 {
|
||||
stone
|
||||
} else {
|
||||
sand
|
||||
grass
|
||||
}
|
||||
} else {
|
||||
air
|
||||
|
Loading…
Reference in New Issue
Block a user