Compare commits
2 Commits
e2c6b557bb
...
db501015d0
Author | SHA1 | Date |
---|---|---|
SeanOMik | db501015d0 | |
SeanOMik | 53837d469b |
|
@ -947,6 +947,18 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-timestep-rotating-model"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-std",
|
||||
"fps_counter",
|
||||
"lyra-engine",
|
||||
"rand 0.8.5",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
|
|
@ -10,7 +10,7 @@ members = [
|
|||
"lyra-ecs",
|
||||
"lyra-reflect",
|
||||
"lyra-scripting",
|
||||
"lyra-game", "lyra-math", "lyra-scene", "examples/many-lights"]
|
||||
"lyra-game", "lyra-math", "lyra-scene", "examples/many-lights", "examples/fixed-timestep-rotating-model"]
|
||||
|
||||
[features]
|
||||
scripting = ["dep:lyra-scripting"]
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "fixed-timestep-rotating-model"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lyra-engine = { path = "../../", features = ["tracy"] }
|
||||
anyhow = "1.0.75"
|
||||
async-std = "1.12.0"
|
||||
tracing = "0.1.37"
|
||||
rand = "0.8.5"
|
||||
fps_counter = "3.0.0"
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "/usr/bin/clang"
|
||||
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
|
@ -0,0 +1,241 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use lyra_engine::{
|
||||
assets::{gltf::Gltf, ResourceManager},
|
||||
ecs::{
|
||||
query::{Res, ResMut, View},
|
||||
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
|
||||
World,
|
||||
},
|
||||
game::Game,
|
||||
input::{
|
||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||
},
|
||||
math::{self, Transform, Vec3},
|
||||
render::light::directional::DirectionalLight,
|
||||
scene::{
|
||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
||||
},
|
||||
DeltaTime,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let action_handler_plugin = |game: &mut Game| {
|
||||
let action_handler = ActionHandler::builder()
|
||||
.add_layout(LayoutId::from(0))
|
||||
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||
.add_action("Debug", Action::new(ActionKind::Button))
|
||||
.add_mapping(
|
||||
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||
.bind(
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_MOVE_LEFT_RIGHT,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_MOVE_UP_DOWN,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_LEFT_RIGHT,
|
||||
&[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_UP_DOWN,
|
||||
&[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_ROLL,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
"Debug",
|
||||
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
|
||||
)
|
||||
.finish(),
|
||||
)
|
||||
.finish();
|
||||
|
||||
let world = game.world_mut();
|
||||
world.add_resource(action_handler);
|
||||
game.with_plugin(InputActionPlugin);
|
||||
};
|
||||
|
||||
Game::initialize()
|
||||
.await
|
||||
.with_plugin(lyra_engine::DefaultPlugins)
|
||||
.with_plugin(setup_scene_plugin)
|
||||
.with_plugin(action_handler_plugin)
|
||||
//.with_plugin(camera_debug_plugin)
|
||||
.with_plugin(FreeFlyCameraPlugin)
|
||||
.run()
|
||||
.await;
|
||||
}
|
||||
|
||||
fn setup_scene_plugin(game: &mut Game) {
|
||||
let world = game.world_mut();
|
||||
let resman = world.get_resource_mut::<ResourceManager>();
|
||||
let camera_gltf = resman
|
||||
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
||||
.unwrap();
|
||||
|
||||
camera_gltf.wait_recurse_dependencies_load();
|
||||
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
|
||||
drop(resman);
|
||||
|
||||
world.spawn((
|
||||
camera_mesh.clone(),
|
||||
WorldTransform::default(),
|
||||
Transform::from_xyz(0.0, -5.0, 0.0),
|
||||
));
|
||||
|
||||
{
|
||||
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
||||
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
||||
world.spawn((
|
||||
DirectionalLight {
|
||||
enabled: true,
|
||||
color: Vec3::ONE,
|
||||
intensity: 0.15, //..Default::default()
|
||||
},
|
||||
light_tran,
|
||||
));
|
||||
}
|
||||
|
||||
let mut camera = CameraComponent::new_3d();
|
||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 1.5);
|
||||
world.spawn((camera, FreeFlyCamera::default()));
|
||||
|
||||
let fps_counter = |mut counter: ResMut<fps_counter::FPSCounter>,
|
||||
delta: Res<DeltaTime>| -> anyhow::Result<()> {
|
||||
let tick = counter.tick();
|
||||
|
||||
info!("FPS: {}, frame time: {}", tick, **delta);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
world.add_resource(fps_counter::FPSCounter::new());
|
||||
|
||||
let rotate_system = |dt: Res<DeltaTime>, view: View<&mut Transform>| -> anyhow::Result<()> {
|
||||
const SPEED: f32 = 4.0;
|
||||
let dt = **dt;
|
||||
|
||||
for mut transform in view.iter() {
|
||||
info!("rotation: {:?}", transform.rotation);
|
||||
transform.rotate_y(math::Angle::Degrees(SPEED * dt));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut sys = BatchedSystem::new();
|
||||
sys.with_criteria(FixedTimestep::new(60));
|
||||
sys.with_system(rotate_system.into_system());
|
||||
sys.with_system(fps_counter.into_system());
|
||||
|
||||
game.with_system("fixed_timestep", sys, &[]);
|
||||
}
|
||||
|
||||
struct FixedTimestep {
|
||||
max_tps: u32,
|
||||
fixed_time: f32,
|
||||
accumulator: f32,
|
||||
old_dt: Option<DeltaTime>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FixedTimestep {
|
||||
pub fn new(max_tps: u32) -> Self {
|
||||
Self {
|
||||
max_tps,
|
||||
fixed_time: Self::calc_fixed_time(max_tps),
|
||||
accumulator: 0.0,
|
||||
old_dt: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_fixed_time(max_tps: u32) -> f32 {
|
||||
1.0 / max_tps as f32
|
||||
}
|
||||
|
||||
fn set_tps(&mut self, tps: u32) {
|
||||
self.max_tps = tps;
|
||||
self.fixed_time = Self::calc_fixed_time(tps);
|
||||
}
|
||||
|
||||
fn tps(&self) -> u32 {
|
||||
self.max_tps
|
||||
}
|
||||
|
||||
fn fixed_time(&self) -> f32 {
|
||||
self.fixed_time
|
||||
}
|
||||
}
|
||||
|
||||
impl Criteria for FixedTimestep {
|
||||
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
||||
let world = unsafe { world.as_mut() };
|
||||
if check_count == 0 {
|
||||
let delta_time = world.get_resource::<DeltaTime>();
|
||||
self.accumulator += **delta_time;
|
||||
}
|
||||
|
||||
if self.accumulator >= self.fixed_time {
|
||||
self.accumulator -= self.fixed_time;
|
||||
return CriteriaSchedule::YesAndLoop;
|
||||
}
|
||||
|
||||
CriteriaSchedule::No
|
||||
}
|
||||
|
||||
fn modify_world(&mut self, mut world: NonNull<World>) {
|
||||
let world = unsafe { world.as_mut() };
|
||||
self.old_dt = world.try_get_resource().map(|r| *r);
|
||||
|
||||
world.add_resource(DeltaTime::from(self.fixed_time));
|
||||
}
|
||||
|
||||
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
||||
let world = unsafe { world.as_mut() };
|
||||
world.add_resource(
|
||||
self.old_dt
|
||||
.expect("DeltaTime resource was somehow never got from the world"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ struct FixedTimestep {
|
|||
max_tps: u32,
|
||||
fixed_time: f32,
|
||||
accumulator: f32,
|
||||
old_dt: Option<DeltaTime>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -16,6 +17,7 @@ impl FixedTimestep {
|
|||
max_tps,
|
||||
fixed_time: Self::calc_fixed_time(max_tps),
|
||||
accumulator: 0.0,
|
||||
old_dt: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +54,21 @@ impl Criteria for FixedTimestep {
|
|||
|
||||
CriteriaSchedule::No
|
||||
}
|
||||
|
||||
fn modify_world(&mut self, mut world: NonNull<World>) {
|
||||
let world = unsafe { world.as_mut() };
|
||||
self.old_dt = world.try_get_resource().map(|r| *r);
|
||||
|
||||
world.add_resource(DeltaTime::from(self.fixed_time));
|
||||
}
|
||||
|
||||
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
||||
let world = unsafe { world.as_mut() };
|
||||
world.add_resource(
|
||||
self.old_dt
|
||||
.expect("DeltaTime resource was somehow never got from the world"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use lyra_ecs::World;
|
||||
use tracing::debug_span;
|
||||
use tracing::{debug_span, instrument};
|
||||
|
||||
use crate::Access;
|
||||
|
||||
use super::{System, Criteria, IntoSystem};
|
||||
use super::{Criteria, GraphExecutorError, IntoSystem, System};
|
||||
|
||||
/// A system that executes a batch of systems in order that they were given.
|
||||
/// You can optionally add criteria that must pass before the systems are
|
||||
|
@ -13,6 +13,7 @@ pub struct BatchedSystem {
|
|||
systems: Vec<Box<dyn System>>,
|
||||
criteria: Vec<Box<dyn Criteria>>,
|
||||
criteria_checks: u32,
|
||||
did_run: bool,
|
||||
}
|
||||
|
||||
impl BatchedSystem {
|
||||
|
@ -47,6 +48,7 @@ impl System for BatchedSystem {
|
|||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, world))]
|
||||
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||
let mut can_run = true;
|
||||
let mut check_again = false;
|
||||
|
@ -69,13 +71,26 @@ impl System for BatchedSystem {
|
|||
}
|
||||
|
||||
if can_run {
|
||||
for criteria in self.criteria.iter_mut() {
|
||||
criteria.modify_world(world);
|
||||
}
|
||||
|
||||
for (idx, system) in self.systems.iter_mut().enumerate() {
|
||||
let sys_span = debug_span!("batch", system=tracing::field::Empty);
|
||||
sys_span.record("system", idx);
|
||||
let _e = sys_span.enter();
|
||||
let span = debug_span!("batch", system=idx);
|
||||
let _e = span.enter();
|
||||
|
||||
system.execute(world)?;
|
||||
|
||||
/* let deferred_span = debug_span!("deferred_exec");
|
||||
let _e = deferred_span.enter();
|
||||
|
||||
if let Err(e) = system.execute_deferred(world)
|
||||
.map_err(|e| GraphExecutorError::Command(e)) {
|
||||
return Err(e.into());
|
||||
} */
|
||||
}
|
||||
|
||||
self.did_run = true;
|
||||
}
|
||||
|
||||
if check_again {
|
||||
|
@ -87,9 +102,26 @@ impl System for BatchedSystem {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_deferred(&mut self, _: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
|
||||
#[instrument(skip(self, world))]
|
||||
fn execute_deferred(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||
if self.did_run {
|
||||
for (idx, system) in self.systems.iter_mut().enumerate() {
|
||||
let span = debug_span!("batch", system=idx);
|
||||
let _e = span.enter();
|
||||
|
||||
system.execute_deferred(world)
|
||||
.map_err(|e| GraphExecutorError::Command(e))?;
|
||||
}
|
||||
|
||||
for criteria in self.criteria.iter_mut() {
|
||||
criteria.undo_world_modifications(world);
|
||||
}
|
||||
}
|
||||
|
||||
self.did_run = false;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,13 +26,17 @@ pub trait Criteria {
|
|||
/// * `world` - The ecs world.
|
||||
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
||||
fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
|
||||
}
|
||||
|
||||
impl<F> Criteria for F
|
||||
where F: FnMut(&mut World, u32) -> CriteriaSchedule
|
||||
{
|
||||
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
||||
let world_mut = unsafe { world.as_mut() };
|
||||
self(world_mut, check_count)
|
||||
}
|
||||
/// Modify the world after the [`Criteria`] in the system batch allows the systems to run.
|
||||
///
|
||||
/// This can be great if this Criteria limits the execution of systems based off of resources.
|
||||
/// A `FixedTimestep` criteria would use this to replace the [`DeltaTime`] resource in the
|
||||
/// world to match the timestep time.
|
||||
fn modify_world(&mut self, world: NonNull<World>);
|
||||
|
||||
/// Undo modifications to the world after the systems in the batch have been executed.
|
||||
///
|
||||
/// The `FixedTimestep` criteria (see docs for [`Criteria::modify_world`]) uses this
|
||||
/// to replace the [`DeltaTime`] resource with its original value before it was replaced.
|
||||
fn undo_world_modifications(&mut self, world: NonNull<World>);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use std::{any::{Any, TypeId}, collections::HashMap, ptr::NonNull};
|
||||
use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
||||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
|
||||
|
|
|
@ -4,9 +4,15 @@ use lyra_reflect::Reflect;
|
|||
|
||||
use crate::{plugin::Plugin, game::GameStages};
|
||||
|
||||
#[derive(Clone, Component, Default, Reflect)]
|
||||
#[derive(Clone, Copy, Component, Default, Reflect)]
|
||||
pub struct DeltaTime(f32, #[reflect(skip)] Option<Instant>);
|
||||
|
||||
impl From<f32> for DeltaTime {
|
||||
fn from(value: f32) -> Self {
|
||||
DeltaTime(value, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DeltaTime {
|
||||
type Target = f32;
|
||||
|
||||
|
|
Loading…
Reference in New Issue