Compare commits
No commits in common. "798719a7a2aa21f23948d50101b12b1d7fa86460" and "9b1cc8c364b6732df58b0437fa2a6a6f8f4c9564" have entirely different histories.
798719a7a2
...
9b1cc8c364
|
@ -1,12 +1,17 @@
|
|||
use lyra_engine::{
|
||||
assets::{gltf::Gltf, ResourceManager}, ecs::query::View, game::App, input::{
|
||||
assets::{gltf::Gltf, ResourceManager},
|
||||
game::App,
|
||||
input::{
|
||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||
}, math::{self, Transform, Vec3}, render::{light::directional::DirectionalLight, window::WindowOptions}, scene::{
|
||||
},
|
||||
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,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
|
@ -47,7 +52,7 @@ async fn main() {
|
|||
.bind(
|
||||
ACTLBL_LOOK_LEFT_RIGHT,
|
||||
&[
|
||||
//ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
|
||||
],
|
||||
|
@ -55,7 +60,7 @@ async fn main() {
|
|||
.bind(
|
||||
ACTLBL_LOOK_UP_DOWN,
|
||||
&[
|
||||
//ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
|
||||
],
|
||||
|
@ -82,7 +87,6 @@ async fn main() {
|
|||
|
||||
let mut a = App::new();
|
||||
a.with_plugin(lyra_engine::DefaultPlugins)
|
||||
.with_system("mouse_pos_print", mouse_pos_system, &[])
|
||||
.with_plugin(setup_scene_plugin)
|
||||
.with_plugin(action_handler_plugin)
|
||||
//.with_plugin(camera_debug_plugin)
|
||||
|
@ -92,7 +96,7 @@ async fn main() {
|
|||
|
||||
fn setup_scene_plugin(app: &mut App) {
|
||||
let world = &mut app.world;
|
||||
let resman = world.get_resource_mut::<ResourceManager>().unwrap();
|
||||
let resman = world.get_resource_mut::<ResourceManager>();
|
||||
|
||||
/* let camera_gltf = resman
|
||||
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
||||
|
@ -141,11 +145,3 @@ fn setup_scene_plugin(app: &mut App) {
|
|||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||
world.spawn((camera, FreeFlyCamera::default()));
|
||||
}
|
||||
|
||||
fn mouse_pos_system(view: View<&WindowOptions>) -> anyhow::Result<()> {
|
||||
for win in view.iter() {
|
||||
//println!("Mouse pos: {:?}", win.cursor_position());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
|
||||
use crate::{resource::ResourceObject, Tick, TrackedResource, World};
|
||||
use crate::{World, resource::ResourceObject};
|
||||
|
||||
use super::{Query, Fetch, AsQuery};
|
||||
|
||||
|
@ -22,18 +22,12 @@ impl<'a, T: ResourceObject + 'a> Fetch<'a> for FetchResource<'a, T> {
|
|||
}
|
||||
|
||||
fn can_visit_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> bool {
|
||||
let w = self.world.unwrap();
|
||||
w.has_resource::<T>()
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let w = self.world.unwrap();
|
||||
Res {
|
||||
// this unwrap is safe since `can_visit_item` ensures the resource exists
|
||||
inner: w.get_tracked_resource::<T>().unwrap(),
|
||||
world_tick: w.current_tick(),
|
||||
_marker: PhantomData::<T>,
|
||||
}
|
||||
Res(w.get_resource::<T>())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -87,37 +81,13 @@ impl<R: ResourceObject> AsQuery for QueryResource<R> {
|
|||
}
|
||||
|
||||
/// A struct used for querying resources from the World.
|
||||
pub struct Res<'a, T: ResourceObject> {
|
||||
pub(crate) inner: AtomicRef<'a, TrackedResource<dyn ResourceObject>>,
|
||||
pub(crate) world_tick: Tick,
|
||||
pub(crate) _marker: PhantomData<T>,
|
||||
}
|
||||
pub struct Res<'a, T: ResourceObject>(pub(crate) AtomicRef<'a, T>);
|
||||
|
||||
impl<'a, T: ResourceObject> std::ops::Deref for Res<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.res.as_any().downcast_ref::<T>().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ResourceObject> Res<'a, T> {
|
||||
/// Get the inner [`AtomicRef`].
|
||||
///
|
||||
/// This inner type does not have change tracking. If you `DerefMut` it, the change will not be tracked!
|
||||
#[inline]
|
||||
pub fn get_inner(self) -> atomic_refcell::AtomicRef<'a, T> {
|
||||
atomic_refcell::AtomicRef::map(self.inner, |r| r.res.as_any().downcast_ref().unwrap())
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if the resource changed.
|
||||
pub fn changed(&self) -> bool {
|
||||
self.inner.tick >= self.world_tick
|
||||
}
|
||||
|
||||
/// The tick that this resource was last modified at
|
||||
pub fn tick(&self) -> Tick {
|
||||
self.inner.tick
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,18 +111,12 @@ impl<'a, T: ResourceObject + 'a> Fetch<'a> for FetchResourceMut<'a, T> {
|
|||
}
|
||||
|
||||
fn can_visit_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> bool {
|
||||
let w = self.world.unwrap();
|
||||
w.has_resource::<T>()
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, _entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let w = self.world.unwrap();
|
||||
ResMut {
|
||||
// this is safe since `can_visit_item` ensures that the resource exists.
|
||||
inner: w.get_tracked_resource_mut::<T>().unwrap(),
|
||||
world_tick: w.current_tick(),
|
||||
_marker: PhantomData::<T>,
|
||||
}
|
||||
ResMut(w.get_resource_mut::<T>())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -205,51 +169,20 @@ impl<R: ResourceObject> AsQuery for QueryResourceMut<R> {
|
|||
}
|
||||
|
||||
/// A struct used for querying resources from the World.
|
||||
//pub struct ResMut<'a, T: ResourceObject>(pub(crate) AtomicRefMut<'a, T>);
|
||||
pub struct ResMut<'a, T: ResourceObject> {
|
||||
pub(crate) inner: AtomicRefMut<'a, TrackedResource<dyn ResourceObject>>,
|
||||
pub(crate) world_tick: Tick,
|
||||
pub(crate) _marker: PhantomData<T>,
|
||||
}
|
||||
pub struct ResMut<'a, T: ResourceObject>(pub(crate) AtomicRefMut<'a, T>);
|
||||
|
||||
impl<'a, T: ResourceObject> std::ops::Deref for ResMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.res.as_any().downcast_ref::<T>().unwrap()
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ResourceObject> std::ops::DerefMut for ResMut<'a, T> {
|
||||
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.mark_changed();
|
||||
self.inner.res.as_any_mut().downcast_mut::<T>().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ResourceObject> ResMut<'a, T> {
|
||||
/// Get the inner [`AtomicRefMut`].
|
||||
///
|
||||
/// This inner type does not have change tracking. If you `DerefMut` it, the change will not be tracked!
|
||||
pub fn get_inner(self) -> atomic_refcell::AtomicRefMut<'a, T> {
|
||||
atomic_refcell::AtomicRefMut::map(self.inner, |r| r.res.as_any_mut().downcast_mut().unwrap())
|
||||
}
|
||||
|
||||
pub fn changed(&self) -> bool {
|
||||
self.inner.tick > self.world_tick
|
||||
}
|
||||
|
||||
/// The tick that this resource was last modified at
|
||||
pub fn tick(&self) -> Tick {
|
||||
self.inner.tick
|
||||
}
|
||||
|
||||
/// Manually mark the resource as changed.
|
||||
///
|
||||
/// This is useful if you have a resource with interior mutability and you want to
|
||||
/// mark this resource as changed.
|
||||
pub fn mark_changed(&mut self) {
|
||||
self.inner.tick = self.world_tick
|
||||
self.0.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ where
|
|||
ViewIter {
|
||||
world: self.world,
|
||||
tick: self.world.current_tick(),
|
||||
has_ticked: false,
|
||||
query: self.query,
|
||||
filter: self.filter,
|
||||
fetcher: None,
|
||||
|
@ -72,6 +73,7 @@ where
|
|||
pub struct ViewIter<'a, Q: Query, F: Filter> {
|
||||
world: &'a World,
|
||||
tick: Tick,
|
||||
has_ticked: bool,
|
||||
query: Q,
|
||||
filter: F,
|
||||
fetcher: Option<Q::Fetch<'a>>,
|
||||
|
@ -110,6 +112,13 @@ where
|
|||
let entity_index = ArchetypeEntityId(entity_index);
|
||||
|
||||
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit_item(entity_index) {
|
||||
// only tick the world if the filter has fetched, and when the world hasn't
|
||||
// been ticked yet.
|
||||
if Q::MUTATES && !self.has_ticked {
|
||||
self.has_ticked = true;
|
||||
self.tick = self.world.tick();
|
||||
}
|
||||
|
||||
let i = unsafe { fetcher.get_item(entity_index) };
|
||||
return Some(i);
|
||||
}
|
||||
|
@ -165,6 +174,9 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
|
|||
if self.query.can_visit_archetype(arch) {
|
||||
let mut fetch = unsafe { self.query.fetch(self.world, arch, self.tick) };
|
||||
if fetch.can_visit_item(record.index) {
|
||||
// only tick the world when something is actually fetched.
|
||||
self.world.tick();
|
||||
|
||||
return Some(unsafe { fetch.get_item(record.index) });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,24 +20,22 @@ impl<T: Send + Sync + Any> ResourceObject for T {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct TrackedResource<T: ?Sized> {
|
||||
pub tick: Tick,
|
||||
pub res: T,
|
||||
}
|
||||
|
||||
/// A type erased storage for a Resource.
|
||||
#[derive(Clone)]
|
||||
pub struct ResourceData {
|
||||
pub(crate) data: Arc<AtomicRefCell<TrackedResource<dyn ResourceObject>>>,
|
||||
pub(crate) data: Arc<AtomicRefCell<dyn ResourceObject>>,
|
||||
type_id: TypeId,
|
||||
// use a tick tracker which has interior mutability
|
||||
pub(crate) tick: TickTracker,
|
||||
}
|
||||
|
||||
impl ResourceData {
|
||||
pub fn new<T: ResourceObject>(data: T, tick: Tick) -> Self {
|
||||
|
||||
Self {
|
||||
data: Arc::new(AtomicRefCell::new(TrackedResource { tick, res: data })),
|
||||
data: Arc::new(AtomicRefCell::new(data)),
|
||||
type_id: TypeId::of::<T>(),
|
||||
tick: TickTracker::from(*tick),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +51,7 @@ impl ResourceData {
|
|||
/// * If the data is already borrowed mutably, this will panic.
|
||||
/// * If the type of `T` is not the same as the resource type.
|
||||
pub fn get<T: ResourceObject>(&self) -> AtomicRef<T> {
|
||||
AtomicRef::map(self.data.borrow(), |a| a.res.as_any().downcast_ref().unwrap())
|
||||
AtomicRef::map(self.data.borrow(), |a| a.as_any().downcast_ref().unwrap())
|
||||
}
|
||||
|
||||
/// Mutably borrow the data inside of the resource.
|
||||
|
@ -63,7 +61,7 @@ impl ResourceData {
|
|||
/// * If the data is already borrowed mutably, this will panic.
|
||||
/// * If the type of `T` is not the same as the resource type.
|
||||
pub fn get_mut<T: ResourceObject>(&self) -> AtomicRefMut<T> {
|
||||
AtomicRefMut::map(self.data.borrow_mut(), |a| a.res.as_any_mut().downcast_mut().unwrap())
|
||||
AtomicRefMut::map(self.data.borrow_mut(), |a| a.as_any_mut().downcast_mut().unwrap())
|
||||
}
|
||||
|
||||
/// Borrow the data inside of the resource.
|
||||
|
@ -73,7 +71,7 @@ impl ResourceData {
|
|||
/// * If the type of `T` is not the same as the resource type.
|
||||
pub fn try_get<T: ResourceObject>(&self) -> Option<AtomicRef<T>> {
|
||||
self.data.try_borrow()
|
||||
.map(|r| AtomicRef::map(r, |a| a.res.as_any().downcast_ref().unwrap()))
|
||||
.map(|r| AtomicRef::map(r, |a| a.as_any().downcast_ref().unwrap()))
|
||||
.ok()
|
||||
}
|
||||
|
||||
|
@ -84,11 +82,11 @@ impl ResourceData {
|
|||
/// * If the type of `T` is not the same as the resource type.
|
||||
pub fn try_get_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<T>> {
|
||||
self.data.try_borrow_mut()
|
||||
.map(|r| AtomicRefMut::map(r, |a| a.res.as_any_mut().downcast_mut().unwrap()))
|
||||
.map(|r| AtomicRefMut::map(r, |a| a.as_any_mut().downcast_mut().unwrap()))
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn changed(&self, tick: Tick) -> bool {
|
||||
self.data.borrow().tick >= tick
|
||||
self.tick.current() >= tick
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ pub trait FnArgFetcher {
|
|||
Access::Read
|
||||
}
|
||||
|
||||
// TODO: check if the fetcher can fetch before getting.
|
||||
/// Get the arg from the world
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -215,9 +214,7 @@ impl<R: ResourceObject> FnArgFetcher for Res<'_, R> {
|
|||
|
||||
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
world.get_resource::<R>()
|
||||
// TODO: check if the resource exists before attempting to fetch
|
||||
.unwrap_or_else(|| panic!("world is missing resource: {}", std::any::type_name::<R>()))
|
||||
Res(world.get_resource::<R>())
|
||||
}
|
||||
|
||||
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||
|
@ -231,9 +228,7 @@ impl<R: ResourceObject> FnArgFetcher for ResMut<'_, R> {
|
|||
|
||||
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull<World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
world.get_resource_mut::<R>()
|
||||
// TODO: check if the resource exists before attempting to fetch
|
||||
.unwrap_or_else(|| panic!("world is missing resource: {}", std::any::type_name::<R>()))
|
||||
ResMut(world.get_resource_mut::<R>())
|
||||
}
|
||||
|
||||
fn apply_deferred(_: Self::State, _: NonNull<World>) { }
|
||||
|
@ -312,8 +307,7 @@ mod tests {
|
|||
world.add_resource(SomeCounter(0));
|
||||
|
||||
let test_system = |world: &World| -> anyhow::Result<()> {
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>();
|
||||
counter.0 += 10;
|
||||
|
||||
Ok(())
|
||||
|
@ -322,8 +316,7 @@ mod tests {
|
|||
test_system.into_system().execute(NonNull::from(&world)).unwrap();
|
||||
|
||||
let test_system = |world: &mut World| -> anyhow::Result<()> {
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>();
|
||||
counter.0 += 10;
|
||||
|
||||
Ok(())
|
||||
|
@ -331,8 +324,7 @@ mod tests {
|
|||
|
||||
test_system.into_system().execute(NonNull::from(&world)).unwrap();
|
||||
|
||||
let counter = world.get_resource::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SomeCounter>();
|
||||
assert_eq!(counter.0, 20);
|
||||
}
|
||||
|
||||
|
@ -344,8 +336,7 @@ mod tests {
|
|||
world.add_resource(SomeCounter(0));
|
||||
|
||||
let test_system = |world: &World| -> anyhow::Result<()> {
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>();
|
||||
counter.0 += 10;
|
||||
|
||||
Ok(())
|
||||
|
@ -355,8 +346,7 @@ mod tests {
|
|||
|
||||
#[allow(dead_code)]
|
||||
fn test_system(world: &mut World) -> anyhow::Result<()> {
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SomeCounter>();
|
||||
counter.0 += 10;
|
||||
|
||||
Ok(())
|
||||
|
@ -364,8 +354,7 @@ mod tests {
|
|||
|
||||
test_system.into_system().execute(NonNull::from(&world)).unwrap();
|
||||
|
||||
let counter = world.get_resource::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SomeCounter>();
|
||||
assert_eq!(counter.0, 20);
|
||||
}
|
||||
|
||||
|
@ -377,15 +366,16 @@ mod tests {
|
|||
world.add_resource(SomeCounter(0));
|
||||
|
||||
let test_system = |mut counter: ResMut<SomeCounter>| -> anyhow::Result<()> {
|
||||
counter.0 += 10;
|
||||
// .0 is twice here since ResMut's tuple field is pub(crate).
|
||||
// Users wont need to do this
|
||||
counter.0.0 += 10;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
test_system.into_system().execute(NonNull::from(&world)).unwrap();
|
||||
|
||||
let counter = world.get_resource::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SomeCounter>();
|
||||
assert_eq!(counter.0, 10);
|
||||
}
|
||||
|
||||
|
@ -399,7 +389,9 @@ mod tests {
|
|||
let test_system = |mut counter: ResMut<SomeCounter>, view: ViewState<QueryBorrow<Vec2>, ()>| -> anyhow::Result<()> {
|
||||
for v2 in view.into_iter() {
|
||||
println!("Got v2 at '{:?}'", v2);
|
||||
counter.0 += 1;
|
||||
// .0 is twice here since ResMut's tuple field is pub(crate).
|
||||
// Users wont need to do this
|
||||
counter.0.0 += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -407,8 +399,7 @@ mod tests {
|
|||
|
||||
test_system.into_system().execute(NonNull::from(&world)).unwrap();
|
||||
|
||||
let counter = world.get_resource::<SomeCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SomeCounter>();
|
||||
assert_eq!(counter.0, 2);
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@ impl GraphExecutor {
|
|||
}
|
||||
|
||||
let world = unsafe { world_ptr.as_mut() };
|
||||
if let Some(mut queue) = world.get_resource_mut::<CommandQueue>() {
|
||||
if let Some(mut queue) = world.try_get_resource_mut::<CommandQueue>() {
|
||||
// Safety: Commands only borrows world.entities when adding commands
|
||||
let world = unsafe { world_ptr.as_mut() };
|
||||
let mut commands = Commands::new(&mut queue, world);
|
||||
|
@ -189,8 +189,7 @@ mod tests {
|
|||
exec.execute(NonNull::from(&world), true).unwrap();
|
||||
println!("Executed systems");
|
||||
|
||||
let order = world.get_resource::<Vec<String>>()
|
||||
.expect("missing Vec<String> resource");
|
||||
let order = world.get_resource::<Vec<String>>();
|
||||
let mut order_iter = order.iter();
|
||||
|
||||
// ... but still executed in order
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|||
/// TickTracker is used for tracking changes of [`Component`](crate::Component)s and entities.
|
||||
///
|
||||
/// TickTracker stores an [`AtomicU64`], making all operations on `TickTracker`, atomic as well.
|
||||
/// Note that [`TickTracker::clone`] only clones the inner value of atomic, and not the atomic itself.
|
||||
/// Note that [`Tick::clone`] only clones the inner value of atomic, and not the atomic itself.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TickTracker {
|
||||
tick: AtomicU64,
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
|||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
|
||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, Res, ResMut, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker, TrackedResource};
|
||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||
|
||||
/// The id of the entity for the Archetype.
|
||||
///
|
||||
|
@ -65,9 +65,10 @@ impl World {
|
|||
where
|
||||
B: Bundle
|
||||
{
|
||||
let tick = self.current_tick();
|
||||
let bundle_types = bundle.type_ids();
|
||||
|
||||
let tick = self.tick();
|
||||
|
||||
// try to find an archetype
|
||||
let archetype = self.archetypes
|
||||
.values_mut()
|
||||
|
@ -111,8 +112,14 @@ impl World {
|
|||
|
||||
/// Despawn an entity from the World
|
||||
pub fn despawn(&mut self, entity: Entity) {
|
||||
let tick = self.current_tick();
|
||||
// Tick the tracker if the entity is spawned. This is done here instead of the `if let`
|
||||
// below due to the borrow checker complaining about multiple mutable borrows to self.
|
||||
let tick = if self.entities.arch_index.contains_key(&entity.id) {
|
||||
Some(self.tick())
|
||||
} else { None };
|
||||
|
||||
if let Some(record) = self.entities.arch_index.get_mut(&entity.id) {
|
||||
let tick = tick.unwrap();
|
||||
let arch = self.archetypes.get_mut(&record.id).unwrap();
|
||||
|
||||
if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) {
|
||||
|
@ -131,7 +138,8 @@ impl World {
|
|||
where
|
||||
B: Bundle
|
||||
{
|
||||
let tick = self.current_tick();
|
||||
let tick = self.tick();
|
||||
|
||||
let record = self.entities.entity_record(entity).unwrap();
|
||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||
let current_arch_len = current_arch.len();
|
||||
|
@ -367,24 +375,32 @@ impl World {
|
|||
}
|
||||
|
||||
/// Add a resource to the world.
|
||||
///
|
||||
/// Ticks the world.
|
||||
pub fn add_resource<T: ResourceObject>(&mut self, data: T) {
|
||||
self.resources.insert(TypeId::of::<T>(), ResourceData::new(data, self.current_tick()));
|
||||
let tick = self.tick();
|
||||
self.resources.insert(TypeId::of::<T>(), ResourceData::new(data, tick));
|
||||
}
|
||||
|
||||
/// Add the default value of a resource.
|
||||
///
|
||||
/// Ticks the world.
|
||||
///
|
||||
/// > Note: This will replace existing values.
|
||||
pub fn add_resource_default<T: ResourceObject + Default>(&mut self) {
|
||||
self.resources.insert(TypeId::of::<T>(), ResourceData::new(T::default(), self.current_tick()));
|
||||
let tick = self.tick();
|
||||
self.resources.insert(TypeId::of::<T>(), ResourceData::new(T::default(), tick));
|
||||
}
|
||||
|
||||
/// Add the default value of a resource if it does not already exist.
|
||||
///
|
||||
/// Returns a boolean indicating if the resource was added.
|
||||
/// Returns a boolean indicating if the resource was added. Ticks the world if the resource
|
||||
/// was added.
|
||||
pub fn add_resource_default_if_absent<T: ResourceObject + Default>(&mut self) -> bool {
|
||||
let id = TypeId::of::<T>();
|
||||
if !self.resources.contains_key(&id) {
|
||||
self.resources.insert(id, ResourceData::new(T::default(), self.current_tick()));
|
||||
let tick = self.tick();
|
||||
self.resources.insert(id, ResourceData::new(T::default(), tick));
|
||||
|
||||
true
|
||||
} else {
|
||||
|
@ -394,60 +410,39 @@ impl World {
|
|||
|
||||
/// Get a resource from the world, or insert it into the world with the provided
|
||||
/// `fn` and return it.
|
||||
pub fn get_resource_or_else<T: ResourceObject, F>(&mut self, f: F) -> ResMut<T>
|
||||
///
|
||||
/// Ticks the world.
|
||||
pub fn get_resource_or_else<T: ResourceObject, F>(&mut self, f: F) -> AtomicRefMut<T>
|
||||
where
|
||||
F: Fn() -> T + 'static
|
||||
{
|
||||
let tick = self.current_tick();
|
||||
let tick = self.tick();
|
||||
let res = self.resources.entry(TypeId::of::<T>())
|
||||
.or_insert_with(|| ResourceData::new(f(), tick));
|
||||
|
||||
ResMut {
|
||||
inner: res.data.borrow_mut(),
|
||||
world_tick: tick,
|
||||
_marker: std::marker::PhantomData::<T>,
|
||||
}
|
||||
res.tick.tick_to(&tick);
|
||||
res.get_mut()
|
||||
}
|
||||
|
||||
/// Get a resource from the world, or insert its default value.
|
||||
pub fn get_resource_or_default<T: ResourceObject + Default>(&mut self) -> ResMut<T>
|
||||
/// Get a resource from the world, or insert it into the world as its default.
|
||||
///
|
||||
/// Ticks the world.
|
||||
pub fn get_resource_or_default<T: ResourceObject + Default>(&mut self) -> AtomicRefMut<T>
|
||||
{
|
||||
let tick = self.current_tick();
|
||||
let tick = self.tick();
|
||||
let res = self.resources.entry(TypeId::of::<T>())
|
||||
.or_insert_with(|| ResourceData::new(T::default(), tick));
|
||||
|
||||
ResMut {
|
||||
inner: res.data.borrow_mut(),
|
||||
world_tick: tick,
|
||||
_marker: std::marker::PhantomData::<T>,
|
||||
}
|
||||
res.tick.tick_to(&tick);
|
||||
res.get_mut()
|
||||
}
|
||||
|
||||
/// Gets a resource from the World.
|
||||
pub fn get_resource<T: ResourceObject>(&self) -> Option<Res<T>> {
|
||||
self.get_tracked_resource::<T>().map(|r| Res {
|
||||
inner: r,
|
||||
world_tick: self.current_tick(),
|
||||
_marker: std::marker::PhantomData::<T>,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets a reference to a change tracked resource.
|
||||
///
|
||||
/// You will have to manually downcast the inner resource. Most people don't need this, see
|
||||
/// [`World::get_resource`].
|
||||
pub fn get_tracked_resource<T: ResourceObject>(&self) -> Option<AtomicRef<TrackedResource<dyn ResourceObject>>> {
|
||||
/// Will panic if the resource is not in the world. See [`World::try_get_resource`] for
|
||||
/// a function that returns an option.
|
||||
pub fn get_resource<T: ResourceObject>(&self) -> AtomicRef<T> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.map(|r| r.data.borrow())
|
||||
}
|
||||
|
||||
/// Gets a mutable borrow to a change tracked resource.
|
||||
///
|
||||
/// You will have to manually downcast the inner resource. Most people don't need this, see
|
||||
/// [`World::get_resource_mut`].
|
||||
pub fn get_tracked_resource_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<TrackedResource<dyn ResourceObject>>> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.map(|r| r.data.borrow_mut())
|
||||
.expect(&format!("World is missing resource of type '{}'", std::any::type_name::<T>()))
|
||||
.get()
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if the resource changed.
|
||||
|
@ -463,7 +458,7 @@ impl World {
|
|||
/// Returns the [`Tick`] that the resource was last modified at.
|
||||
pub fn resource_tick<T: ResourceObject>(&self) -> Option<Tick> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.map(|r| r.data.borrow().tick)
|
||||
.map(|r| r.tick.current())
|
||||
}
|
||||
|
||||
/// Returns boolean indicating if the World contains a resource of type `T`.
|
||||
|
@ -471,33 +466,56 @@ impl World {
|
|||
self.resources.contains_key(&TypeId::of::<T>())
|
||||
}
|
||||
|
||||
/// Attempts to get a resource from the World.
|
||||
///
|
||||
/// Returns `None` if the resource was not found.
|
||||
pub fn try_get_resource<T: ResourceObject>(&self) -> Option<AtomicRef<T>> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.and_then(|r| r.try_get())
|
||||
}
|
||||
|
||||
/// Gets a mutable borrow of a resource from the World.
|
||||
pub fn get_resource_mut<T: ResourceObject>(&self) -> Option<ResMut<T>> {
|
||||
self.get_tracked_resource_mut::<T>().map(|r| ResMut {
|
||||
inner: r,
|
||||
world_tick: self.current_tick(),
|
||||
_marker: std::marker::PhantomData::<T>,
|
||||
///
|
||||
/// Will panic if the resource is not in the world. See [`World::try_get_resource_mut`] for
|
||||
/// a function that returns an option.
|
||||
///
|
||||
/// Ticks the world.
|
||||
pub fn get_resource_mut<T: ResourceObject>(&self) -> AtomicRefMut<T> {
|
||||
self.try_get_resource_mut::<T>()
|
||||
.unwrap_or_else(|| panic!("World is missing resource of type '{}'", std::any::type_name::<T>()))
|
||||
}
|
||||
|
||||
/// Attempts to get a mutable borrow of a resource from the World.
|
||||
///
|
||||
/// Returns `None` if the resource was not found. Ticks the world if the resource was found.
|
||||
pub fn try_get_resource_mut<T: ResourceObject>(&self) -> Option<AtomicRefMut<T>> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.and_then(|r| {
|
||||
// now that the resource was retrieved, tick the world and the resource
|
||||
let new_tick = self.tick();
|
||||
r.tick.tick_to(&new_tick);
|
||||
r.try_get_mut()
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the corresponding [`ResourceData`].
|
||||
pub fn get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
|
||||
///
|
||||
/// > Note: If you borrow the resource mutably, the world and the resource will not be ticked.
|
||||
pub fn try_get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.map(|r| r.clone())
|
||||
}
|
||||
|
||||
/// Increments the world current tick for tracking changes to components and resources.
|
||||
/// Increments the TickTracker which is used for tracking changes to components.
|
||||
///
|
||||
/// # Note:
|
||||
/// For change tracking to work correctly, this must be ran each loop before you run world
|
||||
/// systems.
|
||||
/// Most users wont need to call this manually, its done for you through queries and views.
|
||||
pub fn tick(&self) -> Tick {
|
||||
self.tracker.tick()
|
||||
}
|
||||
|
||||
/// Gets the current tick that the world is at.
|
||||
///
|
||||
/// See [`World::tick`].
|
||||
/// See [`TickTracker`]
|
||||
pub fn current_tick(&self) -> Tick {
|
||||
self.tracker.current()
|
||||
}
|
||||
|
@ -507,7 +525,7 @@ impl World {
|
|||
}
|
||||
|
||||
/// Attempts to find a resource in the world and returns a NonNull pointer to it
|
||||
pub unsafe fn get_resource_ptr<T: ResourceObject>(&self) -> Option<NonNull<T>> {
|
||||
pub unsafe fn try_get_resource_ptr<T: ResourceObject>(&self) -> Option<NonNull<T>> {
|
||||
self.resources.get(&TypeId::of::<T>())
|
||||
.map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) })
|
||||
}
|
||||
|
@ -583,17 +601,15 @@ mod tests {
|
|||
world.add_resource(counter);
|
||||
}
|
||||
|
||||
let counter = world.get_resource::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SimpleCounter>();
|
||||
assert_eq!(counter.0, 0);
|
||||
drop(counter);
|
||||
|
||||
let mut counter = world.get_resource_mut::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SimpleCounter>();
|
||||
counter.0 += 4582;
|
||||
drop(counter);
|
||||
|
||||
assert!(world.get_resource::<u32>().is_none());
|
||||
assert!(world.try_get_resource::<u32>().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -603,11 +619,9 @@ mod tests {
|
|||
world.add_resource(counter);
|
||||
|
||||
// test multiple borrows at the same time
|
||||
let counter = world.get_resource::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource::<SimpleCounter>();
|
||||
assert_eq!(counter.0, 4582);
|
||||
let counter2 = world.get_resource::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter2 = world.get_resource::<SimpleCounter>();
|
||||
assert_eq!(counter.0, 4582);
|
||||
assert_eq!(counter2.0, 4582);
|
||||
}
|
||||
|
@ -621,10 +635,9 @@ mod tests {
|
|||
}
|
||||
|
||||
// test that its only possible to get a single mutable borrow
|
||||
let counter = world.get_resource_mut::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let counter = world.get_resource_mut::<SimpleCounter>();
|
||||
assert_eq!(counter.0, 4582);
|
||||
assert!(world.get_resource_mut::<SimpleCounter>().is_none());
|
||||
assert!(world.try_get_resource_mut::<SimpleCounter>().is_none());
|
||||
assert_eq!(counter.0, 4582);
|
||||
}
|
||||
|
||||
|
@ -750,8 +763,7 @@ mod tests {
|
|||
|
||||
assert!(!world.has_resource_changed::<SimpleCounter>());
|
||||
|
||||
let mut counter = world.get_resource_mut::<SimpleCounter>()
|
||||
.expect("Counter resource is missing");
|
||||
let mut counter = world.get_resource_mut::<SimpleCounter>();
|
||||
counter.0 += 100;
|
||||
|
||||
assert!(world.has_resource_changed::<SimpleCounter>());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use instant::Instant;
|
||||
use lyra_ecs::{query::ResMut, Component};
|
||||
use lyra_ecs::{Component, World};
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{plugin::Plugin, game::GameStages};
|
||||
|
@ -30,8 +30,9 @@ impl std::ops::DerefMut for DeltaTime {
|
|||
/// A system that updates the [`DeltaTime``] resource.
|
||||
///
|
||||
/// The resource is updated in the [`GameStages::First`] stage.
|
||||
pub fn delta_time_system(mut delta: ResMut<DeltaTime>) -> anyhow::Result<()> {
|
||||
pub fn delta_time_system(world: &mut World) -> anyhow::Result<()> {
|
||||
let now = Instant::now();
|
||||
let mut delta = world.get_resource_mut::<DeltaTime>();
|
||||
delta.0 = delta.1.unwrap_or(now).elapsed().as_secs_f32();
|
||||
delta.1 = Some(now);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
|
||||
|
@ -6,83 +6,14 @@ use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
|
|||
pub trait Event: Clone + Send + Sync + 'static {}
|
||||
impl<T: Clone + Send + Sync + 'static> Event for T {}
|
||||
|
||||
/// A Vec with other Vecs in it to track relative age of items.
|
||||
///
|
||||
/// The vec has 3 levels, a `newest`, `medium` and `old`. Items are pushed to the `newest`
|
||||
/// internal vec. When [`WaterfallVec::waterfall`] is called the items in `newest` are
|
||||
/// put into `medium`, and items in `medium` goes to `old`.
|
||||
///
|
||||
/// By checking the items in each internal vec, you can see a relative age between the items.
|
||||
/// The event system uses this to clear the `old` vec to ensure keep events for only two
|
||||
/// frames at a time.
|
||||
struct WaterfallVec<T> {
|
||||
newest: Vec<T>,
|
||||
medium: Vec<T>,
|
||||
old: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for WaterfallVec<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
newest: Default::default(),
|
||||
medium: Default::default(),
|
||||
old: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WaterfallVec<T> {
|
||||
fn total_len(&self) -> usize {
|
||||
self.newest.len() + self.medium.len() + self.old.len()
|
||||
}
|
||||
|
||||
fn get(&self, mut i: usize) -> Option<&T> {
|
||||
if i >= self.old.len() {
|
||||
i -= self.old.len();
|
||||
|
||||
if i >= self.medium.len() {
|
||||
i -= self.medium.len();
|
||||
self.newest.get(i)
|
||||
} else {
|
||||
self.medium.get(i)
|
||||
}
|
||||
} else {
|
||||
self.old.get(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// Age elements.
|
||||
///
|
||||
/// This moves elements in `newest` to `medium` and elements in `medium` to `old`.
|
||||
/// This is what drives the relative age of the [`WaterfallVec`].
|
||||
fn waterfall(&mut self) {
|
||||
self.old.append(&mut self.medium);
|
||||
self.medium.append(&mut self.newest);
|
||||
}
|
||||
|
||||
/// Push a new element to the newest queue.
|
||||
fn push(&mut self, event: T) {
|
||||
self.newest.push(event);
|
||||
}
|
||||
|
||||
/// Clear oldest items.
|
||||
fn clear_oldest(&mut self) {
|
||||
self.old.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Events<T: Event> {
|
||||
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
|
||||
/// Used to track when the old events were last cleared.
|
||||
last_cleared_at: Tick,
|
||||
/// Used to indicate when the cursor in readers should be reset to zero.
|
||||
/// This becomes true after the old events are cleared.
|
||||
reset_cursor: bool,
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
last_updated_tick: Option<Tick>,
|
||||
}
|
||||
|
||||
impl<T: Event> Default for Events<T> {
|
||||
fn default() -> Self {
|
||||
Self { events: Default::default(), last_cleared_at: Default::default(), reset_cursor: false }
|
||||
Self { events: Default::default(), last_updated_tick: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +30,7 @@ impl<T: Event> Events<T> {
|
|||
pub fn reader(&self) -> EventReader<T> {
|
||||
EventReader {
|
||||
events: self.events.clone(),
|
||||
cursor: Rc::new(RefCell::new(0)),
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,28 +42,27 @@ impl<T: Event> Events<T> {
|
|||
}
|
||||
|
||||
pub struct EventReader<T: Event> {
|
||||
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
|
||||
cursor: Rc<RefCell<usize>>,
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<T: Event> EventReader<T> {
|
||||
pub fn read(&mut self) -> Option<atomic_refcell::AtomicRef<T>> {
|
||||
let events = self.events.borrow();
|
||||
|
||||
let mut cursor = self.cursor.borrow_mut();
|
||||
if *cursor >= events.total_len() {
|
||||
if self.cursor >= events.len() {
|
||||
None
|
||||
} else {
|
||||
let e = atomic_refcell::AtomicRef::map(events,
|
||||
|e| e.get(*cursor).unwrap());
|
||||
*cursor += 1;
|
||||
|e| e.get(self.cursor).unwrap());
|
||||
self.cursor += 1;
|
||||
Some(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventWriter<T: Event> {
|
||||
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Event> EventWriter<T> {
|
||||
|
@ -147,48 +77,40 @@ pub fn event_cleaner_system<T>(tick: WorldTick, mut events: ResMut<Events<T>>) -
|
|||
where
|
||||
T: Event
|
||||
{
|
||||
let last_tick = *events.last_cleared_at;
|
||||
let last_tick = *events.last_updated_tick.unwrap_or(*tick);
|
||||
let world_tick = **tick;
|
||||
|
||||
if last_tick + 2 < world_tick {
|
||||
events.last_cleared_at = *tick;
|
||||
events.reset_cursor = true;
|
||||
events.last_updated_tick = Some(*tick);
|
||||
|
||||
let mut events = events.events.borrow_mut();
|
||||
events.clear_oldest();
|
||||
} else {
|
||||
events.reset_cursor = false;
|
||||
events.clear();
|
||||
}
|
||||
|
||||
let mut events = events.events.borrow_mut();
|
||||
events.waterfall();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<T: Event> FnArgFetcher for EventReader<T> {
|
||||
type State = Rc<RefCell<usize>>;
|
||||
type State = usize;
|
||||
|
||||
type Arg<'a, 'state> = EventReader<T>;
|
||||
|
||||
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
|
||||
Rc::new(RefCell::new(0))
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
let events = world.get_resource::<Events<T>>()
|
||||
.unwrap_or_else(|| panic!("world missing Events<{}> resource", std::any::type_name::<T>()));
|
||||
let events = world.get_resource::<Events<T>>();
|
||||
|
||||
if events.reset_cursor {
|
||||
let mut state_num = state.borrow_mut();
|
||||
*state_num = 0;
|
||||
// reset the reader cursor when the events are cleared
|
||||
let world_tick = world.current_tick();
|
||||
if world_tick == events.last_updated_tick.unwrap_or(world_tick) {
|
||||
*state = 0;
|
||||
}
|
||||
|
||||
let reader = EventReader {
|
||||
events: events.events.clone(),
|
||||
cursor: state.clone(),
|
||||
};
|
||||
let mut reader = events.reader();
|
||||
reader.cursor = *state;
|
||||
|
||||
reader
|
||||
}
|
||||
|
@ -207,8 +129,7 @@ impl<T: Event> FnArgFetcher for EventWriter<T> {
|
|||
|
||||
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
let events = world.get_resource::<Events<T>>()
|
||||
.unwrap_or_else(|| panic!("world missing Events<{}> resource", std::any::type_name::<T>()));
|
||||
let events = world.get_resource::<Events<T>>();
|
||||
events.writer()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{cell::OnceCell, collections::VecDeque, ptr::NonNull};
|
|||
|
||||
use lyra_ecs::{system::{IntoSystem, System}, ResourceObject, World};
|
||||
use lyra_math::IVec2;
|
||||
use tracing::{error, info, Level};
|
||||
use tracing::{info, error, Level};
|
||||
use tracing_appender::non_blocking;
|
||||
use tracing_subscriber::{
|
||||
layer::SubscriberExt,
|
||||
|
@ -102,7 +102,6 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.world.tick();
|
||||
let wptr = NonNull::from(&self.world);
|
||||
|
||||
if let Err(e) = self.staged_exec.execute(wptr, true) {
|
||||
|
@ -228,7 +227,7 @@ impl App {
|
|||
|
||||
pub fn push_event<T: Event>(&mut self, event: T) {
|
||||
let world = &mut self.world;
|
||||
let mut events = world.get_resource_mut::<Events<T>>()
|
||||
let mut events = world.try_get_resource_mut::<Events<T>>()
|
||||
.expect("missing events for event type! Must use `App::register_event` first");
|
||||
events.push_event(event);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
use winit::{dpi::PhysicalPosition, event::{AxisId, DeviceId, ElementState, KeyEvent, MouseButton, MouseScrollDelta, Touch, TouchPhase, WindowEvent}};
|
||||
|
||||
/// Wrapper around events from `winit::WindowEvent` that are specific to input related events.
|
||||
///
|
||||
/// The `winit::WindowEvent` enum has many values that are related to inputs.
|
||||
/// Ex:
|
||||
/// * winit::WindowEvent::KeyboardInput
|
||||
/// * winit::WindowEvent::CursorMoved
|
||||
/// * winit::WindowEvent::CursorEntered
|
||||
/// * winit::WindowEvent::MouseWheel
|
||||
/// * winit::WindowEvent::CursorLeft
|
||||
/// * winit::WindowEvent::MouseInput
|
||||
/// etc.
|
||||
///
|
||||
/// Its easier for these to all be in a single `InputEvent` type enum to check if any input was received.
|
||||
/// The comments for all the methods were taken from `winit::WindowEvent`
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum InputEvent {
|
||||
/// An event from the keyboard has been received.
|
||||
KeyboardInput {
|
||||
device_id: DeviceId,
|
||||
event: KeyEvent,
|
||||
|
||||
/// If true, the event was generated synthetically by winit in one of the following circumstances:
|
||||
/// Synthetic key press events are generated for all keys pressed when a window gains focus.
|
||||
/// Likewise, synthetic key release events are generated for all keys pressed when a window goes out of focus.
|
||||
/// Currently, this is only functional on X11 and Windows
|
||||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
position: PhysicalPosition<f64>,
|
||||
},
|
||||
|
||||
/// The cursor has entered the window.
|
||||
CursorEntered {
|
||||
device_id: DeviceId,
|
||||
},
|
||||
|
||||
/// The cursor has left the window.
|
||||
CursorLeft {
|
||||
device_id: DeviceId,
|
||||
},
|
||||
|
||||
/// A mouse wheel movement or touchpad scroll occurred.
|
||||
MouseWheel {
|
||||
device_id: DeviceId,
|
||||
delta: MouseScrollDelta,
|
||||
phase: TouchPhase,
|
||||
},
|
||||
|
||||
/// An mouse button press has been received.
|
||||
MouseInput {
|
||||
device_id: DeviceId,
|
||||
state: ElementState,
|
||||
button: MouseButton,
|
||||
},
|
||||
|
||||
/// Touchpad pressure event.
|
||||
///
|
||||
/// At the moment, only supported on Apple forcetouch-capable macbooks.
|
||||
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
|
||||
/// is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure {
|
||||
device_id: DeviceId,
|
||||
pressure: f32,
|
||||
stage: i64,
|
||||
},
|
||||
|
||||
/// Motion on some analog axis. May report data redundant to other, more specific events.
|
||||
AxisMotion {
|
||||
device_id: DeviceId,
|
||||
axis: AxisId,
|
||||
value: f64,
|
||||
},
|
||||
|
||||
/// Touch event has been received
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web**: Doesn’t take into account CSS border, padding, or transform.
|
||||
/// - **macOS:** Unsupported.
|
||||
Touch(Touch),
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
pub fn from_window_event(value: &WindowEvent) -> Option<Self> {
|
||||
match value {
|
||||
WindowEvent::KeyboardInput { device_id, event, is_synthetic } =>
|
||||
Some(InputEvent::KeyboardInput {
|
||||
device_id: *device_id,
|
||||
event: event.clone(),
|
||||
is_synthetic: *is_synthetic
|
||||
}),
|
||||
#[allow(deprecated, reason="Compatibility")]
|
||||
WindowEvent::CursorMoved { device_id, position, } =>
|
||||
Some(InputEvent::CursorMoved {
|
||||
device_id: *device_id,
|
||||
position: *position,
|
||||
}),
|
||||
WindowEvent::CursorEntered { device_id } =>
|
||||
Some(InputEvent::CursorEntered {
|
||||
device_id: *device_id
|
||||
}),
|
||||
WindowEvent::CursorLeft { device_id } =>
|
||||
Some(InputEvent::CursorLeft {
|
||||
device_id: *device_id
|
||||
}),
|
||||
#[allow(deprecated, reason="Compatibility")]
|
||||
WindowEvent::MouseWheel { device_id, delta, phase } =>
|
||||
Some(InputEvent::MouseWheel {
|
||||
device_id: *device_id,
|
||||
delta: *delta,
|
||||
phase: *phase,
|
||||
}),
|
||||
#[allow(deprecated, reason="Compatibility")]
|
||||
WindowEvent::MouseInput { device_id, state, button } =>
|
||||
Some(InputEvent::MouseInput {
|
||||
device_id: *device_id,
|
||||
state: *state,
|
||||
button: *button,
|
||||
}),
|
||||
WindowEvent::TouchpadPressure { device_id, pressure, stage } =>
|
||||
Some(InputEvent::TouchpadPressure {
|
||||
device_id: *device_id,
|
||||
pressure: *pressure,
|
||||
stage: *stage
|
||||
}),
|
||||
WindowEvent::AxisMotion { device_id, axis, value } =>
|
||||
Some(InputEvent::AxisMotion {
|
||||
device_id: *device_id,
|
||||
axis: *axis,
|
||||
value: *value
|
||||
}),
|
||||
WindowEvent::Touch(t) => Some(InputEvent::Touch(*t)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
mod system;
|
||||
pub mod system;
|
||||
pub use system::*;
|
||||
|
||||
mod events;
|
||||
pub mod input_event;
|
||||
pub use input_event::*;
|
||||
|
||||
pub mod events;
|
||||
pub use events::*;
|
||||
|
||||
mod buttons;
|
||||
pub mod buttons;
|
||||
pub use buttons::*;
|
||||
|
||||
mod action;
|
||||
pub mod action;
|
||||
pub use action::*;
|
||||
|
||||
pub type KeyCode = winit::keyboard::KeyCode;
|
||||
|
@ -119,7 +122,7 @@ pub fn keycode_from_str(s: &str) -> Option<KeyCode> {
|
|||
"numpad_star" => Some(KeyCode::NumpadStar),
|
||||
"quote" => Some(KeyCode::Quote),
|
||||
"launch_app1" => Some(KeyCode::LaunchApp1),
|
||||
"launch_app2" => Some(KeyCode::LaunchApp2),
|
||||
"launch_app1" => Some(KeyCode::LaunchApp2),
|
||||
"backslash" => Some(KeyCode::Backslash),
|
||||
"caps_lock" => Some(KeyCode::CapsLock),
|
||||
"comma" => Some(KeyCode::Comma),
|
||||
|
|
|
@ -6,7 +6,7 @@ use winit::{event::{MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey};
|
|||
|
||||
use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter};
|
||||
|
||||
use super::{events::*, InputButtons};
|
||||
use super::{events::*, InputButtons, InputEvent};
|
||||
|
||||
fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) {
|
||||
let event = match delta {
|
||||
|
@ -119,6 +119,7 @@ impl Plugin for InputPlugin {
|
|||
app.add_resource(InputButtons::<MouseButton>::default());
|
||||
app.add_resource(Touches::default());
|
||||
|
||||
app.register_event::<InputEvent>();
|
||||
app.register_event::<MouseScroll>();
|
||||
app.register_event::<MouseButton>();
|
||||
app.register_event::<MouseMotion>();
|
||||
|
|
|
@ -42,7 +42,7 @@ impl<'a> RenderGraphContext<'a> {
|
|||
pub fn begin_render_pass(
|
||||
&'a mut self,
|
||||
desc: wgpu::RenderPassDescriptor<'a>,
|
||||
) -> wgpu::RenderPass<'a> {
|
||||
) -> wgpu::RenderPass {
|
||||
self.encoder
|
||||
.as_mut()
|
||||
.expect(
|
||||
|
|
|
@ -203,8 +203,7 @@ impl Node for LightCullComputePass {
|
|||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, context: &mut RenderGraphContext) {
|
||||
context.queue_buffer_write_with(LightCullComputePassSlots::IndexCounterBuffer, 0, 0);
|
||||
|
||||
let screen_size = world.get_resource::<ScreenSize>()
|
||||
.expect("world missing ScreenSize resource");
|
||||
let screen_size = world.get_resource::<ScreenSize>();
|
||||
if screen_size.xy() != self.workgroup_size {
|
||||
self.workgroup_size = screen_size.xy();
|
||||
todo!("Resize buffers and other resources");
|
||||
|
|
|
@ -286,8 +286,7 @@ impl Node for MeshPrepNode {
|
|||
Self::try_init_resource::<RenderAssets<Arc<GpuMaterial>>>(world);
|
||||
Self::try_init_resource::<FxHashMap<Entity, uuid::Uuid>>(world);
|
||||
|
||||
let mut render_meshes = world.get_resource_mut::<RenderMeshes>()
|
||||
.expect("world missing RenderMeshes resource");
|
||||
let mut render_meshes = world.get_resource_mut::<RenderMeshes>();
|
||||
render_meshes.clear();
|
||||
}
|
||||
|
||||
|
@ -460,8 +459,7 @@ impl Node for MeshPrepNode {
|
|||
world.insert(en, interp);
|
||||
}
|
||||
|
||||
let mut transforms = world.get_resource_mut::<TransformBuffers>()
|
||||
.expect("world missing TransformBuffers resource");
|
||||
let mut transforms = world.get_resource_mut::<TransformBuffers>();
|
||||
transforms.send_to_gpu(queue);
|
||||
}
|
||||
|
||||
|
|
|
@ -305,17 +305,17 @@ impl Node for MeshPass {
|
|||
});
|
||||
|
||||
let transforms = world
|
||||
.get_resource_data::<TransformBuffers>()
|
||||
.try_get_resource_data::<TransformBuffers>()
|
||||
.expect("Missing transform buffers");
|
||||
self.transform_buffers = Some(transforms.clone());
|
||||
|
||||
let render_meshes = world
|
||||
.get_resource_data::<RenderMeshes>()
|
||||
.try_get_resource_data::<RenderMeshes>()
|
||||
.expect("Missing transform buffers");
|
||||
self.render_meshes = Some(render_meshes.clone());
|
||||
|
||||
let mesh_buffers = world
|
||||
.get_resource_data::<RenderAssets<MeshBufferStorage>>()
|
||||
.try_get_resource_data::<RenderAssets<MeshBufferStorage>>()
|
||||
.expect("Missing render meshes");
|
||||
self.mesh_buffers = Some(mesh_buffers.clone());
|
||||
|
||||
|
|
|
@ -639,8 +639,7 @@ impl Node for ShadowMapsPass {
|
|||
if world.has_resource_changed::<ShadowCasterSettings>() {
|
||||
debug!("Detected change in ShadowSettings, recreating poisson disks");
|
||||
|
||||
let settings = world.get_resource::<ShadowCasterSettings>()
|
||||
.expect("world missing ShadowCasterSettings resource");
|
||||
let settings = world.get_resource::<ShadowCasterSettings>();
|
||||
// convert to uniform now since the from impl limits to max values
|
||||
let uniform = ShadowSettingsUniform::from(*settings);
|
||||
|
||||
|
@ -672,17 +671,16 @@ impl Node for ShadowMapsPass {
|
|||
|
||||
context.queue_buffer_write_with(ShadowMapsPassSlots::ShadowSettingsUniform, 0, uniform);
|
||||
}
|
||||
let settings = *world.get_resource::<ShadowCasterSettings>()
|
||||
.expect("world missing ShadowCasterSettings resource");
|
||||
let settings = *world.get_resource::<ShadowCasterSettings>();
|
||||
|
||||
if settings.use_back_faces {
|
||||
// TODO: shadow maps rendering with back faces
|
||||
todo!("render with back faces");
|
||||
}
|
||||
|
||||
self.render_meshes = world.get_resource_data::<RenderMeshes>();
|
||||
self.transform_buffers = world.get_resource_data::<TransformBuffers>();
|
||||
self.mesh_buffers = world.get_resource_data::<RenderAssets<MeshBufferStorage>>();
|
||||
self.render_meshes = world.try_get_resource_data::<RenderMeshes>();
|
||||
self.transform_buffers = world.try_get_resource_data::<TransformBuffers>();
|
||||
self.mesh_buffers = world.try_get_resource_data::<RenderAssets<MeshBufferStorage>>();
|
||||
|
||||
world.add_resource(self.atlas.clone());
|
||||
|
||||
|
|
|
@ -269,8 +269,7 @@ impl Renderer for BasicRenderer {
|
|||
rt.surface.configure(&self.device, &rt.surface_config); */
|
||||
|
||||
// update screen size resource in ecs
|
||||
let mut world_ss = world.get_resource_mut::<ScreenSize>()
|
||||
.expect("world missing ScreenSize resource");
|
||||
let mut world_ss = world.get_resource_mut::<ScreenSize>();
|
||||
world_ss.0 = glam::UVec2::new(new_size.width, new_size.height);
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,101 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use glam::{DVec2, IVec2, UVec2, Vec2};
|
||||
use lyra_ecs::{query::{filter::Changed, Entities, Res, View}, Component};
|
||||
use lyra_math::Area;
|
||||
use lyra_resource::Image;
|
||||
use tracing::error;
|
||||
use winit::{dpi::{PhysicalPosition, PhysicalSize, Position, Size}, window::{CustomCursor, Fullscreen, Window}};
|
||||
use winit::window::{CustomCursor, Fullscreen, Window};
|
||||
|
||||
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
|
||||
|
||||
use crate::{plugin::Plugin, winit::WinitWindows};
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq)]
|
||||
pub struct Area {
|
||||
position: Position,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Size {
|
||||
Physical { x: u32, y: u32 },
|
||||
Logical { x: f64, y: f64 },
|
||||
}
|
||||
|
||||
impl Default for Size {
|
||||
fn default() -> Self {
|
||||
Self::Physical { x: 0, y: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<winit::dpi::Size> for Size {
|
||||
fn into(self) -> winit::dpi::Size {
|
||||
match self {
|
||||
Size::Physical { x, y } => winit::dpi::PhysicalSize::new(x, y).into(),
|
||||
Size::Logical { x, y } => winit::dpi::LogicalSize::new(x, y).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<winit::dpi::Size> for Size {
|
||||
fn from(value: winit::dpi::Size) -> Self {
|
||||
match value {
|
||||
winit::dpi::Size::Physical(physical_position) => Self::new_physical(physical_position.width, physical_position.height),
|
||||
winit::dpi::Size::Logical(logical_position) => Self::new_logical(logical_position.width, logical_position.height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Size {
|
||||
pub fn new_physical(x: u32, y: u32) -> Self {
|
||||
Self::Physical { x, y }
|
||||
}
|
||||
|
||||
pub fn new_logical(x: f64, y: f64) -> Self {
|
||||
Self::Logical { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Position {
|
||||
Physical { x: i32, y: i32 },
|
||||
Logical { x: f64, y: f64 },
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Self::Physical { x: 0, y: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<winit::dpi::Position> for Position {
|
||||
fn into(self) -> winit::dpi::Position {
|
||||
match self {
|
||||
Position::Physical { x, y } => winit::dpi::PhysicalPosition::new(x, y).into(),
|
||||
Position::Logical { x, y } => winit::dpi::LogicalPosition::new(x, y).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<winit::dpi::Position> for Position {
|
||||
fn from(value: winit::dpi::Position) -> Self {
|
||||
match value {
|
||||
winit::dpi::Position::Physical(physical_position) => Self::new_physical(physical_position.x, physical_position.y),
|
||||
winit::dpi::Position::Logical(logical_position) => Self::new_logical(logical_position.x, logical_position.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new_physical(x: i32, y: i32) -> Self {
|
||||
Self::Physical { x, y }
|
||||
}
|
||||
|
||||
pub fn new_logical(x: f64, y: f64) -> Self {
|
||||
Self::Logical { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
/// Flag component that
|
||||
#[derive(Clone, Component)]
|
||||
pub struct PrimaryWindow;
|
||||
|
@ -83,7 +168,7 @@ pub struct WindowOptions {
|
|||
/// * **Web:** Can only return None or Borderless(None).
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
|
||||
/// Gets/sets the position of the top-left hand corner of the window relative to
|
||||
/// Gets the position of the top-left hand corner of the window’s client area relative to
|
||||
/// the top-left hand corner of the desktop.
|
||||
///
|
||||
/// Note that the top-left hand corner of the desktop is not necessarily the same
|
||||
|
@ -100,7 +185,7 @@ pub struct WindowOptions {
|
|||
/// * **Web:** Value is the top-left coordinates relative to the viewport. Note: this will be
|
||||
/// the same value as [`WindowOptions::outer_position`].
|
||||
/// * **Android / Wayland:** Unsupported.
|
||||
pub position: Option<IVec2>,
|
||||
pub inner_position: Option<Position>,
|
||||
|
||||
/// Gets/sets the size of the view in the window.
|
||||
///
|
||||
|
@ -108,7 +193,7 @@ pub struct WindowOptions {
|
|||
///
|
||||
/// Platform-specific
|
||||
/// * **Web:** The size of the canvas element. Doesn’t account for CSS `transform`.
|
||||
physical_size: UVec2,
|
||||
pub size: Size,
|
||||
|
||||
/// Gets/sets if the window has decorations.
|
||||
///
|
||||
|
@ -152,6 +237,24 @@ pub struct WindowOptions {
|
|||
/// * **iOS:** Setting is not implemented, getting is unsupported.
|
||||
pub visible: Option<bool>,
|
||||
|
||||
/// Gets/sets the position of the top-left hand corner of the window relative to
|
||||
/// the top-left hand corner of the desktop.
|
||||
///
|
||||
/// Note that the top-left hand corner of the desktop is not necessarily the same
|
||||
/// as the screen. If the user uses a desktop with multiple monitors, the top-left
|
||||
/// hand corner of the desktop is the top-left hand corner of the monitor at the
|
||||
/// top-left of the desktop.
|
||||
///
|
||||
/// If this is none, the position will be chosen by the windowing manager at creation, then set
|
||||
/// when the window is created.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **iOS:** Value is the top left coordinates of the window’s safe area in the screen
|
||||
/// space coordinate system.
|
||||
/// * **Web:** Value is the top-left coordinates relative to the viewport.
|
||||
/// * **Android / Wayland:** Unsupported.
|
||||
pub outer_position: Option<Position>,
|
||||
|
||||
/// Gets/sets the window resize increments.
|
||||
///
|
||||
/// This is a niche constraint hint usually employed by terminal emulators and other apps
|
||||
|
@ -205,12 +308,12 @@ pub struct WindowOptions {
|
|||
/// * **X11:** Enabling IME will disable dead keys reporting during compose.
|
||||
pub ime_allowed: bool,
|
||||
|
||||
/// Sets area of IME box in physical coordinates relative to the top left.
|
||||
/// Sets area of IME candidate box in window client area coordinates relative to the top left.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **X11:** - area is not supported, only position.
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
pub physical_ime_cursor_area: Option<Area<Vec2, Vec2>>,
|
||||
pub ime_cursor_area: Option<Area>,
|
||||
|
||||
/// Gets/sets the minimum size of the window.
|
||||
///
|
||||
|
@ -277,12 +380,12 @@ pub struct WindowOptions {
|
|||
pub window_level: WindowLevel,
|
||||
|
||||
/// Show [window menu](https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu)
|
||||
/// at a specified position in physical coordinates.
|
||||
/// at a specified position.
|
||||
///
|
||||
/// This is the context menu that is normally shown when interacting with the title bar. This is useful when implementing custom decorations.
|
||||
/// Platform-specific
|
||||
/// * **Android / iOS / macOS / Orbital / Wayland / Web / X11:** Unsupported.
|
||||
pub physical_window_menu_pos: Option<Vec2>,
|
||||
pub show_window_menu: Option<Position>,
|
||||
|
||||
/// Gets the window's occluded state (completely hidden from view).
|
||||
///
|
||||
|
@ -297,13 +400,6 @@ pub struct WindowOptions {
|
|||
/// * **Web:** Doesn't take into account CSS border, padding, or transform.
|
||||
/// * **Android / Wayland / Windows / Orbital:** Unsupported.
|
||||
pub occluded: bool,
|
||||
|
||||
/// Gets/sets the position of the cursor in physical coordinates.
|
||||
///
|
||||
/// Platform-specific
|
||||
/// * **Wayland:** Cursor must be in [`CursorGrabMode::Locked`].
|
||||
/// * **iOS / Android / Web / Orbital:** Unsupported.
|
||||
physical_cursor_position: Option<DVec2>,
|
||||
}
|
||||
|
||||
impl From<winit::window::WindowAttributes> for WindowOptions {
|
||||
|
@ -312,19 +408,15 @@ impl From<winit::window::WindowAttributes> for WindowOptions {
|
|||
enabled_buttons: value.enabled_buttons,
|
||||
focused: false,
|
||||
fullscreen: value.fullscreen,
|
||||
position: value.position.map(|p| {
|
||||
let s = p.to_physical::<i32>(1.0);
|
||||
IVec2::new(s.x, s.y)
|
||||
}),
|
||||
physical_size: value.inner_size.map(|s| {
|
||||
let s = s.to_physical::<u32>(1.0);
|
||||
UVec2::new(s.width, s.height)
|
||||
}).unwrap_or(UVec2::new(1280, 720)),
|
||||
inner_position: None,
|
||||
size: value.inner_size.map(|s| s.into())
|
||||
.unwrap_or(Size::new_physical(1280, 720)),
|
||||
decorated: value.decorations,
|
||||
maximized: value.maximized,
|
||||
minimized: None,
|
||||
resizable: value.resizable,
|
||||
visible: Some(value.visible),
|
||||
outer_position: value.position.map(|p| p.into()),
|
||||
resize_increments: value.resize_increments.map(|r| r.into()),
|
||||
scale_factor: 1.0,
|
||||
blur: value.blur,
|
||||
|
@ -339,7 +431,7 @@ impl From<winit::window::WindowAttributes> for WindowOptions {
|
|||
visible: true,
|
||||
},
|
||||
ime_allowed: false,
|
||||
physical_ime_cursor_area: None,
|
||||
ime_cursor_area: None,
|
||||
min_size: value.min_inner_size.map(|m| m.into()),
|
||||
max_size: value.max_inner_size.map(|m| m.into()),
|
||||
theme: value.preferred_theme,
|
||||
|
@ -347,9 +439,8 @@ impl From<winit::window::WindowAttributes> for WindowOptions {
|
|||
transparent: value.transparent,
|
||||
window_icon: None,
|
||||
window_level: value.window_level,
|
||||
physical_window_menu_pos: None,
|
||||
show_window_menu: None,
|
||||
occluded: false,
|
||||
physical_cursor_position: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,12 +458,12 @@ impl WindowOptions {
|
|||
|
||||
att.enabled_buttons = self.enabled_buttons.clone();
|
||||
att.fullscreen = self.fullscreen.clone();
|
||||
att.inner_size = Some(Size::Physical(PhysicalSize::new(self.physical_size.x, self.physical_size.y)));
|
||||
att.inner_size = Some(self.size.into());
|
||||
att.decorations = self.decorated;
|
||||
att.maximized = self.maximized;
|
||||
att.resizable = self.resizable;
|
||||
att.visible = self.visible.unwrap_or(true);
|
||||
att.position = self.position.map(|p| Position::Physical(PhysicalPosition::new(p.x, p.y)));
|
||||
att.position = self.outer_position.map(|p| p.into());
|
||||
att.resize_increments = self.resize_increments.map(|i| i.into());
|
||||
att.blur = self.blur;
|
||||
att.content_protected = self.content_protected;
|
||||
|
@ -392,73 +483,6 @@ impl WindowOptions {
|
|||
|
||||
att
|
||||
}
|
||||
|
||||
/// The size of the window in physical coordinates.
|
||||
pub fn physical_size(&self) -> UVec2 {
|
||||
self.physical_size
|
||||
}
|
||||
|
||||
/// Set the size of the window in physical coordinates.
|
||||
pub fn set_physical_size(&mut self, size: UVec2) {
|
||||
self.physical_size = size;
|
||||
}
|
||||
|
||||
/// The size of the window in logical coordinates.
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.physical_size.as_vec2() / self.scale_factor as f32
|
||||
}
|
||||
|
||||
/// Set the size of the window in logical coordinates.
|
||||
pub fn set_size(&mut self, size: Vec2) {
|
||||
self.physical_size = (size * self.scale_factor as f32).as_uvec2();
|
||||
}
|
||||
|
||||
/// Returns a boolean indicating if the mouse is inside the window.
|
||||
pub fn is_mouse_inside(&self) -> bool {
|
||||
if let Some(pos) = self.physical_cursor_position {
|
||||
let s = self.physical_size;
|
||||
return pos.x >= 0.0 && pos.x <= s.x as f64
|
||||
&& pos.y >= 0.0 && pos.y <= s.y as f64;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// The cursor position in the window in logical coordinates.
|
||||
///
|
||||
/// Returns `None` if the cursor is not in the window.
|
||||
pub fn cursor_position(&self) -> Option<Vec2> {
|
||||
if !self.is_mouse_inside() {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.physical_cursor_position.map(|p| (p / self.scale_factor).as_vec2())
|
||||
}
|
||||
|
||||
/// The cursor position in the window in physical coordinates.
|
||||
///
|
||||
/// Returns `None` if the cursor is not in the window.
|
||||
pub fn physical_cursor_position(&self) -> Option<Vec2> {
|
||||
if !self.is_mouse_inside() {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.physical_cursor_position.map(|p| p.as_vec2())
|
||||
}
|
||||
|
||||
/// Set the cursor position in logical coordinates.
|
||||
///
|
||||
/// Can be used to mark the cursor outside of the window as well.
|
||||
pub fn set_cursor_position(&mut self, pos: Option<Vec2>) {
|
||||
self.physical_cursor_position = pos.map(|p| p.as_dvec2() * self.scale_factor);
|
||||
}
|
||||
|
||||
/// Set the cursor position in physical coordinates.
|
||||
///
|
||||
/// Can be used to mark the cursor outside of the window as well.
|
||||
pub fn set_physical_cursor_position(&mut self, pos: Option<DVec2>) {
|
||||
self.physical_cursor_position = pos;
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the window last time it was changed.
|
||||
|
@ -467,7 +491,7 @@ impl WindowOptions {
|
|||
/// when syncing the winit window with the component.
|
||||
#[derive(Clone, Component)]
|
||||
pub struct LastWindow {
|
||||
pub last: WindowOptions,
|
||||
last: WindowOptions,
|
||||
}
|
||||
|
||||
impl Deref for LastWindow {
|
||||
|
@ -508,9 +532,8 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_fullscreen(opts.fullscreen.clone());
|
||||
}
|
||||
|
||||
if opts.physical_size != last.physical_size {
|
||||
let size = PhysicalSize::new(opts.physical_size.x, opts.physical_size.y);
|
||||
if window.request_inner_size(size).is_some() {
|
||||
if opts.size != last.size {
|
||||
if window.request_inner_size(opts.size).is_some() {
|
||||
error!("request to increase window size failed");
|
||||
}
|
||||
}
|
||||
|
@ -531,10 +554,8 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_visible(opts.visible.unwrap());
|
||||
}
|
||||
|
||||
if opts.position != last.position && opts.position.is_some() {
|
||||
let pos = opts.position.unwrap();
|
||||
let pos = PhysicalPosition::new(pos.x, pos.y);
|
||||
window.set_outer_position(pos);
|
||||
if opts.outer_position != last.outer_position && opts.outer_position.is_some() {
|
||||
window.set_outer_position(opts.outer_position.unwrap());
|
||||
}
|
||||
|
||||
if opts.resize_increments != last.resize_increments {
|
||||
|
@ -542,7 +563,7 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
}
|
||||
|
||||
if opts.blur != last.blur {
|
||||
window.set_blur(opts.blur);
|
||||
window.set_blur(opts.blur)
|
||||
}
|
||||
|
||||
if opts.content_protected != last.content_protected {
|
||||
|
@ -576,11 +597,9 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_ime_allowed(opts.ime_allowed);
|
||||
}
|
||||
|
||||
if opts.physical_ime_cursor_area != last.physical_ime_cursor_area && opts.physical_ime_cursor_area.is_some() {
|
||||
let area = opts.physical_ime_cursor_area.unwrap();
|
||||
let pos = PhysicalPosition::new(area.position.x, area.position.y);
|
||||
let size = PhysicalSize::new(area.size.x, area.size.y);
|
||||
window.set_ime_cursor_area(pos, size);
|
||||
if opts.ime_cursor_area != last.ime_cursor_area && opts.ime_cursor_area.is_some() {
|
||||
let area = opts.ime_cursor_area.as_ref().unwrap();
|
||||
window.set_ime_cursor_area(area.position, area.size);
|
||||
}
|
||||
|
||||
if opts.min_size != last.min_size {
|
||||
|
@ -617,18 +636,8 @@ pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &Win
|
|||
window.set_window_level(opts.window_level);
|
||||
}
|
||||
|
||||
if opts.physical_window_menu_pos != last.physical_window_menu_pos && opts.physical_window_menu_pos.is_some() {
|
||||
let pos = opts.physical_window_menu_pos.unwrap();
|
||||
let pos = PhysicalPosition::new(pos.x, pos.y);
|
||||
window.show_window_menu(pos);
|
||||
}
|
||||
|
||||
if opts.physical_cursor_position != last.physical_cursor_position && opts.physical_cursor_position.is_some() {
|
||||
let pos = opts.physical_cursor_position.unwrap();
|
||||
let pos = PhysicalPosition::new(pos.x, pos.y);
|
||||
if let Err(e) = window.set_cursor_position(pos) {
|
||||
error!("failed to set cursor position: {}", e);
|
||||
}
|
||||
if opts.show_window_menu != last.show_window_menu && opts.show_window_menu.is_some() {
|
||||
window.show_window_menu(opts.show_window_menu.unwrap());
|
||||
}
|
||||
|
||||
last.last = opts.clone();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use async_std::task::block_on;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
use glam::IVec2;
|
||||
use lyra_ecs::Entity;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, error, warn};
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
|||
plugin::Plugin,
|
||||
render::{
|
||||
renderer::BasicRenderer,
|
||||
window::{LastWindow, PrimaryWindow, WindowOptions},
|
||||
window::{PrimaryWindow, Size, WindowOptions},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -143,8 +143,7 @@ impl ApplicationHandler for WinitRunner {
|
|||
Err(e) => eprintln!("{:?}", e),
|
||||
}
|
||||
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let windows = self.app.world.get_resource::<WinitWindows>();
|
||||
for window in windows.windows.values() {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
@ -153,15 +152,13 @@ impl ApplicationHandler for WinitRunner {
|
|||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
let world = &mut self.app.world;
|
||||
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>();
|
||||
let to_create_window = windows.window_queue.pop_front().unwrap_or_default();
|
||||
let window_attr = to_create_window.as_attributes();
|
||||
drop(windows);
|
||||
let en = world.spawn((to_create_window.clone(), LastWindow { last: to_create_window }, PrimaryWindow));
|
||||
let en = world.spawn((to_create_window, PrimaryWindow));
|
||||
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let mut windows = world.get_resource_mut::<WinitWindows>();
|
||||
let wid = windows.create_window(event_loop, en, window_attr).unwrap();
|
||||
let window = windows.windows.get(&wid).unwrap().clone();
|
||||
drop(windows);
|
||||
|
@ -198,59 +195,28 @@ impl ApplicationHandler for WinitRunner {
|
|||
|
||||
self.app.push_event(event.clone());
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let windows = self.app.world.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource");
|
||||
let en = windows.window_to_entity.get(&window_id)
|
||||
.expect("missing window entity");
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let (mut en_window, mut en_last_win) = self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*en).get().unwrap();
|
||||
let pos = Some(DVec2::new(position.x, position.y));
|
||||
en_window.set_physical_cursor_position(pos);
|
||||
en_last_win.set_physical_cursor_position(pos);
|
||||
},
|
||||
WindowEvent::ActivationTokenDone { .. } => todo!(),
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.app.on_resize(physical_size);
|
||||
|
||||
let (mut window, mut last_window) = self
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
|
||||
// update the window and its cache so the sync system doesn't try to update the window
|
||||
let size = UVec2::new(physical_size.width, physical_size.height);
|
||||
window.set_physical_size(size);
|
||||
last_window.set_physical_size(size);
|
||||
},
|
||||
// Mark the cursor as outside the window when it leaves
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
let (mut window, mut last_window) = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<(&mut WindowOptions, &mut LastWindow)>(*e).get())
|
||||
.unwrap();
|
||||
window.set_physical_cursor_position(None);
|
||||
last_window.set_physical_cursor_position(None);
|
||||
},
|
||||
window_opts.size = Size::new_physical(physical_size.width, physical_size.height);
|
||||
}
|
||||
WindowEvent::Moved(physical_position) => {
|
||||
let mut state = self.app.world.get_resource_or_else(WindowState::new);
|
||||
state.position = IVec2::new(physical_position.x, physical_position.y);
|
||||
},
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app.on_exit();
|
||||
event_loop.exit();
|
||||
},
|
||||
}
|
||||
WindowEvent::Destroyed => todo!(),
|
||||
WindowEvent::DroppedFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFile(_path_buf) => todo!(),
|
||||
|
@ -260,55 +226,51 @@ impl ApplicationHandler for WinitRunner {
|
|||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.focused = focused;
|
||||
},
|
||||
}
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
debug!("modifiers changed: {:?}", modifiers)
|
||||
},
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.scale_factor = scale_factor;
|
||||
},
|
||||
}
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.theme = Some(theme);
|
||||
},
|
||||
}
|
||||
WindowEvent::Occluded(occ) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.expect("world missing WinitWindows resource")
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.occluded = occ;
|
||||
},
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
//debug!("should redraw");
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Area<P, S>
|
||||
where
|
||||
P: Clone + Copy + PartialEq,
|
||||
S: Clone + Copy + PartialEq,
|
||||
{
|
||||
pub position: P,
|
||||
pub size: S
|
||||
}
|
||||
|
||||
impl<P, S> Area<P, S>
|
||||
where
|
||||
P: Clone + Copy + PartialEq,
|
||||
S: Clone + Copy + PartialEq,
|
||||
{
|
||||
pub fn new(pos: P, size: S) -> Self {
|
||||
Self {
|
||||
position: pos,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,6 @@ pub use glam::*;
|
|||
pub mod angle;
|
||||
pub use angle::*;
|
||||
|
||||
mod area;
|
||||
pub use area::*;
|
||||
|
||||
pub mod transform;
|
||||
pub use transform::*;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::{any::TypeId, any::Any};
|
||||
|
||||
use lyra_ecs::{query::{Res, ResMut}, AtomicRef, AtomicRefMut, World};
|
||||
use lyra_ecs::{AtomicRef, AtomicRefMut, World};
|
||||
|
||||
extern crate self as lyra_reflect;
|
||||
|
||||
|
@ -250,9 +250,9 @@ pub trait FromType<T> {
|
|||
|
||||
pub trait ReflectWorldExt {
|
||||
/// Retrieves the type registry from the world.
|
||||
fn get_type_registry(&self) -> Option<Res<TypeRegistry>>;
|
||||
fn get_type_registry(&self) -> AtomicRef<TypeRegistry>;
|
||||
/// Retrieves the type registry mutably from the world.
|
||||
fn get_type_registry_mut(&self) -> Option<ResMut<TypeRegistry>>;
|
||||
fn get_type_registry_mut(&self) -> AtomicRefMut<TypeRegistry>;
|
||||
/// Get a registered type from the type registry. Returns `None` if the type is not registered
|
||||
fn get_type<T>(&self, type_id: TypeId) -> Option<AtomicRef<RegisteredType>>;
|
||||
/// Get a mutable registered type from the type registry in the world. returns `None` if the type is not registered.
|
||||
|
@ -262,19 +262,17 @@ pub trait ReflectWorldExt {
|
|||
}
|
||||
|
||||
impl ReflectWorldExt for World {
|
||||
fn get_type_registry(&self) -> Option<Res<TypeRegistry>> {
|
||||
fn get_type_registry(&self) -> AtomicRef<TypeRegistry> {
|
||||
self.get_resource::<TypeRegistry>()
|
||||
}
|
||||
|
||||
fn get_type_registry_mut(&self) -> Option<ResMut<TypeRegistry>> {
|
||||
fn get_type_registry_mut(&self) -> AtomicRefMut<TypeRegistry> {
|
||||
self.get_resource_mut::<TypeRegistry>()
|
||||
}
|
||||
|
||||
fn get_type<T>(&self, type_id: TypeId) -> Option<AtomicRef<RegisteredType>> {
|
||||
let r = self.get_resource::<TypeRegistry>()
|
||||
.expect("world is missing TypeRegistry resource");
|
||||
let r = self.get_resource::<TypeRegistry>();
|
||||
if r.has_type(type_id) {
|
||||
let r = r.get_inner();
|
||||
Some(AtomicRef::map(r, |tr| tr.get_type(type_id).unwrap()))
|
||||
} else {
|
||||
None
|
||||
|
@ -282,10 +280,8 @@ impl ReflectWorldExt for World {
|
|||
}
|
||||
|
||||
fn get_type_mut<T>(&self, type_id: TypeId) -> Option<AtomicRefMut<RegisteredType>> {
|
||||
let r = self.get_resource_mut::<TypeRegistry>()
|
||||
.expect("world is missing TypeRegistry resource");
|
||||
let r = self.get_resource_mut::<TypeRegistry>();
|
||||
if r.has_type(type_id) {
|
||||
let r = r.get_inner();
|
||||
Some(AtomicRefMut::map(r, |tr| tr.get_type_mut(type_id).unwrap()))
|
||||
} else {
|
||||
None
|
||||
|
@ -293,9 +289,7 @@ impl ReflectWorldExt for World {
|
|||
}
|
||||
|
||||
fn get_type_or_default<T>(&self, type_id: TypeId) -> AtomicRefMut<RegisteredType> {
|
||||
let r = self.get_resource_mut::<TypeRegistry>()
|
||||
.expect("world is missing TypeRegistry resource");
|
||||
let r = r.get_inner();
|
||||
let r = self.get_resource_mut::<TypeRegistry>();
|
||||
AtomicRefMut::map(r, |tr| tr.get_type_or_default(type_id))
|
||||
}
|
||||
}
|
|
@ -40,23 +40,19 @@ impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource {
|
|||
Self {
|
||||
type_id: TypeId::of::<T>(),
|
||||
fn_reflect: |world: &World| {
|
||||
world.get_resource::<T>()
|
||||
world.try_get_resource::<T>()
|
||||
.map(|r| {
|
||||
// TODO: figure out change tracking for reflected resource
|
||||
let r = r.get_inner();
|
||||
AtomicRef::map(r, |r| r as &dyn Reflect)
|
||||
})
|
||||
},
|
||||
fn_reflect_mut: |world: &mut World| {
|
||||
world.get_resource_mut::<T>()
|
||||
world.try_get_resource_mut::<T>()
|
||||
.map(|r| {
|
||||
// TODO: figure out change tracking for reflected resource
|
||||
let r = r.get_inner();
|
||||
AtomicRefMut::map(r, |r| r as &mut dyn Reflect)
|
||||
})
|
||||
},
|
||||
fn_reflect_ptr: |world: &mut World| unsafe {
|
||||
world.get_resource_ptr::<T>()
|
||||
world.try_get_resource_ptr::<T>()
|
||||
.map(|ptr| ptr.cast::<u8>())
|
||||
},
|
||||
fn_refl_insert: |world: &mut World, this: Box<dyn Reflect>| {
|
||||
|
|
|
@ -59,8 +59,7 @@ impl WorldAssetExt for World {
|
|||
}
|
||||
|
||||
fn res_watcher_recv(&self, path: &str) -> Option<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>> {
|
||||
let man = self.get_resource::<ResourceManager>()
|
||||
.expect("world is missing ResourceManager resource");
|
||||
let man = self.get_resource::<ResourceManager>();
|
||||
man.watcher_event_recv(path)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue