From 70408ca9bb1eb8a84ad2153f598652271ea31011 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 2 Jul 2018 23:48:25 +0100 Subject: [PATCH] Added dynamic terrain, added client payloads --- client/src/lib.rs | 68 ++++++++++++++++++++++++++++------ region/src/lib.rs | 1 + region/src/vol_mgr.rs | 43 +++++++++++++--------- voxygen/src/game.rs | 86 ++++++++++++++++++++++++------------------- voxygen/src/main.rs | 1 - voxygen/src/map.rs | 21 ----------- 6 files changed, 132 insertions(+), 88 deletions(-) delete mode 100644 voxygen/src/map.rs diff --git a/client/src/lib.rs b/client/src/lib.rs index fb4c854800..6cb13f2992 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(nll)] +#![feature(nll, euclidean_division)] // Crates #[macro_use] @@ -28,7 +28,7 @@ use std::net::{ToSocketAddrs}; use coord::prelude::*; // Project -use region::Entity; +use region::{Entity, Chunk, VolMgr, VolGen, FnPayloadFunc}; use common::{get_version, Uid}; use common::net; use common::net::{Connection, ServerMessage, ClientMessage, Callback, UdpMgr}; @@ -57,26 +57,36 @@ pub enum ClientStatus { Disconnected, } -pub struct Client { +pub trait Payloads: 'static { + type Chunk: Send + Sync + 'static; +} + +pub struct Client { status: RwLock, conn: Arc>, player: RwLock, entities: RwLock>, + chunk_mgr: VolMgr::Chunk>, + callbacks: RwLock, finished: Barrier, // We use this to synchronize client shutdown } -impl Callback for Client { +impl Callback for Client

