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