Added time of day, tick time, basic skybox shader and client to voxygen

This commit is contained in:
Joshua Barretto 2019-01-12 15:57:19 +00:00
parent 96d01a9293
commit b37a6b67f9
14 changed files with 212 additions and 30 deletions

View File

@ -5,6 +5,6 @@ authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
common = { path = "../common" } common = { package = "veloren-common", path = "../common" }
specs = "0.14" specs = "0.14"

View File

@ -4,7 +4,8 @@ use std::time::Duration;
// Internal // Internal
use common::state::State; use common::state::State;
pub enum ClientErr { #[derive(Debug)]
pub enum Error {
ServerShutdown, ServerShutdown,
Other(String), Other(String),
} }
@ -20,14 +21,21 @@ pub struct Client {
} }
impl Client { impl Client {
/// Create a new `Client`.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: State::new(), state: State::new(),
} }
} }
/// Get a reference to the client's game state.
pub fn state(&self) -> &State { &self.state }
/// Get a mutable reference to the client's game state.
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
/// Execute a single client tick, handle input and update the game state by the given duration /// Execute a single client tick, handle input and update the game state by the given duration
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<(), ClientErr> { pub fn tick(&mut self, input: Input, dt: Duration) -> Result<(), Error> {
// This tick function is the centre of the Veloren universe. Most client-side things are // This tick function is the centre of the Veloren universe. Most client-side things are
// managed from here, and as such it's important that it stays organised. Please consult // managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the // the core developers before making significant changes to this code. Here is the

View File

@ -1,5 +1,5 @@
[package] [package]
name = "common" name = "veloren-common"
version = "0.1.0" version = "0.1.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"

50
common/src/clock.rs Normal file
View File

@ -0,0 +1,50 @@
// Standard
use std::{
thread,
time::{Duration, SystemTime},
};
const CLOCK_SMOOTHING: f64 = 0.9;
pub struct Clock {
last_sys_time: SystemTime,
last_delta: Option<Duration>,
running_tps_average: f64,
}
impl Clock {
pub fn new() -> Self {
Self {
last_sys_time: SystemTime::now(),
last_delta: None,
running_tps_average: 0.0,
}
}
pub fn get_tps(&self) -> f64 { self.running_tps_average }
pub fn get_last_delta(&self) -> Duration { self.last_delta.unwrap_or(Duration::new(0, 0)) }
pub fn get_avg_delta(&self) -> Duration { Duration::from_float_secs(self.running_tps_average) }
pub fn tick(&mut self, tgt: Duration) {
let delta = SystemTime::now()
.duration_since(self.last_sys_time)
.expect("Time went backwards!");
// Attempt to sleep to fill the gap
if let Some(sleep_dur) = tgt.checked_sub(delta) {
thread::sleep(sleep_dur);
}
let delta = SystemTime::now()
.duration_since(self.last_sys_time)
.expect("Time went backwards!");
self.last_sys_time = SystemTime::now();
self.last_delta = Some(delta);
self.running_tps_average =
CLOCK_SMOOTHING * self.running_tps_average +
(1.0 - CLOCK_SMOOTHING) * delta.as_float_secs();
}
}

View File

@ -1,5 +1,6 @@
#![feature(euclidean_division)] #![feature(euclidean_division, duration_float)]
pub mod clock;
pub mod comp; pub mod comp;
pub mod state; pub mod state;
pub mod terrain; pub mod terrain;

View File

@ -11,8 +11,18 @@ use crate::{
vol::VolSize, vol::VolSize,
}; };
// A type used to represent game state stored on both the client and the server. This includes /// How much faster should an in-game day be compared to a real day?
// things like entity components, terrain data, and global state like weather, time of day, etc. // TODO: Don't hard-code this
const DAY_CYCLE_FACTOR: f64 = 24.0 * 60.0;
/// A resource to store the time of day
struct TimeOfDay(f64);
/// A resource to store the tick (i.e: physics) time
struct Tick(f64);
/// A type used to represent game state stored on both the client and the server. This includes
/// things like entity components, terrain data, and global state like weather, time of day, etc.
pub struct State { pub struct State {
ecs_world: EcsWorld, ecs_world: EcsWorld,
terrain_map: TerrainMap, terrain_map: TerrainMap,
@ -20,9 +30,15 @@ pub struct State {
} }
impl State { impl State {
/// Create a new `State`.
pub fn new() -> Self { pub fn new() -> Self {
let mut ecs_world = EcsWorld::new(); let mut ecs_world = EcsWorld::new();
// Register resources used by the ECS
ecs_world.add_resource(TimeOfDay(0.0));
ecs_world.add_resource(Tick(0.0));
// Register common components with the state
comp::register_local_components(&mut ecs_world); comp::register_local_components(&mut ecs_world);
Self { Self {
@ -32,8 +48,24 @@ impl State {
} }
} }
// Execute a single tick, simulating the game state by the given duration /// Get the current in-game time of day.
///
/// Note that this should not be used for physics, animations or other such localised timings.
pub fn get_time_of_day(&self) -> f64 {
self.ecs_world.read_resource::<TimeOfDay>().0
}
/// Get the current in-game tick time.
///
/// Note that this does not correspond to the time of day.
pub fn get_tick(&self) -> f64 {
self.ecs_world.read_resource::<Tick>().0
}
/// Execute a single tick, simulating the game state by the given duration.
pub fn tick(&mut self, dt: Duration) { pub fn tick(&mut self, dt: Duration) {
println!("Ticked!"); // Change the time accordingly
self.ecs_world.write_resource::<TimeOfDay>().0 += dt.as_float_secs() * DAY_CYCLE_FACTOR;
self.ecs_world.write_resource::<Tick>().0 += dt.as_float_secs();
} }
} }

View File

@ -1,10 +1,10 @@
[package] [package]
name = "server" name = "veloren-server"
version = "0.1.0" version = "0.1.0"
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"] authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
common = { path = "../common" } common = { package = "veloren-common", path = "../common" }
specs = "0.14" specs = "0.14"

View File

@ -4,8 +4,8 @@ use std::time::Duration;
// Internal // Internal
use common::state::State; use common::state::State;
pub enum ClientErr { #[derive(Debug)]
ServerShutdown, pub enum Error {
Other(String), Other(String),
} }
@ -20,14 +20,21 @@ pub struct Server {
} }
impl Server { impl Server {
/// Create a new `Server`.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
state: State::new(), state: State::new(),
} }
} }
/// Get a reference to the client's game state.
pub fn state(&self) -> &State { &self.state }
/// Get a mutable reference to the client's game state.
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
/// Execute a single server tick, handle input and update the game state by the given duration /// Execute a single server tick, handle input and update the game state by the given duration
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<(), ClientErr> { pub fn tick(&mut self, input: Input, dt: Duration) -> Result<(), Error> {
// This tick function is the centre of the Veloren universe. Most server-side things are // This tick function is the centre of the Veloren universe. Most server-side things are
// managed from here, and as such it's important that it stays organised. Please consult // managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the // the core developers before making significant changes to this code. Here is the

View File

@ -10,7 +10,8 @@ gl = ["gfx_device_gl"]
default = [] default = []
[dependencies] [dependencies]
common = { path = "../common" } common = { package = "veloren-common", path = "../common" }
client = { package = "veloren-client", path = "../client" }
# Graphics # Graphics
gfx = "0.17" gfx = "0.17"

View File

@ -20,6 +20,27 @@ uniform u_globals {
out vec4 tgt_color; out vec4 tgt_color;
void main() { const float PI = 3.141592;
tgt_color = vec4(f_pos, 1.0);
vec3 get_sky_color(vec3 dir, float time_of_day) {
const float TIME_FACTOR = (PI * 2.0) / (3600.0 * 24.0);
const vec3 SKY_TOP = vec3(0.0, 0.3, 1.0);
const vec3 SKY_BOTTOM = vec3(0.0, 0.05, 0.2);
const vec3 SUN_HALO_COLOR = vec3(1.0, 0.8, 0.5);
const vec3 SUN_SURF_COLOR = vec3(1.0, 0.8, 0.5);
float sun_angle_rad = time_of_day * TIME_FACTOR;
vec3 sun_dir = vec3(sin(sun_angle_rad), 0.0, cos(sun_angle_rad));
vec3 sun_halo = pow(max(dot(dir, sun_dir), 0.0), 8.0) * SUN_HALO_COLOR;
vec3 sun_surf = min(pow(max(dot(dir, sun_dir), 0.0) + 0.01, 16.0), 1.0) * SUN_SURF_COLOR;
vec3 sun_light = sun_halo + sun_surf;
return mix(SKY_BOTTOM, SKY_TOP, (dir.z + 1.0) / 2.0) + sun_light * 0.5;
}
void main() {
tgt_color = vec4(get_sky_color(normalize(f_pos), time_of_day.x), 1.0);
} }

View File

@ -1,12 +1,17 @@
// Standard // Standard
use std::any; use std::any;
// Project
use client;
// Crate // Crate
use crate::render::RenderError; use crate::render::RenderError;
/// Represents any error that may be triggered by Voxygen /// Represents any error that may be triggered by Voxygen
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// An error relating to the internal client
ClientError(client::Error),
/// A miscellaneous error relating to a backend dependency /// A miscellaneous error relating to a backend dependency
BackendError(Box<any::Any>), BackendError(Box<any::Any>),
/// An error relating the rendering subsystem /// An error relating the rendering subsystem
@ -20,3 +25,9 @@ impl From<RenderError> for Error {
Error::RenderError(err) Error::RenderError(err)
} }
} }
impl From<client::Error> for Error {
fn from(err: client::Error) -> Self {
Error::ClientError(err)
}
}

