From d8f6025b68864cbb64230bee2429e2ed91255cbc Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 12 May 2019 14:02:47 +0100 Subject: [PATCH] Added rendering to character selection menu Former-commit-id: 82992904639ec2f468c70f43f3f8fe721a98de51 --- voxygen/src/menu/char_selection/mod.rs | 10 ++ voxygen/src/menu/char_selection/scene.rs | 150 +++++++++++++++++++++++ voxygen/src/menu/char_selection/ui.rs | 30 ++--- voxygen/src/scene/camera.rs | 15 +++ voxygen/src/scene/figure.rs | 73 +++++++---- voxygen/src/scene/mod.rs | 22 ++-- 6 files changed, 251 insertions(+), 49 deletions(-) create mode 100644 voxygen/src/menu/char_selection/scene.rs diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 565c114527..b3d2ddb10c 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -1,4 +1,5 @@ mod ui; +mod scene; use crate::{ session::SessionState, @@ -9,6 +10,7 @@ use client::{self, Client}; use common::{clock::Clock, msg::ClientMsg}; use std::{cell::RefCell, rc::Rc, time::Duration}; use ui::CharSelectionUi; +use scene::Scene; use vek::*; const FPS: u64 = 60; @@ -16,6 +18,7 @@ const FPS: u64 = 60; pub struct CharSelectionState { char_selection_ui: CharSelectionUi, client: Rc>, + scene: Scene, } impl CharSelectionState { @@ -24,6 +27,7 @@ impl CharSelectionState { Self { char_selection_ui: CharSelectionUi::new(window), client, + scene: Scene::new(window.renderer_mut()), } } } @@ -82,6 +86,12 @@ impl PlayState for CharSelectionState { } } + // Maintain the scene + self.scene.maintain(global_state.window.renderer_mut(), &self.client.borrow()); + + // Render the scene + self.scene.render(global_state.window.renderer_mut(), &self.client.borrow()); + // Draw the UI to the screen self.char_selection_ui .render(global_state.window.renderer_mut()); diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs new file mode 100644 index 0000000000..ec6297ef0e --- /dev/null +++ b/voxygen/src/menu/char_selection/scene.rs @@ -0,0 +1,150 @@ +use vek::*; +use client::Client; +use common::{ + comp::Character, + figure::Segment, +}; +use crate::{ + render::{ + Renderer, + Consts, + Globals, + Model, + SkyboxLocals, + SkyboxPipeline, + create_skybox_mesh, + PostProcessLocals, + PostProcessPipeline, + create_pp_mesh, + FigurePipeline, + }, + scene::{ + camera::Camera, + figure::{FigureModelCache, FigureState}, + }, + anim::{ + Skeleton, + Animation, + character::{ + CharacterSkeleton, + IdleAnimation, + }, + }, +}; + +struct Skybox { + model: Model, + locals: Consts, +} + +struct PostProcess { + model: Model, + locals: Consts, +} + +pub struct Scene { + globals: Consts, + camera: Camera, + + skybox: Skybox, + postprocess: PostProcess, + backdrop_model: Model, + backdrop_state: FigureState, + + figure_model_cache: FigureModelCache, + figure_state: FigureState, +} + +impl Scene { + pub fn new(renderer: &mut Renderer) -> Self { + let resolution = renderer.get_resolution().map(|e| e as f32); + + Self { + globals: renderer.create_consts(&[Globals::default()]).unwrap(), + camera: Camera::new(resolution.x / resolution.y), + + skybox: Skybox { + model: renderer.create_model(&create_skybox_mesh()).unwrap(), + locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(), + }, + postprocess: PostProcess { + model: renderer.create_model(&create_pp_mesh()).unwrap(), + locals: renderer + .create_consts(&[PostProcessLocals::default()]) + .unwrap(), + }, + figure_model_cache: FigureModelCache::new(), + figure_state: FigureState::new(renderer, CharacterSkeleton::new()), + + backdrop_model: renderer.create_model(&FigureModelCache::load_mesh("knight.vox", Vec3::zero())).unwrap(), + backdrop_state: FigureState::new(renderer, CharacterSkeleton::new()), + } + } + + pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { + self.camera.set_focus_pos(Vec3::unit_z() * 1.75); + self.camera.update(client.state().get_time()); + self.camera.set_distance(4.0); + self.camera.set_orientation(Vec3::new( + client.state().get_time() as f32 * 0.2, + 0.3, + 0.0, + )); + + let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client); + + renderer.update_consts( + &mut self.globals, + &[Globals::new( + view_mat, + proj_mat, + cam_pos, + self.camera.get_focus_pos(), + 100.0, + client.state().get_time_of_day(), + client.state().get_time(), + renderer.get_resolution(), + )], + ); + + self.figure_model_cache.clean(client.get_tick()); + + let tgt_skeleton = IdleAnimation::update_skeleton( + self.figure_state.skeleton_mut(), + client.state().get_time(), + client.state().get_time(), + ); + self.figure_state.skeleton_mut().interpolate(&tgt_skeleton); + + self.figure_state.update(renderer, Vec3::zero(), -Vec3::unit_y()); + } + + pub fn render(&mut self, renderer: &mut Renderer, client: &Client) { + renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); + + let model = self.figure_model_cache.get_or_create_model( + renderer, + Character::random(), + client.get_tick(), + ); + renderer.render_figure( + model, + &self.globals, + self.figure_state.locals(), + self.figure_state.bone_consts(), + ); + + renderer.render_figure( + &self.backdrop_model, + &self.globals, + self.backdrop_state.locals(), + self.backdrop_state.bone_consts(), + ); + + renderer.render_post_process( + &self.postprocess.model, + &self.globals, + &self.postprocess.locals, + ); + } +} diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index 469e3a84ea..d53f9f0ffa 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -155,7 +155,6 @@ widget_ids! { warpaint_slider_indicator, warpaint_slider_range, warpaint_slider_text, - } } @@ -316,13 +315,13 @@ impl CharSelectionUi { // Background Image if !self.character_creation { - Image::new(self.imgs.bg_selection) - .middle_of(ui_widgets.window) - .set(self.ids.bg_selection, ui_widgets); + //Image::new(self.imgs.bg_selection) + // .middle_of(ui_widgets.window) + // .set(self.ids.bg_selection, ui_widgets); // Logout_Button if Button::image(self.imgs.button) - .bottom_left_with_margins_on(self.ids.bg_selection, 10.0, 10.0) + .bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0) .w_h(150.0, 40.0) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) @@ -338,7 +337,7 @@ impl CharSelectionUi { // Create Character Button if Button::image(self.imgs.button) - .mid_bottom_with_margin_on(self.ids.bg_selection, 10.0) + .mid_bottom_with_margin_on(ui_widgets.window, 10.0) .w_h(270.0, 50.0) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) @@ -354,7 +353,7 @@ impl CharSelectionUi { } // Test Characters if Button::image(self.imgs.test_char_l_button) - .bottom_left_with_margins_on(self.ids.bg_selection, 395.0, 716.0) + .bottom_left_with_margins_on(ui_widgets.window, 395.0, 716.0) .w_h(95.0, 130.0) .hover_image(self.imgs.test_char_l_button) .press_image(self.imgs.test_char_l_button) @@ -376,7 +375,7 @@ impl CharSelectionUi { .set(self.ids.version, ui_widgets); // Click Character to Login <-- Temporary! Image::new(self.imgs.window_frame_2) - .mid_top_with_margin_on(self.ids.bg_selection, 60.0) + .mid_top_with_margin_on(ui_widgets.window, 60.0) .w_h(700.0, 70.0) .set(self.ids.help_text_bg, ui_widgets); Text::new("Click character to select it") @@ -446,12 +445,13 @@ impl CharSelectionUi { // Character_Creation ////////////// else { // Background - Image::new(self.imgs.bg_creation) - .middle_of(ui_widgets.window) - .set(self.ids.bg_creation, ui_widgets); + //Image::new(self.imgs.bg_creation) + // .middle_of(ui_widgets.window) + // .set(self.ids.bg_creation, ui_widgets); + // Back Button if Button::image(self.imgs.button) - .bottom_left_with_margins_on(self.ids.bg_creation, 10.0, 10.0) + .bottom_left_with_margins_on(ui_widgets.window, 10.0, 10.0) .w_h(150.0, 40.0) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) @@ -466,7 +466,7 @@ impl CharSelectionUi { } // Create Button if Button::image(self.imgs.button) - .bottom_right_with_margins_on(self.ids.bg_creation, 10.0, 10.0) + .bottom_right_with_margins_on(ui_widgets.window, 10.0, 10.0) .w_h(150.0, 40.0) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) @@ -482,7 +482,7 @@ impl CharSelectionUi { } // Character Name Input Rectangle::fill_with([320.0, 50.0], color::rgba(0.0, 0.0, 0.0, 0.99)) - .mid_bottom_with_margin_on(self.ids.bg_creation, 20.0) + .mid_bottom_with_margin_on(ui_widgets.window, 20.0) .set(self.ids.name_input_bg, ui_widgets); Button::image(self.imgs.name_input) .w_h(337.0, 67.0) @@ -514,7 +514,7 @@ impl CharSelectionUi { self.imgs.creation_window }) .w_h(628.0, 814.0) - .top_left_with_margins_on(self.ids.bg_creation, 60.0, 30.0) + .top_left_with_margins_on(ui_widgets.window, 60.0, 30.0) .set(self.ids.creation_window, ui_widgets); // Arrows diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index 3768986e73..afdac9606b 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -95,12 +95,27 @@ impl Camera { self.ori.z = (self.ori.z + delta.z) % (2.0 * PI); } + /// Set the orientation of the camera about its focus + pub fn set_orientation(&mut self, orientation: Vec3) { + // Wrap camera yaw + self.ori.x = orientation.x % (2.0 * PI); + // Clamp camera pitch to the vertical limits + self.ori.y = orientation.y.min(PI / 2.0).max(-PI / 2.0); + // Wrap camera roll + self.ori.z = orientation.z % (2.0 * PI); + } + /// Zoom the camera by the given delta, limiting the input accordingly. pub fn zoom_by(&mut self, delta: f32) { // Clamp camera dist to the 0 <= x <= infinity range self.tgt_dist = (self.tgt_dist + delta).max(0.0); } + /// Set the distance of the camera from the target (i.e: zoom) + pub fn set_distance(&mut self, dist: f32) { + self.tgt_dist = dist; + } + pub fn update(&mut self, time: f64) { // This is horribly frame time dependent, but so is most of the game let delta = self.last_time.replace(time).map_or(0.0, |t| time - t); diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index 62451a6252..f6c4455a23 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -24,31 +24,29 @@ use specs::{Component, Entity as EcsEntity, Join, VecStorage}; use std::{collections::HashMap, f32}; use vek::*; -pub struct FigureCache { +pub struct FigureModelCache { models: HashMap, u64)>, - states: HashMap>, } -impl FigureCache { +impl FigureModelCache { pub fn new() -> Self { Self { models: HashMap::new(), - states: HashMap::new(), } } - pub fn get_or_create_model<'a>( - models: &'a mut HashMap, u64)>, + pub fn get_or_create_model( + &mut self, renderer: &mut Renderer, - tick: u64, character: Character, - ) -> &'a (Model, u64) { - match models.get_mut(&character) { + tick: u64, + ) -> &Model { + match self.models.get_mut(&character) { Some((model, last_used)) => { *last_used = tick; } None => { - models.insert( + self.models.insert( character, ( { @@ -90,7 +88,7 @@ impl FigureCache { } } - &models[&character] + &self.models[&character].0 } pub fn clean(&mut self, tick: u64) { @@ -99,7 +97,8 @@ impl FigureCache { .retain(|_, (_, last_used)| *last_used + 60 > tick); } - fn load_mesh(filename: &str, position: Vec3) -> Mesh { + // TODO: Don't make this public + pub fn load_mesh(filename: &str, position: Vec3) -> Mesh { let fullpath: String = ["/voxygen/voxel/", filename].concat(); Segment::from(assets::load_expect::(fullpath.as_str()).as_ref()) .generate_mesh(position) @@ -187,10 +186,28 @@ impl FigureCache { Vec3::new(0.0, 0.0, -4.0), ) } +} - pub fn maintain(&mut self, renderer: &mut Renderer, client: &mut Client) { +pub struct FigureMgr { + model_cache: FigureModelCache, + states: HashMap>, +} + +impl FigureMgr { + pub fn new() -> Self { + Self { + model_cache: FigureModelCache::new(), + states: HashMap::new(), + } + } + + pub fn clean(&mut self, tick: u64) { + self.model_cache.clean(tick); + } + + pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { let time = client.state().get_time(); - let ecs = client.state_mut().ecs_mut(); + let ecs = client.state().ecs(); for (entity, pos, vel, dir, character, animation_history) in ( &ecs.entities(), &ecs.read_storage::(), @@ -208,17 +225,17 @@ impl FigureCache { let target_skeleton = match animation_history.current { comp::character::Animation::Idle => IdleAnimation::update_skeleton( - &mut state.skeleton, + state.skeleton_mut(), time, animation_history.time, ), comp::character::Animation::Run => RunAnimation::update_skeleton( - &mut state.skeleton, + state.skeleton_mut(), (vel.0.magnitude(), time), animation_history.time, ), comp::character::Animation::Jump => JumpAnimation::update_skeleton( - &mut state.skeleton, + state.skeleton_mut(), time, animation_history.time, ), @@ -229,8 +246,7 @@ impl FigureCache { state.update(renderer, pos.0, dir.0); } - self.states - .retain(|entity, _| ecs.entities().is_alive(*entity)); + self.states.retain(|entity, _| ecs.entities().is_alive(*entity)); } pub fn render( @@ -241,13 +257,12 @@ impl FigureCache { ) { let tick = client.get_tick(); let ecs = client.state().ecs(); - let models = &mut self.models; for (entity, &character) in (&ecs.entities(), &ecs.read_storage::()).join() { if let Some(state) = self.states.get(&entity) { - let model = Self::get_or_create_model(models, renderer, tick, character); - renderer.render_figure(&model.0, globals, &state.locals, &state.bone_consts); + let model = self.model_cache.get_or_create_model(renderer, character, tick); + renderer.render_figure(model, globals, &state.locals(), state.bone_consts()); } } } @@ -270,7 +285,7 @@ impl FigureState { } } - fn update(&mut self, renderer: &mut Renderer, pos: Vec3, dir: Vec3) { + pub fn update(&mut self, renderer: &mut Renderer, pos: Vec3, dir: Vec3) { let mat = Mat4::::identity() * Mat4::translation_3d(pos) * Mat4::rotation_z(-dir.x.atan2(dir.y)); // + f32::consts::PI / 2.0); @@ -282,4 +297,16 @@ impl FigureState { .update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()) .unwrap(); } + + pub fn locals(&self) -> &Consts { + &self.locals + } + + pub fn bone_consts(&self) -> &Consts { + &self.bone_consts + } + + pub fn skeleton_mut(&mut self) -> &mut S { + &mut self.skeleton + } } diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index e445495cf3..b276425701 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -2,7 +2,10 @@ pub mod camera; pub mod figure; pub mod terrain; -use self::{camera::Camera, figure::FigureCache, terrain::Terrain}; +use dot_vox; +use vek::*; +use common::{comp, figure::Segment}; +use client::Client; use crate::{ anim::{ character::{CharacterSkeleton, RunAnimation}, @@ -15,10 +18,7 @@ use crate::{ }, window::Event, }; -use client::Client; -use common::{comp, figure::Segment}; -use dot_vox; -use vek::*; +use self::{camera::Camera, figure::FigureMgr, terrain::Terrain}; // TODO: Don't hard-code this const CURSOR_PAN_SCALE: f32 = 0.005; @@ -41,7 +41,7 @@ pub struct Scene { postprocess: PostProcess, terrain: Terrain, - figure_cache: FigureCache, + figure_mgr: FigureMgr, } impl Scene { @@ -64,7 +64,7 @@ impl Scene { .unwrap(), }, terrain: Terrain::new(), - figure_cache: FigureCache::new(), + figure_mgr: FigureMgr::new(), } } @@ -104,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: &mut Client) { + pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) { // Get player position let player_pos = client .state() @@ -144,10 +144,10 @@ impl Scene { self.terrain.maintain(renderer, client); // Maintain the figures - self.figure_cache.maintain(renderer, client); + self.figure_mgr.maintain(renderer, client); // Remove unused figures - self.figure_cache.clean(client.get_tick()); + self.figure_mgr.clean(client.get_tick()); } /// Render the scene using the provided `Renderer` @@ -157,7 +157,7 @@ impl Scene { // Render terrain and figures self.terrain.render(renderer, &self.globals); - self.figure_cache.render(renderer, client, &self.globals); + self.figure_mgr.render(renderer, client, &self.globals); renderer.render_post_process( &self.postprocess.model,