refactor: begin conversion to bevy, do proper frame wait
Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
3893
Cargo.lock
generated
3893
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
63
Cargo.toml
63
Cargo.toml
@@ -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
58
src/bevy_plugin.rs
Normal 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);
|
||||||
232
src/main.rs
232
src/main.rs
@@ -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 {
|
||||||
AppMode::XR
|
bevy_app.add_plugins(
|
||||||
|
add_xr_plugins(base)
|
||||||
|
.set(OxrInitPlugin {
|
||||||
|
app_info: AppInfo {
|
||||||
|
name: "Stardust XR".into(),
|
||||||
|
version: Version(0, 44, 1),
|
||||||
|
},
|
||||||
|
exts: {
|
||||||
|
let mut exts = OxrExtensions::default();
|
||||||
|
exts.enable_hand_tracking();
|
||||||
|
if args.overlay_priority.is_some() {
|
||||||
|
exts.enable_extx_overlay();
|
||||||
|
}
|
||||||
|
exts
|
||||||
|
},
|
||||||
|
blend_modes: Some(vec![
|
||||||
|
openxr::EnvironmentBlendMode::ALPHA_BLEND,
|
||||||
|
openxr::EnvironmentBlendMode::OPAQUE,
|
||||||
|
]),
|
||||||
|
synchronous_pipeline_compilation: false,
|
||||||
|
..Default::default()
|
||||||
})
|
})
|
||||||
.depth_mode(DepthMode::D32)
|
.disable::<OxrRenderPlugin>()
|
||||||
.log_filter(match EnvFilter::from_default_env().max_level_hint() {
|
.add_after::<XrSessionPlugin>(StardustOxrRenderPlugin),
|
||||||
Some(LevelFilter::ERROR) => LogLevel::Error,
|
);
|
||||||
Some(LevelFilter::WARN) => LogLevel::Warning,
|
if let Some(priority) = args.overlay_priority {
|
||||||
Some(LevelFilter::INFO) => LogLevel::Inform,
|
bevy_app.insert_resource(OxrOverlaySettings {
|
||||||
Some(LevelFilter::DEBUG) => LogLevel::Diagnostic,
|
session_layer_placement: priority,
|
||||||
Some(LevelFilter::TRACE) => LogLevel::Diagnostic,
|
flags: OverlaySessionCreateFlagsEXTX::EMPTY,
|
||||||
Some(LevelFilter::OFF) => LogLevel::None,
|
});
|
||||||
None => LogLevel::Warning,
|
}
|
||||||
})
|
}
|
||||||
.overlay_app(args.overlay_priority.is_some())
|
bevy_app.add_plugins(StardustBevyPlugin);
|
||||||
.overlay_priority(args.overlay_priority.unwrap_or(u32::MAX))
|
bevy_app.add_plugins(BevyLinesPlugin);
|
||||||
.disable_desktop_input_window(true)
|
#[derive(Resource)]
|
||||||
.origin(OriginMode::Local)
|
struct SkyTexture(Handle<Image>);
|
||||||
.init()
|
|
||||||
.expect("StereoKit failed to initialize");
|
|
||||||
info!("Init StereoKit");
|
|
||||||
|
|
||||||
Renderer::multisample(0);
|
|
||||||
Material::default().shader(Shader::pbr_clip());
|
|
||||||
Ui::enable_far_interact(false);
|
|
||||||
|
|
||||||
let left_hand_material = Material::find("default/material_hand").unwrap();
|
|
||||||
let mut right_hand_material = left_hand_material.copy();
|
|
||||||
right_hand_material.id("right_hand");
|
|
||||||
Input::hand_material(Handed::Right, Some(Material::find("right_hand").unwrap()));
|
|
||||||
|
|
||||||
Input::hand_visible(Handed::Left, false);
|
|
||||||
Input::hand_visible(Handed::Right, false);
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
bevy_app.update();
|
||||||
|
if let Some(exit) = bevy_app.should_exit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
audio::update();
|
audio::update();
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
*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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
Aabb3d::new(Vec3A::ZERO, Vec3A::ZERO)
|
||||||
}
|
|
||||||
bounds
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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,9 +97,54 @@ 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<()> {
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
*field = *val;
|
||||||
|
} else {
|
||||||
|
warn!("unknown bool material parameter name: {name}");
|
||||||
}
|
}
|
||||||
MaterialParameter::Int(val) => {
|
|
||||||
params.set_int(parameter_name, &[*val]);
|
|
||||||
}
|
}
|
||||||
MaterialParameter::UInt(val) => {
|
},
|
||||||
params.set_uint(parameter_name, &[*val]);
|
MaterialParameter::Int(val) => match parameter_name {
|
||||||
|
name => {
|
||||||
|
if let Some(field) = material.get_field_mut::<i32>(name) {
|
||||||
|
*field = *val;
|
||||||
|
} else {
|
||||||
|
warn!("unknown bool material parameter name: {name}");
|
||||||
}
|
}
|
||||||
MaterialParameter::Float(val) => {
|
|
||||||
params.set_float(parameter_name, *val);
|
|
||||||
}
|
}
|
||||||
MaterialParameter::Vec2(val) => {
|
},
|
||||||
params.set_vec2(parameter_name, Vec2::from(*val));
|
MaterialParameter::UInt(val) => match parameter_name {
|
||||||
|
name => {
|
||||||
|
if let Some(field) = material.get_field_mut::<u32>(name) {
|
||||||
|
*field = *val;
|
||||||
|
} else {
|
||||||
|
warn!("unknown bool material parameter name: {name}");
|
||||||
}
|
}
|
||||||
MaterialParameter::Vec3(val) => {
|
|
||||||
params.set_vec3(parameter_name, Vec3::from(*val));
|
|
||||||
}
|
}
|
||||||
MaterialParameter::Color(val) => {
|
},
|
||||||
params.set_color(
|
MaterialParameter::Float(val) => match parameter_name {
|
||||||
parameter_name,
|
name => {
|
||||||
Color128::new(val.c.r, val.c.g, val.c.b, val.a),
|
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 {
|
||||||
|
|||||||
@@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,111 +99,123 @@ 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,
|
if let (Some(session), Some(ref_space), Some(time)) =
|
||||||
|
(session, self.ref_space.as_ref(), time)
|
||||||
|
{
|
||||||
|
'hmd: {
|
||||||
|
if let Some(view) = self.view_space.as_ref() {
|
||||||
|
let hmd_pose = match view.locate(ref_space, time) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("error while locating hmd: {err}");
|
||||||
|
break 'hmd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if hmd_pose.location_flags.contains(
|
||||||
|
SpaceLocationFlags::POSITION_TRACKED
|
||||||
|
| SpaceLocationFlags::ORIENTATION_TRACKED,
|
||||||
) {
|
) {
|
||||||
let hmd_pose = Input::get_head();
|
|
||||||
self.hmd
|
self.hmd
|
||||||
.0
|
.0
|
||||||
.set_local_transform(Mat4::from_scale_rotation_translation(
|
.set_local_transform(Mat4::from_scale_rotation_translation(
|
||||||
vec3(1.0, 1.0, 1.0),
|
vec3(1.0, 1.0, 1.0),
|
||||||
hmd_pose.orientation.into(),
|
hmd_pose.pose.orientation.to_quat(),
|
||||||
hmd_pose.position.into(),
|
hmd_pose.pose.position.to_vec3(),
|
||||||
));
|
|
||||||
|
|
||||||
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(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::collapsible_if)]
|
|
||||||
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 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() {
|
// if Input::key(Key::F8).is_just_inactive() {
|
||||||
// self.inputs = Inputs::Hands {
|
// self.inputs = Inputs::Hands {
|
||||||
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
// left: SkHand::new(&self.connection, Handed::Left).unwrap(),
|
||||||
// right: SkHand::new(&self.connection, Handed::Right).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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
src/oxr_render_plugin.rs
Normal file
84
src/oxr_render_plugin.rs
Normal 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)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user