View File

@ -20,8 +20,9 @@ gfx_defines! {
proj_mat: [[f32; 4]; 4] = "proj_mat", proj_mat: [[f32; 4]; 4] = "proj_mat",
cam_pos: [f32; 4] = "cam_pos", cam_pos: [f32; 4] = "cam_pos",
focus_pos: [f32; 4] = "focus_pos", focus_pos: [f32; 4] = "focus_pos",
// TODO: Fix whatever alignment issue requires these uniforms to be aligned
view_distance: [f32; 4] = "view_distance", view_distance: [f32; 4] = "view_distance",
time_of_day: [f32; 4] = "time_of_day", time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64
time: [f32; 4] = "time", time: [f32; 4] = "time",
} }
} }
@ -47,7 +48,7 @@ impl Globals {
cam_pos: Vec3<f32>, cam_pos: Vec3<f32>,
focus_pos: Vec3<f32>, focus_pos: Vec3<f32>,
view_distance: f32, view_distance: f32,
time_of_day: f32, time_of_day: f64,
time: f32, time: f32,
) -> Self { ) -> Self {
Self { Self {
@ -56,7 +57,7 @@ impl Globals {
cam_pos: Vec4::from(cam_pos).into_array(), cam_pos: Vec4::from(cam_pos).into_array(),
focus_pos: Vec4::from(focus_pos).into_array(), focus_pos: Vec4::from(focus_pos).into_array(),
view_distance: [view_distance; 4], view_distance: [view_distance; 4],
time_of_day: [time_of_day; 4], time_of_day: [time_of_day as f32; 4],
time: [time; 4], time: [time; 4],
} }
} }

