Compare commits

...

2 Commits

9 changed files with 353 additions and 19 deletions

12
Cargo.lock generated
View File

@ -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"

View File

@ -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"]

View File

@ -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

View File

@ -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"),
);
}
}

View File

@ -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)]

View File

@ -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(())
}
}

View File

@ -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>);
}

View File

@ -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};

View File

@ -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;