diff --git a/client/src/lib.rs b/client/src/lib.rs index 0fcccc1e27..0101408ed4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -67,7 +67,6 @@ impl Client { // Wait for handshake from server let (state, player) = match postbox.next_message() { Some(ServerMsg::Handshake { ecs_state, player_entity }) => { - println!("STATE PACKAGE! {:?}", ecs_state); let mut state = State::from_state_package(ecs_state); let player_entity = state.ecs().entity_from_uid(player_entity); (state, player_entity) @@ -99,12 +98,6 @@ impl Client { #[allow(dead_code)] pub fn thread_pool(&self) -> &threadpool::ThreadPool { &self.thread_pool } - // TODO: Get rid of this - pub fn with_test_state(mut self) -> Self { - self.chunk = Some(self.world.generate_chunk(Vec3::zero())); - self - } - /// Get a reference to the client's game state. #[allow(dead_code)] pub fn state(&self) -> &State { &self.state } @@ -158,6 +151,9 @@ impl Client { const PLAYER_VELOCITY: f32 = 100.0; // TODO: Set acceleration instead self.state.write_component(ecs_entity, comp::phys::Vel(Vec3::from(input.move_dir * PLAYER_VELOCITY))); + if input.move_dir.magnitude() > 0.01 { + self.state.write_component(ecs_entity, comp::phys::Dir(input.move_dir.normalized().into())); + } } // Tick the client's LocalState (step 3) @@ -207,10 +203,7 @@ impl Client { ServerMsg::Pong => {}, ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)), ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here! - ServerMsg::EcsSync(sync_package) => { - println!("SYNC PACKAGE! {:?}", sync_package); - self.state.ecs_mut().sync_with_package(sync_package) - }, + ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package), } } } else if let Some(err) = self.postbox.error() { diff --git a/server/src/lib.rs b/server/src/lib.rs index 937c70368c..35946c683e 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -182,6 +182,9 @@ impl Server { // Write client components state.write_component(client.entity, player); + state.write_component(client.entity, comp::phys::Pos(Vec3::zero())); + state.write_component(client.entity, comp::phys::Vel(Vec3::zero())); + state.write_component(client.entity, comp::phys::Dir(Vec3::unit_y())); if let Some(character) = character { state.write_component(client.entity, character); } diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index 088f56a4ca..be609f0d59 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -25,7 +25,7 @@ impl Bone { } } -pub trait Skeleton { +pub trait Skeleton: Send + Sync + 'static { fn compute_matrices(&self) -> [FigureBoneData; 16]; } diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 8418ab5850..e0c5ae6ba4 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -1,5 +1,14 @@ -use specs::{Component, VecStorage}; +use std::{ + collections::HashMap, + f32, +}; +use specs::{Entity as EcsEntity, Component, VecStorage, Join}; use vek::*; +use client::Client; +use common::{ + comp, + figure::Segment, +}; use crate::{ Error, render::{ @@ -12,42 +21,50 @@ use crate::{ FigureBoneData, FigureLocals, }, - anim::Skeleton, + anim::{ + Animation, + Skeleton, + character::{ + CharacterSkeleton, + RunAnimation, + }, + }, + mesh::Meshable, }; -pub struct Figure { - // GPU data - model: Model, - bone_consts: Consts, - locals: Consts, - - // CPU data - bone_meshes: [Option>; 16], - pub skeleton: S, +pub struct Figures { + test_model: Model, + states: HashMap>, } -impl Figure { - pub fn new( - renderer: &mut Renderer, - bone_meshes: [Option>; 16], - skeleton: S, - ) -> Result { - let mut this = Self { - model: renderer.create_model(&Mesh::new())?, - bone_consts: renderer.create_consts(&skeleton.compute_matrices())?, - locals: renderer.create_consts(&[FigureLocals::default()])?, +impl Figures { + pub fn new(renderer: &mut Renderer) -> Self { + // TODO: Make a proper asset loading system + fn load_segment(filename: &'static str) -> Segment { + Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap()) + } - bone_meshes, - skeleton, - }; - this.update_model(renderer)?; - Ok(this) - } + let bone_meshes = [ + Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))), + Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))), + Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))), + Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))), + Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))), + Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))), + Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))), + Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))), + Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))), + None, + None, + None, + None, + None, + None, + None, + ]; - pub fn update_model(&mut self, renderer: &mut Renderer) -> Result<(), Error> { let mut mesh = Mesh::new(); - - self.bone_meshes + bone_meshes .iter() .enumerate() .filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm))) @@ -55,39 +72,69 @@ impl Figure { mesh.push_mesh_map(bone_mesh, |vert| vert.with_bone_idx(i as u8)) }); - self.model = renderer.create_model(&mesh)?; - Ok(()) + Self { + test_model: renderer.create_model(&mesh).unwrap(), + states: HashMap::new(), + } } - pub fn update_skeleton(&mut self, renderer: &mut Renderer) -> Result<(), Error> { - renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices())?; - Ok(()) + pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) { + let time = client.state().get_time(); + let ecs = client.state_mut().ecs_mut().internal_mut(); + for (entity, pos, dir, character) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ).join() { + let state = self.states + .entry(entity) + .or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new())); + + state.update(renderer, pos.0, dir.0); + + RunAnimation::update_skeleton(&mut state.skeleton, time); + } + + self.states.retain(|entity, _| ecs.entities().is_alive(*entity)); } - pub fn update_locals(&mut self, renderer: &mut Renderer, locals: FigureLocals) -> Result<(), Error> { - renderer.update_consts(&mut self.locals, &[locals])?; - Ok(()) - } - - pub fn render(&self, renderer: &mut Renderer, globals: &Consts) { - renderer.render_figure( - &self.model, - globals, - &self.locals, - &self.bone_consts, - ); + pub fn render(&self, renderer: &mut Renderer, client: &Client, globals: &Consts) { + for state in self.states.values() { + renderer.render_figure( + &self.test_model, + globals, + &state.locals, + &state.bone_consts, + ); + } } } -/* -#[derive(Copy, Clone, Debug)] -pub struct Figure { +pub struct FigureState { bone_consts: Consts, locals: Consts, skeleton: S, } -impl Component for Figure { - type Storage = VecStorage; +impl FigureState { + pub fn new(renderer: &mut Renderer, skeleton: S) -> Self { + Self { + bone_consts: renderer.create_consts(&skeleton.compute_matrices()).unwrap(), + locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), + skeleton, + } + } + + fn update(&mut self, renderer: &mut Renderer, pos: Vec3, dir: Vec3) { + let mat = + Mat4::::identity() * + Mat4::translation_3d(pos) * + Mat4::rotation_z(dir.y.atan2(dir.x) + f32::consts::PI / 2.0); + + let locals = FigureLocals::new(mat); + renderer.update_consts(&mut self.locals, &[locals]).unwrap(); + + renderer.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()).unwrap(); + } } -*/ diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index e3e885b1ea..d2324d6758 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -29,7 +29,7 @@ use crate::{ }; use self::{ camera::Camera, - figure::Figure, + figure::Figures, terrain::Terrain, }; @@ -47,13 +47,7 @@ pub struct Scene { skybox: Skybox, terrain: Terrain, - - test_figure: Figure, -} - -// TODO: Make a proper asset loading system -fn load_segment(filename: &'static str) -> Segment { - Segment::from(dot_vox::load(&(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/voxel/").to_string() + filename)).unwrap()) + figures: Figures, } impl Scene { @@ -74,30 +68,7 @@ impl Scene { .unwrap(), }, terrain: Terrain::new(), - - test_figure: Figure::new( - renderer, - [ - Some(load_segment("head.vox").generate_mesh(Vec3::new(-7.0, -6.5, -6.0))), - Some(load_segment("chest.vox").generate_mesh(Vec3::new(-6.0, -3.0, 0.0))), - Some(load_segment("belt.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))), - Some(load_segment("pants.vox").generate_mesh(Vec3::new(-5.0, -3.0, 0.0))), - Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))), - Some(load_segment("hand.vox").generate_mesh(Vec3::new(-2.0, -2.0, -1.0))), - Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))), - Some(load_segment("foot.vox").generate_mesh(Vec3::new(-2.5, -3.0, -2.0))), - Some(load_segment("sword.vox").generate_mesh(Vec3::new(-6.5, -1.0, 0.0))), - None, - None, - None, - None, - None, - None, - None, - ], - CharacterSkeleton::new(), - ) - .unwrap(), + figures: Figures::new(renderer), } } @@ -133,7 +104,7 @@ impl Scene { } /// Maintain data such as GPU constant buffers, models, etc. To be called once per tick. - pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { + pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) { // Get player position let player_pos = client .player() @@ -165,24 +136,13 @@ impl Scene { )]) .expect("Failed to update global constants"); - // Maintain the terrain + // Maintain the terrain and figures self.terrain.maintain(renderer, client); - - // TODO: Don't do this here - RunAnimation::update_skeleton( - &mut self.test_figure.skeleton, - client.state().get_time(), - ); - - // Calculate player model matrix - let model_mat = Mat4::::translation_3d(player_pos); - - self.test_figure.update_locals(renderer, FigureLocals::new(model_mat)).unwrap(); - self.test_figure.update_skeleton(renderer).unwrap(); + self.figures.maintain(renderer, client); } /// Render the scene using the provided `Renderer` - pub fn render_to(&self, renderer: &mut Renderer) { + pub fn render(&self, renderer: &mut Renderer, client: &Client) { // Render the skybox first (it appears over everything else so must be rendered first) renderer.render_skybox( &self.skybox.model, @@ -190,10 +150,8 @@ impl Scene { &self.skybox.locals, ); - // Render terrain + // Render terrain and figures self.terrain.render(renderer, &self.globals); - - // Render the test figure - self.test_figure.render(renderer, &self.globals); + self.figures.render(renderer, client, &self.globals); } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index d11a6dfa13..fc9ec54126 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -87,7 +87,7 @@ impl SessionState { renderer.clear(BG_COLOR); // Render the screen using the global renderer - self.scene.render_to(renderer); + self.scene.render(renderer, &self.client.borrow()); // Draw the UI to the screen self.hud.render(renderer); @@ -157,7 +157,7 @@ impl PlayState for SessionState { .expect("Failed to tick the scene"); // Maintain the scene - self.scene.maintain(global_state.window.renderer_mut(), &self.client.borrow()); + self.scene.maintain(global_state.window.renderer_mut(), &mut self.client.borrow_mut()); // Maintain the UI for event in self.hud.maintain(global_state.window.renderer_mut()) { match event {