View File

@ -1,10 +1,20 @@
pub mod camera; pub mod camera;
// Standard
use std::time::Duration;
// Library // Library
use vek::*; use vek::*;
// Project
use client::{
self,
Client,
};
// Crate // Crate
use crate::{ use crate::{
Error,
render::{ render::{
Consts, Consts,
Globals, Globals,
@ -32,6 +42,8 @@ pub struct Scene {
camera: Camera, camera: Camera,
globals: Consts<Globals>, globals: Consts<Globals>,
skybox: Skybox, skybox: Skybox,
client: Client,
} }
impl Scene { impl Scene {
@ -50,9 +62,17 @@ impl Scene {
.create_consts_with(SkyboxLocals::default()) .create_consts_with(SkyboxLocals::default())
.unwrap(), .unwrap(),
}, },
client: Client::new(),
} }
} }
/// Tick the scene (and the client attached to it)
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
self.client.tick(client::Input {}, dt)?;
Ok(())
}
/// Handle an incoming user input event (i.e: cursor moved, key pressed, window closed, etc.). /// Handle an incoming user input event (i.e: cursor moved, key pressed, window closed, etc.).
pub fn handle_input_event(&mut self, event: Event) -> bool { pub fn handle_input_event(&mut self, event: Event) -> bool {
match event { match event {
@ -78,7 +98,7 @@ impl Scene {
cam_pos, cam_pos,
self.camera.get_focus_pos(), self.camera.get_focus_pos(),
10.0, 10.0,
0.0, self.client.state().get_time_of_day(),
0.0, 0.0,
)) ))
.expect("Failed to update global constants"); .expect("Failed to update global constants");

View File

@ -1,6 +1,12 @@
// Standard
use std::time::Duration;
// Library // Library
use vek::*; use vek::*;
// Project
use common::clock::Clock;
// Crate // Crate
use crate::{ use crate::{
PlayState, PlayState,
@ -11,6 +17,8 @@ use crate::{
scene::Scene, scene::Scene,
}; };
const FPS: u64 = 60;
pub struct SessionState { pub struct SessionState {
scene: Scene, scene: Scene,
} }
@ -29,11 +37,33 @@ impl SessionState {
// The background colour // The background colour
const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 }; const BG_COLOR: Rgba<f32> = Rgba { r: 0.0, g: 0.3, b: 1.0, a: 1.0 };
impl SessionState {
/// Render the session to the screen.
///
/// This method should be called once per frame.
pub fn render(&mut self, renderer: &mut Renderer) {
// Maintain scene GPU data
self.scene.maintain_gpu_data(renderer);
// Clear the screen
renderer.clear(BG_COLOR);
// Render the screen using the global renderer
self.scene.render_to(renderer);
// Finish the frame
renderer.flush();
}
}
impl PlayState for SessionState { impl PlayState for SessionState {
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult { fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
// Trap the cursor // Trap the cursor
global_state.window.trap_cursor(); global_state.window.trap_cursor();
// Set up an fps clock
let mut clock = Clock::new();
// Game loop // Game loop
loop { loop {
// Handle window events // Handle window events
@ -48,20 +78,20 @@ impl PlayState for SessionState {
// TODO: Do something if the event wasn't handled? // TODO: Do something if the event wasn't handled?
} }
// Maintain scene GPU data // Perform an in-game tick
self.scene.maintain_gpu_data(global_state.window.renderer_mut()); self.scene.tick(clock.get_last_delta())
.expect("Failed to tick the scene");
// Clear the screen // Render the session
global_state.window.renderer_mut().clear(BG_COLOR); self.render(global_state.window.renderer_mut());
// Render the screen using the global renderer // Display the frame on the window
self.scene.render_to(global_state.window.renderer_mut());
// Finish the frame
global_state.window.renderer_mut().flush();
global_state.window global_state.window
.swap_buffers() .swap_buffers()
.expect("Failed to swap window buffers"); .expect("Failed to swap window buffers");
// Wait for the next tick
clock.tick(Duration::from_millis(1000 / FPS));
} }
} }