Moved figure rendering to a better place

Former-commit-id: 51ca948e131c20151c97f9f8454c80c47a9154b0
This commit is contained in:
Joshua Barretto 2019-04-10 22:51:14 +01:00
parent 62b91eb01b
commit 11630877e3
6 changed files with 119 additions and 118 deletions

View File

@ -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() {

View File

@ -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);
}

View File

@ -25,7 +25,7 @@ impl Bone {
}
}
pub trait Skeleton {
pub trait Skeleton: Send + Sync + 'static {
fn compute_matrices(&self) -> [FigureBoneData; 16];
}

View File

@ -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<S: Skeleton> {
// GPU data
model: Model<FigurePipeline>,
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
// CPU data
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
pub skeleton: S,
pub struct Figures {
test_model: Model<FigurePipeline>,
states: HashMap<EcsEntity, FigureState<CharacterSkeleton>>,
}
impl<S: Skeleton> Figure<S> {
pub fn new(
renderer: &mut Renderer,
bone_meshes: [Option<Mesh<FigurePipeline>>; 16],
skeleton: S,
) -> Result<Self, Error> {
let mut this = Self {
model: renderer.create_model(&Mesh::new())?,
bone_consts: renderer.create_consts(&skeleton.compute_matrices())?,
locals: renderer.create_consts(&[FigureLocals::default()])?,
bone_meshes,
skeleton,
};
this.update_model(renderer)?;
Ok(this)
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())
}
pub fn update_model(&mut self, renderer: &mut Renderer) -> Result<(), Error> {
let mut mesh = Mesh::new();
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,
];
self.bone_meshes
let mut mesh = Mesh::new();
bone_meshes
.iter()
.enumerate()
.filter_map(|(i, bm)| bm.as_ref().map(|bm| (i, bm)))
@ -55,39 +72,69 @@ impl<S: Skeleton> Figure<S> {
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::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Dir>(),
&ecs.read_storage::<comp::Character>(),
).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);
}
pub fn update_locals(&mut self, renderer: &mut Renderer, locals: FigureLocals) -> Result<(), Error> {
renderer.update_consts(&mut self.locals, &[locals])?;
Ok(())
self.states.retain(|entity, _| ecs.entities().is_alive(*entity));
}
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
pub fn render(&self, renderer: &mut Renderer, client: &Client, globals: &Consts<Globals>) {
for state in self.states.values() {
renderer.render_figure(
&self.model,
&self.test_model,
globals,
&self.locals,
&self.bone_consts,
&state.locals,
&state.bone_consts,
);
}
}
}
/*
#[derive(Copy, Clone, Debug)]
pub struct Figure<S: Skeleton> {
pub struct FigureState<S: Skeleton> {
bone_consts: Consts<FigureBoneData>,
locals: Consts<FigureLocals>,
skeleton: S,
}
impl<S: Skeleton> Component for Figure<S> {
type Storage = VecStorage<Self>;
impl<S: Skeleton> FigureState<S> {
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<f32>, dir: Vec3<f32>) {
let mat =
Mat4::<f32>::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();
}
}
*/

View File

@ -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<CharacterSkeleton>,
}
// 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::<f32>::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);
}
}

View File

@ -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 {