mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #26171 - Manishearth:hittest, r=asajeffrey
Implement hit testing API Depends on https://github.com/servo/webxr/pull/149 , https://github.com/servo/servo/pull/26170 This implements non-transient hit tests. The tests that do not pass are due to https://github.com/web-platform-tests/wpt/issues/22898 , https://github.com/web-platform-tests/wpt/issues/22900, https://github.com/web-platform-tests/wpt/issues/22901 , and https://github.com/immersive-web/hit-test/issues/86
This commit is contained in:
commit
99cd30eaad
34 changed files with 650 additions and 77 deletions
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -183,9 +183,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "0.1.7"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "background_hang_monitor"
|
name = "background_hang_monitor"
|
||||||
|
@ -971,14 +971,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-epoch"
|
name = "crossbeam-epoch"
|
||||||
version = "0.8.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"maybe-uninit",
|
||||||
"memoffset",
|
"memoffset",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
@ -995,9 +996,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
|
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -1473,9 +1474,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "euclid"
|
name = "euclid"
|
||||||
version = "0.20.5"
|
version = "0.20.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8813df82772c5ef4c2e9cd4a986773c125ffeafdc08204c9d5c2f06e0abdc17"
|
checksum = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -3846,9 +3847,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.4"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
|
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
|
@ -3926,9 +3930,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.53"
|
version = "0.9.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f"
|
checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -6270,9 +6274,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.2"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
|
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
|
@ -6566,7 +6570,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webxr"
|
name = "webxr"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/webxr#0d9c83f333920b98d95adf9666b0a365258990a3"
|
source = "git+https://github.com/servo/webxr#805811544cafbc8dcebc3cd02c38dd329226088d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_injected_glue",
|
"android_injected_glue",
|
||||||
"bindgen",
|
"bindgen",
|
||||||
|
@ -6590,7 +6594,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webxr-api"
|
name = "webxr-api"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/webxr#0d9c83f333920b98d95adf9666b0a365258990a3"
|
source = "git+https://github.com/servo/webxr#805811544cafbc8dcebc3cd02c38dd329226088d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"euclid",
|
"euclid",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
|
|
|
@ -157,6 +157,7 @@ use webgpu::{
|
||||||
WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule,
|
WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule,
|
||||||
};
|
};
|
||||||
use webrender_api::{DocumentId, ImageKey};
|
use webrender_api::{DocumentId, ImageKey};
|
||||||
|
use webxr_api::Ray;
|
||||||
use webxr_api::SwapChainId as WebXRSwapChainId;
|
use webxr_api::SwapChainId as WebXRSwapChainId;
|
||||||
|
|
||||||
unsafe_no_jsmanaged_fields!(Tm);
|
unsafe_no_jsmanaged_fields!(Tm);
|
||||||
|
@ -552,7 +553,9 @@ unsafe_no_jsmanaged_fields!(
|
||||||
webxr_api::Session,
|
webxr_api::Session,
|
||||||
webxr_api::Frame,
|
webxr_api::Frame,
|
||||||
webxr_api::InputSource,
|
webxr_api::InputSource,
|
||||||
webxr_api::InputId
|
webxr_api::InputId,
|
||||||
|
webxr_api::HitTestId,
|
||||||
|
webxr_api::HitTestResult
|
||||||
);
|
);
|
||||||
unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
|
unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
|
||||||
unsafe_no_jsmanaged_fields!(InteractiveMetrics);
|
unsafe_no_jsmanaged_fields!(InteractiveMetrics);
|
||||||
|
@ -750,6 +753,13 @@ unsafe impl<U> JSTraceable for euclid::Rect<i32, U> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<Space> JSTraceable for Ray<Space> {
|
||||||
|
#[inline]
|
||||||
|
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl JSTraceable for StyleLocked<FontFaceRule> {
|
unsafe impl JSTraceable for StyleLocked<FontFaceRule> {
|
||||||
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
unsafe fn trace(&self, _trc: *mut JSTracer) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
|
||||||
use crate::dom::bindings::codegen::Bindings::FakeXRDeviceBinding::{
|
use crate::dom::bindings::codegen::Bindings::FakeXRDeviceBinding::{
|
||||||
FakeXRDeviceMethods, FakeXRRigidTransformInit, FakeXRViewInit,
|
FakeXRDeviceMethods, FakeXRRegionType, FakeXRRigidTransformInit, FakeXRViewInit,
|
||||||
|
FakeXRWorldInit,
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::codegen::Bindings::FakeXRInputControllerBinding::FakeXRInputSourceInit;
|
use crate::dom::bindings::codegen::Bindings::FakeXRInputControllerBinding::FakeXRInputSourceInit;
|
||||||
use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
|
use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
|
||||||
|
@ -21,15 +23,15 @@ use crate::dom::promise::Promise;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use euclid::{RigidTransform3D, Rotation3D, Transform3D, Vector3D};
|
use euclid::{Point3D, RigidTransform3D, Rotation3D, Transform3D, Vector3D};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use profile_traits::ipc;
|
use profile_traits::ipc;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use webxr_api::{
|
use webxr_api::{
|
||||||
Handedness, InputId, InputSource, MockDeviceMsg, MockInputInit, MockViewInit, MockViewsInit,
|
EntityType, Handedness, InputId, InputSource, MockDeviceMsg, MockInputInit, MockRegion,
|
||||||
TargetRayMode, Visibility,
|
MockViewInit, MockViewsInit, MockWorld, TargetRayMode, Triangle, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
@ -97,6 +99,7 @@ pub fn view<Eye>(view: &FakeXRViewInit) -> Fallible<MockViewInit<Eye>> {
|
||||||
fov,
|
fov,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<MockViewsInit> {
|
pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<MockViewsInit> {
|
||||||
match views.len() {
|
match views.len() {
|
||||||
1 => Ok(MockViewsInit::Mono(view(&views[0])?)),
|
1 => Ok(MockViewsInit::Mono(view(&views[0])?)),
|
||||||
|
@ -133,6 +136,50 @@ pub fn get_origin<T, U>(
|
||||||
Ok(RigidTransform3D::new(o, p))
|
Ok(RigidTransform3D::new(o, p))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_point<T>(pt: &DOMPointInit) -> Point3D<f32, T> {
|
||||||
|
Point3D::new(pt.x / pt.w, pt.y / pt.w, pt.z / pt.w).cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_world(world: &FakeXRWorldInit) -> Fallible<MockWorld> {
|
||||||
|
let regions = world
|
||||||
|
.hitTestRegions
|
||||||
|
.iter()
|
||||||
|
.map(|region| {
|
||||||
|
let ty = region.type_.into();
|
||||||
|
let faces = region
|
||||||
|
.faces
|
||||||
|
.iter()
|
||||||
|
.map(|face| {
|
||||||
|
if face.vertices.len() != 3 {
|
||||||
|
return Err(Error::Type(
|
||||||
|
"Incorrectly sized array for triangle list".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Triangle {
|
||||||
|
first: get_point(&face.vertices[0]),
|
||||||
|
second: get_point(&face.vertices[1]),
|
||||||
|
third: get_point(&face.vertices[2]),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Fallible<Vec<_>>>()?;
|
||||||
|
Ok(MockRegion { faces, ty })
|
||||||
|
})
|
||||||
|
.collect::<Fallible<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(MockWorld { regions })
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FakeXRRegionType> for EntityType {
|
||||||
|
fn from(x: FakeXRRegionType) -> Self {
|
||||||
|
match x {
|
||||||
|
FakeXRRegionType::Point => EntityType::Point,
|
||||||
|
FakeXRRegionType::Plane => EntityType::Plane,
|
||||||
|
FakeXRRegionType::Mesh => EntityType::Mesh,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FakeXRDeviceMethods for FakeXRDevice {
|
impl FakeXRDeviceMethods for FakeXRDevice {
|
||||||
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
|
||||||
fn SetViews(&self, views: Vec<FakeXRViewInit>) -> Fallible<()> {
|
fn SetViews(&self, views: Vec<FakeXRViewInit>) -> Fallible<()> {
|
||||||
|
@ -172,6 +219,17 @@ impl FakeXRDeviceMethods for FakeXRDevice {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-clearworld
|
||||||
|
fn ClearWorld(&self) {
|
||||||
|
let _ = self.sender.send(MockDeviceMsg::ClearWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-setworld
|
||||||
|
fn SetWorld(&self, world: &FakeXRWorldInit) -> Fallible<()> {
|
||||||
|
let _ = self.sender.send(MockDeviceMsg::SetWorld(get_world(world)?));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-simulatevisibilitychange
|
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-simulatevisibilitychange
|
||||||
fn SimulateVisibilityChange(&self, v: XRVisibilityState) {
|
fn SimulateVisibilityChange(&self, v: XRVisibilityState) {
|
||||||
let v = match v {
|
let v = match v {
|
||||||
|
|
|
@ -572,6 +572,8 @@ pub mod xmlhttprequesteventtarget;
|
||||||
pub mod xmlhttprequestupload;
|
pub mod xmlhttprequestupload;
|
||||||
pub mod xmlserializer;
|
pub mod xmlserializer;
|
||||||
pub mod xrframe;
|
pub mod xrframe;
|
||||||
|
pub mod xrhittestresult;
|
||||||
|
pub mod xrhittestsource;
|
||||||
pub mod xrinputsource;
|
pub mod xrinputsource;
|
||||||
pub mod xrinputsourcearray;
|
pub mod xrinputsourcearray;
|
||||||
pub mod xrinputsourceevent;
|
pub mod xrinputsourceevent;
|
||||||
|
@ -579,6 +581,7 @@ pub mod xrinputsourceschangeevent;
|
||||||
pub mod xrlayer;
|
pub mod xrlayer;
|
||||||
pub mod xrmediabinding;
|
pub mod xrmediabinding;
|
||||||
pub mod xrpose;
|
pub mod xrpose;
|
||||||
|
pub mod xrray;
|
||||||
pub mod xrreferencespace;
|
pub mod xrreferencespace;
|
||||||
pub mod xrrenderstate;
|
pub mod xrrenderstate;
|
||||||
pub mod xrrigidtransform;
|
pub mod xrrigidtransform;
|
||||||
|
|
|
@ -25,6 +25,10 @@ interface FakeXRDevice {
|
||||||
|
|
||||||
// behaves as if device was disconnected
|
// behaves as if device was disconnected
|
||||||
Promise<void> disconnect();
|
Promise<void> disconnect();
|
||||||
|
|
||||||
|
// Hit test extensions:
|
||||||
|
[Throws] void setWorld(FakeXRWorldInit world);
|
||||||
|
void clearWorld();
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
|
// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
|
||||||
|
@ -61,3 +65,26 @@ dictionary FakeXRFieldOfViewInit {
|
||||||
required float leftDegrees;
|
required float leftDegrees;
|
||||||
required float rightDegrees;
|
required float rightDegrees;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// hit testing
|
||||||
|
dictionary FakeXRWorldInit {
|
||||||
|
required sequence<FakeXRRegionInit> hitTestRegions;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
dictionary FakeXRRegionInit {
|
||||||
|
required sequence<FakeXRTriangleInit> faces;
|
||||||
|
required FakeXRRegionType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
dictionary FakeXRTriangleInit {
|
||||||
|
required sequence<DOMPointInit> vertices; // size = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum FakeXRRegionType {
|
||||||
|
"point",
|
||||||
|
"plane",
|
||||||
|
"mesh"
|
||||||
|
};
|
||||||
|
|
|
@ -10,5 +10,5 @@ interface XRFrame {
|
||||||
|
|
||||||
[Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
|
[Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
|
||||||
[Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo);
|
[Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo);
|
||||||
// XRInputPose? getInputPose(XRInputSource inputSource, optional XRReferenceSpace referenceSpace);
|
sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource);
|
||||||
};
|
};
|
||||||
|
|
10
components/script/dom/webidls/XRHitTestResult.webidl
Normal file
10
components/script/dom/webidls/XRHitTestResult.webidl
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/* 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/hit-test/#xrhittestresult-interface
|
||||||
|
|
||||||
|
[SecureContext, Exposed=Window]
|
||||||
|
interface XRHitTestResult {
|
||||||
|
XRPose? getPose(XRSpace baseSpace);
|
||||||
|
};
|
22
components/script/dom/webidls/XRHitTestSource.webidl
Normal file
22
components/script/dom/webidls/XRHitTestSource.webidl
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* 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/hit-test/#xrhittestsource-interface
|
||||||
|
|
||||||
|
enum XRHitTestTrackableType {
|
||||||
|
"point",
|
||||||
|
"plane",
|
||||||
|
"mesh"
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary XRHitTestOptionsInit {
|
||||||
|
required XRSpace space;
|
||||||
|
sequence<XRHitTestTrackableType> entityTypes;
|
||||||
|
XRRay offsetRay;
|
||||||
|
};
|
||||||
|
|
||||||
|
[SecureContext, Exposed=Window]
|
||||||
|
interface XRHitTestSource {
|
||||||
|
void cancel();
|
||||||
|
};
|
21
components/script/dom/webidls/XRRay.webidl
Normal file
21
components/script/dom/webidls/XRRay.webidl
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/* 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/hit-test/#xrray-interface
|
||||||
|
|
||||||
|
dictionary XRRayDirectionInit {
|
||||||
|
double x = 0;
|
||||||
|
double y = 0;
|
||||||
|
double z = -1;
|
||||||
|
double w = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"]
|
||||||
|
interface XRRay {
|
||||||
|
[Throws] constructor(optional DOMPointInit origin = {}, optional XRRayDirectionInit direction = {});
|
||||||
|
[Throws] constructor(XRRigidTransform transform);
|
||||||
|
[SameObject] readonly attribute DOMPointReadOnly origin;
|
||||||
|
[SameObject] readonly attribute DOMPointReadOnly direction;
|
||||||
|
[SameObject] readonly attribute Float32Array matrix;
|
||||||
|
};
|
|
@ -36,6 +36,9 @@ interface XRSession : EventTarget {
|
||||||
|
|
||||||
Promise<void> end();
|
Promise<void> end();
|
||||||
|
|
||||||
|
// hit test module
|
||||||
|
Promise<XRHitTestSource> requestHitTestSource(XRHitTestOptionsInit options);
|
||||||
|
|
||||||
// // Events
|
// // Events
|
||||||
attribute EventHandler onend;
|
attribute EventHandler onend;
|
||||||
attribute EventHandler onselect;
|
attribute EventHandler onselect;
|
||||||
|
|
|
@ -20,13 +20,14 @@ interface XRTest {
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary FakeXRDeviceInit {
|
dictionary FakeXRDeviceInit {
|
||||||
required boolean supportsImmersive;
|
boolean supportsImmersive = false;
|
||||||
|
sequence<XRSessionMode> supportedModes;
|
||||||
|
|
||||||
required sequence<FakeXRViewInit> views;
|
required sequence<FakeXRViewInit> views;
|
||||||
|
|
||||||
// this is actually sequence<any>, but we don't support
|
// this is actually sequence<any>, but we don't support
|
||||||
// non-string features anyway
|
// non-string features anyway
|
||||||
sequence<DOMString> supportedFeatures;
|
sequence<DOMString> supportedFeatures;
|
||||||
boolean supportsUnbounded = false;
|
|
||||||
// Whether the space supports tracking in inline sessions
|
// Whether the space supports tracking in inline sessions
|
||||||
boolean supportsTrackingInInline = true;
|
boolean supportsTrackingInInline = true;
|
||||||
// The bounds coordinates. If null, bounded reference spaces are not supported.
|
// The bounds coordinates. If null, bounded reference spaces are not supported.
|
||||||
|
@ -34,5 +35,8 @@ dictionary FakeXRDeviceInit {
|
||||||
// Eye level used for calculating floor-level spaces
|
// Eye level used for calculating floor-level spaces
|
||||||
FakeXRRigidTransformInit floorOrigin;
|
FakeXRRigidTransformInit floorOrigin;
|
||||||
FakeXRRigidTransformInit viewerOrigin;
|
FakeXRRigidTransformInit viewerOrigin;
|
||||||
|
|
||||||
|
// Hit test extensions:
|
||||||
|
FakeXRWorldInit world;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@ use crate::dom::bindings::inheritance::Castable;
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::xrhittestresult::XRHitTestResult;
|
||||||
|
use crate::dom::xrhittestsource::XRHitTestSource;
|
||||||
use crate::dom::xrpose::XRPose;
|
use crate::dom::xrpose::XRPose;
|
||||||
use crate::dom::xrreferencespace::XRReferenceSpace;
|
use crate::dom::xrreferencespace::XRReferenceSpace;
|
||||||
use crate::dom::xrsession::XRSession;
|
use crate::dom::xrsession::{ApiPose, XRSession};
|
||||||
use crate::dom::xrspace::XRSpace;
|
use crate::dom::xrspace::XRSpace;
|
||||||
use crate::dom::xrviewerpose::XRViewerPose;
|
use crate::dom::xrviewerpose::XRViewerPose;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
@ -51,6 +53,10 @@ impl XRFrame {
|
||||||
pub fn set_animation_frame(&self, animation_frame: bool) {
|
pub fn set_animation_frame(&self, animation_frame: bool) {
|
||||||
self.animation_frame.set(animation_frame);
|
self.animation_frame.set(animation_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_pose(&self, space: &XRSpace) -> Option<ApiPose> {
|
||||||
|
space.get_pose(&self.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XRFrameMethods for XRFrame {
|
impl XRFrameMethods for XRFrame {
|
||||||
|
@ -92,12 +98,12 @@ impl XRFrameMethods for XRFrame {
|
||||||
if !self.active.get() {
|
if !self.active.get() {
|
||||||
return Err(Error::InvalidState);
|
return Err(Error::InvalidState);
|
||||||
}
|
}
|
||||||
let space = if let Some(space) = space.get_pose(&self.data) {
|
let space = if let Some(space) = self.get_pose(space) {
|
||||||
space
|
space
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let relative_to = if let Some(r) = relative_to.get_pose(&self.data) {
|
let relative_to = if let Some(r) = self.get_pose(relative_to) {
|
||||||
r
|
r
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -105,4 +111,14 @@ impl XRFrameMethods for XRFrame {
|
||||||
let pose = relative_to.inverse().pre_transform(&space);
|
let pose = relative_to.inverse().pre_transform(&space);
|
||||||
Ok(Some(XRPose::new(&self.global(), pose)))
|
Ok(Some(XRPose::new(&self.global(), pose)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrframe-gethittestresults
|
||||||
|
fn GetHitTestResults(&self, source: &XRHitTestSource) -> Vec<DomRoot<XRHitTestResult>> {
|
||||||
|
self.data
|
||||||
|
.hit_test_results
|
||||||
|
.iter()
|
||||||
|
.filter(|r| r.id == source.id())
|
||||||
|
.map(|r| XRHitTestResult::new(&self.global(), *r, self))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
51
components/script/dom/xrhittestresult.rs
Normal file
51
components/script/dom/xrhittestresult.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* 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::codegen::Bindings::XRHitTestResultBinding::XRHitTestResultMethods;
|
||||||
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||||
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::xrframe::XRFrame;
|
||||||
|
use crate::dom::xrpose::XRPose;
|
||||||
|
use crate::dom::xrspace::XRSpace;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use webxr_api::HitTestResult;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct XRHitTestResult {
|
||||||
|
reflector_: Reflector,
|
||||||
|
#[ignore_malloc_size_of = "defined in webxr"]
|
||||||
|
result: HitTestResult,
|
||||||
|
frame: Dom<XRFrame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRHitTestResult {
|
||||||
|
fn new_inherited(result: HitTestResult, frame: &XRFrame) -> XRHitTestResult {
|
||||||
|
XRHitTestResult {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
result,
|
||||||
|
frame: Dom::from_ref(frame),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
global: &GlobalScope,
|
||||||
|
result: HitTestResult,
|
||||||
|
frame: &XRFrame,
|
||||||
|
) -> DomRoot<XRHitTestResult> {
|
||||||
|
reflect_dom_object(
|
||||||
|
Box::new(XRHitTestResult::new_inherited(result, frame)),
|
||||||
|
global,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRHitTestResultMethods for XRHitTestResult {
|
||||||
|
// https://immersive-web.github.io/hit-test/#dom-xrhittestresult-getpose
|
||||||
|
fn GetPose(&self, base: &XRSpace) -> Option<DomRoot<XRPose>> {
|
||||||
|
let base = self.frame.get_pose(base)?;
|
||||||
|
let pose = base.inverse().pre_transform(&self.result.space);
|
||||||
|
Some(XRPose::new(&self.global(), pose.cast_unit()))
|
||||||
|
}
|
||||||
|
}
|
51
components/script/dom/xrhittestsource.rs
Normal file
51
components/script/dom/xrhittestsource.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* 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::codegen::Bindings::XRHitTestSourceBinding::XRHitTestSourceMethods;
|
||||||
|
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
|
||||||
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::xrsession::XRSession;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use webxr_api::HitTestId;
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct XRHitTestSource {
|
||||||
|
reflector_: Reflector,
|
||||||
|
#[ignore_malloc_size_of = "defined in webxr"]
|
||||||
|
id: HitTestId,
|
||||||
|
session: Dom<XRSession>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRHitTestSource {
|
||||||
|
fn new_inherited(id: HitTestId, session: &XRSession) -> XRHitTestSource {
|
||||||
|
XRHitTestSource {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
id,
|
||||||
|
session: Dom::from_ref(session),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
global: &GlobalScope,
|
||||||
|
id: HitTestId,
|
||||||
|
session: &XRSession,
|
||||||
|
) -> DomRoot<XRHitTestSource> {
|
||||||
|
reflect_dom_object(
|
||||||
|
Box::new(XRHitTestSource::new_inherited(id, session)),
|
||||||
|
global,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> HitTestId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRHitTestSourceMethods for XRHitTestSource {
|
||||||
|
// https://immersive-web.github.io/hit-test/#dom-xrhittestsource-cancel
|
||||||
|
fn Cancel(&self) {
|
||||||
|
self.session.with_session(|s| s.cancel_hit_test(self.id));
|
||||||
|
}
|
||||||
|
}
|
147
components/script/dom/xrray.rs
Normal file
147
components/script/dom/xrray.rs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/* 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::codegen::Bindings::DOMPointBinding::DOMPointInit;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::XRRayBinding::{XRRayDirectionInit, XRRayMethods};
|
||||||
|
use crate::dom::bindings::error::{Error, Fallible};
|
||||||
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||||
|
use crate::dom::bindings::root::DomRoot;
|
||||||
|
use crate::dom::bindings::utils::create_typed_array;
|
||||||
|
use crate::dom::dompointreadonly::DOMPointReadOnly;
|
||||||
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::window::Window;
|
||||||
|
use crate::dom::xrrigidtransform::XRRigidTransform;
|
||||||
|
use crate::script_runtime::JSContext;
|
||||||
|
use dom_struct::dom_struct;
|
||||||
|
use euclid::{Angle, RigidTransform3D, Rotation3D, Vector3D};
|
||||||
|
use js::jsapi::{Heap, JSObject};
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use webxr_api::{ApiSpace, Ray};
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct XRRay {
|
||||||
|
reflector_: Reflector,
|
||||||
|
#[ignore_malloc_size_of = "defined in webxr"]
|
||||||
|
ray: Ray<ApiSpace>,
|
||||||
|
#[ignore_malloc_size_of = "defined in mozjs"]
|
||||||
|
matrix: Heap<*mut JSObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRRay {
|
||||||
|
fn new_inherited(ray: Ray<ApiSpace>) -> XRRay {
|
||||||
|
XRRay {
|
||||||
|
reflector_: Reflector::new(),
|
||||||
|
ray,
|
||||||
|
matrix: Heap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(global: &GlobalScope, ray: Ray<ApiSpace>) -> DomRoot<XRRay> {
|
||||||
|
reflect_dom_object(Box::new(XRRay::new_inherited(ray)), global)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrray-xrray
|
||||||
|
pub fn Constructor(
|
||||||
|
window: &Window,
|
||||||
|
origin: &DOMPointInit,
|
||||||
|
direction: &XRRayDirectionInit,
|
||||||
|
) -> Fallible<DomRoot<Self>> {
|
||||||
|
if origin.w != 1.0 {
|
||||||
|
return Err(Error::Type("Origin w coordinate must be 1".into()));
|
||||||
|
}
|
||||||
|
if *direction.w != 0.0 {
|
||||||
|
return Err(Error::Type("Direction w coordinate must be 0".into()));
|
||||||
|
}
|
||||||
|
if *direction.x == 0.0 && *direction.y == 0.0 && *direction.z == 0.0 {
|
||||||
|
return Err(Error::Type(
|
||||||
|
"Direction vector cannot have zero length".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = Vector3D::new(origin.x as f32, origin.y as f32, origin.z as f32);
|
||||||
|
let direction = Vector3D::new(
|
||||||
|
*direction.x as f32,
|
||||||
|
*direction.y as f32,
|
||||||
|
*direction.z as f32,
|
||||||
|
)
|
||||||
|
.normalize();
|
||||||
|
|
||||||
|
Ok(Self::new(&window.global(), Ray { origin, direction }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrray-xrray-transform
|
||||||
|
pub fn Constructor_(window: &Window, transform: &XRRigidTransform) -> Fallible<DomRoot<Self>> {
|
||||||
|
let transform = transform.transform();
|
||||||
|
let origin = transform.translation;
|
||||||
|
let direction = transform
|
||||||
|
.rotation
|
||||||
|
.transform_vector3d(Vector3D::new(0., 0., -1.));
|
||||||
|
|
||||||
|
Ok(Self::new(&window.global(), Ray { origin, direction }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ray(&self) -> Ray<ApiSpace> {
|
||||||
|
self.ray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XRRayMethods for XRRay {
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrray-origin
|
||||||
|
fn Origin(&self) -> DomRoot<DOMPointReadOnly> {
|
||||||
|
DOMPointReadOnly::new(
|
||||||
|
&self.global(),
|
||||||
|
self.ray.origin.x as f64,
|
||||||
|
self.ray.origin.y as f64,
|
||||||
|
self.ray.origin.z as f64,
|
||||||
|
1.,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrray-direction
|
||||||
|
fn Direction(&self) -> DomRoot<DOMPointReadOnly> {
|
||||||
|
DOMPointReadOnly::new(
|
||||||
|
&self.global(),
|
||||||
|
self.ray.direction.x as f64,
|
||||||
|
self.ray.direction.y as f64,
|
||||||
|
self.ray.direction.z as f64,
|
||||||
|
0.,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://immersive-web.github.io/hit-test/#dom-xrray-matrix
|
||||||
|
fn Matrix(&self, _cx: JSContext) -> NonNull<JSObject> {
|
||||||
|
// https://immersive-web.github.io/hit-test/#xrray-obtain-the-matrix
|
||||||
|
// Step 1
|
||||||
|
if self.matrix.get().is_null() {
|
||||||
|
let cx = self.global().get_cx();
|
||||||
|
// Step 2
|
||||||
|
let z = Vector3D::new(0., 0., -1.);
|
||||||
|
// Step 3
|
||||||
|
let axis = z.cross(self.ray.direction);
|
||||||
|
// Step 4
|
||||||
|
let cos_angle = z.dot(self.ray.direction);
|
||||||
|
// Step 5
|
||||||
|
let rotation = if cos_angle > -1. && cos_angle < 1. {
|
||||||
|
Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
|
||||||
|
} else if cos_angle == -1. {
|
||||||
|
let axis = Vector3D::new(1., 0., 0.);
|
||||||
|
Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
|
||||||
|
} else {
|
||||||
|
Rotation3D::identity()
|
||||||
|
};
|
||||||
|
// Step 6
|
||||||
|
let translation = self.ray.origin;
|
||||||
|
// Step 7
|
||||||
|
// According to the spec all matrices are column-major,
|
||||||
|
// however euclid uses row vectors so we use .to_row_major_array()
|
||||||
|
let arr = RigidTransform3D::new(rotation, translation)
|
||||||
|
.to_transform()
|
||||||
|
.to_row_major_array();
|
||||||
|
create_typed_array(cx, &arr, &self.matrix);
|
||||||
|
}
|
||||||
|
NonNull::new(self.matrix.get()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ use crate::dom::xrsession::{cast_transform, ApiPose, ApiViewerPose, XRSession};
|
||||||
use crate::dom::xrspace::XRSpace;
|
use crate::dom::xrspace::XRSpace;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::RigidTransform3D;
|
use euclid::RigidTransform3D;
|
||||||
use webxr_api::Frame;
|
use webxr_api::{BaseSpace, Frame, Space};
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct XRReferenceSpace {
|
pub struct XRReferenceSpace {
|
||||||
|
@ -57,6 +57,17 @@ impl XRReferenceSpace {
|
||||||
global,
|
global,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn space(&self) -> Space {
|
||||||
|
let base = match self.ty {
|
||||||
|
XRReferenceSpaceType::Local => BaseSpace::Local,
|
||||||
|
XRReferenceSpaceType::Viewer => BaseSpace::Viewer,
|
||||||
|
XRReferenceSpaceType::Local_floor => BaseSpace::Floor,
|
||||||
|
_ => panic!("unsupported reference space found"),
|
||||||
|
};
|
||||||
|
let offset = self.offset.transform();
|
||||||
|
Space { base, offset }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XRReferenceSpaceMethods for XRReferenceSpace {
|
impl XRReferenceSpaceMethods for XRReferenceSpace {
|
||||||
|
|
|
@ -6,6 +6,8 @@ use crate::dom::bindings::callback::ExceptionHandling;
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
|
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestOptionsInit;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestTrackableType;
|
||||||
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
|
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
|
||||||
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
|
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
|
||||||
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
|
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
|
||||||
|
@ -28,6 +30,7 @@ use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::performance::reduce_timing_resolution;
|
use crate::dom::performance::reduce_timing_resolution;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::xrframe::XRFrame;
|
use crate::dom::xrframe::XRFrame;
|
||||||
|
use crate::dom::xrhittestsource::XRHitTestSource;
|
||||||
use crate::dom::xrinputsourcearray::XRInputSourceArray;
|
use crate::dom::xrinputsourcearray::XRInputSourceArray;
|
||||||
use crate::dom::xrinputsourceevent::XRInputSourceEvent;
|
use crate::dom::xrinputsourceevent::XRInputSourceEvent;
|
||||||
use crate::dom::xrreferencespace::XRReferenceSpace;
|
use crate::dom::xrreferencespace::XRReferenceSpace;
|
||||||
|
@ -37,18 +40,20 @@ use crate::dom::xrspace::XRSpace;
|
||||||
use crate::realms::InRealm;
|
use crate::realms::InRealm;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::{Rect, RigidTransform3D, Transform3D};
|
use euclid::{Rect, RigidTransform3D, Transform3D, Vector3D};
|
||||||
use ipc_channel::ipc::IpcReceiver;
|
use ipc_channel::ipc::IpcReceiver;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use metrics::ToMs;
|
use metrics::ToMs;
|
||||||
use profile_traits::ipc;
|
use profile_traits::ipc;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::f64::consts::{FRAC_PI_2, PI};
|
use std::f64::consts::{FRAC_PI_2, PI};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use webxr_api::{
|
use webxr_api::{
|
||||||
self, util, Display, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind,
|
self, util, ApiSpace, Display, EntityTypes, EnvironmentBlendMode, Event as XREvent, Frame,
|
||||||
Session, SessionId, View, Viewer, Visibility,
|
FrameUpdateEvent, HitTestId, HitTestSource, Ray, SelectEvent, SelectKind, Session, SessionId,
|
||||||
|
View, Viewer, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
@ -75,6 +80,10 @@ pub struct XRSession {
|
||||||
end_promises: DomRefCell<Vec<Rc<Promise>>>,
|
end_promises: DomRefCell<Vec<Rc<Promise>>>,
|
||||||
/// https://immersive-web.github.io/webxr/#ended
|
/// https://immersive-web.github.io/webxr/#ended
|
||||||
ended: Cell<bool>,
|
ended: Cell<bool>,
|
||||||
|
#[ignore_malloc_size_of = "defined in webxr"]
|
||||||
|
next_hit_test_id: Cell<HitTestId>,
|
||||||
|
#[ignore_malloc_size_of = "defined in webxr"]
|
||||||
|
pending_hit_test_promises: DomRefCell<HashMap<HitTestId, Rc<Promise>>>,
|
||||||
/// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame"
|
/// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame"
|
||||||
/// https://immersive-web.github.io/webxr/#opaque-framebuffer
|
/// https://immersive-web.github.io/webxr/#opaque-framebuffer
|
||||||
outside_raf: Cell<bool>,
|
outside_raf: Cell<bool>,
|
||||||
|
@ -104,6 +113,8 @@ impl XRSession {
|
||||||
input_sources: Dom::from_ref(input_sources),
|
input_sources: Dom::from_ref(input_sources),
|
||||||
end_promises: DomRefCell::new(vec![]),
|
end_promises: DomRefCell::new(vec![]),
|
||||||
ended: Cell::new(false),
|
ended: Cell::new(false),
|
||||||
|
next_hit_test_id: Cell::new(HitTestId(0)),
|
||||||
|
pending_hit_test_promises: DomRefCell::new(HashMap::new()),
|
||||||
outside_raf: Cell::new(true),
|
outside_raf: Cell::new(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +384,7 @@ impl XRSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
for event in frame.events.drain(..) {
|
for event in frame.events.drain(..) {
|
||||||
self.session.borrow_mut().apply_event(event)
|
self.handle_frame_update(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2
|
// Step 2
|
||||||
|
@ -480,6 +491,22 @@ impl XRSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_frame_update(&self, event: FrameUpdateEvent) {
|
||||||
|
match event {
|
||||||
|
FrameUpdateEvent::HitTestSourceAdded(id) => {
|
||||||
|
if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) {
|
||||||
|
promise.resolve_native(&XRHitTestSource::new(&self.global(), id, &self));
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"received hit test add request for unknown hit test {:?}",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => self.session.borrow_mut().apply_event(event),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XRSessionMethods for XRSession {
|
impl XRSessionMethods for XRSession {
|
||||||
|
@ -709,10 +736,66 @@ impl XRSessionMethods for XRSession {
|
||||||
self.session.borrow_mut().end_session();
|
self.session.borrow_mut().end_session();
|
||||||
p
|
p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://immersive-web.github.io/hit-test/#dom-xrsession-requesthittestsource
|
||||||
|
fn RequestHitTestSource(&self, options: &XRHitTestOptionsInit) -> Rc<Promise> {
|
||||||
|
let p = Promise::new(&self.global());
|
||||||
|
|
||||||
|
if self
|
||||||
|
.session
|
||||||
|
.borrow()
|
||||||
|
.granted_features()
|
||||||
|
.iter()
|
||||||
|
.find(|f| &**f == "hit-test")
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
p.reject_error(Error::NotSupported);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = self.next_hit_test_id.get();
|
||||||
|
self.next_hit_test_id.set(HitTestId(id.0 + 1));
|
||||||
|
|
||||||
|
let space = options.space.space();
|
||||||
|
let ray = if let Some(ref ray) = options.offsetRay {
|
||||||
|
ray.ray()
|
||||||
|
} else {
|
||||||
|
Ray {
|
||||||
|
origin: Vector3D::new(0., 0., 0.),
|
||||||
|
direction: Vector3D::new(0., 0., -1.),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut types = EntityTypes::default();
|
||||||
|
|
||||||
|
if let Some(ref tys) = options.entityTypes {
|
||||||
|
for ty in tys {
|
||||||
|
match ty {
|
||||||
|
XRHitTestTrackableType::Point => types.point = true,
|
||||||
|
XRHitTestTrackableType::Plane => types.plane = true,
|
||||||
|
XRHitTestTrackableType::Mesh => types.mesh = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
types.plane = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = HitTestSource {
|
||||||
|
id,
|
||||||
|
space,
|
||||||
|
ray,
|
||||||
|
types,
|
||||||
|
};
|
||||||
|
self.pending_hit_test_promises
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(id, p.clone());
|
||||||
|
|
||||||
|
self.session.borrow().request_hit_test(source);
|
||||||
|
|
||||||
|
p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct ApiSpace;
|
|
||||||
// The pose of an object in native-space. Should never be exposed.
|
// The pose of an object in native-space. Should never be exposed.
|
||||||
pub type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>;
|
pub type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>;
|
||||||
// The pose of the viewer in some api-space.
|
// The pose of the viewer in some api-space.
|
||||||
|
|
|
@ -11,7 +11,8 @@ use crate::dom::xrinputsource::XRInputSource;
|
||||||
use crate::dom::xrreferencespace::XRReferenceSpace;
|
use crate::dom::xrreferencespace::XRReferenceSpace;
|
||||||
use crate::dom::xrsession::{cast_transform, ApiPose, XRSession};
|
use crate::dom::xrsession::{cast_transform, ApiPose, XRSession};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use webxr_api::Frame;
|
use euclid::RigidTransform3D;
|
||||||
|
use webxr_api::{BaseSpace, Frame, Space};
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct XRSpace {
|
pub struct XRSpace {
|
||||||
|
@ -56,6 +57,24 @@ impl XRSpace {
|
||||||
global,
|
global,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn space(&self) -> Space {
|
||||||
|
if let Some(rs) = self.downcast::<XRReferenceSpace>() {
|
||||||
|
rs.space()
|
||||||
|
} else if let Some(source) = self.input_source.get() {
|
||||||
|
let base = if self.is_grip_space {
|
||||||
|
BaseSpace::Grip(source.id())
|
||||||
|
} else {
|
||||||
|
BaseSpace::TargetRay(source.id())
|
||||||
|
};
|
||||||
|
Space {
|
||||||
|
base,
|
||||||
|
offset: RigidTransform3D::identity(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("invalid space found")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XRSpace {
|
impl XRSpace {
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
use crate::dom::bindings::callback::ExceptionHandling;
|
use crate::dom::bindings::callback::ExceptionHandling;
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode;
|
||||||
use crate::dom::bindings::codegen::Bindings::XRTestBinding::{FakeXRDeviceInit, XRTestMethods};
|
use crate::dom::bindings::codegen::Bindings::XRTestBinding::{FakeXRDeviceInit, XRTestMethods};
|
||||||
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::fakexrdevice::{get_origin, get_views, FakeXRDevice};
|
use crate::dom::fakexrdevice::{get_origin, get_views, get_world, FakeXRDevice};
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::script_thread::ScriptThread;
|
use crate::script_thread::ScriptThread;
|
||||||
|
@ -106,13 +107,40 @@ impl XRTestMethods for XRTest {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let world = if let Some(ref w) = init.world {
|
||||||
|
let w = match get_world(w) {
|
||||||
|
Ok(w) => w,
|
||||||
|
Err(e) => {
|
||||||
|
p.reject_error(e);
|
||||||
|
return p;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Some(w)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut supports_inline, mut supports_vr, mut supports_ar) = (false, false, false);
|
||||||
|
|
||||||
|
if let Some(ref modes) = init.supportedModes {
|
||||||
|
for mode in modes {
|
||||||
|
match mode {
|
||||||
|
XRSessionMode::Immersive_vr => supports_vr = true,
|
||||||
|
XRSessionMode::Immersive_ar => supports_ar = true,
|
||||||
|
XRSessionMode::Inline => supports_inline = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let init = MockDeviceInit {
|
let init = MockDeviceInit {
|
||||||
viewer_origin: origin,
|
viewer_origin: origin,
|
||||||
views,
|
views,
|
||||||
supports_immersive: init.supportsImmersive,
|
supports_inline,
|
||||||
supports_unbounded: init.supportsUnbounded,
|
supports_vr,
|
||||||
|
supports_ar,
|
||||||
floor_origin,
|
floor_origin,
|
||||||
supported_features,
|
supported_features,
|
||||||
|
world,
|
||||||
};
|
};
|
||||||
|
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[xrDevice_isSessionSupported_immersive-ar.https.html]
|
|
||||||
[isSessionSupported resolves to false for immersive-ar on an unsupported device]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[xrDevice_requestSession_immersive-ar.https.html]
|
|
||||||
[Tests requestSession rejects immersive-ar mode when unsupported]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[ar_dom_overlay.https.html]
|
[ar_dom_overlay.https.html]
|
||||||
|
expected: ERROR
|
||||||
[Ensures DOM Overlay element selection works]
|
[Ensures DOM Overlay element selection works]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -6,7 +7,7 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Ensures DOM Overlay input deduplication works]
|
[Ensures DOM Overlay input deduplication works]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Ensures DOM Overlay feature works for immersive-ar, body element]
|
[Ensures DOM Overlay feature works for immersive-ar, body element]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
@ -15,11 +16,11 @@
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Ensures DOM Overlay Fullscreen API doesn't change DOM overlay]
|
[Ensures DOM Overlay Fullscreen API doesn't change DOM overlay]
|
||||||
expected: FAIL
|
expected: NOTRUN
|
||||||
|
|
||||||
[Ensures DOM Overlay feature works for immersive-ar, div element]
|
[Ensures DOM Overlay feature works for immersive-ar, div element]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Ensures DOM Overlay interactions on cross origin iframe are ignored]
|
[Ensures DOM Overlay interactions on cross origin iframe are ignored]
|
||||||
expected: FAIL
|
expected: NOTRUN
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
[ar_dom_overlay_hit_test.https.html]
|
[ar_dom_overlay_hit_test.https.html]
|
||||||
expected: ERROR
|
[Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,10 @@
|
||||||
[ar_hittest_subscription_inputSources.https.html]
|
[ar_hittest_subscription_inputSources.https.html]
|
||||||
expected: ERROR
|
[Ensures subscription to hit test works with an XRSpace from input source - after move - no results]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Ensures subscription to hit test works with an XRSpace from input source - no move]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,10 @@
|
||||||
[ar_hittest_subscription_refSpaces.https.html]
|
[ar_hittest_subscription_refSpaces.https.html]
|
||||||
expected: ERROR
|
[Ensures subscription to hit test works with viewer space - straight ahead - plane]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Ensures subscription to hit test works with local space]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[Ensures subscription to hit test works with local-floor space]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
[ar_hittest_subscription_states_regular.https.html]
|
|
||||||
[Hit test subscription succeeds if the feature was requested]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Hit test subscription fails if the feature was not requested]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Hit test subscription fails if the feature was requested but the session already ended]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +1,11 @@
|
||||||
[ar_hittest_subscription_transientInputSources.https.html]
|
[ar_hittest_subscription_transientInputSources.https.html]
|
||||||
expected: ERROR
|
expected: ERROR
|
||||||
|
[Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
||||||
|
[Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
||||||
|
[Ensures subscription to transient hit test works with an XRSpace from input source - no move]
|
||||||
|
expected: TIMEOUT
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[xrRay_constructor.https.html]
|
|
||||||
[XRRay constructors work]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[xrRay_matrix.https.html]
|
|
||||||
[XRRay matrix works]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -13870,7 +13870,7 @@
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"interfaces.html": [
|
"interfaces.html": [
|
||||||
"dc9a1f5f378bc50487bfb7dc3db6d121d2f325ca",
|
"1a579837cc22d31a7792566615d9e321b3d7fe39",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
@ -14609,21 +14609,21 @@
|
||||||
},
|
},
|
||||||
"webxr": {
|
"webxr": {
|
||||||
"create_session.html": [
|
"create_session.html": [
|
||||||
"af76c5a812d7d05a0158194560933def3fbdb9f9",
|
"5b5d485b372bfffb22204bc162c9e182306395cb",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"layers.html": [
|
"layers.html": [
|
||||||
"31f4b6bd51cfcca47666331857bd2bbdf84d2f5e",
|
"49821d7661f92bc9cf22232d3fcb391c2cdc7295",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"obtain_frame.html": [
|
"obtain_frame.html": [
|
||||||
"74fda5bad43e8ea95552e65380e83952680e8469",
|
"d9ff25729f9cdfd348e7c9914ce2dacd231e13a0",
|
||||||
[
|
[
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -266,11 +266,14 @@ test_interfaces([
|
||||||
"XMLHttpRequestUpload",
|
"XMLHttpRequestUpload",
|
||||||
"XMLSerializer",
|
"XMLSerializer",
|
||||||
"XRFrame",
|
"XRFrame",
|
||||||
|
"XRHitTestResult",
|
||||||
|
"XRHitTestSource",
|
||||||
"XRInputSource",
|
"XRInputSource",
|
||||||
"XRInputSourceArray",
|
"XRInputSourceArray",
|
||||||
"XRInputSourceEvent",
|
"XRInputSourceEvent",
|
||||||
"XRPose",
|
"XRPose",
|
||||||
"XRReferenceSpace",
|
"XRReferenceSpace",
|
||||||
|
"XRRay",
|
||||||
"XRRenderState",
|
"XRRenderState",
|
||||||
"XRRigidTransform",
|
"XRRigidTransform",
|
||||||
"XRSession",
|
"XRSession",
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<script>
|
<script>
|
||||||
async_test(function(t) {
|
async_test(function(t) {
|
||||||
navigator.xr.test.simulateDeviceConnection({
|
navigator.xr.test.simulateDeviceConnection({
|
||||||
supportsImmersive: true,
|
supportedModes: ["immersive-vr"],
|
||||||
views: TEST_VIEWS,
|
views: TEST_VIEWS,
|
||||||
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
||||||
}).then((m) => {
|
}).then((m) => {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
let gl = canvas.getContext('webgl');
|
let gl = canvas.getContext('webgl');
|
||||||
promise_test(async function() {
|
promise_test(async function() {
|
||||||
let mock = await navigator.xr.test.simulateDeviceConnection({
|
let mock = await navigator.xr.test.simulateDeviceConnection({
|
||||||
supportsImmersive: true,
|
supportedModes: ["immersive-vr"],
|
||||||
views: TEST_VIEWS,
|
views: TEST_VIEWS,
|
||||||
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
let gl = canvas.getContext('webgl');
|
let gl = canvas.getContext('webgl');
|
||||||
promise_test(async function() {
|
promise_test(async function() {
|
||||||
let mock = await navigator.xr.test.simulateDeviceConnection({
|
let mock = await navigator.xr.test.simulateDeviceConnection({
|
||||||
supportsImmersive: true,
|
supportedModes: ["immersive-vr"],
|
||||||
views: TEST_VIEWS,
|
views: TEST_VIEWS,
|
||||||
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue