| <!DOCTYPE html> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="resources/webxr_util.js"></script> | |
| <script src="resources/webxr_test_constants.js"></script> | |
| <script src="resources/webxr_test_asserts.js"></script> | |
| <script> | |
| const testName = "Updating XRBoundedReferenceSpace origin offset updates view, input matrices, and bounds geometry."; | |
| const INITIAL_VIEW_OFFSET = { | |
| position: [1, 2, 3], | |
| orientation: [0,0,0,1] | |
| }; | |
| const VIEWS_WITH_OFFSET = [{ | |
| eye:"left", | |
| projectionMatrix: VALID_PROJECTION_MATRIX, | |
| viewOffset: INITIAL_VIEW_OFFSET, | |
| resolution: VALID_RESOLUTION | |
| }, { | |
| eye:"right", | |
| projectionMatrix: VALID_PROJECTION_MATRIX, | |
| viewOffset: INITIAL_VIEW_OFFSET, | |
| resolution: VALID_RESOLUTION | |
| }]; | |
| const FLOOR_TRANSFORM = { | |
| position: [-0.1, -0.2, -0.3], | |
| orientation: [0, 0, 0, 1] | |
| }; | |
| const fakeDeviceInitParams = { | |
| supportsImmersive: true, | |
| supportedModes: ["inline", "immersive-vr"], | |
| views: VIEWS_WITH_OFFSET, | |
| viewerOrigin: IDENTITY_TRANSFORM, | |
| floorOrigin: FLOOR_TRANSFORM, | |
| supportedFeatures: ALL_FEATURES, | |
| boundsCoordinates: [ | |
| { x: 1, z: -1.5 }, | |
| { x: 1, z: 1.5 }, | |
| { x: -1, z: 1.5 }, | |
| { x: -1, z: -1.5 } | |
| ] | |
| }; | |
| function testFunction(session, fakeDeviceController, t) { | |
| const INITIAL_GRIP_TRANSFORM = { | |
| position: [1, 2, 3], | |
| orientation: [0, 0, 0, 1] | |
| }; | |
| const LOCAL_POINTER_TRANSFORM = { | |
| position: [1.01, 2.02, 3.03], | |
| orientation: [0, 0, 0, 1] | |
| } | |
| let input_source = fakeDeviceController.simulateInputSourceConnection({ | |
| handedness: "right", | |
| targetRayMode: "tracked-pointer", | |
| pointerOrigin: LOCAL_POINTER_TRANSFORM, | |
| gripOrigin: INITIAL_GRIP_TRANSFORM, | |
| profiles: [] | |
| }); | |
| return new Promise((resolve, reject) => { | |
| session.requestReferenceSpace('bounded-floor').then((referenceSpace) => { | |
| requestSkipAnimationFrame(session, (time, frame) => { | |
| let input_source = session.inputSources[0]; | |
| function CheckState( | |
| reference_space, | |
| expected_view_matrix, | |
| expected_grip_matrix, | |
| expected_ray_matrix, | |
| expected_bounds_geometry | |
| ) { | |
| t.step(() => { | |
| let pose = frame.getViewerPose(reference_space); | |
| let grip_pose = frame.getPose(input_source.gripSpace, reference_space); | |
| let input_pose = frame.getPose(input_source.targetRaySpace, reference_space); | |
| let view_matrix = pose.views[0].transform.inverse.matrix; | |
| let grip_matrix = grip_pose.transform.matrix; | |
| let ray_matrix = input_pose.transform.matrix; | |
| assert_matrix_approx_equals(view_matrix, expected_view_matrix); | |
| assert_matrix_approx_equals(grip_matrix, expected_grip_matrix); | |
| assert_matrix_approx_equals(ray_matrix, expected_ray_matrix); | |
| assert_equals(reference_space.boundsGeometry.length, expected_bounds_geometry.length); | |
| for (var i = 0; i < reference_space.boundsGeometry.length; ++i) { | |
| assert_point_approx_equals(reference_space.boundsGeometry[i], expected_bounds_geometry[i]); | |
| } | |
| }); | |
| } | |
| const EXPECTED_VIEW_MATRIX_1 = [ | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| -1.1, -2.2, -3.3, 1, | |
| ]; | |
| const EXPECTED_GRIP_MATRIX_1 = [ | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 1.1, 2.2, 3.3, 1, | |
| ]; | |
| const EXPECTED_RAY_MATRIX_1 = [ | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 1.11, 2.22, 3.33, 1, | |
| ]; | |
| const EXPECTED_BOUNDS_GEOMETRY_1 = [ | |
| {x: 1, y: 0, z: -1.5, w: 1}, | |
| {x: 1, y: 0, z: 1.5, w: 1}, | |
| {x: -1, y: 0, z: 1.5, w: 1}, | |
| {x: -1, y: 0, z: -1.5, w: 1}, | |
| ]; | |
| // Check state after initialization | |
| CheckState( | |
| referenceSpace, | |
| EXPECTED_VIEW_MATRIX_1, | |
| EXPECTED_GRIP_MATRIX_1, | |
| EXPECTED_RAY_MATRIX_1, | |
| EXPECTED_BOUNDS_GEOMETRY_1 | |
| ); | |
| const RADIANS_90D = Math.PI / 2; | |
| // Perform arbitrary transformation to reference space originOffset | |
| const new_position1 = { | |
| x: 10, // Translate 10 units along the x-axis | |
| y: -3, // Translate -3 units along the y-axis | |
| z: 5, // Translate 5 units along the z-axis | |
| }; | |
| const new_orientation1 = { | |
| x: Math.sin(RADIANS_90D / 2), // Rotate 90 degrees around the x-axis | |
| y: 0, | |
| z: 0, | |
| w: Math.cos(RADIANS_90D / 2), | |
| }; | |
| referenceSpace = referenceSpace.getOffsetReferenceSpace(new XRRigidTransform(new_position1, new_orientation1)); | |
| const EXPECTED_VIEW_MATRIX_2 = [ | |
| 1, 0, 0, 0, | |
| 0, 0, 1, 0, | |
| 0, -1, 0, 0, | |
| 8.9, -5.2, 1.7, 1, | |
| ]; | |
| const EXPECTED_GRIP_MATRIX_2 = [ | |
| 1, 0, 0, 0, | |
| 0, 0, -1, 0, | |
| 0, 1, 0, 0, | |
| -8.9, -1.7, -5.2, 1, | |
| ]; | |
| const EXPECTED_RAY_MATRIX_2 = [ | |
| 1, 0, 0, 0, | |
| 0, 0, -1, 0, | |
| 0, 1, 0, 0, | |
| -8.89, -1.67, -5.22, 1, | |
| ]; | |
| const EXPECTED_BOUNDS_GEOMETRY_2 = [ | |
| {x: -9, y: -6.5, z: -3, w: 1}, | |
| {x: -9, y: -3.5, z: -3, w: 1}, | |
| {x: -11, y: -3.5, z: -3, w: 1}, | |
| {x: -11, y: -6.5, z: -3, w: 1}, | |
| ]; | |
| // Check state after transformation | |
| CheckState( | |
| referenceSpace, | |
| EXPECTED_VIEW_MATRIX_2, | |
| EXPECTED_GRIP_MATRIX_2, | |
| EXPECTED_RAY_MATRIX_2, | |
| EXPECTED_BOUNDS_GEOMETRY_2 | |
| ); | |
| // Perform arbitrary transformation to reference space originOffset | |
| const new_position2 = { | |
| x: 5, // Translate 5 units along the x-axis | |
| y: 2, // Translate 2 units along the y-axis | |
| z: 0, | |
| }; | |
| const new_orientation2 = { | |
| x: 0, | |
| y: Math.sin(RADIANS_90D / 2), // Rotate 90 degrees about the y-axis | |
| z: 0, | |
| w: Math.cos(RADIANS_90D / 2), | |
| }; | |
| referenceSpace = referenceSpace.getOffsetReferenceSpace(new XRRigidTransform(new_position2, new_orientation2)); | |
| const EXPECTED_VIEW_MATRIX_3 = [ | |
| 0, 1, 0, 0, | |
| 0, 0, 1, 0, | |
| 1, 0, 0, 0, | |
| 13.9, -5.2, 3.7, 1, | |
| ]; | |
| const EXPECTED_GRIP_MATRIX_3 = [ | |
| 0, 0, 1, 0, | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 5.2, -3.7, -13.9, 1, | |
| ]; | |
| const EXPECTED_RAY_MATRIX_3 = [ | |
| 0, 0, 1, 0, | |
| 1, 0, 0, 0, | |
| 0, 1, 0, 0, | |
| 5.22, -3.67, -13.89, 1, | |
| ]; | |
| const EXPECTED_BOUNDS_GEOMETRY_3 = [ | |
| {x: 3, y: -8.5, z: -14, w: 1}, | |
| {x: 3, y: -5.5, z: -14, w: 1}, | |
| {x: 3, y: -5.5, z: -16, w: 1}, | |
| {x: 3, y: -8.5, z: -16, w: 1}, | |
| ]; | |
| // Check state after transformation | |
| CheckState( | |
| referenceSpace, | |
| EXPECTED_VIEW_MATRIX_3, | |
| EXPECTED_GRIP_MATRIX_3, | |
| EXPECTED_RAY_MATRIX_3, | |
| EXPECTED_BOUNDS_GEOMETRY_3 | |
| ); | |
| resolve(); | |
| }); | |
| }); | |
| }); | |
| }; | |
| xr_session_promise_test( | |
| testName, testFunction, fakeDeviceInitParams, 'immersive-vr', { 'requiredFeatures': ['bounded-floor'] }); | |
| </script> |