mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added time of day, tick time, basic skybox shader and client to voxygen
This commit is contained in:
parent
96d01a9293
commit
b37a6b67f9
@ -5,6 +5,6 @@ authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
|
||||
specs = "0.14"
|
||||
|
@ -4,7 +4,8 @@ use std::time::Duration;
|
||||
// Internal
|
||||
use common::state::State;
|
||||
|
||||
pub enum ClientErr {
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ServerShutdown,
|
||||
Other(String),
|
||||
}
|
||||
@ -20,14 +21,21 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new `Client`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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
|
||||
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
|
||||
// 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "common"
|
||||
name = "veloren-common"
|
||||
version = "0.1.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
50
common/src/clock.rs
Normal file
50
common/src/clock.rs
Normal 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();
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#![feature(euclidean_division)]
|
||||
#![feature(euclidean_division, duration_float)]
|
||||
|
||||
pub mod clock;
|
||||
pub mod comp;
|
||||
pub mod state;
|
||||
pub mod terrain;
|
||||
|
@ -11,8 +11,18 @@ use crate::{
|
||||
vol::VolSize,
|
||||
};
|
||||
|
||||
// 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.
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
// 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 {
|
||||
ecs_world: EcsWorld,
|
||||
terrain_map: TerrainMap,
|
||||
@ -20,9 +30,15 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Create a new `State`.
|
||||
pub fn new() -> Self {
|
||||
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);
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "server"
|
||||
name = "veloren-server"
|
||||
version = "0.1.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
|
||||
specs = "0.14"
|
||||
|
@ -4,8 +4,8 @@ use std::time::Duration;
|
||||
// Internal
|
||||
use common::state::State;
|
||||
|
||||
pub enum ClientErr {
|
||||
ServerShutdown,
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Other(String),
|
||||
}
|
||||
|
||||
@ -20,14 +20,21 @@ pub struct Server {
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Create a new `Server`.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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
|
||||
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
|
||||
// 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
|
||||
|
@ -10,7 +10,8 @@ gl = ["gfx_device_gl"]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
client = { package = "veloren-client", path = "../client" }
|
||||
|
||||
# Graphics
|
||||
gfx = "0.17"
|
||||
|
@ -20,6 +20,27 @@ uniform u_globals {
|
||||
|
||||
out vec4 tgt_color;
|
||||
|
||||
void main() {
|
||||
tgt_color = vec4(f_pos, 1.0);
|
||||
const float PI = 3.141592;
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
// Standard
|
||||
use std::any;
|
||||
|
||||
// Project
|
||||
use client;
|
||||
|
||||
// Crate
|
||||
use crate::render::RenderError;
|
||||
|
||||
/// Represents any error that may be triggered by Voxygen
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// An error relating to the internal client
|
||||
ClientError(client::Error),
|
||||
/// A miscellaneous error relating to a backend dependency
|
||||
BackendError(Box<any::Any>),
|
||||
/// An error relating the rendering subsystem
|
||||
@ -20,3 +25,9 @@ impl From<RenderError> for Error {
|
||||
Error::RenderError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<client::Error> for Error {
|
||||
fn from(err: client::Error) -> Self {
|
||||
Error::ClientError(err)
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,9 @@ gfx_defines! {
|
||||
proj_mat: [[f32; 4]; 4] = "proj_mat",
|
||||
cam_pos: [f32; 4] = "cam_pos",
|
||||
focus_pos: [f32; 4] = "focus_pos",
|
||||
// TODO: Fix whatever alignment issue requires these uniforms to be aligned
|
||||
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",
|
||||
}
|
||||
}
|
||||
@ -47,7 +48,7 @@ impl Globals {
|
||||
cam_pos: Vec3<f32>,
|
||||
focus_pos: Vec3<f32>,
|
||||
view_distance: f32,
|
||||
time_of_day: f32,
|
||||
time_of_day: f64,
|
||||
time: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -56,7 +57,7 @@ impl Globals {
|
||||
cam_pos: Vec4::from(cam_pos).into_array(),
|
||||
focus_pos: Vec4::from(focus_pos).into_array(),
|
||||
view_distance: [view_distance; 4],
|
||||
time_of_day: [time_of_day; 4],
|
||||
time_of_day: [time_of_day as f32; 4],
|
||||
time: [time; 4],
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,20 @@
|
||||
pub mod camera;
|
||||
|
||||
// Standard
|
||||
use std::time::Duration;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Project
|
||||
use client::{
|
||||
self,
|
||||
Client,
|
||||
};
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
Error,
|
||||
render::{
|
||||
Consts,
|
||||
Globals,
|
||||
@ -32,6 +42,8 @@ pub struct Scene {
|
||||
camera: Camera,
|
||||
globals: Consts<Globals>,
|
||||
skybox: Skybox,
|
||||
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@ -50,9 +62,17 @@ impl Scene {
|
||||
.create_consts_with(SkyboxLocals::default())
|
||||
.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.).
|
||||
pub fn handle_input_event(&mut self, event: Event) -> bool {
|
||||
match event {
|
||||
@ -78,7 +98,7 @@ impl Scene {
|
||||
cam_pos,
|
||||
self.camera.get_focus_pos(),
|
||||
10.0,
|
||||
0.0,
|
||||
self.client.state().get_time_of_day(),
|
||||
0.0,
|
||||
))
|
||||
.expect("Failed to update global constants");
|
||||
|
@ -1,6 +1,12 @@
|
||||
// Standard
|
||||
use std::time::Duration;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Project
|
||||
use common::clock::Clock;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
PlayState,
|
||||
@ -11,6 +17,8 @@ use crate::{
|
||||
scene::Scene,
|
||||
};
|
||||
|
||||
const FPS: u64 = 60;
|
||||
|
||||
pub struct SessionState {
|
||||
scene: Scene,
|
||||
}
|
||||
@ -29,11 +37,33 @@ impl SessionState {
|
||||
// The background colour
|
||||
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 {
|
||||
fn play(&mut self, global_state: &mut GlobalState) -> PlayStateResult {
|
||||
// Trap the cursor
|
||||
global_state.window.trap_cursor();
|
||||
|
||||
// Set up an fps clock
|
||||
let mut clock = Clock::new();
|
||||
|
||||
// Game loop
|
||||
loop {
|
||||
// Handle window events
|
||||
@ -48,20 +78,20 @@ impl PlayState for SessionState {
|
||||
// TODO: Do something if the event wasn't handled?
|
||||
}
|
||||
|
||||
// Maintain scene GPU data
|
||||
self.scene.maintain_gpu_data(global_state.window.renderer_mut());
|
||||
// Perform an in-game tick
|
||||
self.scene.tick(clock.get_last_delta())
|
||||
.expect("Failed to tick the scene");
|
||||
|
||||
// Clear the screen
|
||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||
// Render the session
|
||||
self.render(global_state.window.renderer_mut());
|
||||
|
||||
// Render the screen using the global renderer
|
||||
self.scene.render_to(global_state.window.renderer_mut());
|
||||
|
||||
// Finish the frame
|
||||
global_state.window.renderer_mut().flush();
|
||||
// Display the frame on the window
|
||||
global_state.window
|
||||
.swap_buffers()
|
||||
.expect("Failed to swap window buffers");
|
||||
|
||||
// Wait for the next tick
|
||||
clock.tick(Duration::from_millis(1000 / FPS));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user