mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Remove Client
dependency from Scene types and audio managers, add an
example for using voxygen as a library to renderer images of characters
This commit is contained in:
parent
511bbd3899
commit
4a0c474be1
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3377,6 +3377,7 @@ dependencies = [
|
||||
"specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)",
|
||||
"treeculler 0.1.0 (git+https://gitlab.com/yusdacra/treeculler.git)",
|
||||
"uvth 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vek 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"veloren-client 0.5.0",
|
||||
"veloren-common 0.5.0",
|
||||
|
@ -63,6 +63,7 @@ chrono = "0.4.9"
|
||||
rust-argon2 = "0.5"
|
||||
bincode = "1.2"
|
||||
deunicode = "1.0"
|
||||
uvth = "3.1.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
dispatch = "0.1.4"
|
||||
@ -74,6 +75,7 @@ winres = "0.1"
|
||||
criterion = "0.3"
|
||||
git2 = "0.10"
|
||||
world = { package = "veloren-world", path = "../world" }
|
||||
gfx_window_glutin = { version = "0.31.0", features = ["headless"] }
|
||||
|
||||
[[bench]]
|
||||
name = "meshing_benchmark"
|
||||
|
58
voxygen/examples/character_renderer.rs
Normal file
58
voxygen/examples/character_renderer.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use common::{assets, comp};
|
||||
use gfx_window_glutin::init_headless;
|
||||
use vek::*;
|
||||
use veloren_voxygen::{render, scene::simple as scene};
|
||||
|
||||
fn main() {
|
||||
// Setup renderer
|
||||
let dim = (200u16, 300u16, 1, gfx::texture::AaMode::Single);
|
||||
let events_loop = glutin::EventsLoop::new();
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2)))
|
||||
.build_headless(&events_loop, (dim.0 as u32, dim.1 as u32).into())
|
||||
.expect("Failed to build headless context");
|
||||
|
||||
let (_context, device, factory, color_view, depth_view) = init_headless(context, dim);
|
||||
|
||||
let mut renderer = render::Renderer::new(
|
||||
device,
|
||||
factory,
|
||||
color_view,
|
||||
depth_view,
|
||||
render::AaMode::SsaaX4,
|
||||
render::CloudMode::Regular,
|
||||
render::FluidMode::Shiny,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create character
|
||||
let body = comp::humanoid::Body::random();
|
||||
const STARTER_BOW: &str = "common.items.weapons.starter_bow";
|
||||
let equipment = comp::Equipment {
|
||||
main: assets::load_cloned(STARTER_BOW).ok(),
|
||||
alt: None,
|
||||
};
|
||||
|
||||
// Setup scene (using the character selection screen `Scene`)
|
||||
let mut scene = scene::Scene::new(&mut renderer, None);
|
||||
let scene_data = scene::SceneData {
|
||||
time: 1.0,
|
||||
delta_time: 1.0,
|
||||
tick: 0,
|
||||
body: Some(body.clone()),
|
||||
gamma: 1.0,
|
||||
};
|
||||
scene.camera_mut().set_focus_pos(Vec3::unit_z() * 0.8);
|
||||
scene.camera_mut().set_distance(1.5);
|
||||
scene.camera_mut().update(0.0);
|
||||
scene.maintain(&mut renderer, scene_data);
|
||||
|
||||
// Render
|
||||
renderer.clear();
|
||||
scene.render(&mut renderer, 0, Some(body), &equipment);
|
||||
|
||||
renderer.flush();
|
||||
// Get image
|
||||
let img = renderer.create_screenshot().unwrap();
|
||||
img.save("character.png").unwrap();
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
use crate::audio::AudioFrontend;
|
||||
use client::Client;
|
||||
use common::assets;
|
||||
use common::{assets, state::State};
|
||||
use rand::{seq::IteratorRandom, thread_rng};
|
||||
use serde::Deserialize;
|
||||
use std::time::Instant;
|
||||
@ -44,18 +43,18 @@ impl MusicMgr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, audio: &mut AudioFrontend, client: &Client) {
|
||||
pub fn maintain(&mut self, audio: &mut AudioFrontend, state: &State) {
|
||||
if audio.music_enabled()
|
||||
&& self.began_playing.elapsed().as_secs_f64() > self.next_track_change
|
||||
{
|
||||
self.play_random_track(audio, client);
|
||||
self.play_random_track(audio, state);
|
||||
}
|
||||
}
|
||||
|
||||
fn play_random_track(&mut self, audio: &mut AudioFrontend, client: &Client) {
|
||||
fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State) {
|
||||
const SILENCE_BETWEEN_TRACKS_SECONDS: f64 = 45.0;
|
||||
|
||||
let game_time = (client.state().get_time_of_day() as u64 % 86400) as u32;
|
||||
let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
|
||||
let current_period_of_day = Self::get_current_day_period(game_time);
|
||||
let mut rng = thread_rng();
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
mod movement;
|
||||
mod progression;
|
||||
|
||||
use common::state::State;
|
||||
|
||||
use movement::MovementEventMapper;
|
||||
use progression::ProgressionEventMapper;
|
||||
|
||||
use super::SfxTriggers;
|
||||
use client::Client;
|
||||
|
||||
pub struct SfxEventMapper {
|
||||
progression_event_mapper: ProgressionEventMapper,
|
||||
@ -20,8 +21,15 @@ impl SfxEventMapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) {
|
||||
self.progression_event_mapper.maintain(client, triggers);
|
||||
self.movement_event_mapper.maintain(client, triggers);
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
triggers: &SfxTriggers,
|
||||
) {
|
||||
self.progression_event_mapper
|
||||
.maintain(state, player_entity, triggers);
|
||||
self.movement_event_mapper
|
||||
.maintain(state, player_entity, triggers);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
/// from
|
||||
use crate::audio::sfx::{SfxTriggerItem, SfxTriggers};
|
||||
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{ActionState, Body, CharacterState, Item, ItemKind, MovementState, Pos, Stats, Vel},
|
||||
event::{EventBus, SfxEvent, SfxEventItem},
|
||||
state::State,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
@ -31,13 +31,13 @@ impl MovementEventMapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) {
|
||||
pub fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) {
|
||||
const SFX_DIST_LIMIT_SQR: f32 = 20000.0;
|
||||
let ecs = client.state().ecs();
|
||||
let ecs = state.ecs();
|
||||
|
||||
let player_position = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(client.entity())
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
for (entity, pos, vel, body, stats, character) in (
|
||||
@ -97,7 +97,7 @@ impl MovementEventMapper {
|
||||
}
|
||||
}
|
||||
|
||||
self.cleanup(client.entity());
|
||||
self.cleanup(player_entity);
|
||||
}
|
||||
|
||||
/// As the player explores the world, we track the last event of the nearby
|
||||
|
@ -2,10 +2,10 @@
|
||||
/// and experience and emits associated SFX
|
||||
use crate::audio::sfx::SfxTriggers;
|
||||
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::Stats,
|
||||
event::{EventBus, SfxEvent, SfxEventItem},
|
||||
state::State,
|
||||
};
|
||||
use specs::WorldExt;
|
||||
|
||||
@ -30,13 +30,18 @@ impl ProgressionEventMapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) {
|
||||
let ecs = client.state().ecs();
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
triggers: &SfxTriggers,
|
||||
) {
|
||||
let ecs = state.ecs();
|
||||
|
||||
// level and exp changes
|
||||
let next_state =
|
||||
ecs.read_storage::<Stats>()
|
||||
.get(client.entity())
|
||||
.get(player_entity)
|
||||
.map_or(self.state.clone(), |stats| ProgressionState {
|
||||
level: stats.level.level(),
|
||||
exp: stats.exp.current(),
|
||||
|
@ -4,11 +4,11 @@
|
||||
mod event_mapper;
|
||||
|
||||
use crate::audio::AudioFrontend;
|
||||
use client::Client;
|
||||
use common::{
|
||||
assets,
|
||||
comp::{Ori, Pos},
|
||||
event::{EventBus, SfxEvent, SfxEventItem},
|
||||
state::State,
|
||||
};
|
||||
use event_mapper::SfxEventMapper;
|
||||
use hashbrown::HashMap;
|
||||
@ -50,23 +50,29 @@ impl SfxMgr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, audio: &mut AudioFrontend, client: &Client) {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
audio: &mut AudioFrontend,
|
||||
state: &State,
|
||||
player_entity: specs::Entity,
|
||||
) {
|
||||
if !audio.sfx_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.event_mapper.maintain(client, &self.triggers);
|
||||
self.event_mapper
|
||||
.maintain(state, player_entity, &self.triggers);
|
||||
|
||||
let ecs = client.state().ecs();
|
||||
let ecs = state.ecs();
|
||||
|
||||
let player_position = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(client.entity())
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
let player_ori = ecs
|
||||
.read_storage::<Ori>()
|
||||
.get(client.entity())
|
||||
.get(player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
audio.set_listener_pos(&player_position, &player_ori);
|
||||
|
@ -38,7 +38,7 @@ use crate::{
|
||||
ecs::comp as vcomp,
|
||||
i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization},
|
||||
render::{AaMode, CloudMode, Consts, FluidMode, Globals, Renderer},
|
||||
scene::camera::Camera,
|
||||
scene::camera::{self, Camera},
|
||||
ui::{fonts::ConrodVoxygenFonts, Graphic, Ingameable, ScaleMode, Ui},
|
||||
window::{Event as WinEvent, GameInput},
|
||||
GlobalState,
|
||||
@ -2112,9 +2112,13 @@ impl Hud {
|
||||
self.ui.focus_widget(maybe_id);
|
||||
}
|
||||
let events = self.update_layout(client, global_state, debug_info, dt);
|
||||
let (v_mat, p_mat, _) = camera.compute_dependents(client);
|
||||
self.ui
|
||||
.maintain(&mut global_state.window.renderer_mut(), Some(p_mat * v_mat));
|
||||
let camera::Dependents {
|
||||
view_mat, proj_mat, ..
|
||||
} = camera.dependents();
|
||||
self.ui.maintain(
|
||||
&mut global_state.window.renderer_mut(),
|
||||
Some(proj_mat * view_mat),
|
||||
);
|
||||
|
||||
// Check if item images need to be reloaded
|
||||
self.item_imgs.reload_if_changed(&mut self.ui);
|
||||
|
@ -1,6 +1,82 @@
|
||||
/// Used by benchmarks
|
||||
pub mod mesh;
|
||||
pub mod render;
|
||||
#![deny(unsafe_code)]
|
||||
#![feature(drain_filter)]
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
// Used by tests
|
||||
#[macro_use]
|
||||
pub mod ui;
|
||||
pub mod anim;
|
||||
pub mod audio;
|
||||
mod ecs;
|
||||
pub mod error;
|
||||
pub mod hud;
|
||||
pub mod i18n;
|
||||
pub mod key_state;
|
||||
pub mod logging;
|
||||
pub mod menu;
|
||||
pub mod mesh;
|
||||
pub mod meta;
|
||||
pub mod render;
|
||||
pub mod scene;
|
||||
pub mod session;
|
||||
pub mod settings;
|
||||
#[cfg(feature = "singleplayer")]
|
||||
pub mod singleplayer;
|
||||
pub mod window;
|
||||
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
|
||||
use crate::{
|
||||
audio::AudioFrontend, meta::Meta, settings::Settings, singleplayer::Singleplayer,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
/// A type used to store state that is shared between all play states.
|
||||
pub struct GlobalState {
|
||||
pub settings: Settings,
|
||||
pub meta: Meta,
|
||||
pub window: Window,
|
||||
pub audio: AudioFrontend,
|
||||
pub info_message: Option<String>,
|
||||
pub singleplayer: Option<Singleplayer>,
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
/// Called after a change in play state has occurred (usually used to
|
||||
/// reverse any temporary effects a state may have made).
|
||||
pub fn on_play_state_changed(&mut self) {
|
||||
self.window.grab_cursor(false);
|
||||
self.window.needs_refresh_resize();
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); }
|
||||
}
|
||||
|
||||
pub enum Direction {
|
||||
Forwards,
|
||||
Backwards,
|
||||
}
|
||||
|
||||
/// States can either close (and revert to a previous state), push a new state
|
||||
/// on top of themselves, or switch to a totally different state.
|
||||
pub enum PlayStateResult {
|
||||
/// Pop all play states in reverse order and shut down the program.
|
||||
Shutdown,
|
||||
/// Close the current play state and pop it from the play state stack.
|
||||
Pop,
|
||||
/// Push a new play state onto the play state stack.
|
||||
Push(Box<dyn PlayState>),
|
||||
/// Switch the current play state with a new play state.
|
||||
Switch(Box<dyn PlayState>),
|
||||
}
|
||||
|
||||
/// A trait representing a playable game state. This may be a menu, a game
|
||||
/// session, the title screen, etc.
|
||||
pub trait PlayState {
|
||||
/// Play the state until some change of state is required (i.e: a menu is
|
||||
/// opened or the game is closed).
|
||||
fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult;
|
||||
|
||||
/// Get a descriptive name for this state type.
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
@ -1,94 +1,21 @@
|
||||
#![deny(unsafe_code)]
|
||||
#![feature(drain_filter)]
|
||||
#![recursion_limit = "2048"]
|
||||
|
||||
#[macro_use]
|
||||
pub mod ui;
|
||||
pub mod anim;
|
||||
pub mod audio;
|
||||
mod ecs;
|
||||
pub mod error;
|
||||
pub mod hud;
|
||||
pub mod i18n;
|
||||
pub mod key_state;
|
||||
mod logging;
|
||||
pub mod menu;
|
||||
pub mod mesh;
|
||||
pub mod meta;
|
||||
pub mod render;
|
||||
pub mod scene;
|
||||
pub mod session;
|
||||
pub mod settings;
|
||||
#[cfg(feature = "singleplayer")]
|
||||
pub mod singleplayer;
|
||||
pub mod window;
|
||||
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
|
||||
use crate::{
|
||||
audio::AudioFrontend,
|
||||
i18n::{i18n_asset_key, VoxygenLocalization},
|
||||
use veloren_voxygen::{
|
||||
audio::{self, AudioFrontend},
|
||||
i18n::{self, i18n_asset_key, VoxygenLocalization},
|
||||
logging,
|
||||
menu::main::MainMenuState,
|
||||
meta::Meta,
|
||||
settings::Settings,
|
||||
singleplayer::Singleplayer,
|
||||
window::Window,
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
|
||||
use common::assets::{load, load_expect};
|
||||
use log::{debug, error};
|
||||
use std::{mem, panic, str::FromStr};
|
||||
|
||||
/// A type used to store state that is shared between all play states.
|
||||
pub struct GlobalState {
|
||||
settings: Settings,
|
||||
meta: Meta,
|
||||
window: Window,
|
||||
audio: AudioFrontend,
|
||||
info_message: Option<String>,
|
||||
singleplayer: Option<Singleplayer>,
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
/// Called after a change in play state has occurred (usually used to
|
||||
/// reverse any temporary effects a state may have made).
|
||||
pub fn on_play_state_changed(&mut self) {
|
||||
self.window.grab_cursor(false);
|
||||
self.window.needs_refresh_resize();
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); }
|
||||
}
|
||||
|
||||
pub enum Direction {
|
||||
Forwards,
|
||||
Backwards,
|
||||
}
|
||||
|
||||
/// States can either close (and revert to a previous state), push a new state
|
||||
/// on top of themselves, or switch to a totally different state.
|
||||
pub enum PlayStateResult {
|
||||
/// Pop all play states in reverse order and shut down the program.
|
||||
Shutdown,
|
||||
/// Close the current play state and pop it from the play state stack.
|
||||
Pop,
|
||||
/// Push a new play state onto the play state stack.
|
||||
Push(Box<dyn PlayState>),
|
||||
/// Switch the current play state with a new play state.
|
||||
Switch(Box<dyn PlayState>),
|
||||
}
|
||||
|
||||
/// A trait representing a playable game state. This may be a menu, a game
|
||||
/// session, the title screen, etc.
|
||||
pub trait PlayState {
|
||||
/// Play the state until some change of state is required (i.e: a menu is
|
||||
/// opened or the game is closed).
|
||||
fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult;
|
||||
|
||||
/// Get a descriptive name for this state type.
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Initialize logging.
|
||||
let term_log_level = std::env::var_os("VOXYGEN_LOG")
|
||||
|
@ -1,16 +1,16 @@
|
||||
mod scene;
|
||||
mod ui;
|
||||
|
||||
use crate::{
|
||||
i18n::{i18n_asset_key, VoxygenLocalization},
|
||||
scene::simple::{self as scene, Scene},
|
||||
session::SessionState,
|
||||
window::Event as WinEvent,
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
use client::{self, Client};
|
||||
use common::{assets, clock::Clock, comp, msg::ClientState};
|
||||
use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime};
|
||||
use log::error;
|
||||
use scene::Scene;
|
||||
use specs::WorldExt;
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
use ui::CharSelectionUi;
|
||||
|
||||
@ -26,7 +26,10 @@ impl CharSelectionState {
|
||||
Self {
|
||||
char_selection_ui: CharSelectionUi::new(global_state),
|
||||
client,
|
||||
scene: Scene::new(global_state.window.renderer_mut()),
|
||||
scene: Scene::new(
|
||||
global_state.window.renderer_mut(),
|
||||
Some("fixture.selection_bg"),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,17 +98,23 @@ impl PlayState for CharSelectionState {
|
||||
});
|
||||
|
||||
// Maintain the scene.
|
||||
self.scene.maintain(
|
||||
global_state.window.renderer_mut(),
|
||||
&self.client.borrow(),
|
||||
humanoid_body.clone(),
|
||||
global_state.settings.graphics.gamma,
|
||||
);
|
||||
{
|
||||
let client = self.client.borrow();
|
||||
let scene_data = scene::SceneData {
|
||||
time: client.state().get_time(),
|
||||
delta_time: client.state().ecs().read_resource::<DeltaTime>().0,
|
||||
tick: client.get_tick(),
|
||||
body: humanoid_body.clone(),
|
||||
gamma: global_state.settings.graphics.gamma,
|
||||
};
|
||||
self.scene
|
||||
.maintain(global_state.window.renderer_mut(), scene_data);
|
||||
}
|
||||
|
||||
// Render the scene.
|
||||
self.scene.render(
|
||||
global_state.window.renderer_mut(),
|
||||
&self.client.borrow(),
|
||||
self.client.borrow().get_tick(),
|
||||
humanoid_body.clone(),
|
||||
&comp::Equipment {
|
||||
main: self
|
||||
|
@ -1,4 +1,3 @@
|
||||
use client::Client;
|
||||
use common::vol::{ReadVol, Vox};
|
||||
use std::f32::consts::PI;
|
||||
use treeculler::Frustum;
|
||||
@ -22,6 +21,13 @@ impl Default for CameraMode {
|
||||
fn default() -> Self { Self::ThirdPerson }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dependents {
|
||||
pub view_mat: Mat4<f32>,
|
||||
pub proj_mat: Mat4<f32>,
|
||||
pub cam_pos: Vec3<f32>,
|
||||
}
|
||||
|
||||
pub struct Camera {
|
||||
tgt_focus: Vec3<f32>,
|
||||
focus: Vec3<f32>,
|
||||
@ -33,6 +39,8 @@ pub struct Camera {
|
||||
mode: CameraMode,
|
||||
|
||||
last_time: Option<f64>,
|
||||
|
||||
dependents: Dependents,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
@ -49,12 +57,18 @@ impl Camera {
|
||||
mode,
|
||||
|
||||
last_time: None,
|
||||
|
||||
dependents: Dependents {
|
||||
view_mat: Mat4::identity(),
|
||||
proj_mat: Mat4::identity(),
|
||||
cam_pos: Vec3::zero(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the transformation matrices (view matrix and projection matrix)
|
||||
/// and position of the camera.
|
||||
pub fn compute_dependents(&self, client: &Client) -> (Mat4<f32>, Mat4<f32>, Vec3<f32>) {
|
||||
pub fn compute_dependents(&mut self, terrain: &impl ReadVol) {
|
||||
let dist = {
|
||||
let (start, end) = (
|
||||
self.focus
|
||||
@ -66,9 +80,7 @@ impl Camera {
|
||||
self.focus,
|
||||
);
|
||||
|
||||
match client
|
||||
.state()
|
||||
.terrain()
|
||||
match terrain
|
||||
.ray(start, end)
|
||||
.ignore_error()
|
||||
.max_iter(500)
|
||||
@ -82,7 +94,7 @@ impl Camera {
|
||||
.max(0.0)
|
||||
};
|
||||
|
||||
let view_mat = Mat4::<f32>::identity()
|
||||
self.dependents.view_mat = Mat4::<f32>::identity()
|
||||
* Mat4::translation_3d(-Vec3::unit_z() * dist)
|
||||
* Mat4::rotation_z(self.ori.z)
|
||||
* Mat4::rotation_x(self.ori.y)
|
||||
@ -90,20 +102,21 @@ impl Camera {
|
||||
* Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x())
|
||||
* Mat4::translation_3d(-self.focus);
|
||||
|
||||
let proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE);
|
||||
self.dependents.proj_mat =
|
||||
Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE);
|
||||
|
||||
// TODO: Make this more efficient.
|
||||
let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w());
|
||||
|
||||
(view_mat, proj_mat, cam_pos)
|
||||
self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w());
|
||||
}
|
||||
|
||||
pub fn frustum(&self, client: &Client) -> Frustum<f32> {
|
||||
let (view_mat, proj_mat, _) = self.compute_dependents(client);
|
||||
|
||||
Frustum::from_modelview_projection((proj_mat * view_mat).into_col_arrays())
|
||||
pub fn frustum(&self) -> Frustum<f32> {
|
||||
Frustum::from_modelview_projection(
|
||||
(self.dependents.proj_mat * self.dependents.view_mat).into_col_arrays(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dependents(&self) -> Dependents { self.dependents.clone() }
|
||||
|
||||
/// Rotate the camera about its focus by the given delta, limiting the input
|
||||
/// accordingly.
|
||||
pub fn rotate_by(&mut self, delta: Vec3<f32>) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
mod cache;
|
||||
mod load;
|
||||
pub mod load;
|
||||
|
||||
pub use cache::FigureModelCache;
|
||||
pub use load::load_mesh; // TODO: Don't make this public.
|
||||
@ -13,14 +13,17 @@ use crate::{
|
||||
quadruped_small::QuadrupedSmallSkeleton, Animation, Skeleton,
|
||||
},
|
||||
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
|
||||
scene::camera::{Camera, CameraMode},
|
||||
scene::{
|
||||
camera::{Camera, CameraMode},
|
||||
SceneData,
|
||||
},
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{
|
||||
ActionState::*, Body, CharacterState, ItemKind, Last, MovementState::*, Ori, Pos, Scale,
|
||||
Stats, Vel,
|
||||
},
|
||||
state::State,
|
||||
terrain::TerrainChunk,
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
@ -96,17 +99,18 @@ impl FigureMgr {
|
||||
self.biped_large_model_cache.clean(tick);
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, camera: &Camera) {
|
||||
let time = client.state().get_time();
|
||||
let tick = client.get_tick();
|
||||
let ecs = client.state().ecs();
|
||||
let view_distance = client.view_distance().unwrap_or(1);
|
||||
let dt = client.state().get_delta_time();
|
||||
let frustum = camera.frustum(client);
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) {
|
||||
let state = scene_data.state;
|
||||
let time = state.get_time();
|
||||
let tick = scene_data.tick;
|
||||
let ecs = state.ecs();
|
||||
let view_distance = scene_data.view_distance;
|
||||
let dt = state.get_delta_time();
|
||||
let frustum = camera.frustum();
|
||||
// Get player position.
|
||||
let player_pos = ecs
|
||||
.read_storage::<Pos>()
|
||||
.get(client.entity())
|
||||
.get(scene_data.player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
for (entity, pos, ori, scale, body, character, last_character, stats) in (
|
||||
@ -1181,7 +1185,7 @@ impl FigureMgr {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear states that have dead entities.
|
||||
// Clear states that have deleted entities.
|
||||
self.character_states
|
||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||
self.quadruped_small_states
|
||||
@ -1209,19 +1213,18 @@ impl FigureMgr {
|
||||
pub fn render(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &mut Client,
|
||||
state: &State,
|
||||
player_entity: EcsEntity,
|
||||
tick: u64,
|
||||
globals: &Consts<Globals>,
|
||||
lights: &Consts<Light>,
|
||||
shadows: &Consts<Shadow>,
|
||||
camera: &Camera,
|
||||
) {
|
||||
let tick = client.get_tick();
|
||||
let ecs = client.state().ecs();
|
||||
let ecs = state.ecs();
|
||||
|
||||
let character_state_storage = client
|
||||
.state()
|
||||
.read_storage::<common::comp::CharacterState>();
|
||||
let character_state = character_state_storage.get(client.entity());
|
||||
let character_state_storage = state.read_storage::<common::comp::CharacterState>();
|
||||
let character_state = character_state_storage.get(player_entity);
|
||||
|
||||
for (entity, _, _, body, stats, _) in (
|
||||
&ecs.entities(),
|
||||
@ -1235,7 +1238,7 @@ impl FigureMgr {
|
||||
// Don't render dead entities
|
||||
.filter(|(_, _, _, _, stats, _)| stats.map_or(true, |s| !s.is_dead))
|
||||
{
|
||||
let is_player = entity == client.entity();
|
||||
let is_player = entity == player_entity;
|
||||
let player_camera_mode = if is_player {
|
||||
camera.get_mode()
|
||||
} else {
|
||||
|
@ -1,29 +1,29 @@
|
||||
pub mod camera;
|
||||
pub mod figure;
|
||||
pub mod simple;
|
||||
pub mod terrain;
|
||||
|
||||
use self::{
|
||||
camera::{Camera, CameraMode},
|
||||
figure::FigureMgr,
|
||||
music::MusicMgr,
|
||||
terrain::Terrain,
|
||||
};
|
||||
use crate::{
|
||||
anim::character::SkeletonAttr,
|
||||
audio::{music, sfx::SfxMgr, AudioFrontend},
|
||||
audio::{music::MusicMgr, sfx::SfxMgr, AudioFrontend},
|
||||
render::{
|
||||
create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals,
|
||||
PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
|
||||
},
|
||||
window::Event,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp,
|
||||
state::State,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use specs::{Join, WorldExt};
|
||||
use specs::{Entity as EcsEntity, Join, WorldExt};
|
||||
use vek::*;
|
||||
|
||||
// TODO: Don't hard-code this.
|
||||
@ -62,6 +62,15 @@ pub struct Scene {
|
||||
music_mgr: MusicMgr,
|
||||
}
|
||||
|
||||
pub struct SceneData<'a> {
|
||||
pub state: &'a State,
|
||||
pub player_entity: specs::Entity,
|
||||
pub loaded_distance: f32,
|
||||
pub view_distance: u32,
|
||||
pub tick: u64,
|
||||
pub thread_pool: &'a uvth::ThreadPool,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
/// Create a new `Scene` with default parameters.
|
||||
pub fn new(renderer: &mut Renderer) -> Self {
|
||||
@ -148,29 +157,29 @@ impl Scene {
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
audio: &mut AudioFrontend,
|
||||
client: &Client,
|
||||
scene_data: &SceneData,
|
||||
gamma: f32,
|
||||
) {
|
||||
// Get player position.
|
||||
let player_pos = client
|
||||
.state()
|
||||
let player_pos = scene_data
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Pos>()
|
||||
.get(client.entity())
|
||||
.get(scene_data.player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
let player_rolling = client
|
||||
.state()
|
||||
let player_rolling = scene_data
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::CharacterState>()
|
||||
.get(client.entity())
|
||||
.get(scene_data.player_entity)
|
||||
.map_or(false, |cs| cs.action.is_roll());
|
||||
|
||||
let player_scale = match client
|
||||
.state()
|
||||
let player_scale = match scene_data
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Body>()
|
||||
.get(client.entity())
|
||||
.get(scene_data.player_entity)
|
||||
{
|
||||
Some(comp::Body::Humanoid(body)) => SkeletonAttr::calculate_scale(body),
|
||||
_ => 1_f32,
|
||||
@ -196,25 +205,30 @@ impl Scene {
|
||||
);
|
||||
|
||||
// Tick camera for interpolation.
|
||||
self.camera.update(client.state().get_time());
|
||||
self.camera.update(scene_data.state.get_time());
|
||||
|
||||
// Compute camera matrices.
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||
self.camera.compute_dependents(&*scene_data.state.terrain());
|
||||
let camera::Dependents {
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
} = self.camera.dependents();
|
||||
|
||||
// Update chunk loaded distance smoothly for nice shader fog
|
||||
let loaded_distance = client.loaded_distance();
|
||||
self.loaded_distance = (0.98 * self.loaded_distance + 0.02 * loaded_distance).max(0.01);
|
||||
self.loaded_distance =
|
||||
(0.98 * self.loaded_distance + 0.02 * scene_data.loaded_distance).max(0.01);
|
||||
|
||||
// Update light constants
|
||||
let mut lights = (
|
||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||
client.state().ecs().read_storage::<comp::Ori>().maybe(),
|
||||
client
|
||||
.state()
|
||||
&scene_data.state.ecs().read_storage::<comp::Pos>(),
|
||||
scene_data.state.ecs().read_storage::<comp::Ori>().maybe(),
|
||||
scene_data
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<crate::ecs::comp::Interpolated>()
|
||||
.maybe(),
|
||||
&client.state().ecs().read_storage::<comp::LightEmitter>(),
|
||||
&scene_data.state.ecs().read_storage::<comp::LightEmitter>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(pos, _, _, _)| {
|
||||
@ -247,15 +261,15 @@ impl Scene {
|
||||
|
||||
// Update shadow constants
|
||||
let mut shadows = (
|
||||
&client.state().ecs().read_storage::<comp::Pos>(),
|
||||
client
|
||||
.state()
|
||||
&scene_data.state.ecs().read_storage::<comp::Pos>(),
|
||||
scene_data
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<crate::ecs::comp::Interpolated>()
|
||||
.maybe(),
|
||||
client.state().ecs().read_storage::<comp::Scale>().maybe(),
|
||||
&client.state().ecs().read_storage::<comp::Body>(),
|
||||
&client.state().ecs().read_storage::<comp::Stats>(),
|
||||
scene_data.state.ecs().read_storage::<comp::Scale>().maybe(),
|
||||
&scene_data.state.ecs().read_storage::<comp::Body>(),
|
||||
&scene_data.state.ecs().read_storage::<comp::Stats>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, _, _, _, stats)| !stats.is_dead)
|
||||
@ -285,13 +299,13 @@ impl Scene {
|
||||
cam_pos,
|
||||
self.camera.get_focus_pos(),
|
||||
self.loaded_distance,
|
||||
client.state().get_time_of_day(),
|
||||
client.state().get_time(),
|
||||
scene_data.state.get_time_of_day(),
|
||||
scene_data.state.get_time(),
|
||||
renderer.get_resolution(),
|
||||
lights.len(),
|
||||
shadows.len(),
|
||||
client
|
||||
.state()
|
||||
scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get(cam_pos.map(|e| e.floor() as i32))
|
||||
.map(|b| b.kind())
|
||||
@ -304,7 +318,7 @@ impl Scene {
|
||||
// Maintain the terrain.
|
||||
self.terrain.maintain(
|
||||
renderer,
|
||||
client,
|
||||
&scene_data,
|
||||
self.camera.get_focus_pos(),
|
||||
self.loaded_distance,
|
||||
view_mat,
|
||||
@ -312,22 +326,31 @@ impl Scene {
|
||||
);
|
||||
|
||||
// Maintain the figures.
|
||||
self.figure_mgr.maintain(renderer, client, &self.camera);
|
||||
self.figure_mgr.maintain(renderer, scene_data, &self.camera);
|
||||
|
||||
// Remove unused figures.
|
||||
self.figure_mgr.clean(client.get_tick());
|
||||
self.figure_mgr.clean(scene_data.tick);
|
||||
|
||||
// Maintain audio
|
||||
self.sfx_mgr.maintain(audio, client);
|
||||
self.music_mgr.maintain(audio, client);
|
||||
self.sfx_mgr
|
||||
.maintain(audio, scene_data.state, scene_data.player_entity);
|
||||
self.music_mgr.maintain(audio, scene_data.state);
|
||||
}
|
||||
|
||||
/// Render the scene using the provided `Renderer`.
|
||||
pub fn render(&mut self, renderer: &mut Renderer, client: &mut Client) {
|
||||
pub fn render(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
state: &State,
|
||||
player_entity: EcsEntity,
|
||||
tick: u64,
|
||||
) {
|
||||
// Render terrain and figures.
|
||||
self.figure_mgr.render(
|
||||
renderer,
|
||||
client,
|
||||
state,
|
||||
player_entity,
|
||||
tick,
|
||||
&self.globals,
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
|
@ -9,21 +9,37 @@ use crate::{
|
||||
PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
|
||||
},
|
||||
scene::{
|
||||
camera::{Camera, CameraMode},
|
||||
camera::{self, Camera, CameraMode},
|
||||
figure::{load_mesh, FigureModelCache, FigureState},
|
||||
},
|
||||
window::{Event, PressState},
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
comp::{humanoid, Body, Equipment},
|
||||
state::DeltaTime,
|
||||
terrain::BlockKind,
|
||||
vol::{BaseVol, ReadVol, Vox},
|
||||
};
|
||||
use log::error;
|
||||
use specs::WorldExt;
|
||||
use vek::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
struct VoidVox;
|
||||
impl Vox for VoidVox {
|
||||
fn empty() -> Self { VoidVox }
|
||||
|
||||
fn is_empty(&self) -> bool { true }
|
||||
|
||||
fn or(self, _other: Self) -> Self { VoidVox }
|
||||
}
|
||||
struct VoidVol;
|
||||
impl BaseVol for VoidVol {
|
||||
type Error = ();
|
||||
type Vox = VoidVox;
|
||||
}
|
||||
impl ReadVol for VoidVol {
|
||||
fn get<'a>(&'a self, _pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) }
|
||||
}
|
||||
|
||||
struct Skybox {
|
||||
model: Model<SkyboxPipeline>,
|
||||
locals: Consts<SkyboxLocals>,
|
||||
@ -42,8 +58,7 @@ pub struct Scene {
|
||||
|
||||
skybox: Skybox,
|
||||
postprocess: PostProcess,
|
||||
backdrop_model: Model<FigurePipeline>,
|
||||
backdrop_state: FigureState<FixtureSkeleton>,
|
||||
backdrop: Option<(Model<FigurePipeline>, FigureState<FixtureSkeleton>)>,
|
||||
|
||||
figure_model_cache: FigureModelCache,
|
||||
figure_state: FigureState<CharacterSkeleton>,
|
||||
@ -52,15 +67,28 @@ pub struct Scene {
|
||||
char_ori: f32,
|
||||
}
|
||||
|
||||
pub struct SceneData {
|
||||
pub time: f64,
|
||||
pub delta_time: f32,
|
||||
pub tick: u64,
|
||||
pub body: Option<humanoid::Body>,
|
||||
pub gamma: f32,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(renderer: &mut Renderer) -> Self {
|
||||
pub fn new(renderer: &mut Renderer, backdrop: Option<&str>) -> Self {
|
||||
let resolution = renderer.get_resolution().map(|e| e as f32);
|
||||
|
||||
let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson);
|
||||
camera.set_focus_pos(Vec3::unit_z() * 1.5);
|
||||
camera.set_distance(3.0); // 4.2
|
||||
camera.set_orientation(Vec3::new(0.0, 0.0, 0.0));
|
||||
|
||||
Self {
|
||||
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
|
||||
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
|
||||
shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(),
|
||||
camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson),
|
||||
camera,
|
||||
|
||||
skybox: Skybox {
|
||||
model: renderer.create_model(&create_skybox_mesh()).unwrap(),
|
||||
@ -75,13 +103,14 @@ impl Scene {
|
||||
figure_model_cache: FigureModelCache::new(),
|
||||
figure_state: FigureState::new(renderer, CharacterSkeleton::new()),
|
||||
|
||||
backdrop_model: renderer
|
||||
.create_model(&load_mesh(
|
||||
"fixture.selection_bg",
|
||||
Vec3::new(-55.0, -49.5, -2.0),
|
||||
))
|
||||
.unwrap(),
|
||||
backdrop_state: FigureState::new(renderer, FixtureSkeleton::new()),
|
||||
backdrop: backdrop.map(|specifier| {
|
||||
(
|
||||
renderer
|
||||
.create_model(&load_mesh(specifier, Vec3::new(-55.0, -49.5, -2.0)))
|
||||
.unwrap(),
|
||||
FigureState::new(renderer, FixtureSkeleton::new()),
|
||||
)
|
||||
}),
|
||||
|
||||
turning: false,
|
||||
char_ori: 0.0,
|
||||
@ -90,6 +119,8 @@ impl Scene {
|
||||
|
||||
pub fn globals(&self) -> &Consts<Globals> { &self.globals }
|
||||
|
||||
pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
|
||||
|
||||
/// Handle an incoming user input event (e.g.: cursor moved, key pressed,
|
||||
/// window closed).
|
||||
///
|
||||
@ -114,20 +145,18 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &Client,
|
||||
body: Option<humanoid::Body>,
|
||||
gamma: f32,
|
||||
) {
|
||||
self.camera.set_focus_pos(Vec3::unit_z() * 1.5);
|
||||
self.camera.update(client.state().get_time());
|
||||
self.camera.set_distance(3.0); // 4.2
|
||||
self.camera
|
||||
.set_orientation(Vec3::new(client.state().get_time() as f32 * 0.0, 0.0, 0.0));
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: SceneData) {
|
||||
self.camera.update(scene_data.time);
|
||||
|
||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||
//self.camera
|
||||
// .set_orientation(Vec3::new(scene_data.time as f32 * 0.0, 0.0, 0.0));
|
||||
|
||||
self.camera.compute_dependents(&VoidVol);
|
||||
let camera::Dependents {
|
||||
view_mat,
|
||||
proj_mat,
|
||||
cam_pos,
|
||||
} = self.camera.dependents();
|
||||
const VD: f32 = 115.0; //View Distance
|
||||
const TIME: f64 = 43200.0; // hours*3600 seconds
|
||||
if let Err(err) = renderer.update_consts(&mut self.globals, &[Globals::new(
|
||||
@ -137,31 +166,30 @@ impl Scene {
|
||||
self.camera.get_focus_pos(),
|
||||
VD,
|
||||
TIME,
|
||||
client.state().get_time(),
|
||||
scene_data.time,
|
||||
renderer.get_resolution(),
|
||||
0,
|
||||
0,
|
||||
BlockKind::Air,
|
||||
None,
|
||||
gamma,
|
||||
scene_data.gamma,
|
||||
)]) {
|
||||
error!("Renderer failed to update: {:?}", err);
|
||||
}
|
||||
|
||||
self.figure_model_cache.clean(client.get_tick());
|
||||
self.figure_model_cache.clean(scene_data.tick);
|
||||
|
||||
if let Some(body) = body {
|
||||
if let Some(body) = scene_data.body {
|
||||
let tgt_skeleton = IdleAnimation::update_skeleton(
|
||||
self.figure_state.skeleton_mut(),
|
||||
client.state().get_time(),
|
||||
client.state().get_time(),
|
||||
scene_data.time,
|
||||
scene_data.time,
|
||||
&mut 0.0,
|
||||
&SkeletonAttr::from(&body),
|
||||
);
|
||||
self.figure_state.skeleton_mut().interpolate(
|
||||
&tgt_skeleton,
|
||||
client.state().ecs().read_resource::<DeltaTime>().0,
|
||||
);
|
||||
self.figure_state
|
||||
.skeleton_mut()
|
||||
.interpolate(&tgt_skeleton, scene_data.delta_time);
|
||||
}
|
||||
|
||||
self.figure_state.update(
|
||||
@ -182,7 +210,7 @@ impl Scene {
|
||||
pub fn render(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &Client,
|
||||
tick: u64,
|
||||
body: Option<humanoid::Body>,
|
||||
equipment: &Equipment,
|
||||
) {
|
||||
@ -195,7 +223,7 @@ impl Scene {
|
||||
renderer,
|
||||
Body::Humanoid(body),
|
||||
Some(equipment),
|
||||
client.get_tick(),
|
||||
tick,
|
||||
CameraMode::default(),
|
||||
None,
|
||||
)
|
||||
@ -211,14 +239,16 @@ impl Scene {
|
||||
);
|
||||
}
|
||||
|
||||
renderer.render_figure(
|
||||
&self.backdrop_model,
|
||||
&self.globals,
|
||||
self.backdrop_state.locals(),
|
||||
self.backdrop_state.bone_consts(),
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
);
|
||||
if let Some((model, state)) = &self.backdrop {
|
||||
renderer.render_figure(
|
||||
model,
|
||||
&self.globals,
|
||||
state.locals(),
|
||||
state.bone_consts(),
|
||||
&self.lights,
|
||||
&self.shadows,
|
||||
);
|
||||
}
|
||||
|
||||
renderer.render_post_process(
|
||||
&self.postprocess.model,
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use client::Client;
|
||||
use super::SceneData;
|
||||
use common::{
|
||||
assets,
|
||||
figure::Segment,
|
||||
@ -1083,26 +1083,26 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
renderer: &mut Renderer,
|
||||
client: &Client,
|
||||
scene_data: &SceneData,
|
||||
focus_pos: Vec3<f32>,
|
||||
loaded_distance: f32,
|
||||
view_mat: Mat4<f32>,
|
||||
proj_mat: Mat4<f32>,
|
||||
) {
|
||||
let current_tick = client.get_tick();
|
||||
let current_time = client.state().get_time();
|
||||
let current_tick = scene_data.tick;
|
||||
let current_time = scene_data.state.get_time();
|
||||
|
||||
// Add any recently created or changed chunks to the list of chunks to be
|
||||
// meshed.
|
||||
for (modified, pos) in client
|
||||
.state()
|
||||
for (modified, pos) in scene_data
|
||||
.state
|
||||
.terrain_changes()
|
||||
.modified_chunks
|
||||
.iter()
|
||||
.map(|c| (true, c))
|
||||
.chain(
|
||||
client
|
||||
.state()
|
||||
scene_data
|
||||
.state
|
||||
.terrain_changes()
|
||||
.new_chunks
|
||||
.iter()
|
||||
@ -1121,8 +1121,8 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
let mut neighbours = true;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
neighbours &= client
|
||||
.state()
|
||||
neighbours &= scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get_key(pos + Vec2::new(i, j))
|
||||
.is_some();
|
||||
@ -1143,20 +1143,20 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
|
||||
// Add the chunks belonging to recently changed blocks to the list of chunks to
|
||||
// be meshed
|
||||
for pos in client
|
||||
.state()
|
||||
for pos in scene_data
|
||||
.state
|
||||
.terrain_changes()
|
||||
.modified_blocks
|
||||
.iter()
|
||||
.map(|(p, _)| *p)
|
||||
{
|
||||
let chunk_pos = client.state().terrain().pos_key(pos);
|
||||
let chunk_pos = scene_data.state.terrain().pos_key(pos);
|
||||
// Only mesh if this chunk has all its neighbors
|
||||
let mut neighbours = true;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
neighbours &= client
|
||||
.state()
|
||||
neighbours &= scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get_key(chunk_pos + Vec2::new(i, j))
|
||||
.is_some();
|
||||
@ -1178,15 +1178,15 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
for x in -1..2 {
|
||||
for y in -1..2 {
|
||||
let neighbour_pos = pos + Vec3::new(x, y, 0);
|
||||
let neighbour_chunk_pos = client.state().terrain().pos_key(neighbour_pos);
|
||||
let neighbour_chunk_pos = scene_data.state.terrain().pos_key(neighbour_pos);
|
||||
|
||||
if neighbour_chunk_pos != chunk_pos {
|
||||
// Only remesh if this chunk has all its neighbors
|
||||
let mut neighbours = true;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
neighbours &= client
|
||||
.state()
|
||||
neighbours &= scene_data
|
||||
.state
|
||||
.terrain()
|
||||
.get_key(neighbour_chunk_pos + Vec2::new(i, j))
|
||||
.is_some();
|
||||
@ -1205,7 +1205,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
}
|
||||
|
||||
// Remove any models for chunks that have been recently removed.
|
||||
for pos in &client.state().terrain_changes().removed_chunks {
|
||||
for pos in &scene_data.state.terrain_changes().removed_chunks {
|
||||
self.chunks.remove(pos);
|
||||
self.mesh_todo.remove(pos);
|
||||
}
|
||||
@ -1220,7 +1220,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
})
|
||||
.min_by_key(|todo| todo.active_worker.unwrap_or(todo.started_tick))
|
||||
{
|
||||
if client.thread_pool().queued_jobs() > 0 {
|
||||
if scene_data.thread_pool.queued_jobs() > 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1239,7 +1239,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
// Copy out the chunk data we need to perform the meshing. We do this by taking
|
||||
// a sample of the terrain that includes both the chunk we want and
|
||||
// its neighbours.
|
||||
let volume = match client.state().terrain().sample(aabr) {
|
||||
let volume = match scene_data.state.terrain().sample(aabr) {
|
||||
Ok(sample) => sample,
|
||||
// Either this chunk or its neighbours doesn't yet exist, so we keep it in the
|
||||
// queue to be processed at a later date when we have its neighbours.
|
||||
@ -1266,7 +1266,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
|
||||
// Queue the worker thread.
|
||||
let started_tick = todo.started_tick;
|
||||
client.thread_pool().execute(move || {
|
||||
scene_data.thread_pool.execute(move || {
|
||||
let _ = send.send(mesh_worker(
|
||||
pos,
|
||||
(min_z as f32, max_z as f32),
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
i18n::{i18n_asset_key, VoxygenLocalization},
|
||||
key_state::KeyState,
|
||||
render::Renderer,
|
||||
scene::Scene,
|
||||
scene::{camera, Scene, SceneData},
|
||||
window::{Event, GameInput},
|
||||
Direction, Error, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
@ -108,7 +108,11 @@ impl SessionState {
|
||||
renderer.clear();
|
||||
|
||||
// Render the screen using the global renderer
|
||||
self.scene.render(renderer, &mut self.client.borrow_mut());
|
||||
{
|
||||
let client = self.client.borrow();
|
||||
self.scene
|
||||
.render(renderer, client.state(), client.entity(), client.get_tick());
|
||||
}
|
||||
// Draw the UI to the screen
|
||||
self.hud.render(renderer, self.scene.globals());
|
||||
|
||||
@ -145,10 +149,12 @@ impl PlayState for SessionState {
|
||||
let mut current_client_state = self.client.borrow().get_client_state();
|
||||
while let ClientState::Pending | ClientState::Character = current_client_state {
|
||||
// Compute camera data
|
||||
let (view_mat, _, cam_pos) = self
|
||||
.scene
|
||||
.camera()
|
||||
.compute_dependents(&self.client.borrow());
|
||||
self.scene
|
||||
.camera_mut()
|
||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||
let camera::Dependents {
|
||||
view_mat, cam_pos, ..
|
||||
} = self.scene.camera().dependents();
|
||||
let cam_dir: Vec3<f32> = Vec3::from(view_mat.inverted() * -Vec4::unit_z());
|
||||
|
||||
// Check to see whether we're aiming at anything
|
||||
@ -396,6 +402,10 @@ impl PlayState for SessionState {
|
||||
// Maintain global state.
|
||||
global_state.maintain(clock.get_last_delta().as_secs_f32());
|
||||
|
||||
// Recompute dependents just in case some input modified the camera
|
||||
self.scene
|
||||
.camera_mut()
|
||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||
// Extract HUD events ensuring the client borrow gets dropped.
|
||||
let mut hud_events = self.hud.maintain(
|
||||
&self.client.borrow(),
|
||||
@ -555,6 +565,9 @@ impl PlayState for SessionState {
|
||||
global_state.settings.graphics.fov = new_fov;
|
||||
global_state.settings.save_to_file_warn();
|
||||
self.scene.camera_mut().set_fov_deg(new_fov);
|
||||
self.scene
|
||||
.camera_mut()
|
||||
.compute_dependents(&*self.client.borrow().state().terrain());
|
||||
},
|
||||
HudEvent::ChangeGamma(new_gamma) => {
|
||||
global_state.settings.graphics.gamma = new_gamma;
|
||||
@ -618,11 +631,19 @@ impl PlayState for SessionState {
|
||||
if global_state.singleplayer.is_none()
|
||||
|| !global_state.singleplayer.as_ref().unwrap().is_paused()
|
||||
{
|
||||
// Maintain the scene.
|
||||
let client = self.client.borrow();
|
||||
let scene_data = SceneData {
|
||||
state: client.state(),
|
||||
player_entity: client.entity(),
|
||||
loaded_distance: client.loaded_distance(),
|
||||
view_distance: client.view_distance().unwrap_or(1),
|
||||
tick: client.get_tick(),
|
||||
thread_pool: client.thread_pool(),
|
||||
};
|
||||
self.scene.maintain(
|
||||
global_state.window.renderer_mut(),
|
||||
&mut global_state.audio,
|
||||
&self.client.borrow(),
|
||||
&scene_data,
|
||||
global_state.settings.graphics.gamma,
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user