refactor: begin conversion to bevy, do proper frame wait

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2024-12-15 00:01:32 +01:00
parent 8708f240b4
commit 9f4420f6c9
11 changed files with 680 additions and 4303 deletions

3893
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,11 +21,10 @@ name = "stardust-xr-server"
path = "src/main.rs" path = "src/main.rs"
[features] [features]
default = ["wayland"] # default = ["wayland"]
wayland = ["dep:smithay", "dep:wayland-scanner", "dep:wayland-backend"] # wayland = ["dep:smithay"]
profile_tokio = ["dep:console-subscriber", "tokio/tracing"] profile_tokio = ["dep:console-subscriber", "tokio/tracing"]
profile_app = ["dep:tracing-tracy"] profile_app = ["dep:tracing-tracy"]
local_deps = ["stereokit-rust/force-local-deps"]
[package.metadata.appimage] [package.metadata.appimage]
auto_link = true auto_link = true
@@ -38,22 +37,12 @@ auto_link_exclude_list = [
"libEGL*", "libEGL*",
] ]
[profile.dev.package."*"]
opt-level = 3
debug = true
strip = false
debug-assertions = true
overflow-checks = true
[profile.release]
opt-level = 3
debug = "line-tables-only"
strip = true
debug-assertions = true
overflow-checks = false
lto = "thin"
[dependencies] [dependencies]
bevy = { version = "0.15", features = ["wayland"] }
bevy_mod_xr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }
bevy_mod_openxr = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }
bevy_xr_utils = { git = "https://github.com/Schmarni-Dev/bevy_openxr", branch = "0.15rc" }
openxr = "0.19"
# small utility thingys # small utility thingys
once_cell = "1.19.0" once_cell = "1.19.0"
nanoid = "0.4.0" nanoid = "0.4.0"
@@ -92,28 +81,24 @@ input-event-codes = "6.2.0"
zbus = { version = "5.0.0", default-features = false, features = ["tokio"] } zbus = { version = "5.0.0", default-features = false, features = ["tokio"] }
directories = "5.0.1" directories = "5.0.1"
xkbcommon-rs = "0.1.0" xkbcommon-rs = "0.1.0"
# wayland
wayland-backend = { version = "0.3.7", optional = true, default-features = false }
wayland-scanner = { version = "0.31.4", optional = true }
thiserror = "2.0.9" thiserror = "2.0.9"
# wayland-scanner = "0.31.2"
[dependencies.smithay] # wayland-backend = "0.3.4"
# git = "https://github.com/technobaboo/smithay.git" #
# git = "https://github.com/colinmarc/smithay.git" # [dependencies.smithay]
git = "https://github.com/smithay/smithay.git" # # git = "https://github.com/technobaboo/smithay.git"
# path = "../smithay" # # git = "https://github.com/colinmarc/smithay.git"
default-features = false # git = "https://github.com/smithay/smithay.git"
features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"] # # path = "../smithay"
optional = true # default-features = false
# features = [
# "desktop",
[dependencies.stereokit-rust] # "backend_drm",
# path = "../StereoKit-rust" # "backend_egl",
git = "https://github.com/mvvvv/StereoKit-rust.git" # "renderer_gl",
# git = "https://github.com/technobaboo/StereoKit-rust.git" # "wayland_frontend",
features = ["no-event-loop"] # ]
default-features = false # optional = true
[dependencies.stardust-xr] [dependencies.stardust-xr]
workspace = true workspace = true

58
src/bevy_plugin.rs Normal file
View File

@@ -0,0 +1,58 @@
use std::ops::Deref as _;
use bevy::{
app::MainScheduleOrder, asset::embedded_asset, ecs::schedule::ScheduleLabel, prelude::*,
};
use bevy_mod_openxr::session::OxrSession;
use bevy_mod_xr::session::{session_available, XrSessionCreated};
use openxr::ReferenceSpaceType;
use crate::objects::Inputs;
pub struct StardustBevyPlugin;
impl Plugin for StardustBevyPlugin {
fn build(&self, app: &mut App) {
app.init_schedule(StardustExtract);
let labels = &mut app.world_mut().resource_mut::<MainScheduleOrder>().labels;
info!("test: {labels:?}");
labels.insert(labels.len() - 2, StardustExtract.intern());
app.add_systems(Startup, spawn_camera.run_if(not(session_available)));
app.add_systems(XrSessionCreated, make_view_space);
embedded_asset!(app, "src/objects/input", "objects/input/cursor.glb");
}
}
fn make_view_space(mut cmds: Commands, session: Res<OxrSession>) {
// idk what errors this function returns
let view_space = session
.create_reference_space(ReferenceSpaceType::VIEW, Transform::IDENTITY)
.unwrap();
// this locates the view space against the default reference space (stage i belive) and sets
// the transform relative to the XrTrackingRoot
cmds.spawn((view_space.0, ViewLocation));
}
fn spawn_camera(mut cmds: Commands) {
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),
}
#[derive(ScheduleLabel, Hash, Debug, Clone, Copy, PartialEq, Eq)]
pub struct StardustExtract;
#[derive(Component, Hash, Debug, Clone, Copy, PartialEq, Eq)]
pub struct TemporaryEntity;
#[derive(Component, Hash, Debug, Clone, Copy, PartialEq, Eq)]
#[require(GlobalTransform)]
pub struct ViewLocation;
#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, Deref)]
pub struct MainWorldEntity(Entity);

View File

