diff --git a/crates/blenvy/src/components/fake_entity.rs b/crates/blenvy/src/components/fake_entity.rs index 0ff7214..948de26 100644 --- a/crates/blenvy/src/components/fake_entity.rs +++ b/crates/blenvy/src/components/fake_entity.rs @@ -1,11 +1,41 @@ -use std::{alloc::Layout, num::NonZeroU32}; +use std::{alloc::Layout, cell::Cell, num::NonZeroU32}; use bevy::{ + core::Name, + ecs::world::DeferredWorld, + gltf::GltfExtras, log::{info, warn}, + prelude::{HierarchyQueryExt, Parent, QueryState, With, World}, reflect::ReflectDeserialize, + scene::{InstanceId, SceneInstance}, }; use serde::Deserialize; +pub(crate) struct BadWorldAccess { + world: *mut World, + names: QueryState<(bevy::ecs::entity::Entity, &'static Name), With>, + hierarchy: QueryState<&'static Parent, ()>, + scene_instances: QueryState<&'static SceneInstance, ()>, +} + +impl BadWorldAccess { + pub unsafe fn new(world: &mut World) -> Self { + BadWorldAccess { + world, + // We have to check that we focus on a node, not a mesh with the same name. + // Currently, the only possible way of checking this is with `GltfExtras`, so selected entities must at least have one component. + names: world.query_filtered::<(bevy::ecs::entity::Entity, &Name), With>(), + hierarchy: world.query::<&Parent>(), + scene_instances: world.query::<&SceneInstance>(), + } + } +} + +thread_local! { + pub(crate) static BAD_WORLD_ACCESS: Cell> = Cell::new(None); + pub(crate) static INSTANCE_ID: Cell> = Cell::new(None); +} + const _: () = { let real = Layout::new::(); let fake = Layout::new::(); @@ -39,7 +69,46 @@ impl<'de> Deserialize<'de> for Entity { let entity = if let Some(name) = entity_data.name { info!("Found name {name}"); - bevy::ecs::entity::Entity::PLACEHOLDER + let BadWorldAccess { + world, + mut names, + mut hierarchy, + mut scene_instances, + } = BAD_WORLD_ACCESS.take().expect("No bad world access :c"); + let instance = INSTANCE_ID.get().expect("No instance id set :c"); + + let mut target = None; + let w = unsafe { &*world.cast_const() }; + 'search: for (e, n) in names.iter(w) { + if !name.eq(n.as_str()) { + continue; + } + + let mut dw = DeferredWorld::from(unsafe { &mut *world }); + let hierarchy = dw.query(&mut hierarchy); + + for parent in hierarchy.iter_ancestors(e) { + let Ok(id) = scene_instances.get(w, parent) else { + continue; + }; + if instance.eq(id) { + target = Some(e); + break 'search; + } + } + } + + BAD_WORLD_ACCESS.set(Some(BadWorldAccess { + world, + names, + hierarchy, + scene_instances, + })); + + target.unwrap_or_else(|| { + warn!("No entity found for '{name}' - perhaps it doesn't contain any components from blender?"); + bevy::ecs::entity::Entity::PLACEHOLDER + }) } else { warn!("No object was specified for Entity relation, using `Entity::PLACEHOLDER`."); bevy::ecs::entity::Entity::PLACEHOLDER diff --git a/crates/blenvy/src/components/process_gltfs.rs b/crates/blenvy/src/components/process_gltfs.rs index d861662..71b895f 100644 --- a/crates/blenvy/src/components/process_gltfs.rs +++ b/crates/blenvy/src/components/process_gltfs.rs @@ -1,20 +1,26 @@ +use std::ops::Deref; + use bevy::{ core::Name, ecs::{ entity::Entity, query::{Added, Without}, reflect::{AppTypeRegistry, ReflectComponent}, - world::World, + world::{DeferredWorld, World}, }, gltf::{GltfExtras, GltfMaterialExtras, GltfMeshExtras, GltfSceneExtras}, hierarchy::Parent, log::{debug, warn}, + prelude::HierarchyQueryExt, reflect::{Reflect, TypeRegistration}, + scene::SceneInstance, utils::HashMap, }; use crate::{ronstring_to_reflect_component, GltfProcessed}; +use super::fake_entity::{self, BadWorldAccess}; + // , mut entity_components: HashMap, TypeRegistration)>> fn find_entity_components( entity: Entity, @@ -57,22 +63,44 @@ fn find_entity_components( /// main function: injects components into each entity in gltf files that have `gltf_extras`, using reflection pub fn add_components_from_gltf_extras(world: &mut World) { - let mut extras = world.query_filtered::<(Entity, Option<&Name>, &GltfExtras, Option<&Parent>), (Added, Without)>(); - let mut scene_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfSceneExtras, Option<&Parent>), (Added, Without)>(); - let mut mesh_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfMeshExtras, Option<&Parent>), (Added, Without)>(); - let mut material_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfMaterialExtras, Option<&Parent>), (Added, Without)>(); + let mut extras = world.query_filtered::<(Entity, Option<&Name>, &GltfExtras), (Added, Without)>(); + let mut scene_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfSceneExtras), (Added, Without)>(); + let mut mesh_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfMeshExtras), (Added, Without)>(); + let mut material_extras = world.query_filtered::<(Entity, Option<&Name>, &GltfMaterialExtras), (Added, Without)>(); + + let mut scene_instances = world.query::<&SceneInstance>(); + + let mut hierarchy_state = world.query::<&Parent>(); + let mut __unsafe_dw = DeferredWorld::from(unsafe { &mut *(world as *mut _) }); + let hierarchy = __unsafe_dw.query(&mut hierarchy_state); let mut entity_components: HashMap, TypeRegistration)>> = HashMap::new(); // let gltf_components_config = world.resource::(); - for (entity, name, extra, parent) in extras.iter(world) { + unsafe { + // SAFETY: we don't do anything harmful until taking this, and have full world access + fake_entity::BAD_WORLD_ACCESS.set(Some(BadWorldAccess::new(world))); + } + + for (entity, name, extra) in extras.iter(world) { + let parent = hierarchy.get(entity).ok(); debug!( "Gltf Extra: Name: {:?}, entity {:?}, parent: {:?}, extras {:?}", name, entity, parent, extra ); + if let Some(instance) = hierarchy + .iter_ancestors(entity) + .find_map(|p| scene_instances.get(world, p).ok()) + { + fake_entity::INSTANCE_ID.set(Some(*instance.deref())); + } else { + warn!("Can't find higher-hierarchy `SceneInstance` for entity '{name:?}'"); + fake_entity::INSTANCE_ID.set(None); + }; + let type_registry: &AppTypeRegistry = world.resource(); let mut type_registry = type_registry.write(); let reflect_components = ronstring_to_reflect_component(&extra.value, &mut type_registry); @@ -80,15 +108,27 @@ pub fn add_components_from_gltf_extras(world: &mut World) { let (target_entity, updated_components) = find_entity_components(entity, name, parent, reflect_components, &entity_components); + entity_components.insert(target_entity, updated_components); } - for (entity, name, extra, parent) in scene_extras.iter(world) { + for (entity, name, extra) in scene_extras.iter(world) { + let parent = hierarchy.get(entity).ok(); debug!( "Gltf Scene Extra: Name: {:?}, entity {:?}, parent: {:?}, scene_extras {:?}", name, entity, parent, extra ); + if let Some(instance) = hierarchy + .iter_ancestors(entity) + .find_map(|p| scene_instances.get(world, p).ok()) + { + fake_entity::INSTANCE_ID.set(Some(*instance.deref())); + } else { + warn!("Can't find higher-hierarchy `SceneInstance` for entity '{name:?}'"); + fake_entity::INSTANCE_ID.set(None); + }; + let type_registry: &AppTypeRegistry = world.resource(); let mut type_registry = type_registry.write(); let reflect_components = ronstring_to_reflect_component(&extra.value, &mut type_registry); @@ -98,12 +138,23 @@ pub fn add_components_from_gltf_extras(world: &mut World) { entity_components.insert(target_entity, updated_components); } - for (entity, name, extra, parent) in mesh_extras.iter(world) { + for (entity, name, extra) in mesh_extras.iter(world) { + let parent = hierarchy.get(entity).ok(); debug!( "Gltf Mesh Extra: Name: {:?}, entity {:?}, parent: {:?}, mesh_extras {:?}", name, entity, parent, extra ); + if let Some(instance) = hierarchy + .iter_ancestors(entity) + .find_map(|p| scene_instances.get(world, p).ok()) + { + fake_entity::INSTANCE_ID.set(Some(*instance.deref())); + } else { + warn!("Can't find higher-hierarchy `SceneInstance` for entity '{name:?}'"); + fake_entity::INSTANCE_ID.set(None); + }; + let type_registry: &AppTypeRegistry = world.resource(); let mut type_registry = type_registry.write(); let reflect_components = ronstring_to_reflect_component(&extra.value, &mut type_registry); @@ -113,12 +164,23 @@ pub fn add_components_from_gltf_extras(world: &mut World) { entity_components.insert(target_entity, updated_components); } - for (entity, name, extra, parent) in material_extras.iter(world) { + for (entity, name, extra) in material_extras.iter(world) { + let parent = hierarchy.get(entity).ok(); debug!( "Name: {:?}, entity {:?}, parent: {:?}, material_extras {:?}", name, entity, parent, extra ); + if let Some(instance) = hierarchy + .iter_ancestors(entity) + .find_map(|p| scene_instances.get(world, p).ok()) + { + fake_entity::INSTANCE_ID.set(Some(*instance.deref())); + } else { + warn!("Can't find higher-hierarchy `SceneInstance` for entity '{name:?}'"); + fake_entity::INSTANCE_ID.set(None); + }; + let type_registry: &AppTypeRegistry = world.resource(); let mut type_registry = type_registry.write(); let reflect_components = ronstring_to_reflect_component(&extra.value, &mut type_registry); @@ -128,6 +190,9 @@ pub fn add_components_from_gltf_extras(world: &mut World) { entity_components.insert(target_entity, updated_components); } + fake_entity::BAD_WORLD_ACCESS.set(None); + fake_entity::INSTANCE_ID.set(None); + for (entity, components) in entity_components { let type_registry: &AppTypeRegistry = world.resource(); let type_registry = type_registry.clone(); diff --git a/examples/relations/Cargo.toml b/examples/relations/Cargo.toml index 03981ed..1f7cdae 100644 --- a/examples/relations/Cargo.toml +++ b/examples/relations/Cargo.toml @@ -6,4 +6,5 @@ license = "MIT OR Apache-2.0" [dependencies] bevy = { version = "0.14", features = ["dynamic_linking"] } +bevy-inspector-egui = "0.25.2" blenvy = { path = "../../crates/blenvy" } diff --git a/examples/relations/art/untitled.blend b/examples/relations/art/untitled.blend index 189b2b9..a344db6 100644 Binary files a/examples/relations/art/untitled.blend and b/examples/relations/art/untitled.blend differ diff --git a/examples/relations/assets/levels/World.glb b/examples/relations/assets/levels/World.glb index 2c9c0cc..a85866c 100644 Binary files a/examples/relations/assets/levels/World.glb and b/examples/relations/assets/levels/World.glb differ diff --git a/examples/relations/assets/registry.json b/examples/relations/assets/registry.json index 3f40219..1916754 100644 --- a/examples/relations/assets/registry.json +++ b/examples/relations/assets/registry.json @@ -3609,6 +3609,30 @@ "type": "object", "typeInfo": "Value" }, + "bevy_egui::EguiSettings": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "bevy_egui::EguiSettings", + "properties": { + "default_open_url_target": { + "type": { + "$ref": "#/$defs/core::option::Option" + } + }, + "scale_factor": { + "type": { + "$ref": "#/$defs/f32" + } + } + }, + "required": [ + "scale_factor" + ], + "short_name": "EguiSettings", + "type": "object", + "typeInfo": "Struct" + }, "bevy_gizmos::aabb::AabbGizmoConfigGroup": { "additionalProperties": false, "isComponent": false, @@ -12776,6 +12800,14 @@ "type": "object", "typeInfo": "Value" }, + "core::ops::Range": { + "isComponent": false, + "isResource": false, + "long_name": "core::ops::Range", + "short_name": "Range", + "type": "object", + "typeInfo": "Value" + }, "core::option::Option<(u8, u8)>": { "isComponent": false, "isResource": false, @@ -13490,6 +13522,295 @@ "type": "object", "typeInfo": "Struct" }, + "glam::BVec2": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::BVec2", + "properties": { + "x": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "y": { + "type": { + "$ref": "#/$defs/bool" + } + } + }, + "required": [ + "x", + "y" + ], + "short_name": "BVec2", + "type": "object", + "typeInfo": "Struct" + }, + "glam::BVec3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::BVec3", + "properties": { + "x": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "y": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "z": { + "type": { + "$ref": "#/$defs/bool" + } + } + }, + "required": [ + "x", + "y", + "z" + ], + "short_name": "BVec3", + "type": "object", + "typeInfo": "Struct" + }, + "glam::BVec3A": { + "isComponent": false, + "isResource": false, + "long_name": "glam::BVec3A", + "short_name": "BVec3A", + "type": "object", + "typeInfo": "Value" + }, + "glam::BVec4": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::BVec4", + "properties": { + "w": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "x": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "y": { + "type": { + "$ref": "#/$defs/bool" + } + }, + "z": { + "type": { + "$ref": "#/$defs/bool" + } + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "short_name": "BVec4", + "type": "object", + "typeInfo": "Struct" + }, + "glam::BVec4A": { + "isComponent": false, + "isResource": false, + "long_name": "glam::BVec4A", + "short_name": "BVec4A", + "type": "object", + "typeInfo": "Value" + }, + "glam::DAffine2": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DAffine2", + "properties": { + "matrix2": { + "type": { + "$ref": "#/$defs/glam::DMat2" + } + }, + "translation": { + "type": { + "$ref": "#/$defs/glam::DVec2" + } + } + }, + "required": [ + "matrix2", + "translation" + ], + "short_name": "DAffine2", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DAffine3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DAffine3", + "properties": { + "matrix3": { + "type": { + "$ref": "#/$defs/glam::DMat3" + } + }, + "translation": { + "type": { + "$ref": "#/$defs/glam::DVec3" + } + } + }, + "required": [ + "matrix3", + "translation" + ], + "short_name": "DAffine3", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DMat2": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DMat2", + "properties": { + "x_axis": { + "type": { + "$ref": "#/$defs/glam::DVec2" + } + }, + "y_axis": { + "type": { + "$ref": "#/$defs/glam::DVec2" + } + } + }, + "required": [ + "x_axis", + "y_axis" + ], + "short_name": "DMat2", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DMat3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DMat3", + "properties": { + "x_axis": { + "type": { + "$ref": "#/$defs/glam::DVec3" + } + }, + "y_axis": { + "type": { + "$ref": "#/$defs/glam::DVec3" + } + }, + "z_axis": { + "type": { + "$ref": "#/$defs/glam::DVec3" + } + } + }, + "required": [ + "x_axis", + "y_axis", + "z_axis" + ], + "short_name": "DMat3", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DMat4": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DMat4", + "properties": { + "w_axis": { + "type": { + "$ref": "#/$defs/glam::DVec4" + } + }, + "x_axis": { + "type": { + "$ref": "#/$defs/glam::DVec4" + } + }, + "y_axis": { + "type": { + "$ref": "#/$defs/glam::DVec4" + } + }, + "z_axis": { + "type": { + "$ref": "#/$defs/glam::DVec4" + } + } + }, + "required": [ + "x_axis", + "y_axis", + "z_axis", + "w_axis" + ], + "short_name": "DMat4", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DQuat": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DQuat", + "properties": { + "w": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "x": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "y": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "z": { + "type": { + "$ref": "#/$defs/f64" + } + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "short_name": "DQuat", + "type": "object", + "typeInfo": "Struct" + }, "glam::DVec2": { "additionalProperties": false, "isComponent": false, @@ -13515,6 +13836,74 @@ "type": "object", "typeInfo": "Struct" }, + "glam::DVec3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DVec3", + "properties": { + "x": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "y": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "z": { + "type": { + "$ref": "#/$defs/f64" + } + } + }, + "required": [ + "x", + "y", + "z" + ], + "short_name": "DVec3", + "type": "object", + "typeInfo": "Struct" + }, + "glam::DVec4": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::DVec4", + "properties": { + "w": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "x": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "y": { + "type": { + "$ref": "#/$defs/f64" + } + }, + "z": { + "type": { + "$ref": "#/$defs/f64" + } + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "short_name": "DVec4", + "type": "object", + "typeInfo": "Struct" + }, "glam::IVec2": { "additionalProperties": false, "isComponent": false, @@ -13540,6 +13929,74 @@ "type": "object", "typeInfo": "Struct" }, + "glam::IVec3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::IVec3", + "properties": { + "x": { + "type": { + "$ref": "#/$defs/i32" + } + }, + "y": { + "type": { + "$ref": "#/$defs/i32" + } + }, + "z": { + "type": { + "$ref": "#/$defs/i32" + } + } + }, + "required": [ + "x", + "y", + "z" + ], + "short_name": "IVec3", + "type": "object", + "typeInfo": "Struct" + }, + "glam::IVec4": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::IVec4", + "properties": { + "w": { + "type": { + "$ref": "#/$defs/i32" + } + }, + "x": { + "type": { + "$ref": "#/$defs/i32" + } + }, + "y": { + "type": { + "$ref": "#/$defs/i32" + } + }, + "z": { + "type": { + "$ref": "#/$defs/i32" + } + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "short_name": "IVec4", + "type": "object", + "typeInfo": "Struct" + }, "glam::Mat2": { "additionalProperties": false, "isComponent": false, @@ -13565,6 +14022,37 @@ "type": "object", "typeInfo": "Struct" }, + "glam::Mat3": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::Mat3", + "properties": { + "x_axis": { + "type": { + "$ref": "#/$defs/glam::Vec3" + } + }, + "y_axis": { + "type": { + "$ref": "#/$defs/glam::Vec3" + } + }, + "z_axis": { + "type": { + "$ref": "#/$defs/glam::Vec3" + } + } + }, + "required": [ + "x_axis", + "y_axis", + "z_axis" + ], + "short_name": "Mat3", + "type": "object", + "typeInfo": "Struct" + }, "glam::Mat3A": { "additionalProperties": false, "isComponent": false, @@ -13726,6 +14214,43 @@ "type": "object", "typeInfo": "Struct" }, + "glam::UVec4": { + "additionalProperties": false, + "isComponent": false, + "isResource": false, + "long_name": "glam::UVec4", + "properties": { + "w": { + "type": { + "$ref": "#/$defs/u32" + } + }, + "x": { + "type": { + "$ref": "#/$defs/u32" + } + }, + "y": { + "type": { + "$ref": "#/$defs/u32" + } + }, + "z": { + "type": { + "$ref": "#/$defs/u32" + } + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "short_name": "UVec4", + "type": "object", + "typeInfo": "Struct" + }, "glam::Vec2": { "additionalProperties": false, "isComponent": false, diff --git a/examples/relations/src/main.rs b/examples/relations/src/main.rs index 0e1c81e..fde8834 100644 --- a/examples/relations/src/main.rs +++ b/examples/relations/src/main.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use bevy_inspector_egui::quick::WorldInspectorPlugin; use blenvy::{BlenvyPlugin, BlueprintInfo, GameWorldTag, HideUntilReady, SpawnBlueprint}; #[derive(Component, Reflect, Debug)] @@ -17,6 +18,7 @@ fn main() { .add_plugins(( DefaultPlugins.set(AssetPlugin::default()), BlenvyPlugin::default(), + WorldInspectorPlugin::new(), )) .register_type::() .register_type::() diff --git a/tools/blenvy/add_ons/bevy_components/registry/registry.py b/tools/blenvy/add_ons/bevy_components/registry/registry.py index 0d3b210..59941e5 100644 --- a/tools/blenvy/add_ons/bevy_components/registry/registry.py +++ b/tools/blenvy/add_ons/bevy_components/registry/registry.py @@ -26,7 +26,7 @@ def property_group_from_infos(property_group_name, property_group_parameters): return (property_group_pointer, property_group_class) def is_entity_poll(self, object): - return bpy.context.scene in object.users_scene # TODO: only select `object.type`s that get converted to entities and maybe something against other collection(instances)? + return True # TODO: only select `object.type`s that get converted to entities and maybe something against other collection(instances)? # this is where we store the information for all available components class ComponentsRegistry(PropertyGroup):