add ReflectAsset
and ReflectHandle
(#5923)
# Objective  ^ enable this Concretely, I need to - list all handle ids for an asset type - fetch the asset as `dyn Reflect`, given a `HandleUntyped` - when encountering a `Handle<T>`, find out what asset type that handle refers to (`T`'s type id) and turn the handle into a `HandleUntyped` ## Solution - add `ReflectAsset` type containing function pointers for working with assets ```rust pub struct ReflectAsset { type_uuid: Uuid, assets_resource_type_id: TypeId, // TypeId of the `Assets<T>` resource get: fn(&World, HandleUntyped) -> Option<&dyn Reflect>, get_mut: fn(&mut World, HandleUntyped) -> Option<&mut dyn Reflect>, get_unchecked_mut: unsafe fn(&World, HandleUntyped) -> Option<&mut dyn Reflect>, add: fn(&mut World, &dyn Reflect) -> HandleUntyped, set: fn(&mut World, HandleUntyped, &dyn Reflect) -> HandleUntyped, len: fn(&World) -> usize, ids: for<'w> fn(&'w World) -> Box<dyn Iterator<Item = HandleId> + 'w>, remove: fn(&mut World, HandleUntyped) -> Option<Box<dyn Reflect>>, } ``` - add `ReflectHandle` type relating the handle back to the asset type and providing a way to create a `HandleUntyped` ```rust pub struct ReflectHandle { type_uuid: Uuid, asset_type_id: TypeId, downcast_handle_untyped: fn(&dyn Any) -> Option<HandleUntyped>, } ``` - add the corresponding `FromType` impls - add a function `app.register_asset_reflect` which is supposed to be called after `.add_asset` and registers `ReflectAsset` and `ReflectHandle` in the type registry --- ## Changelog - add `ReflectAsset` and `ReflectHandle` types, which allow code to use reflection to manipulate arbitrary assets without knowing their types at compile time
This commit is contained in:
parent
0401f04ba9
commit
f867319336
@ -17,7 +17,7 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_hierarchy::Children;
|
||||
use bevy_math::{Quat, Vec3};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
|
||||
use bevy_time::Time;
|
||||
use bevy_transform::{prelude::Transform, TransformSystem};
|
||||
use bevy_utils::{tracing::warn, HashMap};
|
||||
@ -31,7 +31,7 @@ pub mod prelude {
|
||||
}
|
||||
|
||||
/// List of keyframes for one of the attribute of a [`Transform`].
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Reflect, FromReflect, Clone, Debug)]
|
||||
pub enum Keyframes {
|
||||
/// Keyframes for rotation.
|
||||
Rotation(Vec<Quat>),
|
||||
@ -44,7 +44,7 @@ pub enum Keyframes {
|
||||
/// Describes how an attribute of a [`Transform`] should be animated.
|
||||
///
|
||||
/// `keyframe_timestamps` and `keyframes` should have the same length.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Reflect, FromReflect, Clone, Debug)]
|
||||
pub struct VariableCurve {
|
||||
/// Timestamp for each of the keyframes.
|
||||
pub keyframe_timestamps: Vec<f32>,
|
||||
@ -53,14 +53,14 @@ pub struct VariableCurve {
|
||||
}
|
||||
|
||||
/// Path to an entity, with [`Name`]s. Each entity in a path must have a name.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
#[derive(Reflect, FromReflect, Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
pub struct EntityPath {
|
||||
/// Parts of the path
|
||||
pub parts: Vec<Name>,
|
||||
}
|
||||
|
||||
/// A list of [`VariableCurve`], and the [`EntityPath`] to which they apply.
|
||||
#[derive(Clone, TypeUuid, Debug, Default)]
|
||||
#[derive(Reflect, FromReflect, Clone, TypeUuid, Debug, Default)]
|
||||
#[uuid = "d81b7179-0448-4eb0-89fe-c067222725bf"]
|
||||
pub struct AnimationClip {
|
||||
curves: HashMap<EntityPath, Vec<VariableCurve>>,
|
||||
@ -301,6 +301,7 @@ pub struct AnimationPlugin {}
|
||||
impl Plugin for AnimationPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_asset::<AnimationClip>()
|
||||
.register_asset_reflect::<AnimationClip>()
|
||||
.register_type::<AnimationPlayer>()
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{CoreStage, Plugin, PluginGroup, StartupSchedule, StartupStage};
|
||||
pub use bevy_derive::AppLabel;
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
event::{Event, Events},
|
||||
prelude::FromWorld,
|
||||
@ -25,7 +24,7 @@ bevy_utils::define_label!(
|
||||
|
||||
/// The [`Resource`] that stores the [`App`]'s [`TypeRegistry`](bevy_reflect::TypeRegistry).
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[derive(Resource, Clone, Deref, DerefMut, Default)]
|
||||
#[derive(Resource, Clone, bevy_derive::Deref, bevy_derive::DerefMut, Default)]
|
||||
pub struct AppTypeRegistry(pub bevy_reflect::TypeRegistryArc);
|
||||
|
||||
#[allow(clippy::needless_doctest_main)]
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::{
|
||||
update_asset_storage_system, Asset, AssetLoader, AssetServer, AssetStage, Handle, HandleId,
|
||||
RefChange,
|
||||
RefChange, ReflectAsset, ReflectHandle,
|
||||
};
|
||||
use bevy_app::App;
|
||||
use bevy_app::{App, AppTypeRegistry};
|
||||
use bevy_ecs::{
|
||||
event::{EventWriter, Events},
|
||||
system::{ResMut, Resource},
|
||||
world::FromWorld,
|
||||
};
|
||||
use bevy_reflect::{FromReflect, GetTypeRegistration, Reflect};
|
||||
use bevy_utils::HashMap;
|
||||
use crossbeam_channel::Sender;
|
||||
use std::fmt::Debug;
|
||||
@ -279,6 +280,14 @@ pub trait AddAsset {
|
||||
where
|
||||
T: Asset;
|
||||
|
||||
/// Registers the asset type `T` using `[App::register]`,
|
||||
/// and adds [`ReflectAsset`] type data to `T` and [`ReflectHandle`] type data to [`Handle<T>`] in the type registry.
|
||||
///
|
||||
/// This enables reflection code to access assets. For detailed information, see the docs on [`ReflectAsset`] and [`ReflectHandle`].
|
||||
fn register_asset_reflect<T>(&mut self) -> &mut Self
|
||||
where
|
||||
T: Asset + Reflect + FromReflect + GetTypeRegistration;
|
||||
|
||||
/// Registers `T` as a supported internal asset in the application.
|
||||
///
|
||||
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
||||
@ -332,6 +341,23 @@ impl AddAsset for App {
|
||||
.add_event::<AssetEvent<T>>()
|
||||
}
|
||||
|
||||
fn register_asset_reflect<T>(&mut self) -> &mut Self
|
||||
where
|
||||
T: Asset + Reflect + FromReflect + GetTypeRegistration,
|
||||
{
|
||||
let type_registry = self.world.resource::<AppTypeRegistry>();
|
||||
{
|
||||
let mut type_registry = type_registry.write();
|
||||
|
||||
type_registry.register::<T>();
|
||||
type_registry.register::<Handle<T>>();
|
||||
type_registry.register_type_data::<T, ReflectAsset>();
|
||||
type_registry.register_type_data::<Handle<T>, ReflectHandle>();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn add_debug_asset<T: Clone>(&mut self) -> &mut Self
|
||||
where
|
||||
T: Asset,
|
||||
|
@ -410,6 +410,16 @@ impl Drop for HandleUntyped {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Asset> From<Handle<A>> for HandleUntyped {
|
||||
fn from(mut handle: Handle<A>) -> Self {
|
||||
let handle_type = std::mem::replace(&mut handle.handle_type, HandleType::Weak);
|
||||
HandleUntyped {
|
||||
id: handle.id,
|
||||
handle_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&HandleUntyped> for HandleId {
|
||||
fn from(value: &HandleUntyped) -> Self {
|
||||
value.id
|
||||
|
@ -25,6 +25,7 @@ mod info;
|
||||
mod io;
|
||||
mod loader;
|
||||
mod path;
|
||||
mod reflect;
|
||||
|
||||
/// The `bevy_asset` prelude.
|
||||
pub mod prelude {
|
||||
@ -43,6 +44,7 @@ pub use info::*;
|
||||
pub use io::*;
|
||||
pub use loader::*;
|
||||
pub use path::*;
|
||||
pub use reflect::*;
|
||||
|
||||
use bevy_app::{prelude::Plugin, App};
|
||||
use bevy_ecs::schedule::{StageLabel, SystemStage};
|
||||
|
321
crates/bevy_asset/src/reflect.rs
Normal file
321
crates/bevy_asset/src/reflect.rs
Normal file
@ -0,0 +1,321 @@
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
use bevy_ecs::world::World;
|
||||
use bevy_reflect::{FromReflect, FromType, Reflect, Uuid};
|
||||
|
||||
use crate::{Asset, Assets, Handle, HandleId, HandleUntyped};
|
||||
|
||||
/// Type data for the [`TypeRegistry`](bevy_reflect::TypeRegistry) used to operate on reflected [`Asset`]s.
|
||||
///
|
||||
/// This type provides similar methods to [`Assets<T>`] like `get`, `add` and `remove`, but can be used in situations where you don't know which asset type `T` you want
|
||||
/// until runtime.
|
||||
///
|
||||
/// [`ReflectAsset`] can be obtained via [`TypeRegistration::data`](bevy_reflect::TypeRegistration::data) if the asset was registered using [`register_asset_reflect`](crate::AddAsset::register_asset_reflect).
|
||||
#[derive(Clone)]
|
||||
pub struct ReflectAsset {
|
||||
type_uuid: Uuid,
|
||||
handle_type_id: TypeId,
|
||||
assets_resource_type_id: TypeId,
|
||||
|
||||
get: fn(&World, HandleUntyped) -> Option<&dyn Reflect>,
|
||||
get_mut: fn(&mut World, HandleUntyped) -> Option<&mut dyn Reflect>,
|
||||
get_unchecked_mut: unsafe fn(&World, HandleUntyped) -> Option<&mut dyn Reflect>,
|
||||
add: fn(&mut World, &dyn Reflect) -> HandleUntyped,
|
||||
set: fn(&mut World, HandleUntyped, &dyn Reflect) -> HandleUntyped,
|
||||
len: fn(&World) -> usize,
|
||||
ids: for<'w> fn(&'w World) -> Box<dyn Iterator<Item = HandleId> + 'w>,
|
||||
remove: fn(&mut World, HandleUntyped) -> Option<Box<dyn Reflect>>,
|
||||
}
|
||||
|
||||
impl ReflectAsset {
|
||||
/// The [`bevy_reflect::TypeUuid`] of the asset
|
||||
pub fn type_uuid(&self) -> Uuid {
|
||||
self.type_uuid
|
||||
}
|
||||
|
||||
/// The [`TypeId`] of the [`Handle<T>`] for this asset
|
||||
pub fn handle_type_id(&self) -> TypeId {
|
||||
self.handle_type_id
|
||||
}
|
||||
|
||||
/// The [`TypeId`] of the [`Assets<T>`] resource
|
||||
pub fn assets_resource_type_id(&self) -> TypeId {
|
||||
self.assets_resource_type_id
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::get`]
|
||||
pub fn get<'w>(&self, world: &'w World, handle: HandleUntyped) -> Option<&'w dyn Reflect> {
|
||||
(self.get)(world, handle)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::get_mut`]
|
||||
pub fn get_mut<'w>(
|
||||
&self,
|
||||
world: &'w mut World,
|
||||
handle: HandleUntyped,
|
||||
) -> Option<&'w mut dyn Reflect> {
|
||||
(self.get_mut)(world, handle)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::get_mut`], but does not require a mutable reference to the world.
|
||||
///
|
||||
/// Only use this method when you have ensured that you are the *only* one with access to the [`Assets`] resource of the asset type.
|
||||
/// Furthermore, this does *not* allow you to have look up two distinct handles,
|
||||
/// you can only have at most one alive at the same time.
|
||||
/// This means that this is *not allowed*:
|
||||
/// ```rust,no_run
|
||||
/// # use bevy_asset::{ReflectAsset, HandleUntyped};
|
||||
/// # use bevy_ecs::prelude::World;
|
||||
/// # let reflect_asset: ReflectAsset = unimplemented!();
|
||||
/// # let world: World = unimplemented!();
|
||||
/// # let handle_1: HandleUntyped = unimplemented!();
|
||||
/// # let handle_2: HandleUntyped = unimplemented!();
|
||||
/// let a = unsafe { reflect_asset.get_unchecked_mut(&world, handle_1).unwrap() };
|
||||
/// let b = unsafe { reflect_asset.get_unchecked_mut(&world, handle_2).unwrap() };
|
||||
/// // ^ not allowed, two mutable references through the same asset resource, even though the
|
||||
/// // handles are distinct
|
||||
///
|
||||
/// println!("a = {a:?}, b = {b:?}");
|
||||
/// ```
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not prevent you from having two mutable pointers to the same data,
|
||||
/// violating Rust's aliasing rules. To avoid this:
|
||||
/// * Only call this method when you have exclusive access to the world
|
||||
/// (or use a scheduler that enforces unique access to the `Assets` resource).
|
||||
/// * Don't call this method more than once in the same scope.
|
||||
pub unsafe fn get_unchecked_mut<'w>(
|
||||
&self,
|
||||
world: &'w World,
|
||||
handle: HandleUntyped,
|
||||
) -> Option<&'w mut dyn Reflect> {
|
||||
// SAFETY: requirements are deferred to the caller
|
||||
(self.get_unchecked_mut)(world, handle)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::add`]
|
||||
pub fn add<'w>(&self, world: &'w mut World, value: &dyn Reflect) -> HandleUntyped {
|
||||
(self.add)(world, value)
|
||||
}
|
||||
/// Equivalent of [`Assets::set`]
|
||||
pub fn set<'w>(
|
||||
&self,
|
||||
world: &'w mut World,
|
||||
handle: HandleUntyped,
|
||||
value: &dyn Reflect,
|
||||
) -> HandleUntyped {
|
||||
(self.set)(world, handle, value)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::remove`]
|
||||
pub fn remove(&self, world: &mut World, handle: HandleUntyped) -> Option<Box<dyn Reflect>> {
|
||||
(self.remove)(world, handle)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::len`]
|
||||
#[allow(clippy::len_without_is_empty)] // clippy expects the `is_empty` method to have the signature `(&self) -> bool`
|
||||
pub fn len(&self, world: &World) -> usize {
|
||||
(self.len)(world)
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::is_empty`]
|
||||
pub fn is_empty(&self, world: &World) -> bool {
|
||||
self.len(world) == 0
|
||||
}
|
||||
|
||||
/// Equivalent of [`Assets::ids`]
|
||||
pub fn ids<'w>(&self, world: &'w World) -> impl Iterator<Item = HandleId> + 'w {
|
||||
(self.ids)(world)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
|
||||
fn from_type() -> Self {
|
||||
ReflectAsset {
|
||||
type_uuid: A::TYPE_UUID,
|
||||
handle_type_id: TypeId::of::<Handle<A>>(),
|
||||
assets_resource_type_id: TypeId::of::<Assets<A>>(),
|
||||
get: |world, handle| {
|
||||
let assets = world.resource::<Assets<A>>();
|
||||
let asset = assets.get(&handle.typed());
|
||||
asset.map(|asset| asset as &dyn Reflect)
|
||||
},
|
||||
get_mut: |world, handle| {
|
||||
let assets = world.resource_mut::<Assets<A>>().into_inner();
|
||||
let asset = assets.get_mut(&handle.typed());
|
||||
asset.map(|asset| asset as &mut dyn Reflect)
|
||||
},
|
||||
get_unchecked_mut: |world, handle| {
|
||||
let assets = unsafe {
|
||||
world
|
||||
.get_resource_unchecked_mut::<Assets<A>>()
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
};
|
||||
let asset = assets.get_mut(&handle.typed());
|
||||
asset.map(|asset| asset as &mut dyn Reflect)
|
||||
},
|
||||
add: |world, value| {
|
||||
let mut assets = world.resource_mut::<Assets<A>>();
|
||||
let value: A = FromReflect::from_reflect(value)
|
||||
.expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
|
||||
assets.add(value).into()
|
||||
},
|
||||
set: |world, handle, value| {
|
||||
let mut assets = world.resource_mut::<Assets<A>>();
|
||||
let value: A = FromReflect::from_reflect(value)
|
||||
.expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
|
||||
assets.set(handle, value).into()
|
||||
},
|
||||
len: |world| {
|
||||
let assets = world.resource::<Assets<A>>();
|
||||
assets.len()
|
||||
},
|
||||
ids: |world| {
|
||||
let assets = world.resource::<Assets<A>>();
|
||||
Box::new(assets.ids())
|
||||
},
|
||||
remove: |world, handle| {
|
||||
let mut assets = world.resource_mut::<Assets<A>>();
|
||||
let value = assets.remove(handle);
|
||||
value.map(|value| Box::new(value) as Box<dyn Reflect>)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reflect type data struct relating a [`Handle<T>`] back to the `T` asset type.
|
||||
///
|
||||
/// Say you want to look up the asset values of a list of handles when you have access to their `&dyn Reflect` form.
|
||||
/// Assets can be looked up in the world using [`ReflectAsset`], but how do you determine which [`ReflectAsset`] to use when
|
||||
/// only looking at the handle? [`ReflectHandle`] is stored in the type registry on each `Handle<T>` type, so you can use [`ReflectHandle::asset_type_id`] to look up
|
||||
/// the [`ReflectAsset`] type data on the corresponding `T` asset type:
|
||||
///
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use bevy_reflect::{TypeRegistry, prelude::*};
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// use bevy_asset::{ReflectHandle, ReflectAsset};
|
||||
///
|
||||
/// # let world: &World = unimplemented!();
|
||||
/// # let type_registry: TypeRegistry = unimplemented!();
|
||||
/// let handles: Vec<&dyn Reflect> = unimplemented!();
|
||||
/// for handle in handles {
|
||||
/// let reflect_handle = type_registry.get_type_data::<ReflectHandle>(handle.type_id()).unwrap();
|
||||
/// let reflect_asset = type_registry.get_type_data::<ReflectAsset>(reflect_handle.asset_type_id()).unwrap();
|
||||
///
|
||||
/// let handle = reflect_handle.downcast_handle_untyped(handle.as_any()).unwrap();
|
||||
/// let value = reflect_asset.get(world, handle).unwrap();
|
||||
/// println!("{value:?}");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct ReflectHandle {
|
||||
type_uuid: Uuid,
|
||||
asset_type_id: TypeId,
|
||||
downcast_handle_untyped: fn(&dyn Any) -> Option<HandleUntyped>,
|
||||
typed: fn(HandleUntyped) -> Box<dyn Reflect>,
|
||||
}
|
||||
impl ReflectHandle {
|
||||
/// The [`bevy_reflect::TypeUuid`] of the asset
|
||||
pub fn type_uuid(&self) -> Uuid {
|
||||
self.type_uuid
|
||||
}
|
||||
/// The [`TypeId`] of the asset
|
||||
pub fn asset_type_id(&self) -> TypeId {
|
||||
self.asset_type_id
|
||||
}
|
||||
|
||||
/// A way to go from a [`Handle<T>`] in a `dyn Any` to a [`HandleUntyped`]
|
||||
pub fn downcast_handle_untyped(&self, handle: &dyn Any) -> Option<HandleUntyped> {
|
||||
(self.downcast_handle_untyped)(handle)
|
||||
}
|
||||
|
||||
/// A way to go from a [`HandleUntyped`] to a [`Handle<T>`] in a `Box<dyn Reflect>`.
|
||||
/// Equivalent of [`HandleUntyped::typed`].
|
||||
pub fn typed(&self, handle: HandleUntyped) -> Box<dyn Reflect> {
|
||||
(self.typed)(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
|
||||
fn from_type() -> Self {
|
||||
ReflectHandle {
|
||||
type_uuid: A::TYPE_UUID,
|
||||
asset_type_id: TypeId::of::<A>(),
|
||||
downcast_handle_untyped: |handle: &dyn Any| {
|
||||
handle
|
||||
.downcast_ref::<Handle<A>>()
|
||||
.map(|handle| handle.clone_untyped())
|
||||
},
|
||||
typed: |handle: HandleUntyped| Box::new(handle.typed::<A>()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::any::TypeId;
|
||||
|
||||
use bevy_app::{App, AppTypeRegistry};
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectMut, TypeUuid};
|
||||
|
||||
use crate::{AddAsset, AssetPlugin, HandleUntyped, ReflectAsset};
|
||||
|
||||
#[derive(Reflect, FromReflect, TypeUuid)]
|
||||
#[uuid = "09191350-1238-4736-9a89-46f04bda6966"]
|
||||
struct AssetType {
|
||||
field: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reflect_asset_operations() {
|
||||
let mut app = App::new();
|
||||
app.add_plugin(AssetPlugin::default())
|
||||
.add_asset::<AssetType>()
|
||||
.register_asset_reflect::<AssetType>();
|
||||
|
||||
let reflect_asset = {
|
||||
let type_registry = app.world.resource::<AppTypeRegistry>();
|
||||
let type_registry = type_registry.read();
|
||||
|
||||
type_registry
|
||||
.get_type_data::<ReflectAsset>(TypeId::of::<AssetType>())
|
||||
.unwrap()
|
||||
.clone()
|
||||
};
|
||||
|
||||
let value = AssetType {
|
||||
field: "test".into(),
|
||||
};
|
||||
|
||||
let handle = reflect_asset.add(&mut app.world, &value);
|
||||
let strukt = match reflect_asset
|
||||
.get_mut(&mut app.world, handle)
|
||||
.unwrap()
|
||||
.reflect_mut()
|
||||
{
|
||||
ReflectMut::Struct(s) => s,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
strukt
|
||||
.field_mut("field")
|
||||
.unwrap()
|
||||
.apply(&String::from("edited"));
|
||||
|
||||
assert_eq!(reflect_asset.len(&app.world), 1);
|
||||
let ids: Vec<_> = reflect_asset.ids(&app.world).collect();
|
||||
assert_eq!(ids.len(), 1);
|
||||
|
||||
let fetched_handle = HandleUntyped::weak(ids[0]);
|
||||
let asset = reflect_asset
|
||||
.get(&app.world, fetched_handle.clone_weak())
|
||||
.unwrap();
|
||||
assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
|
||||
|
||||
reflect_asset
|
||||
.remove(&mut app.world, fetched_handle)
|
||||
.unwrap();
|
||||
assert_eq!(reflect_asset.len(&app.world), 0);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||
use bevy_reflect::std_traits::ReflectDefault;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect};
|
||||
use bevy_utils::AHasher;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@ -14,7 +14,7 @@ use std::{
|
||||
/// [`Name`] should not be treated as a globally unique identifier for entities,
|
||||
/// as multiple entities can have the same name. [`bevy_ecs::entity::Entity`] should be
|
||||
/// used instead as the default unique identifier.
|
||||
#[derive(Component, Debug, Clone, Reflect)]
|
||||
#[derive(Reflect, FromReflect, Component, Debug, Clone)]
|
||||
#[reflect(Component, Default)]
|
||||
pub struct Name {
|
||||
hash: u64, // TODO: Shouldn't be serialized
|
||||
|
@ -1,11 +1,11 @@
|
||||
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||
use bevy_reflect::std_traits::ReflectDefault;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_reflect::{FromReflect, Reflect};
|
||||
|
||||
// TODO: add discussion about performance.
|
||||
/// Sets how a material's base color alpha channel is used for transparency.
|
||||
#[derive(Component, Debug, Default, Reflect, Copy, Clone, PartialEq)]
|
||||
#[reflect(Component, Default)]
|
||||
#[derive(Component, Debug, Default, Reflect, Copy, Clone, PartialEq, FromReflect)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub enum AlphaMode {
|
||||
/// Base color alpha values are overridden to be fully opaque (1.0).
|
||||
#[default]
|
||||
|
@ -38,7 +38,7 @@ pub mod draw_3d_graph {
|
||||
}
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
|
||||
use bevy_asset::{load_internal_asset, AddAsset, Assets, Handle, HandleUntyped};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_render::{
|
||||
@ -129,6 +129,7 @@ impl Plugin for PbrPlugin {
|
||||
.register_type::<SpotLight>()
|
||||
.add_plugin(MeshRenderPlugin)
|
||||
.add_plugin(MaterialPlugin::<StandardMaterial>::default())
|
||||
.register_asset_reflect::<StandardMaterial>()
|
||||
.register_type::<AmbientLight>()
|
||||
.register_type::<DirectionalLightShadowMap>()
|
||||
.register_type::<PointLightShadowMap>()
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{AlphaMode, Material, MaterialPipeline, MaterialPipelineKey, PBR_SHADER_HANDLE};
|
||||
use bevy_asset::Handle;
|
||||
use bevy_math::Vec4;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, TypeUuid};
|
||||
use bevy_render::{
|
||||
color::Color, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*,
|
||||
texture::Image,
|
||||
@ -12,10 +12,11 @@ use bevy_render::{
|
||||
/// <https://google.github.io/filament/Material%20Properties.pdf>.
|
||||
///
|
||||
/// May be created directly from a [`Color`] or an [`Image`].
|
||||
#[derive(AsBindGroup, Debug, Clone, TypeUuid)]
|
||||
#[derive(AsBindGroup, Reflect, FromReflect, Debug, Clone, TypeUuid)]
|
||||
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
||||
#[bind_group_data(StandardMaterialKey)]
|
||||
#[uniform(0, StandardMaterialUniform)]
|
||||
#[reflect(Default, Debug)]
|
||||
pub struct StandardMaterial {
|
||||
/// The color of the surface of the material before lighting.
|
||||
///
|
||||
@ -193,6 +194,8 @@ pub struct StandardMaterial {
|
||||
/// Your 3D editing software should manage all of that.
|
||||
///
|
||||
/// [`Mesh`]: bevy_render::mesh::Mesh
|
||||
// TODO: include this in reflection somehow (maybe via remote types like serde https://serde.rs/remote-derive.html)
|
||||
#[reflect(ignore)]
|
||||
pub cull_mode: Option<Face>,
|
||||
|
||||
/// Whether to apply only the base color to this material.
|
||||
|
@ -15,7 +15,7 @@ use bevy_asset::HandleUntyped;
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
|
||||
use std::hash::Hash;
|
||||
use thiserror::Error;
|
||||
use wgpu::{
|
||||
@ -101,8 +101,9 @@ impl ImageFormat {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, TypeUuid)]
|
||||
#[derive(Reflect, FromReflect, Debug, Clone, TypeUuid)]
|
||||
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
|
||||
#[reflect_value]
|
||||
pub struct Image {
|
||||
pub data: Vec<u8>,
|
||||
// TODO: this nesting makes accessing Image metadata verbose. Either flatten out descriptor or add accessors
|
||||
|
@ -86,7 +86,8 @@ impl Plugin for ImagePlugin {
|
||||
app.add_plugin(RenderAssetPlugin::<Image>::with_prepare_asset_label(
|
||||
PrepareAssetLabel::PreAssetPrepare,
|
||||
))
|
||||
.add_asset::<Image>();
|
||||
.add_asset::<Image>()
|
||||
.register_asset_reflect::<Image>();
|
||||
app.world
|
||||
.resource_mut::<Assets<Image>>()
|
||||
.set_untracked(DEFAULT_IMAGE_HANDLE, Image::default());
|
||||
|
@ -54,6 +54,7 @@ impl Plugin for SpritePlugin {
|
||||
let sprite_shader = Shader::from_wgsl(include_str!("render/sprite.wgsl"));
|
||||
shaders.set_untracked(SPRITE_SHADER_HANDLE, sprite_shader);
|
||||
app.add_asset::<TextureAtlas>()
|
||||
.register_asset_reflect::<TextureAtlas>()
|
||||
.register_type::<Sprite>()
|
||||
.register_type::<Anchor>()
|
||||
.register_type::<Mesh2dHandle>()
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
|
||||
use bevy_asset::{load_internal_asset, AddAsset, Assets, Handle, HandleUntyped};
|
||||
use bevy_math::Vec4;
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_reflect::{prelude::*, TypeUuid};
|
||||
use bevy_render::{
|
||||
color::Color, prelude::Shader, render_asset::RenderAssets, render_resource::*, texture::Image,
|
||||
};
|
||||
@ -23,7 +23,8 @@ impl Plugin for ColorMaterialPlugin {
|
||||
Shader::from_wgsl
|
||||
);
|
||||
|
||||
app.add_plugin(Material2dPlugin::<ColorMaterial>::default());
|
||||
app.add_plugin(Material2dPlugin::<ColorMaterial>::default())
|
||||
.register_asset_reflect::<ColorMaterial>();
|
||||
|
||||
app.world
|
||||
.resource_mut::<Assets<ColorMaterial>>()
|
||||
@ -38,7 +39,8 @@ impl Plugin for ColorMaterialPlugin {
|
||||
}
|
||||
|
||||
/// A [2d material](Material2d) that renders [2d meshes](crate::Mesh2dHandle) with a texture tinted by a uniform color
|
||||
#[derive(AsBindGroup, Debug, Clone, TypeUuid)]
|
||||
#[derive(AsBindGroup, Reflect, FromReflect, Debug, Clone, TypeUuid)]
|
||||
#[reflect(Default, Debug)]
|
||||
#[uuid = "e228a544-e3ca-4e1e-bb9d-4d8bc1ad8c19"]
|
||||
#[uniform(0, ColorMaterialUniform)]
|
||||
pub struct ColorMaterial {
|
||||
|
@ -2,15 +2,16 @@ use crate::Anchor;
|
||||
use bevy_asset::Handle;
|
||||
use bevy_ecs::component::Component;
|
||||
use bevy_math::{Rect, Vec2};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
|
||||
use bevy_render::{color::Color, texture::Image};
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
/// An atlas containing multiple textures (like a spritesheet or a tilemap).
|
||||
/// [Example usage animating sprite.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_sheet.rs)
|
||||
/// [Example usage loading sprite sheet.](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs)
|
||||
#[derive(Debug, Clone, TypeUuid)]
|
||||
#[derive(Reflect, FromReflect, Debug, Clone, TypeUuid)]
|
||||
#[uuid = "7233c597-ccfa-411f-bd59-9af349432ada"]
|
||||
#[reflect(Debug)]
|
||||
pub struct TextureAtlas {
|
||||
/// The handle to the texture in which the sprites are stored
|
||||
pub texture: Handle<Image>,
|
||||
|
Loading…
Reference in New Issue
Block a user