@@ -1,7 +1,9 @@
#![allow(clippy::empty_docs)] #![allow(clippy::empty_docs)]
pub mod bevy_plugin;
mod core; mod core;
mod nodes; mod nodes;
mod objects; mod objects;
pub mod oxr_render_plugin;
mod session; mod session;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
mod wayland; mod wayland;
@@ -10,27 +12,43 @@ 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, Startup, Update};
use bevy::asset::{AssetServer, Handle};
use bevy::core_pipeline::Skybox;
use bevy::image::Image;
use bevy::pbr::StandardMaterial;
use bevy::prelude::{
on_event, resource_added, Camera3d, ClearColor, Commands, Entity, EventReader,
IntoSystemConfigs, Local, Query, Res, Resource, With,
};
use bevy::render::pipelined_rendering::PipelinedRenderingPlugin;
use bevy::time::Time;
use bevy::DefaultPlugins;
use bevy_mod_openxr::add_xr_plugins;
use bevy_mod_openxr::exts::OxrExtensions;
use bevy_mod_openxr::features::overlay::{OxrOverlaySessionEvent, OxrOverlaySettings};
use bevy_mod_openxr::init::{should_run_frame_loop, OxrInitPlugin};
use bevy_mod_openxr::render::{update_cameras, OxrRenderPlugin};
use bevy_mod_openxr::resources::{OxrFrameState, OxrFrameWaiter, OxrGraphicsInfo};
use bevy_mod_openxr::session::OxrSession;
use bevy_mod_openxr::types::{AppInfo, Version};
use bevy_mod_xr::session::{XrFirst, XrSessionCreated, XrSessionPlugin};
use bevy_plugin::StardustBevyPlugin;
use clap::Parser; use clap::Parser;
use core::client::Client; use core::client::Client;
use core::task; use core::task;
use directories::ProjectDirs; use directories::ProjectDirs;
use nodes::drawable::lines::BevyLinesPlugin;
use objects::ServerObjects; use objects::ServerObjects;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use openxr::OverlaySessionCreateFlagsEXTX;
use oxr_render_plugin::StardustOxrRenderPlugin;
use session::{launch_start, save_session}; use session::{launch_start, save_session};
use stardust_xr::schemas::dbus::object_registry::ObjectRegistry; use stardust_xr::schemas::dbus::object_registry::ObjectRegistry;
use stardust_xr::server; use stardust_xr::server;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use stereokit_rust::material::Material;
use stereokit_rust::shader::Shader;
use stereokit_rust::sk::{
sk_quit, AppMode, DepthMode, DisplayBlend, OriginMode, QuitReason, SkSettings,
};
use stereokit_rust::system::{Handed, Input, LogLevel, Renderer};
use stereokit_rust::tex::{SHCubemap, Tex, TexFormat, TexType};
use stereokit_rust::ui::Ui;
use stereokit_rust::util::{Color128, Time};
use tokio::net::UnixListener; use tokio::net::UnixListener;
use tokio::sync::Notify; use tokio::sync::Notify;
use tracing::metadata::LevelFilter; use tracing::metadata::LevelFilter;
@@ -39,6 +57,8 @@ use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use zbus::fdo::ObjectManager; use zbus::fdo::ObjectManager;
use zbus::Connection; use zbus::Connection;
pub type DefaultMaterial = StandardMaterial;
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
struct CliArgs { struct CliArgs {
@@ -168,7 +188,7 @@ async fn main() {
tokio::select! { tokio::select! {
_ = stereokit_loop => (), _ = stereokit_loop => (),
_ = tokio::signal::ctrl_c() => unsafe {sk_quit(QuitReason::SystemClose)}, _ = tokio::signal::ctrl_c() => {},
} }
info!("Stopping..."); info!("Stopping...");
if let Some(project_dirs) = project_dirs { if let Some(project_dirs) = project_dirs {
@@ -188,63 +208,89 @@ fn stereokit_loop(
dbus_connection: Connection, dbus_connection: Connection,
object_registry: ObjectRegistry, object_registry: ObjectRegistry,
) { ) {
let sk = SkSettings::default() let mut bevy_app = App::new();
.app_name("Stardust XR") let base = DefaultPlugins::build(DefaultPlugins).disable::<PipelinedRenderingPlugin>();
.blend_preference(DisplayBlend::AnyTransparent) if args.flatscreen {
.mode(if args.flatscreen { bevy_app.add_plugins(base);
AppMode::Simulator } else {
} else { bevy_app.add_plugins(
AppMode::XR add_xr_plugins(base)
}) .set(OxrInitPlugin {
.depth_mode(DepthMode::D32) app_info: AppInfo {
.log_filter(match EnvFilter::from_default_env().max_level_hint() { name: "Stardust XR".into(),
Some(LevelFilter::ERROR) => LogLevel::Error, version: Version(0, 44, 1),
Some(LevelFilter::WARN) => LogLevel::Warning, },
Some(LevelFilter::INFO) => LogLevel::Inform, exts: {
Some(LevelFilter::DEBUG) => LogLevel::Diagnostic, let mut exts = OxrExtensions::default();
Some(LevelFilter::TRACE) => LogLevel::Diagnostic, exts.enable_hand_tracking();
Some(LevelFilter::OFF) => LogLevel::None, if args.overlay_priority.is_some() {
None => LogLevel::Warning, exts.enable_extx_overlay();
}) }
.overlay_app(args.overlay_priority.is_some()) exts
.overlay_priority(args.overlay_priority.unwrap_or(u32::MAX)) },
.disable_desktop_input_window(true) blend_modes: Some(vec![
.origin(OriginMode::Local) openxr::EnvironmentBlendMode::ALPHA_BLEND,
.init() openxr::EnvironmentBlendMode::OPAQUE,
.expect("StereoKit failed to initialize"); ]),
info!("Init StereoKit"); synchronous_pipeline_compilation: false,
..Default::default()
Renderer::multisample(0); })
Material::default().shader(Shader::pbr_clip()); .disable::<OxrRenderPlugin>()
Ui::enable_far_interact(false); .add_after::<XrSessionPlugin>(StardustOxrRenderPlugin),
);
let left_hand_material = Material::find("default/material_hand").unwrap(); if let Some(priority) = args.overlay_priority {
let mut right_hand_material = left_hand_material.copy(); bevy_app.insert_resource(OxrOverlaySettings {
right_hand_material.id("right_hand"); session_layer_placement: priority,
Input::hand_material(Handed::Right, Some(Material::find("right_hand").unwrap())); flags: OverlaySessionCreateFlagsEXTX::EMPTY,
});
Input::hand_visible(Handed::Left, false); }
Input::hand_visible(Handed::Right, false); }
bevy_app.add_plugins(StardustBevyPlugin);
bevy_app.add_plugins(BevyLinesPlugin);
#[derive(Resource)]
struct SkyTexture(Handle<Image>);
// Skytex/light stuff // Skytex/light stuff
{ bevy_app.add_systems(Startup, |assests: Res<AssetServer>, mut cmds: Commands| {
if let Some(sky) = project_dirs if let Some(sky) = project_dirs
.as_ref() .as_ref()
.map(|dirs| dirs.config_dir().join("skytex.hdr")) .map(|dirs| dirs.config_dir().join("skytex.hdr"))
.filter(|f| f.exists()) .filter(|f| f.exists())
.and_then(|p| SHCubemap::from_cubemap(p, true, 100).ok()) .map(|p| assests.load(p))
{ {
sky.render_as_sky(); cmds.insert_resource(SkyTexture(sky));
} else {
Renderer::skytex(Tex::gen_color(
Color128::BLACK,
1,
1,
TexType::Cubemap,
TexFormat::RGBA32,
));
} }
});
#[derive(Resource)]
struct RenderBackground(bool);
fn update_background(
graphics_info: Res<OxrGraphicsInfo>,
mut overlay_events: EventReader<OxrOverlaySessionEvent>,
mut last_hidden: Local<bool>,
cams: Query<Entity, With<Camera3d>>,
mut cmds: Commands,
) {
let env_hidden = graphics_info.blend_mode != openxr::EnvironmentBlendMode::OPAQUE
&& overlay_events
.read()
.last()
.map(
|OxrOverlaySessionEvent::MainSessionVisibilityChanged { visible, flags: _ }| {
*visible
},
)
.unwrap_or(true);
if env_hidden && !*last_hidden {
cams.iter().for_each(|e| {
cmds.entity(e).remove::<Skybox>();
});
}
*last_hidden = env_hidden;
} }
bevy_app.add_systems(XrSessionCreated, update_background);
bevy_app.add_systems(
Update,
update_background.run_if(on_event::<OxrOverlaySessionEvent>),
);
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland"); let mut wayland = wayland::Wayland::new().expect("Could not initialize wayland");
@@ -253,61 +299,49 @@ fn stereokit_loop(
sk_ready_notifier.notify_waiters(); sk_ready_notifier.notify_waiters();
info!("Stardust ready!"); info!("Stardust ready!");
let mut objects = ServerObjects::new( let mut objects = ServerObjects::new(dbus_connection.clone());
dbus_connection.clone(),
&sk,
[left_hand_material, right_hand_material],
args.disable_controllers,
args.disable_hands,
);
let mut last_frame_delta = Duration::ZERO; let mut last_frame_delta = Duration::ZERO;
let mut sleep_duration = Duration::ZERO; let mut sleep_duration = Duration::ZERO;
while let Some(token) = sk.step() { debug_span!("bevy").in_scope(|| loop {
let _span = debug_span!("StereoKit step"); let _span = debug_span!("Bevy step");
let _span = _span.enter(); let _span = _span.enter();
camera::update(token); // camera::update(token);
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
wayland.frame_event(); wayland.frame_event();
destroy_queue::clear(); destroy_queue::clear();
objects.update(&sk, token, &dbus_connection, &object_registry); let world = bevy_app.world_mut();
let session = world.remove_resource::<OxrSession>();
let time = world.get_resource::<OxrFrameState>().map(|s| {
openxr::Time::from_nanos(
s.predicted_display_time.as_nanos() + s.predicted_display_period.as_nanos(),
)
});
objects.update(session.as_deref(), time);
input::process_input(); input::process_input();
nodes::root::Root::send_frame_events(Time::get_step_unscaled()); nodes::root::Root::send_frame_events(world.resource::<Time>().delta_secs_f64());
adaptive_sleep( world.run_schedule(XrFirst);
&mut last_frame_delta, if world
&mut sleep_duration, .run_system_cached(should_run_frame_loop)
Duration::from_micros(250), .unwrap_or(true)
); {
let waiter = world.remove_resource::<OxrFrameWaiter>().unwrap();
let state = waiter.wait().unwrap();
world.insert_resource(OxrFrameState(state));
world.run_system_cached(update_cameras);
}
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
wayland.update(); wayland.update();
drawable::draw(token); drawable::draw(token);
audio::update(); bevy_app.update();
} if let Some(exit) = bevy_app.should_exit() {
break;
info!("Cleanly shut down StereoKit");
}
fn adaptive_sleep(
last_frame_delta: &mut Duration,
sleep_duration: &mut Duration,
sleep_duration_increase: Duration,
) {
let frame_delta = Duration::from_secs_f64(Time::get_step_unscaled());
if *last_frame_delta < frame_delta {
if let Some(frame_delta_delta) = frame_delta.checked_sub(*last_frame_delta) {
if let Some(new_sleep_duration) = sleep_duration.checked_sub(frame_delta_delta) {
*sleep_duration = new_sleep_duration;
}
} }
} else { audio::update();
*sleep_duration += sleep_duration_increase;
}
debug_span!("Sleep", ?sleep_duration, ?frame_delta, ?last_frame_delta).in_scope(|| {
*last_frame_delta = frame_delta;
std::thread::sleep(*sleep_duration); // to give clients a chance to even update anything before drawing
}); });
#[cfg(feature = "wayland")]
drop(wayland);
} }

View File

@@ -1,15 +1,29 @@
use super::{Line, LinesAspect}; use super::{Line, LinesAspect};
use crate::{ use crate::{
core::{client::Client, error::Result, registry::Registry}, bevy_plugin::{StardustExtract, TemporaryEntity, ViewLocation},
core::{client::Client, registry::Registry},
nodes::{spatial::Spatial, Node}, nodes::{spatial::Spatial, Node},
}; };
use glam::Vec3; use bevy::{
app::Plugin,
asset::{Asset, Assets, RenderAssetUsages},
color::{Color, ColorToComponents, Srgba},
math::{bounding::Aabb3d, Isometry3d},
pbr::{MaterialExtension, MeshMaterial3d, StandardMaterial},
prelude::{
AlphaMode, Commands, GlobalTransform, Mesh, Mesh3d, ResMut, Single, Transform, With,
},
reflect::Reflect,
render::{
primitives::Aabb,
render_resource::{AsBindGroup, ShaderRef},
},
};
use color_eyre::eyre::Result;
use glam::{Vec3, Vec3A};
use parking_lot::Mutex; use parking_lot::Mutex;
use prisma::Lerp; use prisma::Lerp;
use std::{collections::VecDeque, sync::Arc}; use std::{collections::VecDeque, sync::Arc};
use stereokit_rust::{
maths::Bounds, sk::MainThreadToken, system::LinePoint as SkLinePoint, util::Color128,
};
static LINES_REGISTRY: Registry<Lines> = Registry::new(); static LINES_REGISTRY: Registry<Lines> = Registry::new();
@@ -24,15 +38,18 @@ impl Lines {
.unwrap() .unwrap()
.bounding_box_calc .bounding_box_calc
.set(|node| { .set(|node| {
let mut bounds = Bounds::default();
if let Ok(lines) = node.get_aspect::<Lines>() { if let Ok(lines) = node.get_aspect::<Lines>() {
for line in &*lines.data.lock() { return Aabb3d::from_point_cloud(
for point in &line.points { Isometry3d::IDENTITY,
bounds.grown_point(Vec3::from(point.point)); lines
} .data
} .lock()
.iter()
.flat_map(|line| line.points.iter())
.map(|point| Vec3A::from(point.point)),
);
} }
bounds Aabb3d::new(Vec3A::ZERO, Vec3A::ZERO)
}); });
let lines = LINES_REGISTRY.add(Lines { let lines = LINES_REGISTRY.add(Lines {
@@ -44,30 +61,33 @@ impl Lines {
Ok(lines) Ok(lines)
} }
fn draw(&self, token: &MainThreadToken) { fn draw(&self, mesh: &mut Mesh, view: &GlobalTransform) -> Transform {
let transform_mat = self.space.global_transform(); let transform_mat = self.space.global_transform();
let data = self.data.lock().clone(); let data = self.data.lock().clone();
let global_to_view = view.compute_matrix().inverse();
let local_to_view = transform_mat.inverse() * global_to_view;
let view_to_local = local_to_view.inverse();
for line in &data { for line in &data {
let mut points: VecDeque<SkLinePoint> = line let mut points: VecDeque<BevyLinePoint> = line
.points .points
.iter() .iter()
.map(|p| SkLinePoint { .map(|p| BevyLinePoint {
pt: transform_mat.transform_point3(Vec3::from(p.point)).into(), pt: transform_mat.transform_point3(Vec3::from(p.point)).into(),
thickness: p.thickness, thickness: p.thickness,
color: Color128::new(p.color.c.r, p.color.c.g, p.color.c.b, p.color.a).into(), color: Srgba::new(p.color.c.r, p.color.c.g, p.color.c.b, p.color.a).into(),
}) })
.collect(); .collect();
if line.cyclic && !points.is_empty() { if line.cyclic && !points.is_empty() {
let first = line.points.first().unwrap(); let first = line.points.first().unwrap();
let last = line.points.last().unwrap(); let last = line.points.last().unwrap();
let color = Color128 { let color = Srgba {
r: first.color.c.r.lerp(&last.color.c.r, 0.5), red: first.color.c.r.lerp(&last.color.c.r, 0.5),
g: first.color.c.g.lerp(&last.color.c.g, 0.5), green: first.color.c.g.lerp(&last.color.c.g, 0.5),
b: first.color.c.b.lerp(&last.color.c.b, 0.5), blue: first.color.c.b.lerp(&last.color.c.b, 0.5),
a: first.color.a.lerp(&last.color.a, 0.5), alpha: first.color.a.lerp(&last.color.a, 0.5),
}; };
let connect_point = SkLinePoint { let connect_point = BevyLinePoint {
pt: transform_mat pt: transform_mat
.transform_point3(Vec3::from(first.point).lerp(Vec3::from(last.point), 0.5)) .transform_point3(Vec3::from(first.point).lerp(Vec3::from(last.point), 0.5))
.into(), .into(),
@@ -77,10 +97,55 @@ impl Lines {
points.push_front(connect_point); points.push_front(connect_point);
points.push_back(connect_point); points.push_back(connect_point);
} }
stereokit_rust::system::Lines::add_list(token, points.make_contiguous()); let mut last_points: Option<(Vec3A, Vec3, Srgba)> = None;
let mut vertecies: Vec<[f32; 3]> = Vec::new();
let mut colors: Vec<[f32; 4]> = Vec::new();
let mut normals: Vec<[f32; 3]> = Vec::new();
for point in points.into_iter() {
let pt_view = local_to_view.transform_point3a(point.pt.into());
let point1_view = pt_view + (Vec3A::Y * (point.thickness / 2.0));
let point2_view = pt_view + (Vec3A::NEG_Y * (point.thickness / 2.0));
let point1 = view_to_local.transform_point3a(point1_view);
let point2 = view_to_local.transform_point3a(point2_view);
if let Some((last1, last2, last_color)) = last_points.take() {
let normal = view_to_local.transform_vector3a(Vec3A::Z).to_array();
for _ in 0..6 {
normals.push(normal);
}
vertecies.push(last1.to_array());
vertecies.push(point1.to_array());
vertecies.push(last2.to_array());
vertecies.push(last2.to_array());
vertecies.push(point1.to_array());
vertecies.push(point2.to_array());
colors.push(last_color.to_f32_array());
colors.push(point.color.to_f32_array());
colors.push(last_color.to_f32_array());
colors.push(last_color.to_f32_array());
colors.push(point.color.to_f32_array());
colors.push(point.color.to_f32_array());
}
}
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertecies);
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
} }
GlobalTransform::from(transform_mat).into()
} }
} }
#[derive(Clone, Copy)]
struct BevyLinePoint {
pt: Vec3,
color: Srgba,
thickness: f32,
}
impl Aspect for Lines {
const NAME: &'static str = "Lines";
}
impl LinesAspect for Lines { impl LinesAspect for Lines {
fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> { fn set_lines(node: Arc<Node>, _calling_client: Arc<Client>, lines: Vec<Line>) -> Result<()> {
let lines_aspect = node.get_aspect::<Lines>()?; let lines_aspect = node.get_aspect::<Lines>()?;
@@ -94,12 +159,42 @@ impl Drop for Lines {
} }
} }
pub fn draw_all(token: &MainThreadToken) { pub fn draw_all(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut cmds: Commands,
hmd: Single<&GlobalTransform, With<ViewLocation>>,
) {
let material = StandardMaterial {
base_color: Color::WHITE,
alpha_mode: AlphaMode::Blend,
..Default::default()
};
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.draw(token); // Does this rebuild the mesh every frame? yes, is this problematic? probably,
// would a shader work better? yes, do i care? not right now
let mut mesh = Mesh::new(
bevy::render::mesh::PrimitiveTopology::TriangleList,
RenderAssetUsages::RENDER_WORLD,
);
let transform = lines.draw(&mut mesh, &hmd);
let mesh_handle = meshes.add(mesh);
cmds.spawn((
Mesh3d(mesh_handle),
MeshMaterial3d(mat_handle.clone()),
TemporaryEntity,
transform,
));
} }
} }
} }
} }
pub struct BevyLinesPlugin;
impl Plugin for BevyLinesPlugin {
fn build(&self, app: &mut bevy::prelude::App) {
app.add_systems(StardustExtract, draw_all);
}
}

View File

@@ -17,7 +17,6 @@ use model::ModelPart;
use parking_lot::Mutex; use parking_lot::Mutex;
use stardust_xr::values::ResourceID; use stardust_xr::values::ResourceID;
use std::{ffi::OsStr, path::PathBuf, sync::Arc}; use std::{ffi::OsStr, path::PathBuf, sync::Arc};
use stereokit_rust::{sk::MainThreadToken, system::Renderer, tex::SHCubemap};
// #[instrument(level = "debug", skip(sk))] // #[instrument(level = "debug", skip(sk))]
pub fn draw(token: &MainThreadToken) { pub fn draw(token: &MainThreadToken) {

View File

@@ -6,122 +6,117 @@ use crate::core::registry::Registry;
use crate::core::resource::get_resource_file; 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::{Aspect, Node};
use crate::DefaultMaterial;
use bevy::app::Plugin;
use bevy::asset::Handle;
use bevy::color::{Alpha, Color, LinearRgba, Srgba};
use bevy::prelude::AlphaMode;
use bevy::reflect::{GetField, PartialReflect, Reflect};
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use glam::{Mat4, Vec2, Vec3}; use glam::{Mat4, Vec2, Vec3};
use once_cell::sync::{Lazy, OnceCell}; use once_cell::sync::{Lazy, 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::warn;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::hash::{Hash, Hasher}; use std::ops::Deref;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use stereokit_rust::material::Transparency;
use stereokit_rust::maths::Bounds;
use stereokit_rust::sk::MainThreadToken;
use stereokit_rust::{material::Material, model::Model as SKModel, tex::Tex, util::Color128};
pub struct MaterialWrapper(pub Material);
impl Hash for MaterialWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.get_shader().0.as_ptr().hash(state);
for param in self.0.get_all_param_info() {
param.to_string().hash(state)
}
self.0.get_chain().map(MaterialWrapper).hash(state)
}
}
impl PartialEq for MaterialWrapper {
fn eq(&self, other: &Self) -> bool {
if self.0.get_shader().0.as_ptr() != other.0.get_shader().0.as_ptr() {
return false;
}
if self.0.get_all_param_info().count() != other.0.get_all_param_info().count() {
return false;
}
for self_param in self.0.get_all_param_info() {
let Some(other_param) = other
.0
.get_all_param_info()
.get_data(self_param.get_name(), self_param.get_type())
else {
return false;
};
if self_param.to_string() != other_param.to_string() {
return false;
}
}
self.0.get_chain().map(MaterialWrapper) == other.0.get_chain().map(MaterialWrapper)
}
}
impl Eq for MaterialWrapper {}
unsafe impl Send for MaterialWrapper {}
unsafe impl Sync for MaterialWrapper {}
#[derive(Default)]
struct MaterialRegistry(Mutex<FxHashMap<u64, String>>);
impl MaterialRegistry {
fn add_or_get(&self, material: Arc<MaterialWrapper>) -> Arc<MaterialWrapper> {
let mut lock = self.0.lock();
let hash = {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
material.hash(&mut hasher);
hasher.finish()
};
if let Some(id) = lock.get(&hash) {
if let Ok(existing) = Material::find(id) {
return Arc::new(MaterialWrapper(existing));
}
}
lock.insert(hash, material.0.get_id().to_string());
material
}
}
static MATERIAL_REGISTRY: Lazy<MaterialRegistry> = Lazy::new(MaterialRegistry::default);
static MODEL_REGISTRY: Registry<Model> = Registry::new(); static MODEL_REGISTRY: Registry<Model> = Registry::new();
static HOLDOUT_MATERIAL: OnceCell<Arc<MaterialWrapper>> = OnceCell::new(); static HOLDOUT_MATERIAL: OnceCell<Arc<DefaultMaterial>> = OnceCell::new();
impl MaterialParameter { impl MaterialParameter {
fn apply_to_material(&self, client: &Client, material: &Material, parameter_name: &str) { fn apply_to_material(
let mut params = material.get_all_param_info(); &self,
client: &Client,
material: &mut DefaultMaterial,
parameter_name: &str,
) {
match self { match self {
MaterialParameter::Bool(val) => { MaterialParameter::Bool(val) => match parameter_name {
params.set_bool(parameter_name, *val); name => {
} if let Some(field) = material.get_field_mut::<bool>(name) {
MaterialParameter::Int(val) => { *field = *val;
params.set_int(parameter_name, &[*val]); } else {
} warn!("unknown bool material parameter name: {name}");
MaterialParameter::UInt(val) => { }
params.set_uint(parameter_name, &[*val]); }
} },
MaterialParameter::Float(val) => { MaterialParameter::Int(val) => match parameter_name {
params.set_float(parameter_name, *val); name => {
} if let Some(field) = material.get_field_mut::<i32>(name) {
MaterialParameter::Vec2(val) => { *field = *val;
params.set_vec2(parameter_name, Vec2::from(*val)); } else {
} warn!("unknown bool material parameter name: {name}");
MaterialParameter::Vec3(val) => { }
params.set_vec3(parameter_name, Vec3::from(*val)); }
} },
MaterialParameter::Color(val) => { MaterialParameter::UInt(val) => match parameter_name {
params.set_color( name => {
parameter_name, if let Some(field) = material.get_field_mut::<u32>(name) {
Color128::new(val.c.r, val.c.g, val.c.b, val.a), *field = *val;
); } else {
} warn!("unknown bool material parameter name: {name}");
}
}
},
MaterialParameter::Float(val) => match parameter_name {
name => {
if let Some(field) = material.get_field_mut::<f32>(name) {
*field = *val;
} else {
warn!("unknown bool material parameter name: {name}");
}
}
},
MaterialParameter::Vec2(val) => match parameter_name {
name => {
if let Some(field) = material.get_field_mut::<Vec2>(name) {
*field = (*val).into();
} else {
warn!("unknown bool material parameter name: {name}");
}
}
},
MaterialParameter::Vec3(val) => match parameter_name {
name => {
if let Some(field) = material.get_field_mut::<Vec3>(name) {
*field = (*val).into();
} else {
warn!("unknown bool material parameter name: {name}");
}
}
},
MaterialParameter::Color(val) => match parameter_name {
"color" => {
material.base_color = LinearRgba::new(val.c.r, val.c.g, val.c.b, val.a).into()
}
name => {
if let Some(field) = material.get_field_mut::<Color>(name) {
*field = LinearRgba::new(val.c.r, val.c.g, val.c.b, val.a).into();
} else {
warn!("unknown bool material parameter name: {name}");
}
}
},
MaterialParameter::Texture(resource) => { MaterialParameter::Texture(resource) => {
match parameter_name {
name => {
warn!("unknown texture material parameter name: {name}");
}
}
let Some(texture_path) = let Some(texture_path) =
get_resource_file(resource, client, &[OsStr::new("png"), OsStr::new("jpg")]) get_resource_file(resource, client, &[OsStr::new("png"), OsStr::new("jpg")])
else { else {
return; return;
}; };
if let Ok(tex) = Tex::from_file(texture_path, true, None) { // if let Ok(tex) = Tex::from_file(texture_path, true, None) {
params.set_texture(parameter_name, &tex); // params.set_texture(parameter_name, &tex);
} // }
} }
} }
} }
@@ -133,16 +128,26 @@ pub struct ModelPart {
space: Arc<Spatial>, space: Arc<Spatial>,
model: Weak<Model>, model: Weak<Model>,
pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>, pending_material_parameters: Mutex<FxHashMap<String, MaterialParameter>>,
pending_material_replacement: Mutex<Option<Arc<MaterialWrapper>>>, pending_material_replacement: Mutex<Option<Arc<Handle<DefaultMaterial>>>>,
aliases: AliasList, aliases: AliasList,
} }
pub struct StardustModelPlugin;
impl Plugin for StardustModelPlugin {
fn build(&self, app: &mut bevy::prelude::App) {}
}
impl ModelPart { impl ModelPart {
fn create_for_model(model: &Arc<Model>, sk_model: &SKModel) { fn create_for_model(model: &Arc<Model>, sk_model: &SKModel) {
// The value isn't even used?!
HOLDOUT_MATERIAL.get_or_init(|| { HOLDOUT_MATERIAL.get_or_init(|| {
let mut mat = Material::copy(&Material::unlit()); let mut mat = DefaultMaterial {
mat.transparency(Transparency::None); unlit: true,
mat.color_tint(Color128::BLACK_TRANSPARENT); alpha_mode: AlphaMode::Opaque,
Arc::new(MaterialWrapper(mat)) base_color: Srgba::BLACK.with_alpha(0.0).into(),
..Default::default()
};
Arc::new(mat)
}); });
let nodes = sk_model.get_nodes(); let nodes = sk_model.get_nodes();
@@ -307,7 +312,7 @@ impl ModelPartAspect for ModelPart {
pub struct Model { pub struct Model {
space: Arc<Spatial>, space: Arc<Spatial>,
_resource_id: ResourceID, _resource_id: ResourceID,
sk_model: OnceCell<SKModel>, sk_model: OnceCell<()>,
parts: Mutex<Vec<Arc<ModelPart>>>, parts: Mutex<Vec<Arc<ModelPart>>>,
} }
impl Model { impl Model {

View File

@@ -9,6 +9,7 @@ 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;
use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO}; use crate::nodes::{Node, OWNED_ASPECT_ALIAS_INFO};
use bevy::math::bounding::{Aabb3d, BoundingVolume};
use color_eyre::eyre::OptionExt; use color_eyre::eyre::OptionExt;
use glam::{vec3a, Mat4, Quat, Vec3}; use glam::{vec3a, Mat4, Quat, Vec3};
use mint::Vector3; use mint::Vector3;
@@ -18,7 +19,6 @@ use rustc_hash::FxHashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::ptr; use std::ptr;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use stereokit_rust::maths::Bounds;
stardust_xr_server_codegen::codegen_spatial_protocol!(); stardust_xr_server_codegen::codegen_spatial_protocol!();
impl Transform { impl Transform {
@@ -59,7 +59,7 @@ pub struct Spatial {
transform: Mutex<Mat4>, transform: Mutex<Mat4>,
zone: Mutex<Weak<Zone>>, zone: Mutex<Weak<Zone>>,
children: Registry<Spatial>, children: Registry<Spatial>,
pub bounding_box_calc: OnceCell<fn(&Node) -> Bounds>, pub bounding_box_calc: OnceCell<fn(&Node) -> Aabb3d>,
} }
impl Spatial { impl Spatial {
@@ -103,17 +103,12 @@ impl Spatial {
} }
// the output bounds are probably way bigger than they need to be // the output bounds are probably way bigger than they need to be
pub fn get_bounding_box(&self) -> Bounds { pub fn get_bounding_box(&self) -> Aabb3d {
let Some(node) = self.node() else { let mut bounds = *self.bounding_box_calc.lock();
return Bounds::default();
};
let mut bounds = self
.bounding_box_calc
.get()
.map(|b| (b)(&node))
.unwrap_or_default();
for child in self.children.get_valid_contents() { for child in self.children.get_valid_contents() {
bounds.grown_box(child.get_bounding_box(), child.local_transform()); let b = child.get_bounding_box();
let t = child.local_transform();
bounds = bounds.grown_box(&b, Some(t));
} }
bounds bounds
} }
@@ -354,8 +349,8 @@ impl SpatialRefAspect for SpatialRef {
let bounds = this_spatial.get_bounding_box(); let bounds = this_spatial.get_bounding_box();
Ok(BoundingBox { Ok(BoundingBox {
center: Vec3::from(bounds.center).into(), center: Vec3::from(bounds.center()).into(),
size: Vec3::from(bounds.dimensions).into(), size: Vec3::from(bounds.half_size() * 2.0).into(),
}) })
} }
@@ -368,18 +363,18 @@ impl SpatialRefAspect for SpatialRef {
let relative_spatial = relative_to.get_aspect::<Spatial>()?; let relative_spatial = relative_to.get_aspect::<Spatial>()?;
let center = Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)) let center = Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial))
.transform_point3([0.0; 3].into()); .transform_point3([0.0; 3].into());
let mut bounds = Bounds { let mut bounds = Aabb3d::new(center, [0.0; 3]);
center: center.into(), bounds = bounds.grown_box(
dimensions: [0.0; 3].into(), &this_spatial.get_bounding_box(),
}; Some(Spatial::space_to_space_matrix(
bounds.grown_box( Some(&this_spatial),
this_spatial.get_bounding_box(), Some(&relative_spatial),
Spatial::space_to_space_matrix(Some(&this_spatial), Some(&relative_spatial)), )),
); );
Ok(BoundingBox { Ok(BoundingBox {
center: Vec3::from(bounds.center).into(), center: Vec3::from(bounds.center()).into(),
size: Vec3::from(bounds.dimensions).into(), size: Vec3::from(bounds.half_size() * 2.0).into(),
}) })
} }

View File

@@ -8,19 +8,15 @@ use crate::{
Node, OwnedNode, Node, OwnedNode,
}, },
objects::{ObjectHandle, SpatialRef}, objects::{ObjectHandle, SpatialRef},
DefaultMaterial,
}; };
use bevy::{asset::Handle, prelude::Mesh};
use bevy_mod_xr::hands::HandSide;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use glam::{Mat4, Vec2, Vec3}; use glam::{Mat4, Vec2, Vec3};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use stardust_xr::values::Datamap; use stardust_xr::values::Datamap;
use std::sync::Arc; use std::sync::Arc;
use stereokit_rust::{
material::Material,
model::Model,
sk::MainThreadToken,
system::{Handed, Input},
util::Color128,
};
use zbus::Connection; use zbus::Connection;
#[derive(Default, Debug, Deserialize, Serialize)] #[derive(Default, Debug, Deserialize, Serialize)]
@@ -35,10 +31,9 @@ struct ControllerDatamap {
pub struct SkController { pub struct SkController {
object_handle: ObjectHandle<SpatialRef>, object_handle: ObjectHandle<SpatialRef>,
input: Arc<InputMethod>, input: Arc<InputMethod>,
handed: Handed, handed: HandSide,
model: Model, color: bevy::color::Color,
material: Material, capture: Option<Arc<InputHandler>>,
capture_manager: CaptureManager,
datamap: ControllerDatamap, datamap: ControllerDatamap,
} }
impl SkController { impl SkController {

View File

@@ -8,26 +8,22 @@ use crate::{
Node, OwnedNode, Node, OwnedNode,
}, },
}; };
use bevy_mod_openxr::helper_traits::{ToQuat, ToVec3};
use glam::{vec3, Mat4}; use glam::{vec3, Mat4};
use input::{ use input::{
eye_pointer::EyePointer, mouse_pointer::MousePointer, sk_controller::SkController, eye_pointer::EyePointer, mouse_pointer::MousePointer, sk_controller::SkController,
sk_hand::SkHand, sk_hand::SkHand,
}; };
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 stereokit_rust::{
material::Material,
sk::{DisplayMode, MainThreadToken, Sk},
system::{Handed, Input, Key, World},
util::Device,
};
use zbus::{interface, object_server::Interface, zvariant::OwnedObjectPath, Connection}; use zbus::{interface, object_server::Interface, zvariant::OwnedObjectPath, Connection};
pub mod input; pub mod input;
pub mod play_space; pub mod play_space;
enum Inputs { pub(crate) enum Inputs {
XR { XR {
controller_left: SkController, controller_left: SkController,
controller_right: SkController, controller_right: SkController,
@@ -43,39 +39,51 @@ enum Inputs {
}, },
} }
struct ControllerInput {
pose: openxr::Space,
select: openxr::Action<f32>,
grab: openxr::Action<f32>,
scroll: openxr::Action<openxr::Vector2f>,
}
pub struct ServerObjects { pub struct ServerObjects {
connection: Connection, connection: Connection,
hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>), hmd: (Arc<Spatial>, ObjectHandle<SpatialRef>),
play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>, play_space: Option<(Arc<Spatial>, ObjectHandle<SpatialRef>)>,
hand_materials: [Material; 2], inputs: Option<Inputs>,
inputs: Inputs, view_space: Option<openxr::Space>,
disable_controllers: bool, ref_space: Option<openxr::Space>,
disable_hands: bool,
} }
pub struct TrackingRefs {
view_space: openxr::Space,
ref_space: openxr::Space,
left_controller_space: openxr::Space,
right_controller_space: openxr::Space,
left_hand_tracker: Option<openxr::HandTracker>,
right_hand_tracker: Option<openxr::HandTracker>,
}
impl ServerObjects { impl ServerObjects {
pub fn new( pub fn new(connection: Connection) -> ServerObjects {
connection: Connection,
sk: &Sk,
hand_materials: [Material; 2],
disable_controllers: bool,
disable_hands: bool,
) -> ServerObjects {
let hmd = SpatialRef::create(&connection, "/org/stardustxr/HMD"); let hmd = SpatialRef::create(&connection, "/org/stardustxr/HMD");
let play_space = (World::has_bounds() // TODO: implement in bevy_mod_openxr
&& World::get_bounds_size().x != 0.0 // let play_space = (World::has_bounds()
&& World::get_bounds_size().y != 0.0) // && World::get_bounds_size().x != 0.0
.then(|| SpatialRef::create(&connection, "/org/stardustxr/PlaySpace")); // && World::get_bounds_size().y != 0.0)
if play_space.is_some() { // .then(|| SpatialRef::create(&connection, "/org/stardustxr/PlaySpace"));
let dbus_connection = connection.clone(); // let play_space = None;
tokio::task::spawn(async move { // if play_space.is_some() {
PlaySpaceBounds::create(&dbus_connection).await; // let dbus_connection = connection.clone();
dbus_connection // tokio::task::spawn(async move {
.request_name("org.stardustxr.PlaySpace") // PlaySpaceBounds::create(&dbus_connection).await;
.await // dbus_connection
.unwrap(); // .request_name("org.stardustxr.PlaySpace")
}); // .await
} // .unwrap();
// });
// }
tokio::task::spawn({ tokio::task::spawn({
let connection = connection.clone(); let connection = connection.clone();
@@ -91,112 +99,124 @@ impl ServerObjects {
} }
}); });
let inputs = if sk.get_active_display_mode() == DisplayMode::MixedReality { // let inputs = if sk.get_active_display_mode() == DisplayMode::MixedReality {
Inputs::XR { // Inputs::XR {
controller_left: SkController::new(&connection, Handed::Left).unwrap(), // controller_left: SkController::new(&connection, Handed::Left).unwrap(),
controller_right: SkController::new(&connection, Handed::Right).unwrap(), // controller_right: SkController::new(&connection, Handed::Right).unwrap(),
hand_left: SkHand::new(&connection, Handed::Left).unwrap(), // hand_left: SkHand::new(&connection, Handed::Left).unwrap(),
hand_right: SkHand::new(&connection, Handed::Right).unwrap(), // hand_right: SkHand::new(&connection, Handed::Right).unwrap(),
eye_pointer: Device::has_eye_gaze() // // TODO: implement in bevy_mod_openxr
.then(EyePointer::new) // eye_pointer: false.then(EyePointer::new).transpose().unwrap(),
.transpose() // }
.unwrap(), // } else {
} // Inputs::MousePointer(MousePointer::new().unwrap())
} else { // };
Inputs::MousePointer(MousePointer::new().unwrap())
};
ServerObjects { ServerObjects {
connection, connection,
hmd, hmd,
play_space, play_space: None,
hand_materials, inputs: None,
inputs, ref_space: None,
disable_controllers, view_space: None,
disable_hands,
} }
} }
pub fn update( pub fn update(
&mut self, &mut self,
sk: &Sk, session: Option<&openxr::Session<openxr::AnyGraphics>>,
token: &MainThreadToken, time: Option<openxr::Time>,
dbus_connection: &Connection,
object_registry: &ObjectRegistry,
) { ) {
let hmd_pose = Input::get_head(); if let (Some(session), Some(ref_space), Some(time)) =
self.hmd (session, self.ref_space.as_ref(), time)
.0 {
.set_local_transform(Mat4::from_scale_rotation_translation( 'hmd: {
vec3(1.0, 1.0, 1.0), if let Some(view) = self.view_space.as_ref() {
hmd_pose.orientation.into(), let hmd_pose = match view.locate(ref_space, time) {
hmd_pose.position.into(), Ok(v) => v,
)); Err(err) => {
tracing::error!("error while locating hmd: {err}");
if let Some(play_space) = self.play_space.as_ref() { break 'hmd;
let pose = World::get_bounds_pose(); }
play_space };
.0 if hmd_pose.location_flags.contains(
.set_local_transform(Mat4::from_rotation_translation( SpaceLocationFlags::POSITION_TRACKED
pose.orientation.into(), | SpaceLocationFlags::ORIENTATION_TRACKED,
pose.position.into(), ) {
)); self.hmd
} .0
.set_local_transform(Mat4::from_scale_rotation_translation(
#[allow(clippy::collapsible_if)] vec3(1.0, 1.0, 1.0),
if sk.get_active_display_mode() != DisplayMode::MixedReality { hmd_pose.pose.orientation.to_quat(),
if Input::key(Key::F6).is_just_inactive() { hmd_pose.pose.position.to_vec3(),
self.inputs = Inputs::MousePointer(MousePointer::new().unwrap()); ));
}
}
} }
// if Input::key(Key::F7).is_just_inactive() {
// self.inputs = Inputs::Controllers((
// SkController::new(Handed::Left).unwrap(),
// SkController::new(Handed::Right).unwrap(),
// ));
// }
// if Input::key(Key::F8).is_just_inactive() {
// self.inputs = Inputs::Hands {
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
// right: SkHand::new(&self.connection, Handed::Right).unwrap(),
// };
// }
} }
// if let Some(play_space) = self.play_space.as_ref() {
// let pose = World::get_bounds_pose();
// play_space
// .0
// .set_local_transform(Mat4::from_rotation_translation(
// pose.orientation.into(),
// pose.position.into(),
// ));
// }
// if sk.get_active_display_mode() != DisplayMode::MixedReality {
// if Input::key(Key::F6).is_just_inactive() {
// self.inputs = Inputs::MousePointer(MousePointer::new().unwrap());
// }
// // if Input::key(Key::F7).is_just_inactive() {
// // self.inputs = Inputs::Controllers((
// // SkController::new(Handed::Left).unwrap(),
// // SkController::new(Handed::Right).unwrap(),
// // ));
// // }
// if Input::key(Key::F8).is_just_inactive() {
// self.inputs = Inputs::Hands {
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
// right: SkHand::new(&self.connection, Handed::Right).unwrap(),
// };
// }
// }
match &mut self.inputs { match &mut self.inputs {
Inputs::XR { Some(Inputs::XR {
controller_left, controller_left,
controller_right, controller_right,
hand_left, hand_left,
hand_right, hand_right,
eye_pointer, eye_pointer,
} => { }) => {
if !self.disable_controllers { // controller_left.update(token);
controller_left.update(token); // controller_right.update(token);
controller_right.update(token); // hand_left.update(sk, token);
} // hand_right.update(sk, token);
Input::hand_visible(Handed::Left, !self.disable_hands);
Input::hand_visible(Handed::Right, !self.disable_hands);
if !self.disable_hands {
hand_left.update(sk, token, &mut self.hand_materials[0]);
hand_right.update(sk, token, &mut self.hand_materials[1]);
}
if let Some(eye_pointer) = eye_pointer { if let Some(eye_pointer) = eye_pointer {
eye_pointer.update(); eye_pointer.update();
} }
} }
Inputs::MousePointer(mouse_pointer) => { Some(Inputs::MousePointer(mouse_pointer)) => mouse_pointer.update(),
mouse_pointer.update(dbus_connection, object_registry)
}
// Inputs::Controllers((left, right)) => { // Inputs::Controllers((left, right)) => {
// left.update(token); // left.update(token);
// right.update(token); // right.update(token);
// } // }
Inputs::Hands { left, right } => { Some(Inputs::Hands { left, right }) => {
left.update(sk, token, &mut self.hand_materials[0]); // left.update(sk, token);
right.update(sk, token, &mut self.hand_materials[1]); // right.update(sk, token);
} }
None => {}
} }
} }
pub fn set_inputs(&mut self, inputs: Inputs) {
self.inputs = Some(inputs);
}
pub fn unset_inputs(&mut self, inputs: Inputs) {
self.inputs = None;
}
} }
pub struct ObjectHandle<I: Interface>(Connection, OwnedObjectPath, PhantomData<I>); pub struct ObjectHandle<I: Interface>(Connection, OwnedObjectPath, PhantomData<I>);

84
src/oxr_render_plugin.rs Normal file
View File

@@ -0,0 +1,84 @@
use bevy::{
app::{App, MainScheduleOrder, Plugin, PostUpdate},
ecs::schedule::ScheduleLabel,
prelude::{resource_added, IntoSystemConfigs as _, TransformSystem},
render::{
extract_resource::ExtractResourcePlugin, pipelined_rendering::PipelinedRenderingPlugin,
Render, RenderApp,
},
};
use bevy_mod_openxr::{
init::should_run_frame_loop,
layer_builder::ProjectionLayer,
render::{
begin_frame, clean_views, end_frame, init_views, insert_texture_views, locate_views,
release_image, update_views, update_views_render_world, wait_image,
},
resources::{
OxrFrameState, OxrGraphicsInfo, OxrRenderLayers, OxrSwapchainImages, OxrViews, Pipelined,
},
session::OxrSession,
};
use bevy_mod_xr::session::{XrFirst, XrPreDestroySession, XrRenderSet, XrSessionCreated};
pub struct StardustOxrRenderPlugin;
impl Plugin for StardustOxrRenderPlugin {
fn build(&self, app: &mut App) {
if app.is_plugin_added::<PipelinedRenderingPlugin>() {
app.init_resource::<Pipelined>();
}
app.add_plugins((
ExtractResourcePlugin::<OxrFrameState>::default(),
ExtractResourcePlugin::<OxrGraphicsInfo>::default(),
ExtractResourcePlugin::<OxrSwapchainImages>::default(),
ExtractResourcePlugin::<OxrViews>::default(),
))
.add_systems(XrPreDestroySession, clean_views)
.add_systems(
XrSessionCreated,
init_views.run_if(resource_added::<OxrSession>),
)
.add_systems(
PostUpdate,
(locate_views, update_views)
.before(TransformSystem::TransformPropagate)
.chain()
.run_if(should_run_frame_loop),
)
.init_resource::<OxrViews>();
let labels = &mut app.world_mut().resource_mut::<MainScheduleOrder>().labels;
let xr_first_intern = (XrFirst).intern();
if labels.remove(0) != xr_first_intern {
panic!("first schedule was not XrFirst!");
}
let render_app = app.sub_app_mut(RenderApp);
render_app
.add_systems(XrPreDestroySession, clean_views)
.add_systems(
Render,
(
begin_frame,
insert_texture_views,
locate_views,
update_views_render_world,
wait_image,
)
.chain()
.in_set(XrRenderSet::PreRender)
.run_if(should_run_frame_loop),
)
.add_systems(
Render,
(release_image, end_frame)
.chain()
.run_if(should_run_frame_loop)
.in_set(XrRenderSet::PostRender),
)
.insert_resource(OxrRenderLayers(vec![Box::new(ProjectionLayer)]));
}
}