game: create viewport position to world position function, still wip
The viewport size is hard defined, and render target scale is not incorporated in the calculation
This commit is contained in:
parent
e223bf669b
commit
d44eb3b5cf
7 changed files with 272 additions and 94 deletions
|
@ -146,6 +146,14 @@ impl<T: Event> EventReader<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Event> Iterator for EventReader<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.read()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventWriter<T: Event> {
|
||||
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@ use crate::{
|
|||
render::{
|
||||
camera::CameraUniform,
|
||||
graph::{
|
||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
|
||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue,
|
||||
},
|
||||
render_buffer::BufferWrapper, texture::RenderTexture,
|
||||
}, scene::{Camera, CameraProjection},
|
||||
render_buffer::BufferWrapper,
|
||||
texture::RenderTexture,
|
||||
},
|
||||
scene::{Camera, CameraProjection},
|
||||
};
|
||||
|
||||
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
|
||||
|
@ -69,7 +71,8 @@ impl Node for BasePass {
|
|||
let camera_bg = Arc::new(camera_bg);
|
||||
|
||||
// create the depth texture using the utility struct, then take all the required fields
|
||||
let mut depth_texture = RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
|
||||
let mut depth_texture =
|
||||
RenderTexture::create_depth_texture(graph.device(), self.screen_size, "depth_texture");
|
||||
depth_texture.create_bind_group(&graph.device);
|
||||
|
||||
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
||||
|
@ -87,8 +90,16 @@ impl Node for BasePass {
|
|||
// (RenderGraphLabel, wgpu::BindGroup, Option<wgpu::BindGroupLabel>)
|
||||
//
|
||||
// This could make it slightly easier to create this
|
||||
(&BasePassSlots::DepthTexture, depth_texture_bg, Some(depth_texture_bgl)),
|
||||
(&BasePassSlots::ScreenSize, screen_size_bg, Some(screen_size_bgl)),
|
||||
(
|
||||
&BasePassSlots::DepthTexture,
|
||||
depth_texture_bg,
|
||||
Some(depth_texture_bgl),
|
||||
),
|
||||
(
|
||||
&BasePassSlots::ScreenSize,
|
||||
screen_size_bg,
|
||||
Some(screen_size_bgl),
|
||||
),
|
||||
(&BasePassSlots::Camera, camera_bg, Some(camera_bgl)),
|
||||
],
|
||||
);
|
||||
|
@ -112,13 +123,24 @@ impl Node for BasePass {
|
|||
desc
|
||||
}
|
||||
|
||||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
||||
fn prepare(
|
||||
&mut self,
|
||||
graph: &mut RenderGraph,
|
||||
world: &mut lyra_ecs::World,
|
||||
context: &mut RenderGraphContext,
|
||||
) {
|
||||
let mut found_camera = false;
|
||||
|
||||
for (camera, projection, transform) in world.view_iter::<(&Camera, &CameraProjection, &Transform)>() {
|
||||
for (mut camera, projection, transform) in
|
||||
world.view_iter::<(&mut Camera, &CameraProjection, &Transform)>()
|
||||
{
|
||||
if camera.is_active {
|
||||
let screen_size = graph.view_target().size();
|
||||
let uniform = CameraUniform::from_component(*transform, *projection, screen_size.as_vec2());
|
||||
let uniform =
|
||||
CameraUniform::from_component(*transform, *projection, screen_size.as_vec2());
|
||||
camera.values.projection_matrix = uniform.projection;
|
||||
camera.values.view_matrix = uniform.view;
|
||||
|
||||
context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform);
|
||||
|
||||
found_camera = true;
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
use glam::{Mat4, Vec2, Vec3};
|
||||
use core::f32;
|
||||
|
||||
use glam::{Mat4, UVec2, Vec2, Vec3};
|
||||
use lyra_ecs::{Bundle, Component};
|
||||
use lyra_math::{Angle, Rect};
|
||||
use lyra_math::{Angle, Dir3, Ray3, Rect};
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_scene::WorldTransform;
|
||||
|
||||
/// The axis to sort render jobs by.
|
||||
///
|
||||
///
|
||||
/// By default, [`Camera2dBundle`] sets the sorting axis to the positive Y axis.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Reflect, Component)]
|
||||
pub struct CameraSortingAxis(pub Vec3);
|
||||
|
||||
/// The offset to apply to the entity's position when sorting for rendering.
|
||||
///
|
||||
///
|
||||
/// This is only used if [`CameraSortingAxis`] is spawned on an entity.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Reflect, Component)]
|
||||
pub struct SortingOffset(pub Vec3);
|
||||
|
@ -21,15 +24,15 @@ pub enum ScaleMode {
|
|||
/// No scaling, keep everything the same size as the viewport.
|
||||
Viewport,
|
||||
/// The width of the projection in world units.
|
||||
///
|
||||
///
|
||||
/// Height will be set based off of the viewport's aspect ratio.
|
||||
Width(f32),
|
||||
/// The height of the projection in world units.
|
||||
///
|
||||
///
|
||||
/// Width will be set based off of the viewport's aspect ratio.
|
||||
Height(f32),
|
||||
/// The exact size of the projection in world units.
|
||||
///
|
||||
///
|
||||
/// Ignoring viewport size, likely causes stretching.
|
||||
Size(Vec2),
|
||||
/// Keep the projection size below a maximum value in world units, while keeping the aspect ratio.
|
||||
|
@ -73,26 +76,26 @@ impl OrthographicProjection {
|
|||
match self.scale_mode {
|
||||
ScaleMode::Viewport => {
|
||||
size = viewport_size;
|
||||
},
|
||||
}
|
||||
ScaleMode::Width(width) => {
|
||||
let aspect = viewport_size.x / viewport_size.y;
|
||||
let scaled_height = width / aspect;
|
||||
|
||||
size = Vec2::new(width, scaled_height);
|
||||
},
|
||||
}
|
||||
ScaleMode::Height(height) => {
|
||||
let aspect = viewport_size.x / viewport_size.y;
|
||||
let scaled_width = height * aspect;
|
||||
|
||||
size = Vec2::new(scaled_width, height);
|
||||
},
|
||||
}
|
||||
ScaleMode::Size(s) => {
|
||||
size = s;
|
||||
},
|
||||
}
|
||||
ScaleMode::MaxSize(s) => {
|
||||
let clamped = s.min(viewport_size);
|
||||
size = clamped;
|
||||
},
|
||||
}
|
||||
ScaleMode::MinSize(s) => {
|
||||
let clamped = s.max(viewport_size);
|
||||
size = clamped;
|
||||
|
@ -101,22 +104,19 @@ impl OrthographicProjection {
|
|||
|
||||
let origin = size * self.viewport_origin;
|
||||
|
||||
Rect::new(self.scale * -origin.x,
|
||||
Rect::new(
|
||||
self.scale * -origin.x,
|
||||
self.scale * -origin.y,
|
||||
self.scale * (size.x - origin.x),
|
||||
self.scale * (size.y - origin.y))
|
||||
self.scale * (size.y - origin.y),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_mat(&self, viewport_size: Vec2) -> Mat4 {
|
||||
let rect = self.get_rect(viewport_size);
|
||||
|
||||
glam::Mat4::orthographic_rh(
|
||||
rect.min.x,
|
||||
rect.max.x,
|
||||
rect.min.y,
|
||||
rect.max.y,
|
||||
-1000.0,
|
||||
1000.0,
|
||||
rect.min.x, rect.max.x, rect.min.y, rect.max.y, -1000.0, 1000.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -171,14 +171,79 @@ impl CameraProjection {
|
|||
#[derive(Clone, Default, Component, Reflect)]
|
||||
pub struct Camera2d;
|
||||
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct Viewport {
|
||||
/// The physical position of the viewport on the window.
|
||||
///
|
||||
/// Top left is (0, 0).
|
||||
physical_position: UVec2,
|
||||
/// The physical size of the viewport in the window.
|
||||
///
|
||||
/// The origin of the rectangle is the top left corner.
|
||||
physical_size: UVec2,
|
||||
}
|
||||
|
||||
impl Default for Viewport {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
physical_position: Default::default(),
|
||||
// TODO: dont hard code this
|
||||
physical_size: UVec2::new(1280, 720),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Reflect, Default)]
|
||||
pub struct CameraValues {
|
||||
pub(crate) projection_matrix: Mat4,
|
||||
pub(crate) view_matrix: Mat4,
|
||||
}
|
||||
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct Camera {
|
||||
pub viewport: Viewport,
|
||||
pub is_active: bool,
|
||||
pub values: CameraValues,
|
||||
}
|
||||
|
||||
impl Default for Camera {
|
||||
fn default() -> Self {
|
||||
Self { is_active: true }
|
||||
Self {
|
||||
viewport: Default::default(),
|
||||
is_active: true,
|
||||
values: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
/// Creates a ray from the camera that passes through everything beyond `viewport_pos`.
|
||||
///
|
||||
/// The ray starts on the near plane of the camera.
|
||||
///
|
||||
/// If the camera's projection is orthographic the direction of the ray is always equal to
|
||||
/// `camera_transform.forward()`.
|
||||
pub fn viewport_to_world(
|
||||
&self,
|
||||
camera_transform: &WorldTransform,
|
||||
mut viewport_pos: Vec2,
|
||||
) -> Option<Ray3> {
|
||||
let target_size = self.viewport.physical_size.as_vec2(); // TODO: divide by render target scale factor
|
||||
|
||||
// flip the Y coordinate origin
|
||||
viewport_pos.y = target_size.y - viewport_pos.y;
|
||||
let ndc = (viewport_pos / target_size) * 2.0 - Vec2::ONE;
|
||||
|
||||
let ndc_to_world =
|
||||
camera_transform.calculate_mat4() * self.values.projection_matrix.inverse();
|
||||
let world_near_plane = ndc_to_world.transform_point3(ndc.extend(1.0));
|
||||
// Using EPSILON because an ndc with Z=0 returns NaNs
|
||||
let world_far_plane = ndc_to_world.transform_point3(ndc.extend(f32::EPSILON));
|
||||
|
||||
Dir3::new(world_far_plane - world_near_plane).map(|dir| Ray3 {
|
||||
origin: world_near_plane,
|
||||
direction: dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,7 +252,7 @@ impl Default for Camera {
|
|||
pub struct CameraBundle {
|
||||
pub camera: Camera,
|
||||
pub projection: CameraProjection,
|
||||
pub sorting_axis: CameraSortingAxis
|
||||
pub sorting_axis: CameraSortingAxis,
|
||||
}
|
||||
|
||||
/// A component bundle for a 2d Camera entity.
|
||||
|
@ -196,7 +261,7 @@ pub struct Camera2dBundle {
|
|||
pub camera: Camera,
|
||||
pub projection: CameraProjection,
|
||||
pub camera_2d: Camera2d,
|
||||
pub sorting_axis: CameraSortingAxis
|
||||
pub sorting_axis: CameraSortingAxis,
|
||||
}
|
||||
|
||||
impl Default for Camera2dBundle {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use lyra_ecs::Component;
|
||||
use lyra_math::{Vec2, Vec3};
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_resource::ResHandle;
|
||||
use lyra_math::{Vec3, Vec2};
|
||||
|
||||
mod texture_atlas;
|
||||
pub use texture_atlas::*;
|
||||
|
@ -13,7 +13,7 @@ mod tilemap;
|
|||
pub use tilemap::*;
|
||||
|
||||
/// How the sprite is positioned and rotated relative to its [`Transform`].
|
||||
///
|
||||
///
|
||||
/// Default pivot is `Pivot::Center`, this makes it easier to rotate the sprites.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Default, Component, Reflect)]
|
||||
pub enum Pivot {
|
||||
|
@ -28,14 +28,14 @@ pub enum Pivot {
|
|||
BottomRight,
|
||||
BottomCenter,
|
||||
/// A custom anchor point.
|
||||
///
|
||||
///
|
||||
/// Top left is (-0.5, 0.5), center is (0.0, 0.0).
|
||||
Custom(Vec2)
|
||||
Custom(Vec2),
|
||||
}
|
||||
|
||||
impl Pivot {
|
||||
/// Get the pivot point as a Vec2.
|
||||
///
|
||||
///
|
||||
/// The point is offset from the top left `(0.0, 0.0)`.
|
||||
pub fn as_vec(&self) -> Vec2 {
|
||||
match self {
|
||||
|
@ -59,3 +59,13 @@ pub struct Sprite {
|
|||
pub color: Vec3,
|
||||
pub pivot: Pivot,
|
||||
}
|
||||
|
||||
impl Default for Sprite {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
texture: Default::default(),
|
||||
color: Vec3::ONE,
|
||||
pivot: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ pub use i32::*;
|
|||
pub mod transform;
|
||||
pub use transform::*;
|
||||
|
||||
mod ray3;
|
||||
pub use ray3::*;
|
||||
|
||||
/// A matrix used to convert an OpenGL matrix to a matrix compatible with Wgpu
|
||||
#[rustfmt::skip]
|
||||
pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols_array(&[
|
||||
|
@ -28,4 +31,4 @@ pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols_array(&[
|
|||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.0, 0.0, 0.5, 1.0,
|
||||
]);
|
||||
]);
|
||||
|
|
35
crates/lyra-math/src/ray3.rs
Normal file
35
crates/lyra-math/src/ray3.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use glam::Vec3;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Dir3(Vec3);
|
||||
|
||||
impl Dir3 {
|
||||
/// Create a direction from a finite, nonzero [`Vec3`], normalizing it.
|
||||
///
|
||||
/// Returns ``None` if the length of the given vector is zero or close to zero, infinite,
|
||||
/// or `NaN`.
|
||||
pub fn new(dir: Vec3) -> Option<Self> {
|
||||
let length = dir.length();
|
||||
let direction = (length.is_finite() && length > 0.0).then_some(dir / length);
|
||||
|
||||
direction.map(|d| Self(d))
|
||||
}
|
||||
|
||||
pub fn x(&self) -> f32 {
|
||||
self.0.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> f32 {
|
||||
self.0.y
|
||||
}
|
||||
|
||||
pub fn z(&self) -> f32 {
|
||||
self.0.z
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Ray3 {
|
||||
pub origin: Vec3,
|
||||
pub direction: Dir3,
|
||||
}
|
|
@ -1,13 +1,22 @@
|
|||
use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{loader::LoaderError, lyra_engine, DependencyState};
|
||||
use lyra_ecs::Component;
|
||||
use lyra_reflect::Reflect;
|
||||
use crate::{loader::LoaderError, lyra_engine, DependencyState};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::ResourceStorage;
|
||||
|
||||
pub fn optionally_add_to_dep<R: ResourceData>(deps: &mut Vec<UntypedResHandle>, handle: &Option<ResHandle<R>>) {
|
||||
pub fn optionally_add_to_dep<R: ResourceData>(
|
||||
deps: &mut Vec<UntypedResHandle>,
|
||||
handle: &Option<ResHandle<R>>,
|
||||
) {
|
||||
if let Some(h) = handle {
|
||||
deps.push(h.untyped_clone());
|
||||
}
|
||||
|
@ -22,7 +31,7 @@ pub trait ResourceData: Send + Sync + Any + 'static {
|
|||
fn dependencies(&self) -> Vec<UntypedResHandle>;
|
||||
|
||||
/// Recursively collect the dependencies of the Resource.
|
||||
///
|
||||
///
|
||||
/// If a dependency has a child dependency, it will not show up in this list until its
|
||||
/// parent is loaded.
|
||||
fn recur_dependencies(&self) -> Vec<UntypedResHandle> {
|
||||
|
@ -35,7 +44,7 @@ pub trait ResourceData: Send + Sync + Any + 'static {
|
|||
ResourceState::Ready(data) => {
|
||||
let mut deps_dep = data.dependencies();
|
||||
all_deps.append(&mut deps_dep);
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -81,8 +90,8 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRefMut<'a, T> {
|
|||
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
||||
let d = d.as_ref().as_any();
|
||||
d.downcast_ref::<T>().unwrap()
|
||||
},
|
||||
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +103,8 @@ impl<'a, T: 'static> std::ops::DerefMut for ResourceDataRefMut<'a, T> {
|
|||
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
||||
let d = d.as_mut().as_any_mut();
|
||||
d.downcast_mut::<T>().unwrap()
|
||||
},
|
||||
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,8 +123,8 @@ impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
|
|||
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
||||
let d = d.as_ref().as_any();
|
||||
d.downcast_ref::<T>().unwrap()
|
||||
},
|
||||
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
_ => unreachable!(), // ResHandler::data_ref shouldn't allow this to run
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +140,7 @@ pub struct UntypedResource {
|
|||
}
|
||||
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct UntypedResHandle{
|
||||
pub struct UntypedResHandle {
|
||||
#[reflect(skip)]
|
||||
pub(crate) res: Arc<RwLock<UntypedResource>>,
|
||||
tyid: TypeId,
|
||||
|
@ -141,7 +150,7 @@ impl UntypedResHandle {
|
|||
pub fn new(res: UntypedResource, tyid: TypeId) -> Self {
|
||||
Self {
|
||||
res: Arc::new(RwLock::new(res)),
|
||||
tyid
|
||||
tyid,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +203,7 @@ impl UntypedResHandle {
|
|||
|
||||
/// Wait for the resource to be loaded, not including its dependencies
|
||||
/// (see[`UntypedResHandle::wait_recurse_dependencies_load`]).
|
||||
///
|
||||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_for_load(&self) -> Result<(), LoaderError> {
|
||||
|
@ -203,14 +212,14 @@ impl UntypedResHandle {
|
|||
}
|
||||
|
||||
/// Does the same as [`UntypedResHandle::wait_for_load`] but has a timeout.
|
||||
///
|
||||
///
|
||||
/// Returns true if the resource was loaded before hitting the timeout.
|
||||
pub fn wait_for_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
|
||||
self.wait_for_load_timeout_option_impl(Some(timeout))
|
||||
}
|
||||
|
||||
/// Wait for the entire resource, including its dependencies to be loaded.
|
||||
///
|
||||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_recurse_dependencies_load(&self) -> Result<(), LoaderError> {
|
||||
|
@ -219,13 +228,19 @@ impl UntypedResHandle {
|
|||
}
|
||||
|
||||
/// Does the same as [`UntypedResHandle::wait_recurse_dependencies_load`] but has a timeout.
|
||||
///
|
||||
///
|
||||
/// Returns true if the resource was loaded before hitting the timeout.
|
||||
pub fn wait_recurse_dependencies_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
|
||||
pub fn wait_recurse_dependencies_load_timeout(
|
||||
&self,
|
||||
timeout: Duration,
|
||||
) -> Result<bool, LoaderError> {
|
||||
self.wait_recurse_dependencies_load_timeout_option_impl(Some(timeout))
|
||||
}
|
||||
|
||||
fn wait_for_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
|
||||
fn wait_for_load_timeout_option_impl(
|
||||
&self,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<bool, LoaderError> {
|
||||
let d = self.read();
|
||||
if matches!(d.state, ResourceState::Ready(_)) {
|
||||
return Ok(true);
|
||||
|
@ -252,7 +267,10 @@ impl UntypedResHandle {
|
|||
}
|
||||
}
|
||||
|
||||
fn wait_recurse_dependencies_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
|
||||
fn wait_recurse_dependencies_load_timeout_option_impl(
|
||||
&self,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<bool, LoaderError> {
|
||||
if !self.wait_for_load_timeout_option_impl(timeout)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
@ -270,16 +288,16 @@ impl UntypedResHandle {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(true)
|
||||
},
|
||||
}
|
||||
// self.wait_for_load at the start ensures that the state is ready
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively get the state of the dependencies.
|
||||
///
|
||||
///
|
||||
/// This doesn't return any resource data, it can be used to check if the resource and its
|
||||
/// dependencies are loaded.
|
||||
pub fn recurse_dependency_state(&self) -> DependencyState {
|
||||
|
@ -287,14 +305,14 @@ impl UntypedResHandle {
|
|||
}
|
||||
|
||||
/// Retrieve a typed handle to the resource.
|
||||
///
|
||||
///
|
||||
/// Returns `None` if the types do not match
|
||||
pub fn as_typed<T: ResourceData>(&self) -> Option<ResHandle<T>> {
|
||||
self.clone().into_typed()
|
||||
}
|
||||
|
||||
/// Convert `self` into a typed handle.
|
||||
///
|
||||
///
|
||||
/// Returns `None` if the types do not match
|
||||
pub fn into_typed<T: ResourceData>(self) -> Option<ResHandle<T>> {
|
||||
if self.tyid == TypeId::of::<T>() {
|
||||
|
@ -302,7 +320,9 @@ impl UntypedResHandle {
|
|||
handle: self,
|
||||
_marker: PhantomData::<T>,
|
||||
})
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the type id of the resource in the handle.
|
||||
|
@ -311,8 +331,8 @@ impl UntypedResHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// A handle to a resource.
|
||||
///
|
||||
/// A handle to a resource.
|
||||
///
|
||||
/// # Note
|
||||
/// This struct has an inner [`RwLock`] to the resource data, so most methods may be blocking.
|
||||
/// However, the only times it will be blocking is if another thread is reloading the resource
|
||||
|
@ -333,7 +353,7 @@ impl<T: ResourceData> Clone for ResHandle<T> {
|
|||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
handle: self.handle.clone(),
|
||||
_marker: PhantomData::<T>
|
||||
_marker: PhantomData::<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +407,7 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
let d = self.handle.read();
|
||||
Some(ResourceDataRef {
|
||||
guard: d,
|
||||
_marker: PhantomData::<T>
|
||||
_marker: PhantomData::<T>,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -399,7 +419,7 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
let d = self.handle.write();
|
||||
Some(ResourceDataRefMut {
|
||||
guard: d,
|
||||
_marker: PhantomData::<T>
|
||||
_marker: PhantomData::<T>,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
@ -407,7 +427,7 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
}
|
||||
|
||||
/// Same as [`on_load`] but consumes self, making it possible to chain from a request.
|
||||
///
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// let tree_tileset = resman
|
||||
|
@ -418,18 +438,18 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn then_on_load<F>(self, f: F) -> Self
|
||||
where
|
||||
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static
|
||||
where
|
||||
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static,
|
||||
{
|
||||
self.on_load(f);
|
||||
self
|
||||
}
|
||||
|
||||
/// When the asset is loaded, run a function with a mutable handle to it.
|
||||
///
|
||||
///
|
||||
/// This uses async_std to spawn a blocking thread which will wait for the asset to load.
|
||||
/// When its loaded, it will get a mutable handle to it and run `f`.
|
||||
///
|
||||
///
|
||||
/// /// Example:
|
||||
/// ```
|
||||
/// let tree_tileset = resman
|
||||
|
@ -440,20 +460,20 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn on_load<F>(&self, f: F) -> &Self
|
||||
where
|
||||
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static
|
||||
where
|
||||
F: for<'a> FnOnce(ResourceDataRefMut<'a, T>) + Send + 'static,
|
||||
{
|
||||
let handle = self.handle.clone();
|
||||
async_std::task::spawn_blocking(move || {
|
||||
let d = handle.read();
|
||||
if matches!(d.state, ResourceState::Ready(_)) {
|
||||
let d = handle.write();
|
||||
|
||||
|
||||
f(ResourceDataRefMut {
|
||||
guard: d,
|
||||
_marker: PhantomData::<T>
|
||||
_marker: PhantomData::<T>,
|
||||
});
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -467,7 +487,7 @@ impl<T: ResourceData> ResHandle<T> {
|
|||
let d = handle.write();
|
||||
f(ResourceDataRefMut {
|
||||
guard: d,
|
||||
_marker: PhantomData::<T>
|
||||
_marker: PhantomData::<T>,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -521,7 +541,7 @@ impl<T: ResourceData> ResourceStorage for ResHandle<T> {
|
|||
let mut d = self.handle.write();
|
||||
d.state = new;
|
||||
}
|
||||
|
||||
|
||||
fn clone_untyped(&self) -> UntypedResHandle {
|
||||
self.handle.clone()
|
||||
}
|
||||
|
@ -569,17 +589,17 @@ mod tests {
|
|||
&[]
|
||||
}
|
||||
|
||||
fn load(&self, _: crate::ResourceManager, path: &str) -> crate::loader::PinedBoxLoaderFuture {
|
||||
fn load(
|
||||
&self,
|
||||
_: crate::ResourceManager,
|
||||
path: &str,
|
||||
) -> crate::loader::PinedBoxLoaderFuture {
|
||||
let path = path.to_string();
|
||||
Box::pin(async move {
|
||||
let path = PathBuf::from_str(&path).unwrap();
|
||||
|
||||
let file_name = path.file_name()
|
||||
.and_then(|os| os.to_str())
|
||||
.unwrap();
|
||||
let path_ext = path.extension()
|
||||
.and_then(|os| os.to_str())
|
||||
.unwrap();
|
||||
let file_name = path.file_name().and_then(|os| os.to_str()).unwrap();
|
||||
let path_ext = path.extension().and_then(|os| os.to_str()).unwrap();
|
||||
|
||||
let res = rand::thread_rng().gen_range(500..1000);
|
||||
|
||||
|
@ -594,7 +614,13 @@ mod tests {
|
|||
})
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
|
||||
fn load_bytes(
|
||||
&self,
|
||||
_: crate::ResourceManager,
|
||||
_: Vec<u8>,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> crate::loader::PinedBoxLoaderFuture {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
@ -603,7 +629,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct SimpleResource {
|
||||
depend_a: ResHandle<SimpleDepend>,
|
||||
}
|
||||
|
@ -634,7 +659,11 @@ mod tests {
|
|||
&[]
|
||||
}
|
||||
|
||||
fn load(&self, res_man: crate::ResourceManager, _: &str) -> crate::loader::PinedBoxLoaderFuture {
|
||||
fn load(
|
||||
&self,
|
||||
res_man: crate::ResourceManager,
|
||||
_: &str,
|
||||
) -> crate::loader::PinedBoxLoaderFuture {
|
||||
Box::pin(async move {
|
||||
let res = rand::thread_rng().gen_range(500..1000);
|
||||
|
||||
|
@ -652,7 +681,13 @@ mod tests {
|
|||
})
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _: crate::ResourceManager, _: Vec<u8>, _: usize, _: usize) -> crate::loader::PinedBoxLoaderFuture {
|
||||
fn load_bytes(
|
||||
&self,
|
||||
_: crate::ResourceManager,
|
||||
_: Vec<u8>,
|
||||
_: usize,
|
||||
_: usize,
|
||||
) -> crate::loader::PinedBoxLoaderFuture {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
@ -678,4 +713,4 @@ mod tests {
|
|||
let state = res.recurse_dependency_state();
|
||||
assert!(!state.is_loading());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue