diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-05-07 19:23:55 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-07 19:23:55 -0400 |
commit | 670a32c9e93cfc8773de61cd6990a23e34f31907 (patch) | |
tree | 4df9f4ad825ee1e38662d85b0ca4ffad6278cf0d | |
parent | 28028a618e6ea46f4be0830ab5d20484ee240e05 (diff) | |
parent | f17f0664950b769e2e57dd7628463f9ad7e60699 (diff) | |
download | servo-670a32c9e93cfc8773de61cd6990a23e34f31907.tar.gz servo-670a32c9e93cfc8773de61cd6990a23e34f31907.zip |
Auto merge of #23292 - Manishearth:input, r=asajeffrey
Add support for XRInputSource and target ray spaces
Untested, but compiles.
r? @jdm or @asajeffrey
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23292)
<!-- Reviewable:end -->
-rw-r--r-- | Cargo.lock | 16 | ||||
-rw-r--r-- | components/canvas_traits/webgl.rs | 8 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 4 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/vrdisplay.rs | 87 | ||||
-rw-r--r-- | components/script/dom/webidls/XRInputSource.webidl | 26 | ||||
-rw-r--r-- | components/script/dom/webidls/XRSession.webidl | 5 | ||||
-rw-r--r-- | components/script/dom/xrinputsource.rs | 83 | ||||
-rw-r--r-- | components/script/dom/xrreferencespace.rs | 2 | ||||
-rw-r--r-- | components/script/dom/xrrigidtransform.rs | 2 | ||||
-rw-r--r-- | components/script/dom/xrsession.rs | 11 | ||||
-rw-r--r-- | components/script/dom/xrspace.rs | 39 | ||||
-rw-r--r-- | components/script/dom/xrstationaryreferencespace.rs | 4 | ||||
-rw-r--r-- | components/webvr/Cargo.toml | 2 | ||||
-rw-r--r-- | components/webvr/webvr_thread.rs | 48 | ||||
-rw-r--r-- | components/webvr_traits/Cargo.toml | 2 | ||||
-rw-r--r-- | components/webvr_traits/lib.rs | 6 | ||||
-rw-r--r-- | components/webvr_traits/webvr_traits.rs | 4 | ||||
-rw-r--r-- | ports/glutin/Cargo.toml | 2 |
19 files changed, 315 insertions, 37 deletions
diff --git a/Cargo.lock b/Cargo.lock index 3a3d0abc071..fad9cab9fb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3457,7 +3457,7 @@ dependencies = [ [[package]] name = "rust-webvr" -version = "0.10.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3470,13 +3470,13 @@ dependencies = [ "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "ovr-mobile-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rust-webvr-api" -version = "0.10.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3810,7 +3810,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "osmesa-src 0.1.0 (git+https://github.com/servo/osmesa-src)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5137,7 +5137,7 @@ dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "servo_config 0.0.1", "webvr_traits 0.0.1", @@ -5149,7 +5149,7 @@ version = "0.0.1" dependencies = [ "ipc-channel 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5647,8 +5647,8 @@ dependencies = [ "checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399" -"checksum rust-webvr 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "dd268264db4808e78ad1738ce1ff10a8bd53fb5ba408f595763174ca4a6fb875" -"checksum rust-webvr-api 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1b2af5c6c86fb79e70b5a34daa3d488411225707c73dc5467c7da7c83d557daf" +"checksum rust-webvr 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9c827417035beccd02f5e1dde7866c80662efafb8bcfe659f0082d10def4faa" +"checksum rust-webvr-api 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5208a3b3f0b02abf17e66c0fe1e0cd3a4f5172c9bf6d1a3e1ac6338a3d218d3" "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8eb11f5b0a98c8eca2fb1483f42646d8c340e83e46ab416f8a063a0fd0eeb20" diff --git a/components/canvas_traits/webgl.rs b/components/canvas_traits/webgl.rs index 474ae4b603b..af88ca905aa 100644 --- a/components/canvas_traits/webgl.rs +++ b/components/canvas_traits/webgl.rs @@ -12,7 +12,7 @@ use std::fmt; use std::num::NonZeroU32; use std::ops::Deref; use webrender_api::{DocumentId, ImageKey, PipelineId}; -use webvr_traits::WebVRFutureFrameData; +use webvr_traits::WebVRPoseInformation; /// Helper function that creates a WebGL channel (WebGLSender, WebGLReceiver) to be used in WebGLCommands. pub use crate::webgl_channel::webgl_channel; @@ -508,9 +508,13 @@ pub enum WebVRCommand { /// Synchronize the pose information to be used in the frame. SyncPoses( WebVRDeviceId, + // near f64, + // far f64, - WebGLSender<Result<WebVRFutureFrameData, ()>>, + // sync gamepads too + bool, + WebGLSender<Result<WebVRPoseInformation, ()>>, ), /// Submit the frame to a VR device using the specified texture coordinates. SubmitFrame(WebVRDeviceId, [f32; 4], [f32; 4]), diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 56805e4d9fe..2132e487efb 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -138,7 +138,7 @@ use tendril::{StrTendril, TendrilSink}; use time::{Duration, Timespec}; use uuid::Uuid; use webrender_api::{DocumentId, ImageKey, RenderApiSender}; -use webvr_traits::WebVRGamepadHand; +use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; /// A trait to allow tracing (only) DOM objects. pub unsafe trait JSTraceable { @@ -478,7 +478,7 @@ unsafe_no_jsmanaged_fields!(WebGLVertexArrayId); unsafe_no_jsmanaged_fields!(WebGLVersion); unsafe_no_jsmanaged_fields!(WebGLSLVersion); unsafe_no_jsmanaged_fields!(MediaList); -unsafe_no_jsmanaged_fields!(WebVRGamepadHand); +unsafe_no_jsmanaged_fields!(WebVRGamepadData, WebVRGamepadState, WebVRGamepadHand); unsafe_no_jsmanaged_fields!(ScriptToConstellationChan); unsafe_no_jsmanaged_fields!(InteractiveMetrics); unsafe_no_jsmanaged_fields!(InteractiveWindow); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index ff1688fb6c3..4e9f49e46f9 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -540,6 +540,7 @@ pub mod xmlhttprequestupload; pub mod xmlserializer; pub mod xr; pub mod xrframe; +pub mod xrinputsource; pub mod xrlayer; pub mod xrpose; pub mod xrreferencespace; diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index a8cb594a2b1..77817bae2fc 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -22,7 +22,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom}; +use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; @@ -36,6 +36,7 @@ use crate::dom::vrpose::VRPose; use crate::dom::vrstageparameters::VRStageParameters; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use crate::dom::xrframe::XRFrame; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrsession::XRSession; use crate::dom::xrwebgllayer::XRWebGLLayer; use crate::script_runtime::CommonScriptMsg; @@ -47,11 +48,12 @@ use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; use profile_traits::ipc; use std::cell::Cell; +use std::collections::HashMap; use std::mem; use std::ops::Deref; use std::rc::Rc; use std::thread; -use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRFutureFrameData}; +use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVRFrameData, WebVRPoseInformation}; use webvr_traits::{WebVRLayer, WebVRMsg}; #[dom_struct] @@ -86,12 +88,16 @@ pub struct VRDisplay { // Compositor VRFrameData synchonization frame_data_status: Cell<VRFrameDataStatus>, #[ignore_malloc_size_of = "closures are hard"] - frame_data_receiver: DomRefCell<Option<WebGLReceiver<Result<WebVRFutureFrameData, ()>>>>, + frame_data_receiver: DomRefCell<Option<WebGLReceiver<Result<WebVRPoseInformation, ()>>>>, running_display_raf: Cell<bool>, paused: Cell<bool>, stopped_on_pause: Cell<bool>, /// Whether or not this is XR mode, and the session xr_session: MutNullableDom<XRSession>, + /// Have inputs been initialized? (i.e, has getInputSources() been called?) + /// XR only + initialized_inputs: Cell<bool>, + input_sources: DomRefCell<HashMap<u32, Dom<XRInputSource>>>, } unsafe_no_jsmanaged_fields!(WebVRDisplayData); @@ -115,6 +121,8 @@ struct VRRAFUpdate { /// Number uniquely identifying the WebGL context /// so that we may setup/tear down VR compositors as things change context_id: usize, + /// Do we need input data? + needs_inputs: bool, } type VRRAFUpdateSender = Sender<Result<VRRAFUpdate, ()>>; @@ -164,6 +172,8 @@ impl VRDisplay { // When the VR Resume event is received and the flag is set, VR presentation automatically restarts. stopped_on_pause: Cell::new(false), xr_session: MutNullableDom::default(), + initialized_inputs: Cell::new(false), + input_sources: DomRefCell::new(HashMap::new()), } } @@ -627,6 +637,7 @@ impl VRDisplay { depth_far: self.depth_far.get(), api_sender: self.api_sender(), context_id: self.context_id(), + needs_inputs: self.initialized_inputs.get(), } } @@ -690,6 +701,7 @@ impl VRDisplay { let (raf_sender, raf_receiver) = unbounded(); let (wakeup_sender, wakeup_receiver) = unbounded(); *self.raf_wakeup_sender.borrow_mut() = Some(wakeup_sender); + let mut needs_inputs = false; // The render loop at native headset frame rate is implemented using a dedicated thread. // Every loop iteration syncs pose data with the HMD, submits the pixels to the display and waits for Vsync. @@ -726,8 +738,13 @@ impl VRDisplay { .unwrap(); // Run Sync Poses in parallell on Render thread - let msg = - WebVRCommand::SyncPoses(display_id, near, far, sync_sender.clone()); + let msg = WebVRCommand::SyncPoses( + display_id, + near, + far, + needs_inputs, + sync_sender.clone(), + ); api_sender.send_vr(msg).unwrap(); } else { let _ = wakeup_receiver.recv(); @@ -752,6 +769,7 @@ impl VRDisplay { if let Ok(update) = raf_receiver.recv().unwrap() { near = update.depth_near; far = update.depth_far; + needs_inputs = update.needs_inputs; if update.context_id != context_id { if let Some(ref api_sender) = update.api_sender { api_sender @@ -808,8 +826,16 @@ impl VRDisplay { fn sync_frame_data(&self) { let status = if let Some(receiver) = self.frame_data_receiver.borrow().as_ref() { match receiver.recv().unwrap() { - Ok(future_data) => { - *self.frame_data.borrow_mut() = future_data.block(); + Ok(pose) => { + *self.frame_data.borrow_mut() = pose.frame.block(); + if self.initialized_inputs.get() { + let inputs = self.input_sources.borrow(); + for (id, state) in pose.gamepads { + if let Some(input) = inputs.get(&id) { + input.update_state(state); + } + } + } VRFrameDataStatus::Synced }, Err(()) => VRFrameDataStatus::Exit, @@ -909,6 +935,53 @@ impl VRDisplay { pair.1 = None; } } + + /// Initialize XRInputSources + fn initialize_inputs(&self) { + if self.initialized_inputs.get() { + return; + } + self.initialized_inputs.set(true); + + let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); + let display = self.display.borrow().display_id; + self.webvr_thread() + .send(WebVRMsg::GetGamepadsForDisplay(display, sender)) + .unwrap(); + match receiver.recv().unwrap() { + Ok(gamepads) => { + let global = self.global(); + let session = self + .xr_session + .get() + .expect("initialize_inputs called on a VR session"); + let roots: Vec<_> = gamepads + .into_iter() + .map(|g| { + ( + g.1.gamepad_id, + XRInputSource::new(&global, &session, g.0, g.1), + ) + }) + .collect(); + + let mut inputs = self.input_sources.borrow_mut(); + for (id, root) in &roots { + inputs.insert(*id, Dom::from_ref(&root)); + } + }, + Err(_) => {}, + } + } + + pub fn get_input_sources(&self) -> Vec<DomRoot<XRInputSource>> { + self.initialize_inputs(); + self.input_sources + .borrow() + .iter() + .map(|(_, x)| DomRoot::from_ref(&**x)) + .collect() + } } // WebVR Spec: If the number of values in the leftBounds/rightBounds arrays diff --git a/components/script/dom/webidls/XRInputSource.webidl b/components/script/dom/webidls/XRInputSource.webidl new file mode 100644 index 00000000000..5ad1e48628f --- /dev/null +++ b/components/script/dom/webidls/XRInputSource.webidl @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://immersive-web.github.io/webxr/#xrinputsource-interface + +enum XRHandedness { + "none", + "left", + "right" +}; + +enum XRTargetRayMode { + "gaze", + "tracked-pointer", + "screen" +}; + +[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"] +interface XRInputSource { + readonly attribute XRHandedness handedness; + // [SameObject] readonly attribute XRTargetRayMode targetRayMode; + [SameObject] readonly attribute XRSpace targetRaySpace; + // [SameObject] readonly attribute XRSpace? gripSpace; + // [SameObject] readonly attribute Gamepad? gamepad; +}; diff --git a/components/script/dom/webidls/XRSession.webidl b/components/script/dom/webidls/XRSession.webidl index f22918d469d..0399f2d0bc1 100644 --- a/components/script/dom/webidls/XRSession.webidl +++ b/components/script/dom/webidls/XRSession.webidl @@ -20,12 +20,15 @@ interface XRSession : EventTarget { readonly attribute XREnvironmentBlendMode environmentBlendMode; readonly attribute XRRenderState renderState; - readonly attribute XRSpace viewerSpace; + [SameObject] readonly attribute XRSpace viewerSpace; // // Methods Promise<XRReferenceSpace> requestReferenceSpace(XRReferenceSpaceOptions options); + // workaround until we have FrozenArray + // see https://github.com/servo/servo/issues/10427#issuecomment-449593626 // FrozenArray<XRInputSource> getInputSources(); + sequence<XRInputSource> getInputSources(); Promise<void> updateRenderState(optional XRRenderStateInit state); long requestAnimationFrame(XRFrameRequestCallback callback); diff --git a/components/script/dom/xrinputsource.rs b/components/script/dom/xrinputsource.rs new file mode 100644 index 00000000000..2b623864d56 --- /dev/null +++ b/components/script/dom/xrinputsource.rs @@ -0,0 +1,83 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding; +use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{ + XRHandedness, XRInputSourceMethods, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::xrsession::XRSession; +use crate::dom::xrspace::XRSpace; +use dom_struct::dom_struct; +use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState, WebVRPose}; + +#[dom_struct] +pub struct XRInputSource { + reflector: Reflector, + session: Dom<XRSession>, + #[ignore_malloc_size_of = "Defined in rust-webvr"] + data: WebVRGamepadData, + #[ignore_malloc_size_of = "Defined in rust-webvr"] + state: DomRefCell<WebVRGamepadState>, + target_ray_space: MutNullableDom<XRSpace>, +} + +impl XRInputSource { + pub fn new_inherited( + session: &XRSession, + data: WebVRGamepadData, + state: WebVRGamepadState, + ) -> XRInputSource { + XRInputSource { + reflector: Reflector::new(), + session: Dom::from_ref(session), + data, + state: DomRefCell::new(state), + target_ray_space: Default::default(), + } + } + + pub fn new( + global: &GlobalScope, + session: &XRSession, + data: WebVRGamepadData, + state: WebVRGamepadState, + ) -> DomRoot<XRInputSource> { + reflect_dom_object( + Box::new(XRInputSource::new_inherited(session, data, state)), + global, + XRInputSourceBinding::Wrap, + ) + } + + pub fn update_state(&self, state: WebVRGamepadState) { + *self.state.borrow_mut() = state; + } + + pub fn pose(&self) -> WebVRPose { + self.state.borrow().pose + } +} + +impl XRInputSourceMethods for XRInputSource { + /// https://immersive-web.github.io/webxr/#dom-xrinputsource-handedness + fn Handedness(&self) -> XRHandedness { + match self.data.hand { + WebVRGamepadHand::Unknown => XRHandedness::None, + WebVRGamepadHand::Left => XRHandedness::Left, + WebVRGamepadHand::Right => XRHandedness::Right, + } + } + + /// https://immersive-web.github.io/webxr/#dom-xrinputsource-targetrayspace + fn TargetRaySpace(&self) -> DomRoot<XRSpace> { + self.target_ray_space.or_init(|| { + let global = self.global(); + XRSpace::new_inputspace(&global, &self.session, &self) + }) + } +} diff --git a/components/script/dom/xrreferencespace.rs b/components/script/dom/xrreferencespace.rs index 664dc4f0e23..932f917b5a5 100644 --- a/components/script/dom/xrreferencespace.rs +++ b/components/script/dom/xrreferencespace.rs @@ -110,7 +110,7 @@ impl XRReferenceSpace { // non-subclassed XRReferenceSpaces exist, obtained via the "identity" // type. These are equivalent to the viewer pose and follow the headset // around - XRSpace::viewer_pose_from_frame_data(base_pose) + XRSpace::pose_to_transform(&base_pose.pose) } } } diff --git a/components/script/dom/xrrigidtransform.rs b/components/script/dom/xrrigidtransform.rs index 1d8ae2d9996..f75d3f085fb 100644 --- a/components/script/dom/xrrigidtransform.rs +++ b/components/script/dom/xrrigidtransform.rs @@ -94,7 +94,7 @@ impl XRRigidTransformMethods for XRRigidTransform { } // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation fn Orientation(&self) -> DomRoot<DOMPointReadOnly> { - self.position.or_init(|| { + self.orientation.or_init(|| { let r = &self.transform.rotation; DOMPointReadOnly::new(&self.global(), r.i, r.j, r.k, r.r) }) diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 4b855cba4c8..5aab6341ca4 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -19,6 +19,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::vrdisplay::VRDisplay; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrlayer::XRLayer; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrrenderstate::XRRenderState; @@ -33,6 +34,7 @@ pub struct XRSession { display: Dom<VRDisplay>, base_layer: MutNullableDom<XRLayer>, blend_mode: XREnvironmentBlendMode, + viewer_space: MutNullableDom<XRSpace>, } impl XRSession { @@ -43,6 +45,7 @@ impl XRSession { base_layer: Default::default(), // we don't yet support any AR devices blend_mode: XREnvironmentBlendMode::Opaque, + viewer_space: Default::default(), } } @@ -86,7 +89,8 @@ impl XRSessionMethods for XRSession { // https://immersive-web.github.io/webxr/#dom-xrsession-viewerspace fn ViewerSpace(&self) -> DomRoot<XRSpace> { - XRSpace::new_viewerspace(&self.global(), &self) + self.viewer_space + .or_init(|| XRSpace::new_viewerspace(&self.global(), &self)) } /// https://immersive-web.github.io/webxr/#dom-xrsession-requestanimationframe @@ -153,4 +157,9 @@ impl XRSessionMethods for XRSession { p } + + /// https://immersive-web.github.io/webxr/#dom-xrsession-getinputsources + fn GetInputSources(&self) -> Vec<DomRoot<XRInputSource>> { + self.display.get_input_sources() + } } diff --git a/components/script/dom/xrspace.rs b/components/script/dom/xrspace.rs index 8948ac03c8e..30f9246c354 100644 --- a/components/script/dom/xrspace.rs +++ b/components/script/dom/xrspace.rs @@ -5,20 +5,22 @@ use crate::dom::bindings::codegen::Bindings::XRSpaceBinding; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; +use crate::dom::xrinputsource::XRInputSource; use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrsession::XRSession; use dom_struct::dom_struct; use euclid::{RigidTransform3D, Rotation3D, Vector3D}; -use webvr_traits::WebVRFrameData; +use webvr_traits::{WebVRFrameData, WebVRPose}; #[dom_struct] pub struct XRSpace { eventtarget: EventTarget, session: Dom<XRSession>, is_viewerspace: bool, + input_source: MutNullableDom<XRInputSource>, } impl XRSpace { @@ -27,6 +29,7 @@ impl XRSpace { eventtarget: EventTarget::new_inherited(), session: Dom::from_ref(session), is_viewerspace: false, + input_source: Default::default(), } } @@ -35,6 +38,7 @@ impl XRSpace { eventtarget: EventTarget::new_inherited(), session: Dom::from_ref(session), is_viewerspace: true, + input_source: Default::default(), } } @@ -45,6 +49,27 @@ impl XRSpace { XRSpaceBinding::Wrap, ) } + + fn new_inputspace_inner(session: &XRSession, input: &XRInputSource) -> XRSpace { + XRSpace { + eventtarget: EventTarget::new_inherited(), + session: Dom::from_ref(session), + is_viewerspace: false, + input_source: MutNullableDom::new(Some(input)), + } + } + + pub fn new_inputspace( + global: &GlobalScope, + session: &XRSession, + input: &XRInputSource, + ) -> DomRoot<XRSpace> { + reflect_dom_object( + Box::new(XRSpace::new_inputspace_inner(session, input)), + global, + XRSpaceBinding::Wrap, + ) + } } impl XRSpace { @@ -57,16 +82,18 @@ impl XRSpace { if let Some(reference) = self.downcast::<XRReferenceSpace>() { reference.get_pose(base_pose) } else if self.is_viewerspace { - XRSpace::viewer_pose_from_frame_data(base_pose) + XRSpace::pose_to_transform(&base_pose.pose) + } else if let Some(source) = self.input_source.get() { + XRSpace::pose_to_transform(&source.pose()) } else { unreachable!() } } - pub fn viewer_pose_from_frame_data(data: &WebVRFrameData) -> RigidTransform3D<f64> { - let pos = data.pose.position.unwrap_or([0., 0., 0.]); + pub fn pose_to_transform(pose: &WebVRPose) -> RigidTransform3D<f64> { + let pos = pose.position.unwrap_or([0., 0., 0.]); let translation = Vector3D::new(pos[0] as f64, pos[1] as f64, pos[2] as f64); - let orient = data.pose.orientation.unwrap_or([0., 0., 0., 0.]); + let orient = pose.orientation.unwrap_or([0., 0., 0., 0.]); let rotation = Rotation3D::quaternion( orient[0] as f64, orient[1] as f64, diff --git a/components/script/dom/xrstationaryreferencespace.rs b/components/script/dom/xrstationaryreferencespace.rs index a867aa0d976..65a0aebb790 100644 --- a/components/script/dom/xrstationaryreferencespace.rs +++ b/components/script/dom/xrstationaryreferencespace.rs @@ -55,7 +55,7 @@ impl XRStationaryReferenceSpace { /// /// Does not apply originOffset, use get_viewer_pose on XRReferenceSpace instead pub fn get_unoffset_viewer_pose(&self, viewer_pose: &WebVRFrameData) -> RigidTransform3D<f64> { - let viewer_pose = XRSpace::viewer_pose_from_frame_data(viewer_pose); + let viewer_pose = XRSpace::pose_to_transform(&viewer_pose.pose); // all math is in column-vector notation // we use the following equation to verify correctness here: // get_viewer_pose(space) = get_pose(space).inverse() * get_pose(viewer_space) @@ -113,7 +113,7 @@ impl XRStationaryReferenceSpace { }, XRStationaryReferenceSpaceSubtype::Position_disabled => { // This space follows the user around, but does not mirror the user's orientation - let viewer_pose = XRSpace::viewer_pose_from_frame_data(viewer_pose); + let viewer_pose = XRSpace::pose_to_transform(&viewer_pose.pose); viewer_pose.translation.into() }, } diff --git a/components/webvr/Cargo.toml b/components/webvr/Cargo.toml index faca3eb57a6..f4d195d29fe 100644 --- a/components/webvr/Cargo.toml +++ b/components/webvr/Cargo.toml @@ -22,7 +22,7 @@ gleam = "0.6" ipc-channel = "0.11" log = "0.4" msg = {path = "../msg"} -rust-webvr = {version = "0.10.2", features = ["openvr", "vrexternal"]} +rust-webvr = {version = "=0.11.0", features = ["openvr", "vrexternal"]} script_traits = {path = "../script_traits"} servo_config = {path = "../config"} webvr_traits = {path = "../webvr_traits" } diff --git a/components/webvr/webvr_thread.rs b/components/webvr/webvr_thread.rs index e7803509c82..e90ddbf7df5 100644 --- a/components/webvr/webvr_thread.rs +++ b/components/webvr/webvr_thread.rs @@ -16,7 +16,7 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::{thread, time}; use webvr_traits::webvr::*; -use webvr_traits::{WebVRMsg, WebVRResult}; +use webvr_traits::{WebVRMsg, WebVRPoseInformation, WebVRResult}; /// WebVRThread owns native VRDisplays, handles their life cycle inside Servo and /// acts a doorman for untrusted VR requests from DOM Objects. These are the key components @@ -128,6 +128,9 @@ impl WebVRThread { WebVRMsg::GetGamepads(synced_ids, sender) => { self.handle_get_gamepads(synced_ids, sender); }, + WebVRMsg::GetGamepadsForDisplay(display_id, sender) => { + self.handle_get_gamepads_for_display(display_id, sender); + }, WebVRMsg::Exit => break, } } @@ -251,6 +254,32 @@ impl WebVRThread { self.vr_compositor_chan.send(compositor).unwrap(); } + fn handle_get_gamepads_for_display( + &mut self, + display_id: u32, + sender: IpcSender<WebVRResult<Vec<(VRGamepadData, VRGamepadState)>>>, + ) { + match self.service.get_display(display_id) { + Some(display) => { + let gamepads = display.borrow_mut().fetch_gamepads(); + match gamepads { + Ok(gamepads) => { + let data = gamepads + .iter() + .map(|g| { + let g = g.borrow(); + (g.data(), g.state()) + }) + .collect(); + sender.send(Ok(data)).unwrap(); + }, + Err(e) => sender.send(Err(e)).unwrap(), + } + }, + None => sender.send(Err("Device not found".into())).unwrap(), + } + } + fn handle_get_gamepads( &mut self, synced_ids: Vec<u32>, @@ -386,10 +415,23 @@ impl webgl::WebVRRenderHandler for WebVRCompositorHandler { unsafe { (*compositor.0).start_present(None) }; } }, - webgl::WebVRCommand::SyncPoses(compositor_id, near, far, sender) => { + webgl::WebVRCommand::SyncPoses(compositor_id, near, far, get_gamepads, sender) => { if let Some(compositor) = self.compositors.get(&compositor_id) { let pose = unsafe { (*compositor.0).future_frame_data(near, far) }; - let _ = sender.send(Ok(pose)); + let mut pose_information = WebVRPoseInformation { + frame: pose, + gamepads: vec![], + }; + if get_gamepads { + let gamepads = unsafe { (*compositor.0).fetch_gamepads() }; + if let Ok(gamepads) = gamepads { + for gamepad in gamepads { + let g = gamepad.borrow(); + pose_information.gamepads.push((g.id(), g.state())); + } + } + } + let _ = sender.send(Ok(pose_information)); } else { let _ = sender.send(Err(())); } diff --git a/components/webvr_traits/Cargo.toml b/components/webvr_traits/Cargo.toml index 032729334f3..bf268053a9d 100644 --- a/components/webvr_traits/Cargo.toml +++ b/components/webvr_traits/Cargo.toml @@ -13,5 +13,5 @@ path = "lib.rs" [dependencies] ipc-channel = "0.11" msg = {path = "../msg"} -rust-webvr-api = {version = "0.10.3", features = ["ipc"]} +rust-webvr-api = {version = "=0.11.0", features = ["ipc"]} serde = "1.0" diff --git a/components/webvr_traits/lib.rs b/components/webvr_traits/lib.rs index ab31436c136..f96fee056e7 100644 --- a/components/webvr_traits/lib.rs +++ b/components/webvr_traits/lib.rs @@ -30,3 +30,9 @@ pub use rust_webvr_api::VRLayer as WebVRLayer; pub use rust_webvr_api::VRMainThreadHeartbeat as WebVRMainThreadHeartbeat; pub use rust_webvr_api::VRPose as WebVRPose; pub use rust_webvr_api::VRStageParameters as WebVRStageParameters; + +#[derive(Deserialize, Serialize)] +pub struct WebVRPoseInformation { + pub frame: WebVRFutureFrameData, + pub gamepads: Vec<(u32, WebVRGamepadState)>, +} diff --git a/components/webvr_traits/webvr_traits.rs b/components/webvr_traits/webvr_traits.rs index 0f6dd069867..d21e6c3ff16 100644 --- a/components/webvr_traits/webvr_traits.rs +++ b/components/webvr_traits/webvr_traits.rs @@ -30,5 +30,9 @@ pub enum WebVRMsg { Vec<u32>, IpcSender<WebVRResult<Vec<(Option<VRGamepadData>, VRGamepadState)>>>, ), + GetGamepadsForDisplay( + u32, + IpcSender<WebVRResult<Vec<(VRGamepadData, VRGamepadState)>>>, + ), Exit, } diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index 4edbef5fd85..3ef0c32fe0c 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -52,7 +52,7 @@ lazy_static = "1" libservo = {path = "../../components/servo"} libc = "0.2" log = "0.4" -rust-webvr = { version = "0.10.2", features = ["glwindow"] } +rust-webvr = { version = "=0.11.0", features = ["glwindow"] } tinyfiledialogs = "3.0" [target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies] |