{ fn recv(&self, msg: Result) { self.handle_packet(msg.unwrap()); } } -impl Client { - pub fn new(mode: ClientMode, alias: String, remote_addr: U) -> Result, Error> { +fn gen_chunk(pos: Vec2) -> Chunk { + Chunk::test(vec3!(pos.x * 16, pos.y * 16, 0), vec3!(16, 16, 128)) +} + +impl Client

{ + pub fn new>(mode: ClientMode, alias: String, remote_addr: U, gen_payload: GF) -> Result>, Error> { let mut conn = Connection::new::(&remote_addr, Box::new(|m| { // //self.handle_packet(m); @@ -91,6 +101,8 @@ impl Client { player: RwLock::new(Player::new(alias)), entities: RwLock::new(HashMap::new()), + chunk_mgr: VolMgr::new(16, VolGen::new(gen_chunk, gen_payload)), + callbacks: RwLock::new(Callbacks::new()), finished: Barrier::new(2), @@ -109,15 +121,47 @@ impl Client { fn tick(&self, dt: f32) { if let Some(uid) = self.player().entity_uid { - if let Some(player_entry) = self.entities_mut().get_mut(&uid) { + if let Some(player_entity) = self.entities_mut().get_mut(&uid) { self.conn.send(ClientMessage::PlayerEntityUpdate { - pos: player_entry.pos(), - move_dir: player_entry.move_dir(), - look_dir: player_entry.look_dir(), + pos: player_entity.pos(), + move_dir: player_entity.move_dir(), + look_dir: player_entity.look_dir(), }); } } + // Generate terrain around the player + if let Some(uid) = self.player().entity_uid { + if let Some(player_entity) = self.entities_mut().get_mut(&uid) { + let (x, y) = ( + (player_entity.pos().x as i64).div_euc(16), + (player_entity.pos().y as i64).div_euc(16) + ); + + // TODO: define a view distance?! + for i in x - 3 .. x + 4 { + for j in y - 3 .. y + 4 { + if !self.chunk_mgr().contains(vec2!(i, j)) { + self.chunk_mgr().gen(vec2!(i, j)); + } + } + } + + // This should also be tied to view distance, and could be more efficient + // (maybe? careful: deadlocks) + let chunk_pos = self.chunk_mgr() + .volumes() + .keys() + .map(|p| *p) + .collect::>(); + for pos in chunk_pos { + if (pos - vec2!(x, y)).snake_length() > 10 { + self.chunk_mgr().remove(pos); + } + } + } + } + for (uid, entity) in self.entities_mut().iter_mut() { let move_dir = entity.move_dir(); *entity.pos_mut() += move_dir * dt; @@ -165,7 +209,7 @@ impl Client { } } - fn start(client: Arc) { + fn start(client: Arc>) { let client_ref = client.clone(); thread::spawn(move || { @@ -195,6 +239,8 @@ impl Client { Ok(self.conn.send(ClientMessage::SendCmd { cmd })) } + pub fn chunk_mgr<'a>(&'a self) -> &'a VolMgr { &self.chunk_mgr } + pub fn status<'a>(&'a self) -> RwLockReadGuard<'a, ClientStatus> { self.status.read().unwrap() } pub fn callbacks<'a>(&'a self) -> RwLockReadGuard<'a, Callbacks> { self.callbacks.read().unwrap() } diff --git a/region/src/lib.rs b/region/src/lib.rs index 59e2334683..74244d0ce7 100644 --- a/region/src/lib.rs +++ b/region/src/lib.rs @@ -19,6 +19,7 @@ pub use chunk::Chunk; pub use cell::{Cell}; pub use model::Model; pub use entity::Entity; +pub use vol_mgr::{VolMgr, VolGen, VolState, FnGenFunc, FnPayloadFunc}; use coord::vec3::Vec3; diff --git a/region/src/vol_mgr.rs b/region/src/vol_mgr.rs index 6a2b8069bf..6c69519b21 100644 --- a/region/src/vol_mgr.rs +++ b/region/src/vol_mgr.rs @@ -1,8 +1,9 @@ // Standard use std::thread; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, RwLock, RwLockReadGuard}; use std::collections::HashMap; use std::marker::PhantomData; +use std::any::Any; // Library use coord::prelude::*; @@ -10,20 +11,24 @@ use coord::prelude::*; // Local use {Volume, Voxel}; -pub trait VolPayload: Send + Sync {} - -pub enum VolState { +pub enum VolState { Loading, - Exists(V, Box), + Exists(V, P), } -pub struct VolGen { - gen_func: Arc) -> V>, - payload_func: Arc Box>, +pub trait FnGenFunc: Fn(Vec2) -> V + Send + Sync + 'static {} +impl) -> V + Send + Sync + 'static> FnGenFunc for T {} + +pub trait FnPayloadFunc: Fn(&V) -> P + Send + Sync + 'static {} +impl P + Send + Sync + 'static> FnPayloadFunc for T {} + +pub struct VolGen { + gen_func: Arc>, + payload_func: Arc>, } -impl VolGen { - pub fn new(gen_func: fn(Vec2) -> V, payload_func: fn(&V) -> Box) -> VolGen { +impl VolGen { + pub fn new, PF: FnPayloadFunc>(gen_func: GF, payload_func: PF) -> VolGen { VolGen { gen_func: Arc::new(gen_func), payload_func: Arc::new(payload_func), @@ -31,14 +36,14 @@ impl VolGen { } } -pub struct VolMgr { +pub struct VolMgr { vol_size: i64, - vols: RwLock, Arc>>>>, - gen: VolGen, + vols: RwLock, Arc>>>>, + gen: VolGen, } -impl VolMgr { - pub fn new(vol_size: i64, gen: VolGen) -> VolMgr { +impl VolMgr { + pub fn new(vol_size: i64, gen: VolGen) -> VolMgr { VolMgr { vol_size, vols: RwLock::new(HashMap::new()), @@ -46,10 +51,14 @@ impl VolMgr { } } - pub fn at(&self, pos: Vec2) -> Option>>> { + pub fn at(&self, pos: Vec2) -> Option>>> { self.vols.read().unwrap().get(&pos).map(|v| v.clone()) } + pub fn volumes<'a>(&'a self) -> RwLockReadGuard<'a, HashMap, Arc>>>> { + self.vols.read().unwrap() + } + pub fn contains(&self, pos: Vec2) -> bool { self.vols.read().unwrap().contains_key(&pos) } @@ -74,7 +83,7 @@ impl VolMgr { }); } - pub fn set(&self, pos: Vec2, vol: V, payload: Box) { + pub fn set(&self, pos: Vec2, vol: V, payload: P) { self.vols.write().unwrap().insert(pos, Arc::new(RwLock::new(VolState::Exists(vol, payload)))); } diff --git a/voxygen/src/game.rs b/voxygen/src/game.rs index 018df9281b..7f94dd60b1 100644 --- a/voxygen/src/game.rs +++ b/voxygen/src/game.rs @@ -3,6 +3,7 @@ use std::net::ToSocketAddrs; use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicBool, Ordering}; use std::f32::consts::PI; +use std::collections::HashMap; //use std::f32::{sin, cos}; // Library @@ -12,21 +13,26 @@ use glutin::{ElementState, VirtualKeyCode}; use dot_vox; // Project +use client; use client::{Client, ClientMode}; // Local -use map::Map; use camera::Camera; use window::{RenderWindow, Event}; use model_object::{ModelObject, Constants}; use mesh::{Mesh}; -use region::Chunk; +use region::{Chunk, VolState}; use key_state::KeyState; use vox::vox_to_model; +struct Payloads {} +impl client::Payloads for Payloads { + type Chunk = (Mesh, Option); +} + pub struct Game { running: AtomicBool, - client: Arc, + client: Arc>, window: RenderWindow, data: Mutex, camera: Mutex, @@ -37,7 +43,10 @@ pub struct Game { struct Data { player_model: ModelObject, other_player_model: ModelObject, - map: Map, +} + +fn gen_payload(chunk: &Chunk) -> ::Chunk { + (Mesh::from(chunk), None) } impl Game { @@ -64,35 +73,13 @@ impl Game { &other_player_mesh, ); - let chunk = Chunk::test(vec3!(0, 0, 0), vec3!(100,100,100)); - let test_mesh = Mesh::from(&chunk); - - let test_model = ModelObject::new( - &mut window.renderer_mut(), - &test_mesh, - ); - - let mut map = Map::new(); - map.chunks().insert(Vector3::new(0,0,0), test_model); - - let chunk = Chunk::test(Vec3::from((100,0,0)),Vec3::from((100,100,100))); - let test_mesh = Mesh::from(&chunk); - - let test_model = ModelObject::new( - &mut window.renderer_mut(), - &test_mesh, - ); - - map.chunks().insert(Vector3::new(100,0,0), test_model); - Game { data: Mutex::new(Data { player_model, other_player_model, - map, }), running: AtomicBool::new(true), - client: Client::new(mode, alias.to_string(), remote_addr) + client: Client::new(mode, alias.to_string(), remote_addr, gen_payload) .expect("Could not create new client"), window, camera: Mutex::new(Camera::new()), @@ -176,6 +163,19 @@ impl Game { self.running.load(Ordering::Relaxed) } + pub fn mesh_chunks(&self) { + for (pos, vol) in self.client.chunk_mgr().volumes().iter() { + if let VolState::Exists(ref chunk, ref mut payload) = *vol.write().unwrap() { + if let None = payload.1 { + payload.1 = Some(ModelObject::new( + &mut self.window.renderer_mut(), + &payload.0, + )); + } + } + } + } + pub fn render_frame(&self) { let mut renderer = self.window.renderer_mut(); renderer.begin_frame(); @@ -189,17 +189,26 @@ impl Game { let camera_mats = self.camera.lock().unwrap().get_mats(); let camera_ori = self.camera.lock().unwrap().ori(); - // Render the test model - - for (pos, model) in self.data.lock().unwrap().map.chunks() { - renderer.update_model_object( - &model, - Constants::new(//&Matrix4::::identity(), - &Translation3::::from_vector(convert(*pos)).to_homogeneous(), - &camera_mats.0, - &camera_mats.1) - ); - renderer.render_model_object(&model); + for (pos, vol) in self.client.chunk_mgr().volumes().iter() { + if let VolState::Exists(ref chunk, ref payload) = *vol.read().unwrap() { + if let Some(ref model) = payload.1 { + let model_mat = &Translation3::::from_vector(Vector3::::new( + pos.x as f32 * 16.0, + pos.y as f32 * 16.0, + 0.0 + )).to_homogeneous(); + + renderer.update_model_object( + &model, + Constants::new( + &model_mat, // TODO: Improve this + &camera_mats.0, + &camera_mats.1, + ) + ); + renderer.render_model_object(&model); + } + } } for (eid, entity) in self.client.entities().iter() { @@ -229,6 +238,7 @@ impl Game { pub fn run(&self) { while self.handle_window_events() { + self.mesh_chunks(); self.render_frame(); } diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index fdd5dcc2a0..2effcbe208 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -28,7 +28,6 @@ mod pipeline; mod camera; mod render_volume; mod key_state; -mod map; mod vox; use std::io; diff --git a/voxygen/src/map.rs b/voxygen/src/map.rs deleted file mode 100644 index a4d1d12407..0000000000 --- a/voxygen/src/map.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::collections::HashMap; - -use nalgebra::{Vector3}; - -use model_object::{ModelObject}; - -pub struct Map { - chunks: HashMap, ModelObject> -} - -impl Map { - pub fn new() -> Map { - Map { - chunks: HashMap::new(), - } - } - - pub fn chunks(&mut self) -> &mut HashMap, ModelObject> { - &mut self.chunks - } -}