refactor: get models fully working

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2024-12-19 16:34:51 +01:00
parent e3321c54fb
commit 19367927a8
8 changed files with 272 additions and 81 deletions

View File

@@ -26,6 +26,14 @@ path = "src/main.rs"
profile_tokio = ["dep:console-subscriber", "tokio/tracing"] profile_tokio = ["dep:console-subscriber", "tokio/tracing"]
profile_app = ["dep:tracing-tracy"] profile_app = ["dep:tracing-tracy"]
# Enable a small amount of optimization in the dev profile.
[profile.dev]
opt-level = 1
# Enable a large amount of optimization in the dev profile for dependencies.
[profile.dev.package."*"]
opt-level = 3
[package.metadata.appimage] [package.metadata.appimage]
auto_link = true auto_link = true
auto_link_exclude_list = [ auto_link_exclude_list = [

View File

@@ -1,3 +1,5 @@
use std::ops::Deref;
use bevy::{ use bevy::{
app::MainScheduleOrder, app::MainScheduleOrder,
ecs::schedule::{ExecutorKind, ScheduleLabel}, ecs::schedule::{ExecutorKind, ScheduleLabel},
@@ -6,13 +8,25 @@ use bevy::{
}; };
use bevy_mod_openxr::session::OxrSession; use bevy_mod_openxr::session::OxrSession;
use bevy_mod_xr::session::{session_available, XrFirst, XrSessionCreated}; use bevy_mod_xr::session::{session_available, XrFirst, XrSessionCreated};
use once_cell::sync::OnceCell;
use openxr::ReferenceSpaceType; use openxr::ReferenceSpaceType;
use stardust_xr::values::color::{color_space::LinearRgb, AlphaColor, Rgb}; use stardust_xr::values::color::{color_space::LinearRgb, AlphaColor, Rgb};
use crate::objects::Inputs;
pub struct StardustBevyPlugin; pub struct StardustBevyPlugin;
pub static DESTROY_ENTITY: DestroySender = DestroySender(OnceCell::new());
pub struct DestroySender(OnceCell<crossbeam_channel::Sender<Entity>>);
impl Deref for DestroySender {
type Target = crossbeam_channel::Sender<Entity>;
fn deref(&self) -> &Self::Target {
self.0.get().unwrap()
}
}
#[derive(Resource, Deref)]
struct DestroyEntityReader(crossbeam_channel::Receiver<Entity>);
#[derive(Resource, Deref)] #[derive(Resource, Deref)]
pub struct DbusConnection(pub zbus::Connection); pub struct DbusConnection(pub zbus::Connection);
@@ -22,6 +36,12 @@ pub struct InputUpdate;
pub struct StardustFirst; pub struct StardustFirst;
impl Plugin for StardustBevyPlugin { impl Plugin for StardustBevyPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let (tx, rx) = crossbeam_channel::unbounded();
DESTROY_ENTITY
.0
.set(tx)
.expect("unable to set destroy entity sender, yell at schmarni pls thx");
app.insert_resource(DestroyEntityReader(rx));
app.init_schedule(StardustExtract); app.init_schedule(StardustExtract);
let labels = &mut app.world_mut().resource_mut::<MainScheduleOrder>().labels; let labels = &mut app.world_mut().resource_mut::<MainScheduleOrder>().labels;
info!("test: {labels:?}"); info!("test: {labels:?}");
@@ -42,9 +62,24 @@ impl Plugin for StardustBevyPlugin {
panic!("first schedule was not XrFirst!"); panic!("first schedule was not XrFirst!");
} }
labels.insert(0, (StardustFirst).intern()); labels.insert(0, (StardustFirst).intern());
app.add_systems(First, yeet_entities);
} }
} }
fn yeet_entities(
mut cmds: Commands,
query: Query<Entity, With<TemporaryEntity>>,
reader: Res<DestroyEntityReader>,
) {
query
.iter()
.for_each(|e| cmds.entity(e).despawn_recursive());
reader
.0
.try_iter()
.for_each(|e| cmds.entity(e).despawn_recursive());
}
fn make_view_space(mut cmds: Commands, session: Res<OxrSession>) { fn make_view_space(mut cmds: Commands, session: Res<OxrSession>) {
// idk what errors this function returns // idk what errors this function returns
let view_space = session let view_space = session
@@ -58,17 +93,6 @@ fn make_view_space(mut cmds: Commands, session: Res<OxrSession>) {
fn spawn_camera(mut cmds: Commands) { fn spawn_camera(mut cmds: Commands) {
cmds.spawn((Camera3d::default(), ViewLocation)); cmds.spawn((Camera3d::default(), ViewLocation));
} }
#[derive(Deref, DerefMut, Resource)]
pub struct BevyToStardustEvents(pub Vec<BevyToStardustEvent>);
pub enum BevyToStardustEvent {
InputsCreated(Inputs),
SessionDestroyed,
SessionEnding,
SessionCreated(OxrSession),
MainSessionVisible(bool),
}
pub trait StardustAabb3dExt { pub trait StardustAabb3dExt {
fn grown_box(&self, aabb: &Self, opt_box_transform: Option<impl Into<Mat4>>) -> Self; fn grown_box(&self, aabb: &Self, opt_box_transform: Option<impl Into<Mat4>>) -> Self;
fn grown_point(&self, pt: impl Into<Vec3>) -> Self; fn grown_point(&self, pt: impl Into<Vec3>) -> Self;

View File

@@ -12,15 +12,16 @@ use crate::core::destroy_queue;
// use crate::nodes::items::camera; // use crate::nodes::items::camera;
use crate::nodes::{audio, drawable, input}; use crate::nodes::{audio, drawable, input};
use bevy::app::{App, PluginGroup, PluginsState, Startup, Update}; use bevy::app::{App, PluginGroup, PluginsState, PostUpdate, Startup, Update};
use bevy::asset::{AssetServer, Handle}; use bevy::asset::{AssetServer, Handle};
use bevy::color::Color;
use bevy::core_pipeline::Skybox; use bevy::core_pipeline::Skybox;
use bevy::image::Image; use bevy::image::Image;
use bevy::log::LogPlugin; use bevy::log::LogPlugin;
use bevy::pbr::StandardMaterial; use bevy::pbr::StandardMaterial;
use bevy::prelude::{ use bevy::prelude::{
on_event, resource_added, Camera3d, ClearColor, Commands, Entity, EventReader, on_event, resource_added, Camera3d, ClearColor, Commands, Entity, EventReader,
IntoSystemConfigs, Local, Query, Res, Resource, With, World, IntoSystemConfigs, Local, Query, Res, ResMut, Resource, Transform, With, World,
}; };
use bevy::render::pipelined_rendering::PipelinedRenderingPlugin; use bevy::render::pipelined_rendering::PipelinedRenderingPlugin;
use bevy::time::Time; use bevy::time::Time;
@@ -34,9 +35,11 @@ use bevy_mod_openxr::init::{should_run_frame_loop, OxrInitPlugin};
use bevy_mod_openxr::render::{update_cameras, OxrRenderPlugin}; use bevy_mod_openxr::render::{update_cameras, OxrRenderPlugin};
use bevy_mod_openxr::resources::{OxrFrameState, OxrFrameWaiter, OxrGraphicsInfo}; use bevy_mod_openxr::resources::{OxrFrameState, OxrFrameWaiter, OxrGraphicsInfo};
use bevy_mod_openxr::session::OxrSession; use bevy_mod_openxr::session::OxrSession;
use bevy_mod_openxr::spaces::OxrSpaceExt;
use bevy_mod_openxr::types::{AppInfo, Version}; use bevy_mod_openxr::types::{AppInfo, Version};
use bevy_mod_openxr::{add_xr_plugins, openxr_session_running}; use bevy_mod_openxr::{add_xr_plugins, openxr_session_running};
use bevy_mod_xr::session::{XrFirst, XrSessionCreated, XrSessionPlugin}; use bevy_mod_xr::session::{XrFirst, XrPreDestroySession, XrSessionCreated, XrSessionPlugin};
use bevy_mod_xr::spaces::XrPrimaryReferenceSpace;
use bevy_plugin::{DbusConnection, InputUpdate, StardustBevyPlugin, StardustFirst}; use bevy_plugin::{DbusConnection, InputUpdate, StardustBevyPlugin, StardustFirst};
use clap::Parser; use clap::Parser;
use core::client::Client; use core::client::Client;
@@ -47,6 +50,7 @@ use nodes::drawable::lines::BevyLinesPlugin;
use nodes::drawable::model::StardustModelPlugin; use nodes::drawable::model::StardustModelPlugin;
use nodes::drawable::text::StardustTextPlugin; use nodes::drawable::text::StardustTextPlugin;
use objects::input::sk_controller::StardustControllerPlugin; use objects::input::sk_controller::StardustControllerPlugin;
use objects::input::sk_hand::StardustHandPlugin;
use objects::ServerObjects; use objects::ServerObjects;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use openxr::OverlaySessionCreateFlagsEXTX; use openxr::OverlaySessionCreateFlagsEXTX;
@@ -260,7 +264,7 @@ fn stereokit_loop(
exts exts
}, },
blend_modes: Some(vec![ blend_modes: Some(vec![
// openxr::EnvironmentBlendMode::ALPHA_BLEND, openxr::EnvironmentBlendMode::ALPHA_BLEND,
openxr::EnvironmentBlendMode::OPAQUE, openxr::EnvironmentBlendMode::OPAQUE,
]), ]),
synchronous_pipeline_compilation: false, synchronous_pipeline_compilation: false,
@@ -283,6 +287,7 @@ fn stereokit_loop(
bevy_app.add_plugins(( bevy_app.add_plugins((
BevyLinesPlugin, BevyLinesPlugin,
StardustModelPlugin, StardustModelPlugin,
StardustHandPlugin,
StardustTextPlugin, StardustTextPlugin,
StardustSoundPlugin, StardustSoundPlugin,
StardustControllerPlugin, StardustControllerPlugin,
@@ -326,10 +331,36 @@ fn stereokit_loop(
cams.iter().for_each(|e| { cams.iter().for_each(|e| {
cmds.entity(e).remove::<Skybox>(); cmds.entity(e).remove::<Skybox>();
}); });
cmds.insert_resource(ClearColor(Color::NONE));
} }
*last_hidden = env_hidden; *last_hidden = env_hidden;
} }
bevy_app.add_systems(XrSessionCreated, update_background); bevy_app.add_systems(XrSessionCreated, update_background);
bevy_app.add_systems(
PostUpdate,
(|mut objects: ResMut<ServerObjects>,
ref_space: Res<XrPrimaryReferenceSpace>,
session: Res<OxrSession>| {
objects
.ref_space
.replace(unsafe { ref_space.as_openxr_space(&session) });
objects.view_space.replace(
session
.deref()
.deref()
.create_reference_space(
openxr::ReferenceSpaceType::VIEW,
openxr::Posef::IDENTITY,
)
.unwrap(),
);
})
.run_if(on_event::<bevy_mod_xr::session::XrSessionCreatedEvent>),
);
bevy_app.add_systems(XrPreDestroySession, |mut objetcs: ResMut<ServerObjects>| {
objetcs.ref_space = None;
objetcs.view_space = None;
});
bevy_app.add_systems( bevy_app.add_systems(
Update, Update,
update_background.run_if(on_event::<OxrOverlaySessionEvent>), update_background.run_if(on_event::<OxrOverlaySessionEvent>),

View File

@@ -163,8 +163,18 @@ impl Drop for Sound {
} }
} }
<<<<<<< HEAD
=======
<<<<<<< HEAD
create_interface!(AudioInterface);
>>>>>>> 0ec4c0f (refactor: get models fully working)
struct AudioInterface; struct AudioInterface;
impl InterfaceAspect for AudioInterface { impl InterfaceAspect for AudioInterface {
=======
pub fn update() {}
impl InterfaceAspect for Interface {
>>>>>>> 2f5c92d (refactor: get models fully working)
#[doc = "Create a sound node. WAV and MP3 are supported."] #[doc = "Create a sound node. WAV and MP3 are supported."]
fn create_sound( fn create_sound(
_node: Arc<Node>, _node: Arc<Node>,

View File

@@ -169,7 +169,7 @@ pub fn draw_all(
let mat_handle = materials.add(material); let mat_handle = materials.add(material);
for lines in LINES_REGISTRY.get_valid_contents() { for lines in LINES_REGISTRY.get_valid_contents() {
if let Some(node) = lines.space.node() { if let Some(node) = lines.space.node() {
if node.enabled() { if node.enabled() && !lines.data.lock().is_empty() {
// Does this rebuild the mesh every frame? yes, is this problematic? probably, // Does this rebuild the mesh every frame? yes, is this problematic? probably,
// would a shader work better? yes, do i care? not right now // would a shader work better? yes, do i care? not right now
let mut mesh = Mesh::new( let mut mesh = Mesh::new(

View File

@@ -1,6 +1,6 @@
use super::{MaterialParameter, ModelAspect, ModelPartAspect, MODEL_PART_ASPECT_ALIAS_INFO}; use super::{MaterialParameter, ModelAspect, ModelPartAspect, MODEL_PART_ASPECT_ALIAS_INFO};
use crate::bail; use crate::bail;
use crate::bevy_plugin::MainWorldEntity; use crate::bevy_plugin::{MainWorldEntity, DESTROY_ENTITY};
use crate::core::client::Client; use crate::core::client::Client;
use crate::core::error::Result; use crate::core::error::Result;
use crate::core::registry::Registry; use crate::core::registry::Registry;
@@ -8,8 +8,8 @@ use crate::core::resource::get_resource_file;
use crate::nodes::alias::{Alias, AliasList}; use crate::nodes::alias::{Alias, AliasList};
use crate::nodes::spatial::Spatial; use crate::nodes::spatial::Spatial;
use crate::nodes::Node; use crate::nodes::Node;
use crate::DefaultMaterial; use crate::{DefaultMaterial, TOKIO};
use bevy::app::{Plugin, PostUpdate}; use bevy::app::{Plugin, PostUpdate, PreUpdate, Update};
use bevy::asset::{AssetServer, Assets}; use bevy::asset::{AssetServer, Assets};
use bevy::color::{Alpha, Color, LinearRgba, Srgba}; use bevy::color::{Alpha, Color, LinearRgba, Srgba};
use bevy::core::Name; use bevy::core::Name;
@@ -18,23 +18,27 @@ use bevy::math::bounding::Aabb3d;
use bevy::pbr::MeshMaterial3d; use bevy::pbr::MeshMaterial3d;
use bevy::prelude::AlphaMode; use bevy::prelude::AlphaMode;
use bevy::prelude::{ use bevy::prelude::{
Children, Commands, Component, Deref, Entity, HierarchyQueryExt, Mesh3d, Parent, Query, Res, BuildChildrenTransformExt, Children, Commands, Component, Deref, Entity, Has,
ResMut, Resource, Transform, Visibility, With, Without, HierarchyQueryExt, Mesh3d, Parent, Query, Res, ResMut, Resource, Transform, Visibility, With,
Without,
}; };
use bevy::reflect::{GetField, PartialReflect, Reflect}; use bevy::reflect::{GetField, PartialReflect, Reflect};
use bevy::render::primitives::Aabb; use bevy::render::primitives::Aabb;
use bevy::scene::SceneRoot; use bevy::scene::SceneRoot;
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use glam::{Vec2, Vec3}; use bevy::tasks::futures_lite::FutureExt;
use glam::{Mat4, Vec2, Vec3};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use stardust_xr::values::ResourceID; use stardust_xr::values::ResourceID;
use tracing::{error, warn}; use tokio::sync::Notify;
use tracing::{error, info, warn};
use std::ffi::OsStr; use std::ffi::OsStr;
use std::future::IntoFuture;
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf; use std::path::{self, PathBuf};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
static MODEL_REGISTRY: Registry<Model> = Registry::new(); static MODEL_REGISTRY: Registry<Model> = Registry::new();
@@ -132,7 +136,7 @@ impl MaterialParameter {
} }
pub struct ModelPart { pub struct ModelPart {
entity: MainWorldEntity, entity: OnceCell<Entity>,
path: String, path: String,
space: Arc<Spatial>, space: Arc<Spatial>,
model: Weak<Model>, model: Weak<Model>,
@@ -142,7 +146,7 @@ pub struct ModelPart {
} }
#[derive(Component, Clone)] #[derive(Component, Clone)]
pub struct StardustModel(Arc<Model>); pub struct StardustModel(Weak<Model>);
#[derive(Component, Clone)] #[derive(Component, Clone)]
pub struct UnprocessedModel; pub struct UnprocessedModel;
pub struct StardustModelPlugin; pub struct StardustModelPlugin;
@@ -151,7 +155,10 @@ impl Plugin for StardustModelPlugin {
let (tx, rx) = crossbeam_channel::unbounded(); let (tx, rx) = crossbeam_channel::unbounded();
LOAD_MODEL_SENDER.set(tx); LOAD_MODEL_SENDER.set(tx);
app.insert_resource(LoadModelReader(rx)); app.insert_resource(LoadModelReader(rx));
app.add_systems(PostUpdate, create_model_parts_for_loaded_models); app.add_systems(Update, create_model_parts_for_loaded_models);
app.add_systems(PreUpdate, load_models);
app.add_systems(PostUpdate, update_models);
app.add_systems(PostUpdate, update_model_parts);
} }
} }
static LOAD_MODEL_SENDER: OnceCell<crossbeam_channel::Sender<(PathBuf, Arc<Model>)>> = static LOAD_MODEL_SENDER: OnceCell<crossbeam_channel::Sender<(PathBuf, Arc<Model>)>> =
@@ -161,8 +168,11 @@ struct LoadModelReader(crossbeam_channel::Receiver<(PathBuf, Arc<Model>)>);
fn update_models(mut query: Query<(&StardustModel, &mut Visibility, &mut Transform)>) { fn update_models(mut query: Query<(&StardustModel, &mut Visibility, &mut Transform)>) {
for (model, mut vis, mut transform) in query.iter_mut() { for (model, mut vis, mut transform) in query.iter_mut() {
*transform = Transform::from_matrix(model.0.space.global_transform()); let Some(model) = model.0.upgrade() else {
if let Some(node) = model.0.space.node() { continue;
};
*transform = Transform::from_matrix(model.space.global_transform());
if let Some(node) = model.space.node() {
*vis = match node.enabled() { *vis = match node.enabled() {
true => Visibility::Inherited, true => Visibility::Inherited,
false => Visibility::Hidden, false => Visibility::Hidden,
@@ -177,7 +187,7 @@ fn load_models(rx: Res<LoadModelReader>, mut cmds: Commands, asset_server: Res<A
let entity = cmds let entity = cmds
.spawn(( .spawn((
SceneRoot(handle), SceneRoot(handle),
StardustModel(model.clone()), StardustModel(Arc::downgrade(&model)),
UnprocessedModel, UnprocessedModel,
)) ))
.id(); .id();
@@ -186,29 +196,32 @@ fn load_models(rx: Res<LoadModelReader>, mut cmds: Commands, asset_server: Res<A
} }
fn update_model_parts( fn update_model_parts(
models: Query<&StardustModel>, models: Query<&StardustModel, Without<UnprocessedModel>>,
mut mats: ResMut<Assets<DefaultMaterial>>, mut mats: ResMut<Assets<DefaultMaterial>>,
mut part_query: Query<( mut part_query: Query<(
&mut Transform, &mut Transform,
&mut MeshMaterial3d<DefaultMaterial>, &mut MeshMaterial3d<DefaultMaterial>,
&mut Mesh3d, Has<Parent>,
)>, )>,
mut cmds: Commands,
) { ) {
for model in &models { for model in &models {
let model = &model.0; let Some(model) = model.0.upgrade() else {
continue;
};
for part in model.parts.lock().iter() { for part in model.parts.lock().iter() {
let Ok((mut transform, mut mat, mut _mesh)) = part_query.get_mut(*part.entity) else { let Some((entity, (mut transform, mut mat, has_parent))) = part
.entity
.get()
.and_then(|e| Some((*e, part_query.get_mut(*e).ok()?)))
else {
continue; continue;
}; };
*transform = Transform::from_matrix(Spatial::space_to_space_matrix( if has_parent {
Some(&part.space), cmds.entity(entity).remove_parent_in_place();
Some(&model.space),
));
if let Some(material_replacement) = part.pending_material_replacement.lock().take() {
let material = material_replacement.deref().clone();
let mat_handle = mats.add(material);
mat.0 = mat_handle;
} }
*transform = Transform::from_matrix(part.space.global_transform());
// todo: find all materials with identical parameters and batch them into 1 material again // todo: find all materials with identical parameters and batch them into 1 material again
'mat_params: { 'mat_params: {
let mut material_parameters = part.pending_material_parameters.lock(); let mut material_parameters = part.pending_material_parameters.lock();
@@ -237,36 +250,41 @@ fn update_model_parts(
fn get_path( fn get_path(
entity: Entity, entity: Entity,
query: &Query<(Entity, Option<&Parent>, &Transform, &Name, &Aabb), Without<SceneRoot>>, query: &Query<(&Parent, &Name), Without<SceneRoot>>,
) -> Option<String> { mut in_vec: Vec<String>,
let (_, parent, _, name, _) = query.get(entity).ok()?; ) -> Vec<String> {
let next = parent.and_then(|p| get_path(p.get(), query)); let Ok((parent, name)) = query.get(entity) else {
match next { return in_vec;
Some(next) => Some(format!("{name}/{next}")), };
None => Some(name.to_string()), in_vec.push(name.to_string());
} get_path(parent.get(), query, in_vec)
} }
fn create_model_parts_for_loaded_models( fn create_model_parts_for_loaded_models(
query: Query<(Entity, &StardustModel), With<UnprocessedModel>>, query: Query<(Entity, &StardustModel), (With<UnprocessedModel>, With<Children>)>,
children: Query<&Children>, children: Query<&Children>,
gltf_model_parts: Query< gltf_model_parts: Query<(Entity, &Transform, &Aabb), Without<SceneRoot>>,
(Entity, Option<&Parent>, &Transform, &Name, &Aabb), name_query: Query<(&Parent, &Name), Without<SceneRoot>>,
Without<SceneRoot>,
>,
mut cmds: Commands, mut cmds: Commands,
) { ) {
for (entity, model) in &query { for (entity, model) in &query {
let model = &model.0; info!("creating parts!");
let mut parts = model.parts.lock(); let Some(model) = model.0.upgrade() else {
cmds.entity(entity).remove::<UnprocessedModel>(); continue;
for (entity, parent, transform, name, aabb) in children };
.iter_descendants(entity) // let mut parts = model.parts.lock();
let mut parts = Vec::<Arc<ModelPart>>::new();
for (entity, transform, aabb) in children
.iter_descendants_depth_first(entity)
.filter_map(|e| gltf_model_parts.get(e).ok()) .filter_map(|e| gltf_model_parts.get(e).ok())
{ {
let parent_part = parent let mut path_parts = get_path(entity, &name_query, Vec::new());
.and_then(|e| gltf_model_parts.get(e.get()).ok()) path_parts.remove(0);
.and_then(|(e, _, _, _, _)| parts.iter().find(|v| v.entity.0 == e)); path_parts.reverse();
let part_path = path_parts.join("/");
path_parts.pop();
let parent_path = path_parts.join("/");
let parent_part = parts.iter().find(|v| v.path == parent_path);
let Some(stardust_model_part) = model.space.node() else { let Some(stardust_model_part) = model.space.node() else {
continue; continue;
@@ -274,22 +292,51 @@ fn create_model_parts_for_loaded_models(
let Some(client) = stardust_model_part.get_client() else { let Some(client) = stardust_model_part.get_client() else {
continue; continue;
}; };
let part_path = get_path(entity, &gltf_model_parts).unwrap_or_else(|| name.to_string()); let model_part = model
.parts
.lock()
.iter()
.find(|v| v.path == part_path)
.cloned()
.map(|v| {
*v.space.bounding_box_calc.lock() = Aabb3d::new(aabb.center, aabb.half_extents);
if v.entity.set(entity).is_err() {
error!(
"trying to set entity for already init model part?!
please yell at schmarni if you see this"
);
};
let node = client.scenegraph.add_node(Node::generate(&client, false)); if let Err(err) = v.space.set_spatial_parent(Some(
let spatial_parent = parent_part parent_part.map(|n| &n.space).unwrap_or_else(|| {
.map(|n| n.space.clone()) info!("model is spatial parent");
.unwrap_or_else(|| model.space.clone()); &model.space
}),
)) {
error!("error setting spatial parent for existing model part: {err}");
}
v.space.set_local_transform(transform.compute_matrix());
info!("not fresh {}", &v.path);
v
})
.unwrap_or_else(|| {
let node = client.scenegraph.add_node(Node::generate(&client, false));
let spatial_parent =
parent_part.map(|n| n.space.clone()).unwrap_or_else(|| {
info!("model is spatial parent");
model.space.clone()
});
let space = Spatial::add_to( let space = Spatial::add_to(
&node, &node,
Some(spatial_parent), Some(spatial_parent),
transform.compute_matrix(), transform.compute_matrix(),
false, false,
); );
*space.bounding_box_calc.lock() = Aabb3d::new(aabb.center, aabb.half_extents); *space.bounding_box_calc.lock() = Aabb3d::new(aabb.center, aabb.half_extents);
<<<<<<< HEAD
let model_part = Arc::new(ModelPart { let model_part = Arc::new(ModelPart {
entity: MainWorldEntity(entity), entity: MainWorldEntity(entity),
path: part_path, path: part_path,
@@ -300,8 +347,26 @@ fn create_model_parts_for_loaded_models(
aliases: AliasList::default(), aliases: AliasList::default(),
}); });
node.add_aspect_raw(model_part.clone()); node.add_aspect_raw(model_part.clone());
=======
let model_part = Arc::new(ModelPart {
entity: OnceCell::from(entity),
path: part_path,
space,
model: Arc::downgrade(&model),
pending_material_parameters: Mutex::new(FxHashMap::default()),
pending_material_replacement: Mutex::new(None),
aliases: AliasList::default(),
});
node.add_aspect_raw(model_part.clone());
info!("fresh {}", &model_part.path);
model_part
});
>>>>>>> 0ec4c0f (refactor: get models fully working)
parts.push(model_part.clone()); parts.push(model_part.clone());
} }
cmds.entity(entity).remove::<UnprocessedModel>();
info!("created parts! {}", parts.len());
*model.parts.lock() = parts;
} }
} }
@@ -375,11 +440,43 @@ impl ModelAspect for Model {
part_path: String, part_path: String,
) -> Result<()> { ) -> Result<()> {
let model = node.get_aspect::<Model>()?; let model = node.get_aspect::<Model>()?;
<<<<<<< HEAD
let parts = model.parts.lock(); let parts = model.parts.lock();
let Some(part) = parts.iter().find(|p| p.path == part_path) else { let Some(part) = parts.iter().find(|p| p.path == part_path) else {
let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>(); let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>();
bail!("Couldn't find model part at path {part_path}, all available paths: {paths:?}",); bail!("Couldn't find model part at path {part_path}, all available paths: {paths:?}",);
}; };
=======
let mut parts = model.parts.lock();
let part =
parts
.iter()
.find(|p| p.path == part_path)
.cloned()
.unwrap_or_else(|| {
let paths = parts.iter().map(|p| &p.path).collect::<Vec<_>>();
error!("Couldn't find model part at path {part_path}, all available paths: {paths:?}");
let node = calling_client
.scenegraph
.add_node(Node::generate(&calling_client, false));
let space = Spatial::add_to(&node, None, Mat4::IDENTITY, false);
let model_part = Arc::new(ModelPart {
entity: OnceCell::new(),
path: part_path,
space,
model: Arc::downgrade(&model),
pending_material_parameters: Mutex::new(FxHashMap::default()),
pending_material_replacement: Mutex::new(None),
aliases: AliasList::default(),
});
node.add_aspect_raw(model_part.clone());
parts.push(model_part.clone());
model_part
});
>>>>>>> 0ec4c0f (refactor: get models fully working)
Alias::create_with_id( Alias::create_with_id(
&part.space.node().unwrap(), &part.space.node().unwrap(),
&calling_client, &calling_client,
@@ -390,8 +487,18 @@ impl ModelAspect for Model {
Ok(()) Ok(())
} }
} }
impl Drop for ModelPart {
fn drop(&mut self) {
if let Some(e) = self.entity.get() {
_ = DESTROY_ENTITY.send(*e);
}
}
}
impl Drop for Model { impl Drop for Model {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(e) = self.entity.get() {
_ = DESTROY_ENTITY.send(*e);
}
MODEL_REGISTRY.remove(self); MODEL_REGISTRY.remove(self);
} }
} }

View File

@@ -10,6 +10,7 @@ use crate::nodes::{
}; };
use crate::objects::{ObjectHandle, SpatialRef}; use crate::objects::{ObjectHandle, SpatialRef};
use crate::DefaultMaterial; use crate::DefaultMaterial;
use bevy::app::{Plugin, PostUpdate};
use bevy::asset::{AssetServer, Assets, Handle}; use bevy::asset::{AssetServer, Assets, Handle};
use bevy::prelude::{Commands, Component, Entity, Query, Res, ResMut}; use bevy::prelude::{Commands, Component, Entity, Query, Res, ResMut};
use bevy_mod_openxr::helper_traits::{ToQuat, ToVec3}; use bevy_mod_openxr::helper_traits::{ToQuat, ToVec3};
@@ -17,6 +18,7 @@ use bevy_mod_openxr::resources::OxrFrameState;
use bevy_mod_openxr::session::OxrSession; use bevy_mod_openxr::session::OxrSession;
use bevy_mod_openxr::spaces::OxrSpaceLocationFlags; use bevy_mod_openxr::spaces::OxrSpaceLocationFlags;
use bevy_mod_xr::hands::{HandBone, HandSide}; use bevy_mod_xr::hands::{HandBone, HandSide};
use bevy_mod_xr::session::XrSessionCreated;
use bevy_mod_xr::spaces::XrPrimaryReferenceSpace; use bevy_mod_xr::spaces::XrPrimaryReferenceSpace;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{Mat4, Quat, Vec3}; use glam::{Mat4, Quat, Vec3};
@@ -34,6 +36,14 @@ fn update_joint(joint: &mut Joint, oxr_joint: openxr::HandJointLocation) {
} }
} }
pub struct StardustHandPlugin;
impl Plugin for StardustHandPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_systems(XrSessionCreated, create_hands);
app.add_systems(PostUpdate, update_hands);
}
}
fn update_hands( fn update_hands(
mut mats: ResMut<Assets<DefaultMaterial>>, mut mats: ResMut<Assets<DefaultMaterial>>,
mut query: Query<&mut SkHand>, mut query: Query<&mut SkHand>,
@@ -76,7 +86,7 @@ fn update_hands(
update_joint(&mut finger.distal, joints[finger_index + 3]); update_joint(&mut finger.distal, joints[finger_index + 3]);
update_joint(&mut finger.intermediate, joints[finger_index + 2]); update_joint(&mut finger.intermediate, joints[finger_index + 2]);
update_joint(&mut finger.proximal, joints[finger_index + 1]); update_joint(&mut finger.proximal, joints[finger_index + 1]);
update_joint(&mut finger.metacarpal, joints[finger_index + 0]); update_joint(&mut finger.metacarpal, joints[finger_index]);
// Why? // Why?
finger.tip.radius = 0.0; finger.tip.radius = 0.0;
} }

View File

@@ -20,6 +20,7 @@ use openxr::SpaceLocationFlags;
use play_space::PlaySpaceBounds; use play_space::PlaySpaceBounds;
use stardust_xr::schemas::dbus::object_registry::ObjectRegistry; use stardust_xr::schemas::dbus::object_registry::ObjectRegistry;
use std::{marker::PhantomData, sync::Arc}; use std::{marker::PhantomData, sync::Arc};
use tracing::info;
use zbus::{interface, object_server::Interface, zvariant::OwnedObjectPath, Connection}; use zbus::{interface, object_server::Interface, zvariant::OwnedObjectPath, Connection};
pub mod input; pub mod input;
@@ -54,8 +55,8 @@ pub struct ServerObjects {
hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>), hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>),
play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>, play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>,
inputs: Option<Inputs>, inputs: Option<Inputs>,
view_space: Option<openxr::Space>, pub view_space: Option<openxr::Space>,
ref_space: Option<openxr::Space>, pub ref_space: Option<openxr::Space>,
} }
pub struct TrackingRefs { pub struct TrackingRefs {
@@ -125,7 +126,7 @@ impl ServerObjects {
} }
} }
pub fn update( pub(crate) fn update(
&mut self, &mut self,
session: Option<&openxr::Session<openxr::AnyGraphics>>, session: Option<&openxr::Session<openxr::AnyGraphics>>,
time: Option<openxr::Time>, time: Option<openxr::Time>,