ar_hittest_subscription_refSpaces.https.html (6788B)
1 <!DOCTYPE html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 <script src="../resources/webxr_util.js"></script> 5 <script src="../resources/webxr_math_utils.js"></script> 6 <script src="../resources/webxr_test_asserts.js"></script> 7 <script src="../resources/webxr_test_constants.js"></script> 8 <script src="../resources/webxr_test_constants_fake_world.js"></script> 9 10 <script> 11 12 // 1m above world origin. 13 const VIEWER_ORIGIN_TRANSFORM = { 14 position: [0, 1, 0], 15 orientation: [0, 0, 0, 1], 16 }; 17 18 // 0.25m above world origin. 19 const FLOOR_ORIGIN_TRANSFORM = { 20 position: [0, 0.25, 0], 21 orientation: [0, 0, 0, 1], 22 }; 23 24 const fakeDeviceInitParams = { 25 supportedModes: ["immersive-ar"], 26 views: VALID_VIEWS, 27 floorOrigin: FLOOR_ORIGIN_TRANSFORM, // aka mojo_from_floor 28 viewerOrigin: VIEWER_ORIGIN_TRANSFORM, // aka mojo_from_viewer 29 supportedFeatures: ALL_FEATURES, 30 world: createFakeWorld(5.0, 2.0, 5.0), // webxr_test_constants_fake_world.js has detailed description of the fake world 31 }; 32 33 // Generates a test function given the parameters for the hit test. 34 // |ray| - ray that will be used to subscribe to hit test. 35 // |expectedPoses| - array of expected pose objects. The poses are expected to be expressed in local space. 36 // Null entries in the array mean that the given entry will not be validated. 37 // |refSpaceName| - XRReferenceSpaceType - either 'local', 'local-floor' or 'viewer'. 38 let testFunctionGenerator = function(ray, expectedPoses, refSpaceName) { 39 const testFunction = function(session, fakeDeviceController, t) { 40 return Promise.all([ 41 session.requestReferenceSpace('local'), 42 session.requestReferenceSpace('viewer'), 43 session.requestReferenceSpace('local-floor'), 44 ]).then(([localRefSpace, viewerRefSpace, localFloorRefSpace]) => { 45 46 const refSpaceNameToSpace = { 47 'local' : localRefSpace, 48 'viewer' : viewerRefSpace, 49 'local-floor' : localFloorRefSpace 50 }; 51 52 const hitTestOptionsInit = { 53 space: refSpaceNameToSpace[refSpaceName], 54 offsetRay: ray, 55 }; 56 57 return session.requestHitTestSource(hitTestOptionsInit).then( 58 (hitTestSource) => new Promise((resolve, reject) => { 59 60 const requestAnimationFrameCallback = function(time, frame) { 61 const hitTestResults = frame.getHitTestResults(hitTestSource); 62 63 t.step(() => { 64 assert_equals(hitTestResults.length, expectedPoses.length, "Results length should match expected results length"); 65 for(const [index, expectedPose] of expectedPoses.entries()) { 66 const pose = hitTestResults[index].getPose(localRefSpace); 67 assert_true(pose != null, "Each hit test result should have a pose in local space"); 68 if(expectedPose != null) { 69 assert_transform_approx_equals(pose.transform, expectedPose); 70 } 71 } 72 }); 73 74 resolve(); 75 }; 76 77 t.step(() => { 78 assert_true(hitTestSource != null, "Hit test source should not be null"); 79 }); 80 81 session.requestAnimationFrame(requestAnimationFrameCallback); 82 })); 83 }); 84 }; 85 86 return testFunction; 87 }; 88 89 // Generates a test function that will use local space for hit test subscription. 90 // See testFunctionGenerator for explanation of other parameters. 91 const localBasedTestFunctionGenerator = function(ray, expectedPoses) { 92 return testFunctionGenerator(ray, expectedPoses, 'local'); 93 }; 94 95 // Generates a test function that will use viewer space for hit test subscription. 96 // See testFunctionGenerator for explanation of other parameters. 97 const viewerBasedTestFunctionGenerator = function(ray, expectedPoses) { 98 return testFunctionGenerator(ray, expectedPoses, 'viewer'); 99 }; 100 101 // Generates a test function that will use local-floor space for hit test subscription. 102 // See testFunctionGenerator for explanation of other parameters. 103 const localFloorBasedTestFunctionGenerator = function(ray, expectedPoses) { 104 return testFunctionGenerator(ray, expectedPoses, 'local-floor'); 105 }; 106 107 // All test cases require local-floor and hit-test. 108 const sessionInit = { 'requiredFeatures': ['local-floor', 'hit-test'] }; 109 110 // Pose of the first expected hit test result - straight ahead of the viewer, viewer-facing. 111 const pose_1 = { 112 position: {x: 0.0, y: 1.0, z: -2.5, w: 1.0}, 113 orientation: {x: 0.0, y: -0.707, z: -0.707, w: 0.0}, 114 // Hit test API will set Y axis to the surface normal at the intersection point, 115 // Z axis towards the ray origin and X axis to cross product of Y axis & Z axis. 116 // If the surface normal and Z axis would be parallel, the hit test API 117 // will attempt to use `up` vector ([0, 1, 0]) as the Z axis, and if it so happens that Z axis 118 // and the surface normal would still be parallel, it will use the `right` vector ([1, 0, 0]) as the Z axis. 119 // In this particular case, `up` vector will work so the resulting pose.orientation 120 // becomes a rotation around [0, 1, 1] vector by 180 degrees. 121 }; 122 123 xr_session_promise_test( 124 "Ensures subscription to hit test works with viewer space - straight ahead - plane", 125 viewerBasedTestFunctionGenerator(new XRRay(), [pose_1]), 126 fakeDeviceInitParams, 'immersive-ar', sessionInit); 127 128 xr_session_promise_test("Ensures subscription to hit test works with viewer space - straight up - no results", 129 viewerBasedTestFunctionGenerator(new XRRay({}, {x: 0.0, y: 1.0, z : 0.0}), []), 130 fakeDeviceInitParams, 'immersive-ar', sessionInit); 131 132 const pose_2 = { 133 position: {x: 0.0, y: 0.0, z: -2.5, w: 1.0}, 134 orientation: {x: 0.0, y: -0.707, z: -0.707, w: 0.0}, 135 // See comment for pose_1.orientation for details. 136 // In this case, the hit test pose will have Y and Z axis towards the ray origin so it won't be used, 137 // but `up` vector will work so the resulting pose.orientation 138 // becomes a rotation around [0, 1, 1] vector by 180 degrees. 139 }; 140 141 xr_session_promise_test("Ensures subscription to hit test works with local space", 142 localBasedTestFunctionGenerator(new XRRay(), [pose_2]), 143 fakeDeviceInitParams, 'immersive-ar', sessionInit); 144 145 const pose_3 = { 146 position: {x: 0.0, y: 0.25, z: -2.5, w: 1.0}, 147 orientation: {x: 0.0, y: -0.707, z: -0.707, w: 0.0}, 148 // See comment for pose_1.orientation for details. 149 // In this case, the hit test pose will have Y and Z axis towards the ray origin so it won't be used, 150 // but `up` vector will work so the resulting pose.orientation 151 // becomes a rotation around [0, 1, 1] vector by 180 degrees. 152 }; 153 154 xr_session_promise_test("Ensures subscription to hit test works with local-floor space", 155 localFloorBasedTestFunctionGenerator(new XRRay(), [pose_3]), 156 fakeDeviceInitParams, 'immersive-ar', sessionInit); 157 158 </script>