Skip to content

Commit

Permalink
add ReflectAsset and ReflectHandle (bevyengine#5923)
Browse files Browse the repository at this point in the history
# Objective
![image](https://user-images.githubusercontent.com/22177966/189350194-639a0211-e984-4f73-ae62-0ede44891eb9.png)

^ 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
  • Loading branch information
jakobhellermann authored and ItsDoot committed Feb 1, 2023
1 parent 6b15500 commit bdd9d07
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 26 deletions.
11 changes: 6 additions & 5 deletions crates/bevy_animation/src/lib.rs
Expand Up @@ -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};
Expand All @@ -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>),
Expand All @@ -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>,
Expand All @@ -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>>,
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions crates/bevy_app/src/app.rs
@@ -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,
Expand All @@ -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)]
Expand Down
30 changes: 28 additions & 2 deletions crates/bevy_asset/src/assets.rs
@@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_asset/src/handle.rs
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_asset/src/lib.rs
Expand Up @@ -25,6 +25,7 @@ mod info;
mod io;
mod loader;
mod path;
mod reflect;

/// The `bevy_asset` prelude.
pub mod prelude {
Expand All @@ -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};
Expand Down

0 comments on commit bdd9d07

Please sign in to comment.