refactor(input): switch to manual handler order
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2031,8 +2031,8 @@ checksum = "2f2b15926089e5526bb2dd738a2eb0e59034356e06eb71e1cd912358c0e62c4d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr"
|
name = "stardust-xr"
|
||||||
version = "0.44.0"
|
version = "0.45.0"
|
||||||
source = "git+https://github.com/StardustXR/core.git#c587446076b92d8a945f4d0e2a872c09f80cc575"
|
source = "git+https://github.com/StardustXR/core.git#21120b8924d2a450f4dd92a022d4df40b7e3885d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cluFlock",
|
"cluFlock",
|
||||||
"color-rs",
|
"color-rs",
|
||||||
@@ -2052,7 +2052,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "stardust-xr-schemas"
|
name = "stardust-xr-schemas"
|
||||||
version = "1.5.3"
|
version = "1.5.3"
|
||||||
source = "git+https://github.com/StardustXR/core.git#c587446076b92d8a945f4d0e2a872c09f80cc575"
|
source = "git+https://github.com/StardustXR/core.git#21120b8924d2a450f4dd92a022d4df40b7e3885d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flatbuffers",
|
"flatbuffers",
|
||||||
"flexbuffers",
|
"flexbuffers",
|
||||||
|
|||||||
@@ -170,8 +170,8 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let mut hands = (!cli_args.flatscreen)
|
let mut hands = (!cli_args.flatscreen)
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let left = SkHand::new(Handed::Left).ok();
|
let left = SkHand::new(Handed::Left, &sk).ok();
|
||||||
let right = SkHand::new(Handed::Right).ok();
|
let right = SkHand::new(Handed::Right, &sk).ok();
|
||||||
left.zip(right)
|
left.zip(right)
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{DistanceLink, Finger, Hand, InputDataTrait, Joint, Thumb};
|
use super::{Finger, Hand, InputDataTrait, InputLink, Joint, Thumb};
|
||||||
use crate::nodes::fields::Field;
|
use crate::nodes::fields::Field;
|
||||||
use crate::nodes::spatial::Spatial;
|
use crate::nodes::spatial::Spatial;
|
||||||
use glam::{vec3a, Mat4, Quat};
|
use glam::{vec3a, Mat4, Quat};
|
||||||
@@ -52,10 +52,7 @@ impl Default for Hand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InputDataTrait for Hand {
|
impl InputDataTrait for Hand {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
self.true_distance(space, field).abs()
|
|
||||||
}
|
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
|
||||||
let mut min_distance = f32::MAX;
|
let mut min_distance = f32::MAX;
|
||||||
|
|
||||||
for tip in [
|
for tip in [
|
||||||
@@ -70,7 +67,7 @@ impl InputDataTrait for Hand {
|
|||||||
|
|
||||||
min_distance
|
min_distance
|
||||||
}
|
}
|
||||||
fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4) {
|
fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4) {
|
||||||
let mut joints: Vec<&mut Joint> = Vec::new();
|
let mut joints: Vec<&mut Joint> = Vec::new();
|
||||||
|
|
||||||
joints.extend([&mut self.palm, &mut self.wrist]);
|
joints.extend([&mut self.palm, &mut self.wrist]);
|
||||||
@@ -104,10 +101,10 @@ impl InputDataTrait for Hand {
|
|||||||
let (_, rotation, position) = joint_matrix.to_scale_rotation_translation();
|
let (_, rotation, position) = joint_matrix.to_scale_rotation_translation();
|
||||||
joint.position = position.into();
|
joint.position = position.into();
|
||||||
joint.rotation = rotation.into();
|
joint.rotation = rotation.into();
|
||||||
joint.distance = distance_link
|
joint.distance = input_link
|
||||||
.handler
|
.handler
|
||||||
.field
|
.field
|
||||||
.distance(&distance_link.handler.spatial, position.into());
|
.distance(&input_link.handler.spatial, position.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
input_handler_client, DistanceLink, InputHandlerAspect, INPUT_HANDLER_REGISTRY,
|
input_handler_client, InputHandlerAspect, InputLink, INPUT_HANDLER_REGISTRY,
|
||||||
INPUT_METHOD_REGISTRY,
|
INPUT_METHOD_REGISTRY,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -7,13 +7,11 @@ use crate::{
|
|||||||
nodes::{fields::Field, spatial::Spatial, Aspect, Node},
|
nodes::{fields::Field, spatial::Spatial, Aspect, Node},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
pub struct InputHandler {
|
pub struct InputHandler {
|
||||||
pub enabled: Arc<AtomicBool>,
|
|
||||||
pub uid: String,
|
pub uid: String,
|
||||||
pub node: Weak<Node>,
|
pub node: Weak<Node>,
|
||||||
pub spatial: Arc<Spatial>,
|
pub spatial: Arc<Spatial>,
|
||||||
@@ -23,7 +21,6 @@ pub struct InputHandler {
|
|||||||
impl InputHandler {
|
impl InputHandler {
|
||||||
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
|
pub fn add_to(node: &Arc<Node>, field: &Arc<Field>) -> Result<()> {
|
||||||
let handler = InputHandler {
|
let handler = InputHandler {
|
||||||
enabled: node.enabled.clone(),
|
|
||||||
uid: node.uid.clone(),
|
uid: node.uid.clone(),
|
||||||
node: Arc::downgrade(node),
|
node: Arc::downgrade(node),
|
||||||
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
|
spatial: node.get_aspect::<Spatial>().unwrap().clone(),
|
||||||
@@ -39,28 +36,28 @@ impl InputHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, distance_link))]
|
#[instrument(level = "debug", skip(self, input_link))]
|
||||||
pub(super) fn send_input(
|
pub(super) fn send_input(
|
||||||
&self,
|
&self,
|
||||||
order: u32,
|
order: u32,
|
||||||
captured: bool,
|
captured: bool,
|
||||||
distance_link: &DistanceLink,
|
input_link: &InputLink,
|
||||||
datamap: Datamap,
|
datamap: Datamap,
|
||||||
) {
|
) {
|
||||||
let Some(node) = self.node.upgrade() else {
|
let Some(node) = self.node.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(method_alias) = distance_link
|
let Some(method_alias) = input_link
|
||||||
.handler
|
.handler
|
||||||
.method_aliases
|
.method_aliases
|
||||||
.get(&(Arc::as_ptr(&distance_link.method) as usize))
|
.get(&(Arc::as_ptr(&input_link.method) as usize))
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let _ = input_handler_client::input(
|
let _ = input_handler_client::input(
|
||||||
&node,
|
&node,
|
||||||
&method_alias,
|
&method_alias,
|
||||||
&distance_link.serialize(order, captured, datamap),
|
&input_link.serialize(order, captured, datamap),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ pub struct InputMethod {
|
|||||||
pub data: Mutex<InputDataType>,
|
pub data: Mutex<InputDataType>,
|
||||||
pub datamap: Mutex<Datamap>,
|
pub datamap: Mutex<Datamap>,
|
||||||
|
|
||||||
pub(super) captures: Registry<InputHandler>,
|
pub captures: Registry<InputHandler>,
|
||||||
pub(super) handler_aliases: LifeLinkedNodeMap<String>,
|
pub(super) handler_aliases: LifeLinkedNodeMap<String>,
|
||||||
pub(super) handler_order: Mutex<Option<Vec<Weak<InputHandler>>>>,
|
pub(super) handler_order: Mutex<Vec<Weak<InputHandler>>>,
|
||||||
}
|
}
|
||||||
impl InputMethod {
|
impl InputMethod {
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
@@ -44,7 +44,7 @@ impl InputMethod {
|
|||||||
captures: Registry::new(),
|
captures: Registry::new(),
|
||||||
datamap: Mutex::new(datamap),
|
datamap: Mutex::new(datamap),
|
||||||
handler_aliases: LifeLinkedNodeMap::default(),
|
handler_aliases: LifeLinkedNodeMap::default(),
|
||||||
handler_order: Mutex::new(None),
|
handler_order: Mutex::new(Vec::new()),
|
||||||
};
|
};
|
||||||
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
|
||||||
method.handle_new_handler(&handler);
|
method.handle_new_handler(&handler);
|
||||||
@@ -84,16 +84,12 @@ impl InputMethod {
|
|||||||
.add(self as *const InputMethod as usize, &method_alias);
|
.add(self as *const InputMethod as usize, &method_alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_distance(&self, to: &InputHandler) -> f32 {
|
pub fn distance(&self, to: &Field) -> f32 {
|
||||||
let distance = self.data.lock().compare_distance(&self.spatial, &to.field);
|
self.data.lock().distance(&self.spatial, to)
|
||||||
if self.captures.contains(to) {
|
|
||||||
distance * 0.5
|
|
||||||
} else {
|
|
||||||
distance
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn true_distance(&self, to: &Field) -> f32 {
|
|
||||||
self.data.lock().true_distance(&self.spatial, to)
|
pub fn set_handler_order<'a>(&self, handlers: impl Iterator<Item = &'a Arc<InputHandler>>) {
|
||||||
|
*self.handler_order.lock() = handlers.map(Arc::downgrade).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn handle_new_handler(&self, handler: &InputHandler) {
|
pub(super) fn handle_new_handler(&self, handler: &InputHandler) {
|
||||||
@@ -137,7 +133,7 @@ impl InputMethod {
|
|||||||
.add(handler.uid.clone() + "-field", &rx_field_alias);
|
.add(handler.uid.clone() + "-field", &rx_field_alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = input_method_client::new_handler(&method_node, &handler.uid, &handler_node);
|
let _ = input_method_client::create_handler(&method_node, &handler.uid, &handler_node);
|
||||||
}
|
}
|
||||||
pub(super) fn handle_drop_handler(&self, handler: &InputHandler) {
|
pub(super) fn handle_drop_handler(&self, handler: &InputHandler) {
|
||||||
let uid = handler.uid.as_str();
|
let uid = handler.uid.as_str();
|
||||||
@@ -147,7 +143,7 @@ impl InputMethod {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = input_method_client::drop_handler(&tx_node, &uid);
|
let _ = input_method_client::destroy_handler(&tx_node, &uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Aspect for InputMethod {
|
impl Aspect for InputMethod {
|
||||||
@@ -176,25 +172,25 @@ impl InputMethodAspect for InputMethod {
|
|||||||
fn set_handler_order(
|
fn set_handler_order(
|
||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
handlers: Option<Vec<Arc<Node>>>,
|
handlers: Vec<Arc<Node>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let input_method = node.get_aspect::<InputMethod>()?;
|
let input_method = node.get_aspect::<InputMethod>()?;
|
||||||
let Some(handlers) = handlers else {
|
|
||||||
*input_method.handler_order.lock() = None;
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let handlers = handlers
|
let handlers = handlers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|p| p.get_aspect::<InputHandler>().ok())
|
.filter_map(|p| p.get_aspect::<InputHandler>().ok())
|
||||||
.map(|i| Arc::downgrade(&i))
|
.map(|i| Arc::downgrade(&i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
*input_method.handler_order.lock() = Some(handlers);
|
*input_method.handler_order.lock() = handlers;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc = "Have the input handler that this method reference came from capture the method for the next frame."]
|
#[doc = "Have the input handler that this method reference came from capture the method for the next frame."]
|
||||||
fn capture(node: Arc<Node>, _calling_client: Arc<Client>, handler: Arc<Node>) -> Result<()> {
|
fn request_capture(
|
||||||
|
node: Arc<Node>,
|
||||||
|
_calling_client: Arc<Client>,
|
||||||
|
handler: Arc<Node>,
|
||||||
|
) -> Result<()> {
|
||||||
let input_method = node.get_aspect::<InputMethod>()?;
|
let input_method = node.get_aspect::<InputMethod>()?;
|
||||||
let input_handler = handler.get_aspect::<InputHandler>()?;
|
let input_handler = handler.get_aspect::<InputHandler>()?;
|
||||||
|
|
||||||
|
|||||||
@@ -21,22 +21,17 @@ use std::sync::{Arc, Weak};
|
|||||||
use tracing::{debug_span, instrument};
|
use tracing::{debug_span, instrument};
|
||||||
|
|
||||||
static INPUT_METHOD_REGISTRY: Registry<InputMethod> = Registry::new();
|
static INPUT_METHOD_REGISTRY: Registry<InputMethod> = Registry::new();
|
||||||
static INPUT_HANDLER_REGISTRY: Registry<InputHandler> = Registry::new();
|
pub static INPUT_HANDLER_REGISTRY: Registry<InputHandler> = Registry::new();
|
||||||
|
|
||||||
stardust_xr_server_codegen::codegen_input_protocol!();
|
stardust_xr_server_codegen::codegen_input_protocol!();
|
||||||
|
|
||||||
pub struct DistanceLink {
|
pub struct InputLink {
|
||||||
distance: f32,
|
|
||||||
method: Arc<InputMethod>,
|
method: Arc<InputMethod>,
|
||||||
handler: Arc<InputHandler>,
|
handler: Arc<InputHandler>,
|
||||||
}
|
}
|
||||||
impl DistanceLink {
|
impl InputLink {
|
||||||
fn from(method: Arc<InputMethod>, handler: Arc<InputHandler>) -> Self {
|
fn from(method: Arc<InputMethod>, handler: Arc<InputHandler>) -> Self {
|
||||||
DistanceLink {
|
InputLink { method, handler }
|
||||||
distance: method.compare_distance(&handler),
|
|
||||||
method,
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_input(&self, order: u32, captured: bool, datamap: Datamap) {
|
fn send_input(&self, order: u32, captured: bool, datamap: Datamap) {
|
||||||
@@ -53,7 +48,7 @@ impl DistanceLink {
|
|||||||
InputData {
|
InputData {
|
||||||
uid: self.method.uid.clone(),
|
uid: self.method.uid.clone(),
|
||||||
input,
|
input,
|
||||||
distance: self.method.true_distance(&self.handler.field),
|
distance: self.method.distance(&self.handler.field),
|
||||||
datamap,
|
datamap,
|
||||||
order,
|
order,
|
||||||
captured,
|
captured,
|
||||||
@@ -61,32 +56,23 @@ impl DistanceLink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait InputDataTrait {
|
pub trait InputDataTrait {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32;
|
fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4);
|
||||||
fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4);
|
|
||||||
}
|
}
|
||||||
impl InputDataTrait for InputDataType {
|
impl InputDataTrait for InputDataType {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
InputDataType::Pointer(i) => i.compare_distance(space, field),
|
InputDataType::Pointer(i) => i.distance(space, field),
|
||||||
InputDataType::Hand(i) => i.compare_distance(space, field),
|
InputDataType::Hand(i) => i.distance(space, field),
|
||||||
InputDataType::Tip(i) => i.compare_distance(space, field),
|
InputDataType::Tip(i) => i.distance(space, field),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn update_to(&mut self, input_link: &InputLink, local_to_handler_matrix: Mat4) {
|
||||||
match self {
|
match self {
|
||||||
InputDataType::Pointer(i) => i.true_distance(space, field),
|
InputDataType::Pointer(i) => i.update_to(input_link, local_to_handler_matrix),
|
||||||
InputDataType::Hand(i) => i.true_distance(space, field),
|
InputDataType::Hand(i) => i.update_to(input_link, local_to_handler_matrix),
|
||||||
InputDataType::Tip(i) => i.true_distance(space, field),
|
InputDataType::Tip(i) => i.update_to(input_link, local_to_handler_matrix),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_to(&mut self, distance_link: &DistanceLink, local_to_handler_matrix: Mat4) {
|
|
||||||
match self {
|
|
||||||
InputDataType::Pointer(i) => i.update_to(distance_link, local_to_handler_matrix),
|
|
||||||
InputDataType::Hand(i) => i.update_to(distance_link, local_to_handler_matrix),
|
|
||||||
InputDataType::Tip(i) => i.update_to(distance_link, local_to_handler_matrix),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,66 +130,42 @@ pub fn process_input() {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|method| *method.enabled.lock())
|
.filter(|method| *method.enabled.lock())
|
||||||
});
|
});
|
||||||
let handlers = INPUT_HANDLER_REGISTRY.get_valid_contents();
|
// const LIMIT: usize = 50;
|
||||||
const LIMIT: usize = 50;
|
|
||||||
for method in methods {
|
for method in methods {
|
||||||
for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() {
|
for alias in method.node.upgrade().unwrap().aliases.get_valid_contents() {
|
||||||
alias.enabled.store(false, Ordering::Release);
|
alias.enabled.store(false, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_span!("Process input method").in_scope(|| {
|
debug_span!("Process input method").in_scope(|| {
|
||||||
// Get all valid input handlers and convert them to DistanceLink objects
|
// Get all valid input handlers and convert them to InputLink objects
|
||||||
let distance_links: Vec<DistanceLink> = debug_span!("Generate distance links")
|
let input_links: Vec<InputLink> = debug_span!("Generate input links").in_scope(|| {
|
||||||
.in_scope(|| {
|
method
|
||||||
if let Some(handler_order) = &*method.handler_order.lock() {
|
.handler_order
|
||||||
handler_order
|
.lock()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(Weak::upgrade)
|
.filter_map(Weak::upgrade)
|
||||||
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
.filter(|handler| {
|
||||||
.map(|handler| DistanceLink::from(method.clone(), handler))
|
let Some(node) = handler.node.upgrade() else {
|
||||||
.collect()
|
return false;
|
||||||
} else {
|
};
|
||||||
let mut distance_links: Vec<_> = handlers
|
node.enabled()
|
||||||
.iter()
|
})
|
||||||
.filter(|handler| handler.enabled.load(Ordering::Relaxed))
|
.map(|handler| InputLink::from(method.clone(), handler))
|
||||||
.map(|handler| {
|
.collect()
|
||||||
debug_span!("Create distance link").in_scope(|| {
|
});
|
||||||
DistanceLink::from(method.clone(), handler.clone())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Sort the distance links by their distance in ascending order
|
method.captures.clear();
|
||||||
debug_span!("Sort distance links").in_scope(|| {
|
|
||||||
distance_links.sort_unstable_by(|a, b| {
|
|
||||||
a.distance.abs().partial_cmp(&b.distance.abs()).unwrap()
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
distance_links.truncate(LIMIT);
|
|
||||||
distance_links
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let captures = method.captures.take_valid_contents();
|
|
||||||
// Iterate over the distance links and send input to them
|
// Iterate over the distance links and send input to them
|
||||||
for (i, distance_link) in distance_links.into_iter().enumerate() {
|
for (i, input_link) in input_links.into_iter().enumerate() {
|
||||||
if let Some(method_alias) = distance_link
|
if let Some(method_alias) = input_link
|
||||||
.handler
|
.handler
|
||||||
.method_aliases
|
.method_aliases
|
||||||
.get(&(Arc::as_ptr(&distance_link.method) as usize))
|
.get(&(Arc::as_ptr(&input_link.method) as usize))
|
||||||
.and_then(|a| a.get_aspect::<Alias>().ok())
|
.and_then(|a| a.get_aspect::<Alias>().ok())
|
||||||
{
|
{
|
||||||
method_alias.enabled.store(true, Ordering::Release);
|
method_alias.enabled.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
let captured = captures.contains(&distance_link.handler);
|
input_link.send_input(i as u32, true, method.datamap.lock().clone());
|
||||||
distance_link.send_input(i as u32, captured, method.datamap.lock().clone());
|
|
||||||
|
|
||||||
// If the current distance link is in the list of captured input handlers,
|
|
||||||
// break out of the loop to avoid sending input to the remaining distance links
|
|
||||||
if captured {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{DistanceLink, InputDataTrait, Pointer};
|
use super::{InputDataTrait, InputLink, Pointer};
|
||||||
use crate::nodes::{
|
use crate::nodes::{
|
||||||
fields::{Field, Ray, RayMarchResult},
|
fields::{Field, Ray, RayMarchResult},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
@@ -29,27 +29,17 @@ impl Pointer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InputDataTrait for Pointer {
|
impl InputDataTrait for Pointer {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
let ray_info = self.ray_march(space, field);
|
|
||||||
if ray_info.min_distance > 0.0 {
|
|
||||||
ray_info.deepest_point_distance + 1000.0
|
|
||||||
} else {
|
|
||||||
ray_info
|
|
||||||
.deepest_point_distance
|
|
||||||
.hypot(0.001 / ray_info.min_distance)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
|
||||||
let ray_info = self.ray_march(space, field);
|
let ray_info = self.ray_march(space, field);
|
||||||
ray_info.min_distance
|
ray_info.min_distance
|
||||||
}
|
}
|
||||||
fn update_to(&mut self, distance_link: &DistanceLink, mut local_to_handler_matrix: Mat4) {
|
fn update_to(&mut self, input_link: &InputLink, mut local_to_handler_matrix: Mat4) {
|
||||||
local_to_handler_matrix =
|
local_to_handler_matrix =
|
||||||
Mat4::from_rotation_translation(self.orientation.into(), self.origin.into())
|
Mat4::from_rotation_translation(self.orientation.into(), self.origin.into())
|
||||||
* local_to_handler_matrix;
|
* local_to_handler_matrix;
|
||||||
let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation();
|
let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation();
|
||||||
|
|
||||||
let ray_march = self.ray_march(&distance_link.method.spatial, &distance_link.handler.field);
|
let ray_march = self.ray_march(&input_link.method.spatial, &input_link.handler.field);
|
||||||
let direction = local_to_handler_matrix
|
let direction = local_to_handler_matrix
|
||||||
.transform_vector3(vec3(0.0, 0.0, -1.0))
|
.transform_vector3(vec3(0.0, 0.0, -1.0))
|
||||||
.normalize();
|
.normalize();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{DistanceLink, InputDataTrait, Tip};
|
use super::{InputDataTrait, InputLink, Tip};
|
||||||
use crate::nodes::{fields::Field, spatial::Spatial};
|
use crate::nodes::{fields::Field, spatial::Spatial};
|
||||||
use glam::{Mat4, Quat};
|
use glam::{Mat4, Quat};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -12,13 +12,10 @@ impl Default for Tip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InputDataTrait for Tip {
|
impl InputDataTrait for Tip {
|
||||||
fn compare_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
fn distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
||||||
field.distance(space, self.origin.into()).abs()
|
|
||||||
}
|
|
||||||
fn true_distance(&self, space: &Arc<Spatial>, field: &Field) -> f32 {
|
|
||||||
field.distance(space, self.origin.into())
|
field.distance(space, self.origin.into())
|
||||||
}
|
}
|
||||||
fn update_to(&mut self, _distance_link: &DistanceLink, mut local_to_handler_matrix: Mat4) {
|
fn update_to(&mut self, _input_link: &InputLink, mut local_to_handler_matrix: Mat4) {
|
||||||
local_to_handler_matrix *=
|
local_to_handler_matrix *=
|
||||||
Mat4::from_rotation_translation(self.orientation.into(), self.origin.into());
|
Mat4::from_rotation_translation(self.orientation.into(), self.origin.into());
|
||||||
let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation();
|
let (_, orientation, origin) = local_to_handler_matrix.to_scale_rotation_translation();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ pub type Method = fn(Arc<Node>, Arc<Client>, Message, MethodResponseSender);
|
|||||||
stardust_xr_server_codegen::codegen_node_protocol!();
|
stardust_xr_server_codegen::codegen_node_protocol!();
|
||||||
|
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub enabled: Arc<AtomicBool>,
|
enabled: Arc<AtomicBool>,
|
||||||
pub(super) uid: String,
|
pub(super) uid: String,
|
||||||
path: String,
|
path: String,
|
||||||
client: Weak<Client>,
|
client: Weak<Client>,
|
||||||
@@ -113,6 +113,9 @@ impl Node {
|
|||||||
.scenegraph
|
.scenegraph
|
||||||
.add_node(self))
|
.add_node(self))
|
||||||
}
|
}
|
||||||
|
pub fn enabled(&self) -> bool {
|
||||||
|
self.enabled.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
pub fn destroy(&self) {
|
pub fn destroy(&self) {
|
||||||
if let Some(client) = self.get_client() {
|
if let Some(client) = self.get_client() {
|
||||||
client.scenegraph.remove_node(self.get_path());
|
client.scenegraph.remove_node(self.get_path());
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl Root {
|
|||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let prefixes: Vec<PathBuf> = deserialize(message.as_ref())?;
|
let prefixes: Vec<PathBuf> = deserialize(message.as_ref())?;
|
||||||
info!(?client, ?prefixes, "Set base prefixes");
|
info!(?calling_client, ?prefixes, "Set base prefixes");
|
||||||
*calling_client.base_resource_prefixes.lock() = prefixes;
|
*calling_client.base_resource_prefixes.lock() = prefixes;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
input::{InputDataType, InputMethod, Pointer},
|
fields::Ray,
|
||||||
|
input::{InputDataType, InputMethod, Pointer, INPUT_HANDLER_REGISTRY},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
Node,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use glam::Mat4;
|
use glam::{vec3, Mat4};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
@@ -56,5 +57,47 @@ impl EyePointer {
|
|||||||
// Set pointer input datamap
|
// Set pointer input datamap
|
||||||
*self.pointer.datamap.lock() = Datamap::from_typed(EyeDatamap { eye: 2 }).unwrap();
|
*self.pointer.datamap.lock() = Datamap::from_typed(EyeDatamap { eye: 2 }).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send input to all the input handlers that are the closest to the ray as possible
|
||||||
|
let rx = INPUT_HANDLER_REGISTRY
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
// filter out all the disabled handlers
|
||||||
|
.filter(|handler| {
|
||||||
|
let Some(node) = handler.node.upgrade() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
node.enabled()
|
||||||
|
})
|
||||||
|
// ray march to all the enabled handlers' fields
|
||||||
|
.map(|handler| {
|
||||||
|
let result = handler.field.ray_march(Ray {
|
||||||
|
origin: vec3(0.0, 0.0, 0.0),
|
||||||
|
direction: vec3(0.0, 0.0, -1.0),
|
||||||
|
space: self.spatial.clone(),
|
||||||
|
});
|
||||||
|
(vec![handler], result)
|
||||||
|
})
|
||||||
|
// make sure the field isn't at the pointer origin and that it's being hit
|
||||||
|
.filter(|(_, result)| result.deepest_point_distance > 0.01 && result.min_distance < 0.0)
|
||||||
|
// .inspect(|(_, result)| {
|
||||||
|
// dbg!(result);
|
||||||
|
// })
|
||||||
|
// now collect all handlers that are same distance if they're the closest
|
||||||
|
.reduce(|(mut handlers_a, result_a), (handlers_b, result_b)| {
|
||||||
|
if (result_a.deepest_point_distance - result_b.deepest_point_distance).abs() < 0.001
|
||||||
|
{
|
||||||
|
// distance is basically the same
|
||||||
|
handlers_a.extend(handlers_b);
|
||||||
|
(handlers_a, result_a)
|
||||||
|
} else if result_a.deepest_point_distance < result_b.deepest_point_distance {
|
||||||
|
(handlers_a, result_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, result_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx)
|
||||||
|
.unwrap_or_default();
|
||||||
|
self.pointer.set_handler_order(rx.iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY,
|
mask_matches, pulse_receiver_client, PulseSender, KEYMAPS, PULSE_RECEIVER_REGISTRY,
|
||||||
},
|
},
|
||||||
fields::{Field, Ray},
|
fields::{Field, Ray},
|
||||||
input::{InputDataType, InputMethod, Pointer},
|
input::{InputDataType, InputHandler, InputMethod, Pointer, INPUT_HANDLER_REGISTRY},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
Node,
|
||||||
},
|
},
|
||||||
@@ -52,6 +52,7 @@ pub struct MousePointer {
|
|||||||
node: Arc<Node>,
|
node: Arc<Node>,
|
||||||
spatial: Arc<Spatial>,
|
spatial: Arc<Spatial>,
|
||||||
pointer: Arc<InputMethod>,
|
pointer: Arc<InputMethod>,
|
||||||
|
capture: Option<Arc<InputHandler>>,
|
||||||
mouse_datamap: MouseEvent,
|
mouse_datamap: MouseEvent,
|
||||||
keyboard_datamap: KeyboardEvent,
|
keyboard_datamap: KeyboardEvent,
|
||||||
keyboard_sender: Arc<PulseSender>,
|
keyboard_sender: Arc<PulseSender>,
|
||||||
@@ -84,6 +85,7 @@ impl MousePointer {
|
|||||||
node,
|
node,
|
||||||
spatial,
|
spatial,
|
||||||
pointer,
|
pointer,
|
||||||
|
capture: None,
|
||||||
mouse_datamap: Default::default(),
|
mouse_datamap: Default::default(),
|
||||||
keyboard_datamap: Default::default(),
|
keyboard_datamap: Default::default(),
|
||||||
keyboard_sender,
|
keyboard_sender,
|
||||||
@@ -134,9 +136,106 @@ impl MousePointer {
|
|||||||
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
|
self.mouse_datamap.scroll_discrete = vec2(0.0, mouse.scroll_change / 120.0);
|
||||||
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
|
*self.pointer.datamap.lock() = Datamap::from_typed(&self.mouse_datamap).unwrap();
|
||||||
}
|
}
|
||||||
|
self.target_pointer_input();
|
||||||
self.send_keyboard_input(sk);
|
self.send_keyboard_input(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn target_pointer_input(&mut self) {
|
||||||
|
// remove the capture when it's removed from captures list
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
if !self
|
||||||
|
.pointer
|
||||||
|
.captures
|
||||||
|
.get_valid_contents()
|
||||||
|
.contains(&capture)
|
||||||
|
{
|
||||||
|
self.capture.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the capture that's the closest if we don't have one
|
||||||
|
if self.capture.is_none() {
|
||||||
|
if let Some(new_capture) = self
|
||||||
|
.pointer
|
||||||
|
.captures
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
.map(|h| {
|
||||||
|
(
|
||||||
|
h.clone(),
|
||||||
|
h.field.ray_march(Ray {
|
||||||
|
origin: vec3(0.0, 0.0, 0.0),
|
||||||
|
direction: vec3(0.0, 0.0, -1.0),
|
||||||
|
space: self.spatial.clone(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.reduce(|(handlers_a, result_a), (handlers_b, result_b)| {
|
||||||
|
if result_a.min_distance < result_b.min_distance {
|
||||||
|
(handlers_a, result_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, result_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx)
|
||||||
|
{
|
||||||
|
self.capture = Some(new_capture.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that if something is captured only send input to it
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
self.pointer.set_handler_order([capture].into_iter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send input to all the input handlers that are the closest to the ray as possible
|
||||||
|
self.pointer.set_handler_order(
|
||||||
|
INPUT_HANDLER_REGISTRY
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
// filter out all the disabled handlers
|
||||||
|
.filter(|handler| {
|
||||||
|
let Some(node) = handler.node.upgrade() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
node.enabled()
|
||||||
|
})
|
||||||
|
// ray march to all the enabled handlers' fields
|
||||||
|
.map(|handler| {
|
||||||
|
let result = handler.field.ray_march(Ray {
|
||||||
|
origin: vec3(0.0, 0.0, 0.0),
|
||||||
|
direction: vec3(0.0, 0.0, -1.0),
|
||||||
|
space: self.spatial.clone(),
|
||||||
|
});
|
||||||
|
(vec![handler], result)
|
||||||
|
})
|
||||||
|
// make sure the field isn't at the pointer origin and that it's being hit
|
||||||
|
.filter(|(_, result)| {
|
||||||
|
result.deepest_point_distance > 0.01 && result.min_distance < 0.0
|
||||||
|
})
|
||||||
|
// .inspect(|(_, result)| {
|
||||||
|
// dbg!(result);
|
||||||
|
// })
|
||||||
|
// now collect all handlers that are same distance if they're the closest
|
||||||
|
.reduce(|(mut handlers_a, result_a), (handlers_b, result_b)| {
|
||||||
|
if (result_a.deepest_point_distance - result_b.deepest_point_distance).abs()
|
||||||
|
< 0.001
|
||||||
|
{
|
||||||
|
// distance is basically the same
|
||||||
|
handlers_a.extend(handlers_b);
|
||||||
|
(handlers_a, result_a)
|
||||||
|
} else if result_a.deepest_point_distance < result_b.deepest_point_distance {
|
||||||
|
(handlers_a, result_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, result_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn send_keyboard_input(&mut self, sk: &impl StereoKitMultiThread) {
|
fn send_keyboard_input(&mut self, sk: &impl StereoKitMultiThread) {
|
||||||
let rx = PULSE_RECEIVER_REGISTRY
|
let rx = PULSE_RECEIVER_REGISTRY
|
||||||
.get_valid_contents()
|
.get_valid_contents()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::client::INTERNAL_CLIENT,
|
core::client::INTERNAL_CLIENT,
|
||||||
nodes::{
|
nodes::{
|
||||||
input::{InputDataType, InputMethod, Tip},
|
input::{InputDataType, InputHandler, InputMethod, Tip, INPUT_HANDLER_REGISTRY},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
Node,
|
Node,
|
||||||
},
|
},
|
||||||
@@ -26,8 +26,9 @@ struct ControllerDatamap {
|
|||||||
pub struct SkController {
|
pub struct SkController {
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
input: Arc<InputMethod>,
|
input: Arc<InputMethod>,
|
||||||
model: Model,
|
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
|
model: Model,
|
||||||
|
capture: Option<Arc<InputHandler>>,
|
||||||
datamap: ControllerDatamap,
|
datamap: ControllerDatamap,
|
||||||
}
|
}
|
||||||
impl SkController {
|
impl SkController {
|
||||||
@@ -56,6 +57,7 @@ impl SkController {
|
|||||||
input,
|
input,
|
||||||
handed,
|
handed,
|
||||||
model,
|
model,
|
||||||
|
capture: None,
|
||||||
datamap: Default::default(),
|
datamap: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -79,5 +81,85 @@ impl SkController {
|
|||||||
self.datamap.grab = controller.grip;
|
self.datamap.grab = controller.grip;
|
||||||
self.datamap.scroll = controller.stick;
|
self.datamap.scroll = controller.stick;
|
||||||
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
|
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
|
||||||
|
|
||||||
|
// remove the capture when it's removed from captures list
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
if !self.input.captures.get_valid_contents().contains(&capture) {
|
||||||
|
self.capture.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the capture that's the closest if we don't have one
|
||||||
|
if self.capture.is_none() {
|
||||||
|
self.capture = self
|
||||||
|
.input
|
||||||
|
.captures
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
.map(|handler| {
|
||||||
|
(
|
||||||
|
handler.clone(),
|
||||||
|
handler
|
||||||
|
.field
|
||||||
|
.distance(&self.input.spatial, [0.0; 3].into())
|
||||||
|
.abs(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| {
|
||||||
|
if distance_a < distance_b {
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, distance_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that if something is captured only send input to it
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
self.input.set_handler_order([capture].into_iter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send input to all the input handlers that are the closest to the ray as possible
|
||||||
|
self.input.set_handler_order(
|
||||||
|
INPUT_HANDLER_REGISTRY
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
// filter out all the disabled handlers
|
||||||
|
.filter(|handler| {
|
||||||
|
let Some(node) = handler.node.upgrade() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
node.enabled()
|
||||||
|
})
|
||||||
|
// get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input)
|
||||||
|
.map(|handler| {
|
||||||
|
(
|
||||||
|
vec![handler.clone()],
|
||||||
|
handler
|
||||||
|
.field
|
||||||
|
.distance(&self.input.spatial, [0.0; 3].into())
|
||||||
|
.abs(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// .inspect(|(_, result)| {
|
||||||
|
// dbg!(result);
|
||||||
|
// })
|
||||||
|
// now collect all handlers that are same distance if they're the closest
|
||||||
|
.reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| {
|
||||||
|
if (distance_a - distance_b).abs() < 0.001 {
|
||||||
|
// distance is basically the same (within 1mm)
|
||||||
|
handlers_a.extend(handlers_b);
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else if distance_a < distance_b {
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, distance_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::core::client::INTERNAL_CLIENT;
|
use crate::core::client::INTERNAL_CLIENT;
|
||||||
use crate::nodes::input::InputDataType;
|
use crate::nodes::fields::Field;
|
||||||
|
use crate::nodes::input::{InputDataType, InputHandler, INPUT_HANDLER_REGISTRY};
|
||||||
use crate::nodes::{
|
use crate::nodes::{
|
||||||
input::{Hand, InputMethod, Joint},
|
input::{Hand, InputMethod, Joint},
|
||||||
spatial::Spatial,
|
spatial::Spatial,
|
||||||
@@ -10,8 +11,9 @@ use glam::Mat4;
|
|||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use stardust_xr::values::Datamap;
|
use stardust_xr::values::Datamap;
|
||||||
|
use std::f32::INFINITY;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use stereokit::{ButtonState, HandJoint, Handed, StereoKitMultiThread};
|
use stereokit::{ButtonState, Color128, HandJoint, Handed, Material, StereoKitMultiThread};
|
||||||
|
|
||||||
fn convert_joint(joint: HandJoint) -> Joint {
|
fn convert_joint(joint: HandJoint) -> Joint {
|
||||||
Joint {
|
Joint {
|
||||||
@@ -30,12 +32,14 @@ struct HandDatamap {
|
|||||||
|
|
||||||
pub struct SkHand {
|
pub struct SkHand {
|
||||||
_node: Arc<Node>,
|
_node: Arc<Node>,
|
||||||
input: Arc<InputMethod>,
|
|
||||||
handed: Handed,
|
handed: Handed,
|
||||||
|
material: Material,
|
||||||
|
input: Arc<InputMethod>,
|
||||||
|
capture: Option<Arc<InputHandler>>,
|
||||||
datamap: HandDatamap,
|
datamap: HandDatamap,
|
||||||
}
|
}
|
||||||
impl SkHand {
|
impl SkHand {
|
||||||
pub fn new(handed: Handed) -> Result<Self> {
|
pub fn new(handed: Handed, sk: &impl StereoKitMultiThread) -> Result<Self> {
|
||||||
let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
let _node = Node::create_parent_name(&INTERNAL_CLIENT, "", &nanoid!(), false)
|
||||||
.add_to_scenegraph()?;
|
.add_to_scenegraph()?;
|
||||||
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
|
Spatial::add_to(&_node, None, Mat4::IDENTITY, false);
|
||||||
@@ -45,10 +49,15 @@ impl SkHand {
|
|||||||
});
|
});
|
||||||
let datamap = Datamap::from_typed(HandDatamap::default())?;
|
let datamap = Datamap::from_typed(HandDatamap::default())?;
|
||||||
let input = InputMethod::add_to(&_node, hand, datamap)?;
|
let input = InputMethod::add_to(&_node, hand, datamap)?;
|
||||||
|
|
||||||
|
let material = sk.material_copy(Material::HAND);
|
||||||
|
sk.input_hand_material(handed, Material(material.0));
|
||||||
Ok(SkHand {
|
Ok(SkHand {
|
||||||
_node,
|
_node,
|
||||||
input,
|
|
||||||
handed,
|
handed,
|
||||||
|
material,
|
||||||
|
input,
|
||||||
|
capture: None,
|
||||||
datamap: Default::default(),
|
datamap: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -98,5 +107,94 @@ impl SkHand {
|
|||||||
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
self.datamap.pinch_strength = sk_hand.pinch_activation;
|
||||||
self.datamap.grab_strength = sk_hand.grip_activation;
|
self.datamap.grab_strength = sk_hand.grip_activation;
|
||||||
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
|
*self.input.datamap.lock() = Datamap::from_typed(&self.datamap).unwrap();
|
||||||
|
|
||||||
|
// remove the capture when it's removed from captures list
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
if !self.input.captures.get_valid_contents().contains(&capture) {
|
||||||
|
self.capture.take();
|
||||||
|
sk.material_set_color(&self.material, "color", Color128::new_rgb(1.0, 1.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the capture that's the closest if we don't have one
|
||||||
|
if self.capture.is_none() {
|
||||||
|
self.capture = self
|
||||||
|
.input
|
||||||
|
.captures
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
.map(|handler| (handler.clone(), self.compare_distance(&handler.field).abs()))
|
||||||
|
.reduce(|(handlers_a, distance_a), (handlers_b, distance_b)| {
|
||||||
|
if distance_a < distance_b {
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, distance_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx);
|
||||||
|
if self.capture.is_some() {
|
||||||
|
sk.material_set_color(&self.material, "color", Color128::new_rgb(0.0, 1.0, 0.75));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that if something is captured only send input to it
|
||||||
|
if let Some(capture) = &self.capture {
|
||||||
|
self.input.set_handler_order([capture].into_iter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send input to all the input handlers that are the closest to the ray as possible
|
||||||
|
self.input.set_handler_order(
|
||||||
|
INPUT_HANDLER_REGISTRY
|
||||||
|
.get_valid_contents()
|
||||||
|
.into_iter()
|
||||||
|
// filter out all the disabled handlers
|
||||||
|
.filter(|handler| {
|
||||||
|
let Some(node) = handler.node.upgrade() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
node.enabled()
|
||||||
|
})
|
||||||
|
// get the unsigned distance to the handler's field (unsigned so giant fields won't always eat input)
|
||||||
|
.map(|handler| {
|
||||||
|
(
|
||||||
|
vec![handler.clone()],
|
||||||
|
self.compare_distance(&handler.field).abs(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// .inspect(|(_, result)| {
|
||||||
|
// dbg!(result);
|
||||||
|
// })
|
||||||
|
// now collect all handlers that are same distance if they're the closest
|
||||||
|
.reduce(|(mut handlers_a, distance_a), (handlers_b, distance_b)| {
|
||||||
|
if (distance_a - distance_b).abs() < 0.001 {
|
||||||
|
// distance is basically the same (within 1mm)
|
||||||
|
handlers_a.extend(handlers_b);
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else if distance_a < distance_b {
|
||||||
|
(handlers_a, distance_a)
|
||||||
|
} else {
|
||||||
|
(handlers_b, distance_b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|(rx, _)| rx)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_distance(&self, field: &Field) -> f32 {
|
||||||
|
let InputDataType::Hand(hand) = &*self.input.data.lock() else {
|
||||||
|
return INFINITY;
|
||||||
|
};
|
||||||
|
let spatial = &self.input.spatial;
|
||||||
|
let thumb_tip_distance = field.distance(spatial, hand.thumb.tip.position.into());
|
||||||
|
let index_tip_distance = field.distance(spatial, hand.index.tip.position.into());
|
||||||
|
let middle_tip_distance = field.distance(spatial, hand.middle.tip.position.into());
|
||||||
|
let ring_tip_distance = field.distance(spatial, hand.ring.tip.position.into());
|
||||||
|
|
||||||
|
(thumb_tip_distance * 0.3)
|
||||||
|
+ (index_tip_distance * 0.4)
|
||||||
|
+ (middle_tip_distance * 0.15)
|
||||||
|
+ (ring_tip_distance * 0.15)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user