-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathindex.bs
892 lines (657 loc) · 52.3 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
<pre class="metadata">
Shortname: webxr-test
Title: WebXR Test API
Group: immersivewebwg
Status: ED
ED: https://immersive-web.github.io/webxr-test-api/
Repository: immersive-web/webxr-test-api
Level: 1
Editor: Manish Goregaokar 109489, Google [Mozilla until 2020], manishearth@google.com
Editor: Alex Cooper 114716, Google http://google.com/, alcooper@google.com
Abstract: The WebXR Test API module provides a mocking interface for <a href="https://github.com/web-platform-tests/">Web Platform Tests</a> to be able to test the <a href="https://www.w3.org/TR/webxr/">WebXR Device API</a>.
Warning: custom
Custom Warning Title: Testing-only API
Custom Warning Text:
<b>The API represented in this document is for testing only and should not be exposed to users.</b>
</pre>
<pre class="link-defaults">
spec:infra;
type:dfn; text:string
type:dfn; text:list
type:dfn; for:list; text:extend
spec:webxr-1;
type:event; text:reset
type:dfn; text:xr device
type:dfn; for: XRBoundedReferenceSpace; text:native bounds geometry
type:dfn; for: XRSpace; text:native origin
type:dfn; text:viewer
type:dfn; text:view
type:dfn; text:view offset
type:dfn; for:view; text:eye
type:dfn; for:view; text:projection matrix
type:dfn; text:xr animation frame
type:dfn; text:capable of supporting
type:dfn; text:list of supported modes
type:dfn; text:list of animation frame callbacks
type:dfn; text:inline xr device
type:dfn; text:list of immersive xr devices
type:dfn; text:primary action
type:dfn; text:primary squeeze action
type:dfn; text:xr input source
</pre>
<pre class="anchors">
spec: Gamepad; urlPrefix: https://www.w3.org/TR/gamepad/#
type: enum-value; text: "xr-standard"; for: GamepadMappingType; url: dfn-xr-standard
spec:webxr-dom-overlays; urlPrefix: https://immersive-web.github.io/dom-overlays#
type:dfn; text:beforexrselect
spec:WebXR Anchors Module; urlPrefix: https://immersive-web.github.io/anchors/#
for: XRAnchor;
type: method; text: delete(); url: dom-xranchor-delete
type: dfn; text: native origin; url: xranchor-native-origin
spec:WebXR Lighting Estimation; urlPrefix: https://immersive-web.github.io/lighting-estimation/
type:interface; text: XRLightEstimate; url: xrlightestimate-interface
spec: WebXR Depth Sensing Module; urlPrefix: https://immersive-web.github.io/depth-sensing/#
type: dfn; text: create a CPU depth information instance; url: create-a-cpu-depth-information-instance
type: dfn; text: create a GPU depth information instance; url: create-a-gpu-depth-information-instance
type: dfn; text: depth coordinates transformation matrix; url: depth-coordinates-transformation-matrix
type: dfn; text: native depth sensing; url: native-depth-sensing
spec: WebXR Raw Camera Access Module; urlPrefix: https://immersive-web.github.io/raw-camera-access/#
type: dfn; text: obtain camera
</pre>
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="favicon-96x96.png">
<style>
.unstable::before {
content: "This section is not stable";
display: block;
font-weight: bold;
text-align: right;
color: red;
}
.unstable {
border: thin solid pink;
border-radius: .5em;
padding: .5em;
margin: .5em calc(-0.5em - 1px);
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='290'><text transform='rotate(-45)' text-anchor='middle' font-family='sans-serif' font-weight='bold' font-size='70' y='210' opacity='.1'>Unstable</text></svg>");
background-repeat: repeat;
background-color: #FFF4F4;
}
.unstable h3:first-of-type {
margin-top: 0.5rem;
}
.unstable.example:not(.no-marker)::before {
content: "Example " counter(example) " (Unstable)";
float: none;
}
.non-normative::before {
content: "This section is non-normative.";
font-style: italic;
}
.tg {
border-collapse: collapse;
border-spacing: 0;
}
.tg th {
border-style: solid;
border-width: 1px;
background: #90b8de;
color: #fff;
font-family: sans-serif;
font-weight: bold;
border-color: grey;
}
.tg td {
padding: 4px 5px;
background-color: rgb(221, 238, 255);
font-family: monospace;
border-style: solid;
border-width: 1px;
border-color: grey;
overflow: hidden;
word-break: normal;
}
</style>
Introduction {#intro}
============
<section class="non-normative">
In order to allow <a href="https://web-platform-tests.org/">Web Platform Tests</a> for WebXR there are some basic functions which are common across all tests, such as adding a fake test device and specifying poses. Below is an API which attempts to capture the necessary functions, based off what was defined in the spec. Different browser vendors can implement this API in whatever way is most compatible with their browser. For example, some browsers may back the interface with a WebDriver API while others may use HTTP or IPC mechanisms to communicate with an out of process fake backend.
These initialization object and control interfaces do not represent a complete set of WebXR functionality, and are expected to be expanded on as the WebXR spec grows.
</section>
Conformance {#conformance}
============
Interfaces and functionality exposed by this specification SHOULD NOT be exposed to typical browsing experiences, and instead SHOULD only be used when running <a href="https://web-platform-tests.org/">Web Platform Tests</a>.
Simulated devices {#simulated-devices}
============
Simulated XR Device {#simulated-devices-xr}
------------
This API gives tests the ability to spin up a <dfn>simulated XR device</dfn> which is an [=/XR device=] which from the point of view of the WebXR API behaves like a normal [=/XR device=]. These [=simulated XR device|simulated XR devices=] can be controlled by the associated {{FakeXRDevice}} object.
Every [=simulated XR device=] may have an <dfn for="simulated XR device">native bounds geometry</dfn> which is an array of {{DOMPointReadOnly}}s, used to initialize the [=XRBoundedReferenceSpace/native bounds geometry=] of any {{XRBoundedReferenceSpace}}s created for the device. If <code>null</code>, the device is treated as if it is not currently tracking a bounded reference space.
Every [=simulated XR device=] may have a <dfn for="simulated XR device">floor origin</dfn> which is an {{XRRigidTransform}} used to note the position of the physical floor. If <code>null</code>, the device is treated as if it is unable to identify the physical floor.
Every [=simulated XR device=] may have an <dfn for="simulated XR device">viewer origin</dfn> which is an {{XRRigidTransform}} used to set the position and orientation of the [=viewer=]. If <code>null</code>, the device is treated as if it has lost tracking.
Every [=simulated XR device=] has an <dfn for="simulated XR device">emulated position boolean</dfn> which is a boolean used to set the {{XRPose/emulatedPosition}} of any {{XRPose}}s produced involving the [=viewer=]. This is initially <code>false</code>.
Every [=simulated XR device=] has an <dfn for="simulated XR device">visibility state</dfn> which is an {{XRVisibilityState}} used to set the {{XRSession/visibilityState}} of any {{XRSession}}s associated with the [=simulated XR device=] . This is initially {{XRVisibilityState/"visible"}}. When it is changed, the associated changes must be reflected on the {{XRSession}}, including triggering {{XRSession/onvisibilitychange}} events if necessary.
Every [=simulated XR device=] has a <dfn for="simulated XR device">list of primary views</dfn> which is a list of [=view|views=] that must be rendered to for an immersive experience. There must be at least one primary view.
Every [=simulated XR device=] may have a <dfn for="simulated XR device">list of secondary views</dfn> which is a list of [=view|views=] that may or may not be rendered to. There may be any number of secondary views.
Every [=view=] for a [=simulated XR device=] has an associated <dfn for=view>device resolution</dfn>, which is an instance of {{FakeXRDeviceResolution}}. This resolution must be used when constructing {{XRViewport}} values for the [=view=], based on the canvas size.
Every [=view=] for a [=simulated XR device=] may have an associated <dfn for=view>field of view</dfn>, which is an instance of {{FakeXRFieldOfViewInit}} used to calculate projection matrices using depth values. If the [=field of view=] is set, projection matrix values are calculated using the [=field of view=] and {{XRRenderState/depthNear}} and {{XRRenderState/depthFar}} values.
Simulated Input Device {#simulated-devices-input}
------------
This API gives tests the ability to spin up a <dfn>simulated XR input source</dfn> which is an [=/XR input source=] which from the point of view of the WebXR API behaves like a normal [=/XR input source=]. These [=simulated XR input source|simulated XR input sources=] can be controlled by the associated {{FakeXRInputController}} object.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">handedness</dfn> which is an {{XRHandedness}} value that MUST be returned for the corresponding [=/XR input source|XR input source's=] {{XRInputSource/handedness}} attribute.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">targetRayMode</dfn> which is an {{XRTargetRayMode}} value that MUST be returned for the corresponding [=/XR input source|XR input source's=] {{XRInputSource/targetRayMode}} attribute.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">pointerOrigin</dfn> which is an {{XRRigidTransform}} used to note the origin of the {{XRInputSource/targetRaySpace}}.
A [=simulated XR input source=] may have a <dfn for="simulated XR input source">gripOrigin</dfn> which is an {{XRRigidTransform}} used to note the origin of the {{XRInputSource/gripSpace}}. If this is <code>null</code> the [=simulated XR input source=] is not tracked.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">profiles</dfn> array which is an array of {{DOMString}}s which MUST be returned for the corresponding [=/XR input source|XR input source's=] {{XRInputSource/profiles}} attribute.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">buttonState</dfn> array which is an array of {{FakeXRButtonStateInit}}s. If a {{FakeXRButtonType/"grip"}} button is specified, it SHOULD drive the [=/primary squeeze action=]. If a UA implements the <a href="https://immersive-web.github.io/webxr-gamepads-module/">WebXR Gamepads Module</a> [=buttonState=] SHOULD be used to set the state for the corresponding [=/XR input source|XR input source's=] {{XRInputSource/gamepad}} object, which SHOULD be of type {{GamepadMappingType/"xr-standard"}} if enough buttons are specified to support it.
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">connectionState</dfn> which is a boolean that is initially <code>true</code> and indicates whether the associated [=/XR input source=] should appear in {{XRSession/inputSources}}. When it is changed the associated changes must be reflected on the XRSession, including triggering the {{XRSession/inputsourceschange}} event if necessary by the [=next animation frame=].
Every [=simulated XR input source=] has a <dfn for="simulated XR input source">primaryActionStarted</dfn> which is a boolean, initially set to <code>false</code>, that indicates whether or not the primary action of the XR input source has been started.
Initialization {#initialization}
==============
navigator.xr.test {#xr-test-attribute}
------------
<script type="idl">
partial interface XRSystem {
[SameObject] readonly attribute XRTest test;
};
</script>
The <dfn attribute for="XRSystem">test</dfn> attribute's getter MUST return the {{XRTest}} object that is associated with it. This object MAY be lazily created.
XRTest {#xrtest-interface}
------------
The {{XRTest}} object is the entry point for all testing.
<script type="idl">
interface XRTest {
Promise<FakeXRDevice> simulateDeviceConnection(FakeXRDeviceInit init);
undefined simulateUserActivation(Function f);
Promise<undefined> disconnectAllDevices();
};
</script>
<div class="algorithm" data-algorithm="simulate-device-connection">
The <dfn method for="XRTest">simulateDeviceConnection(|init|)</dfn> method creates a new [=simulated XR device=].
When this method is invoked, the user agent MUST run the following steps:
1. Let |promise| be [=a new Promise=].
1. Run the following steps [=in parallel=]:
1. Let |device| be a new [=simulated XR device=].
1. For each |view| in |init|'s {{FakeXRDeviceInit/views}}:
1. Let |p| be the result of running [=parse a view=] on |view|.
1. If running [=parse a view=] threw an error, reject |promise| with this error and abort these steps.
1. [=list/Append=] |p| to |device|'s [=simulated XR device/list of primary views=].
1. If |init|'s {{FakeXRDeviceInit/secondaryViews}} is set, for each |secondaryView| in |init|'s {{FakeXRDeviceInit/secondaryViews}}:
1. Let |s| be the result of running [=parse a view=] on |secondaryView|.
1. If running [=parse a view=] threw an error, reject |promise| with this error and abort these steps.
1. [=list/Append=] |s| to |device|'s [=simulated XR device/list of secondary views=].
1. If |init|'s {{FakeXRDeviceInit/boundsCoordinates}} is set, perform the following steps:
1. If |init|'s {{FakeXRDeviceInit/boundsCoordinates}} has less than 3 elements, reject |promise| with {{TypeError}} and abort these steps.
1. Set |device|'s [=simulated XR device/native bounds geometry=] to |init|'s {{FakeXRDeviceInit/boundsCoordinates}}.
1. If |init|'s {{FakeXRDeviceInit/floorOrigin}} is set, set |device|'s [=simulated XR device/floor origin=] to |init|'s {{FakeXRDeviceInit/floorOrigin}}.
1. If |init|'s {{FakeXRDeviceInit/viewerOrigin}} is set, set |device|'s [=simulated XR device/viewer origin=] to |init|'s {{FakeXRDeviceInit/viewerOrigin}}.
1. Let |supportedModes| be an empty list of {{XRSessionMode}}s.
1. Modify |supportedModes| as follows:
<dl class="switch">
<dt>If |init|'s {{FakeXRDeviceInit/supportedModes}} is present:</dt>
<dd>
1. Append the contents of |init|'s {{FakeXRDeviceInit/supportedModes}} to |supportedModes|.
1. If |supportedModes| is empty, append <code>"inline"</code> to it.
</dd>
<dt>Else</dt>
<dd>
1. Append <code>"inline"</code> to |supportedModes|.
1. If |init|'s {{FakeXRDeviceInit/supportsImmersive}} is <code>true</code>, append <code>"immersive-vr"</code> to |supportedModes|.
</dd>
</dl>
1. If |init|'s {{FakeXRDeviceInit/supportedFeatures}} is set, for each |mode| in |supportedModes|:
1. Associate |init|'s {{FakeXRDeviceInit/supportedFeatures}} to |mode|
NOTE: each |device| stores a [=capable of supporting|list of features it is capable of supporting=] per {{XRSessionMode}}. Most tests only test one mode anyway so there isn't much to be gained by splitting features per mode in {{FakeXRDeviceInit}}. Users wishing different modes supporting different features should create multiple devices instead.
1. Set |device|'s [=list of supported modes=] to |supportedModes|.
1. Register |device| based on the following:
1. If |supportedModes| contains <code>"immersive-vr"</code> or <code>"immersive-ar"</code>, append |device| to the {{Navigator/xr}}'s [=list of immersive XR devices=].
1. If |supportedModes| contains <code>"inline"</code>, set the [=inline XR device=] to |device|.
1. Let |d| be a new {{FakeXRDevice}} object with [=FakeXRDevice/device=] as |device|.
1. [=/Resolve=] |promise| with |d|.
1. Return |promise|.
</div>
When <dfn method for=XRTest>simulateUserActivation(f)</dfn> is called, invoke <code>f</code> as if it had [=transient activation=].
When <dfn method for=XRTest>disconnectAllDevices()</dfn> is called, remove all [=simulated XR devices=] from the {{Navigator/xr}}'s [=list of immersive XR devices=] as if they were disconnected. If the [=inline XR device=] is a [=simulated XR device=], reset it to the default [=inline XR device=].
FakeXRDeviceInit {#fakexrdeviceinit-dict}
------------
<script type="idl">
dictionary FakeXRDeviceInit {
required boolean supportsImmersive;
sequence<XRSessionMode> supportedModes;
required sequence<FakeXRViewInit> views;
sequence<FakeXRViewInit> secondaryViews;
sequence<any> supportedFeatures;
sequence<FakeXRBoundsPoint> boundsCoordinates;
FakeXRRigidTransformInit floorOrigin;
FakeXRRigidTransformInit viewerOrigin;
// Hit test extensions:
FakeXRWorldInit world;
// Depth sensing extensions:
FakeXRDepthSensingDataInit depthSensingData;
};
dictionary FakeXRViewInit {
required XREye eye;
required sequence<float> projectionMatrix;
required FakeXRDeviceResolution resolution;
required FakeXRRigidTransformInit viewOffset;
FakeXRFieldOfViewInit fieldOfView;
// Raw camera access extensions:
FakeXRCameraImage cameraImageInit;
};
dictionary FakeXRFieldOfViewInit {
required float upDegrees;
required float downDegrees;
required float leftDegrees;
required float rightDegrees;
};
dictionary FakeXRDeviceResolution {
required long width;
required long height;
};
dictionary FakeXRBoundsPoint {
double x; double z;
};
dictionary FakeXRRigidTransformInit {
required sequence<float> position;
required sequence<float> orientation;
};
</script>
<p class="advisement">
The {{FakeXRDeviceInit/supportsImmersive}} is deprecated in favor of {{FakeXRDeviceInit/supportedModes}} and will be removed in future revisions of the specification.
</p>
<div class="algorithm" data-algorithm="parse-rigid-transform">
To <dfn>parse a rigid transform</dfn> given a {{FakeXRRigidTransformInit}} |init|, perform the following steps:
1. Let |p| be |init|'s {{FakeXRRigidTransformInit/position}}.
1. If |p| does not have three elements, throw a {{TypeError}}.
1. Let |o| be |init|'s {{FakeXRRigidTransformInit/orientation}}.
1. If |o| does not have four elements, throw a {{TypeError}}.
1. Let |position| be a {{DOMPointInit}} with {{DOMPointInit/x}}, {{DOMPointInit/y}} and {{DOMPointInit/z}} equal to the three elements of |p| in order, and {{DOMPointInit/w}} equal to <code>1</code>.
1. Let |orientation| be a {{DOMPointInit}} with {{DOMPointInit/x}}, {{DOMPointInit/y}}, {{DOMPointInit/z}}, and {{DOMPointInit/w}} equal to the four elements of |o| in order.
1. {{XRRigidTransform/constructor|Construct an XRRigidTransform}} |transform| with {{XRRigidTransform/position}} |position| and {{XRRigidTransform/orientation}} |orientation|.
1. Return |transform|.
</div>
<div class="algorithm" data-algorithm="parse-view">
To <dfn>parse a view</dfn> given a {{FakeXRViewInit}} |init|, perform the following steps:
1. Let |view| be a new [=view=].
1. Set |view|'s [=view/eye=] to |init|'s {{FakeXRViewInit/eye}}.
1. If |init|'s {{FakeXRViewInit/projectionMatrix}} does not have 16 elements, throw a {{TypeError}}.
1. Set |view|'s [=view/projection matrix=] to |init|'s {{FakeXRViewInit/projectionMatrix}}.
1. Set |view|'s [=view offset=] to the result of running [=parse a rigid transform=] |init|'s {{FakeXRViewInit/viewOffset}}.
1. Set |view|'s [=view/device resolution=] to |init|'s {{FakeXRViewInit/resolution}}.
1. If |init|'s {{FakeXRViewInit/fieldOfView}} is set, perform the following steps:
1. Set |view|'s [=view/field of view=] to |init|'s {{FakeXRViewInit/fieldOfView}}.
1. Set |view|'s [=view/projection matrix=] to the projection matrix corresponding to this field of view, and depth values equal to {{XRRenderState/depthNear}} and {{XRRenderState/depthFar}} of any {{XRSession}} associated with the device. If there currently is none, use the default values of <code>near=0.1, far=1000.0</code>.
1. Return |view|.
</div>
FakeXRRigidTransformInit {#fakexrrigidtransform-base-space}
------------
The WebXR API never exposes native origins directly, instead exposing transforms between them, so we need to specify a <dfn>base reference space</dfn> for {{FakeXRRigidTransformInit}}s so that we can have consistent numerical values across implementations. When used as an origin, {{FakeXRRigidTransformInit}}s are in the [=base reference space=] where the [=viewer=]'s [=native origin=] is identity at initialization, unless otherwise specified. In this space, the {{XRReferenceSpaceType/"local"}} reference space has a [=native origin=] of identity. This is an arbitrary choice: changing this reference space doesn't affect the data returned by the WebXR API, but we must make such a choice so that the tests produce the same results across different UAs. When used as an origin it is logically a transform <i>from</i> the origin's space <i>to</i> the underlying [=base reference space=] described above.
Mocking {#mocking}
==============
FakeXRDevice {#fakexrdevice-interface}
------------
<script type="idl">
interface FakeXRDevice : EventTarget {
undefined setViews(sequence<FakeXRViewInit> views, optional sequence<FakeXRViewInit> secondaryViews);
Promise<undefined> disconnect();
undefined setViewerOrigin(FakeXRRigidTransformInit origin, optional boolean emulatedPosition = false);
undefined clearViewerOrigin();
undefined setFloorOrigin(FakeXRRigidTransformInit origin);
undefined clearFloorOrigin();
undefined setBoundsGeometry(sequence<FakeXRBoundsPoint> boundsCoordinates);
undefined simulateResetPose();
undefined simulateVisibilityChange(XRVisibilityState state);
FakeXRInputController simulateInputSourceConnection(FakeXRInputSourceInit init);
// Hit test extensions:
undefined setWorld(FakeXRWorldInit world);
undefined clearWorld();
// Depth sensing extensions:
undefined setDepthSensingData(FakeXRDepthSensingDataInit depthSensingData);
undefined clearDepthSensingData();
};
</script>
Each {{FakeXRDevice}} object has an associated <dfn for=FakeXRDevice>device</dfn>, which is a [=simulated XR device=] that it is able to control.
<div class="algorithm" data-algorithm="next-animation-frame">
Operations on the {{FakeXRDevice}}'s [=FakeXRDevice/device=] typically take place on the <dfn for=XRSession>next animation frame</dfn>, i.e. they are not immediately observable until a future {{XRSession/requestAnimationFrame()}} callback.
To determine when this frame is, for a given operation, choose a frame based on the following:
<dl class=switch>
<dt>If such an operation is triggered within an [=XR animation frame=]:</dt>
<dd>Choose the next [=XR animation frame=], whenever it may occur</dd>
<dt>If such an operation is triggered outside of an [=XR animation frame=]:</dt>
<dd>Choose either the next or next-to-next [=XR animation frame=]. The precise choice is up to the user agent and may be dependent on the exact timing of these events.
</dd>
</dl>
NOTE: The reason we defer an extra frame when there are pending animation frame callbacks is to avoid having to deal with potential race conditions when the device is ready to trigger an animation frame callback, but has not yet. In practice, this means that tests should be written so that they wait until they have performed all such operations <i>before</i> calling the next {{XRSession/requestAnimationFrame()}}, and in case they are running outside of an [=XR animation frame=], should always wait two frames before expecting any updates to take effect.
</div>
<div class="algorithm" data-algorithm="parse-views-list">
To <dfn>parse a list of views</dfn> on a [=list=] |views| run the following steps:
1. Let |l| be an empty [=list=]
1. For each |view| in |views|:
1. Let |v| be the result of running [=parse a view=] on |view|.
1. [=list/Append=] |v| to |l|.
1. Return |l|.
</div>
<div class="algorithm" data-algorithm="set-views">
The <dfn method for=FakeXRDevice>setViews(|views|, |secondaryViews|)</dfn> method performs the following steps:
1. On the [=next animation frame=], run the following steps:
1. Let |p| be the result of running [=parse a list of views=] on |views|.
1. Set [=FakeXRDevice/device=]'s [=simulated XR device/list of primary views=] to |p|.
1. If |secondaryViews| is set, let |s| be the result of running [=parse a list of views=] on |secondaryViews|.
1. Set [=FakeXRDevice/device=]'s [=simulated XR device/list of secondary views=] to |s|.
</div>
<div class="algorithm" data-algorithm="disconnect">
When <dfn method for=FakeXRDevice>disconnect()</dfn> method is called, perform the following steps:
1. Remove [=FakeXRDevice/device=] from the {{Navigator/xr}}'s [=list of immersive XR devices=] as if it were disconnected.
1. If the [=inline XR device=] is equal to the {{FakeXRDevice}}, reset it to the default [=inline XR device=].
</div>
<div class="algorithm" data-algorithm="set-viewer-origin">
The <dfn method for=FakeXRDevice>setViewerOrigin(|origin|, |emulatedPosition|)</dfn> performs the following steps:
1. Let |o| be the result of running [=parse a rigid transform=] on |origin|.
1. On the [=next animation frame=], perform the following steps:
1. Set [=FakeXRDevice/device=]'s [=simulated XR device/viewer origin=] to |o|.
1. Set [=FakeXRDevice/device=]'s [=simulated XR device/emulated position boolean=] to |emulatedPosition|.
</div>
The <dfn method for=FakeXRDevice>clearViewerOrigin()</dfn> method will, on the [=next animation frame=], set [=FakeXRDevice/device=]'s [=simulated XR device/viewer origin=] to <code>null</code>.
The <dfn method for=FakeXRDevice>simulateVisibilityChange(|state|)</dfn> method will, as soon as possible, set [=FakeXRDevice/device=]'s [=simulated XR device/visibility state=] to |state|.
<div class="algorithm" data-algorithm="set-floor-origin">
The <dfn method for=FakeXRDevice>setFloorOrigin(|origin|)</dfn> performs the following steps:
1. Let |o| be the result of running [=parse a rigid transform=] on |origin|.
1. On the [=next animation frame=], set [=FakeXRDevice/device=]'s [=simulated XR device/floor origin=] to |o|.
</div>
The <dfn method for=FakeXRDevice>clearFloorOrigin()</dfn> method will, on the [=next animation frame=], set [=FakeXRDevice/device=]'s [=simulated XR device/floor origin=] to <code>null</code>.
<div class="algorithm" data-algorithm="set-bounds-geometry">
The <dfn method for=FakeXRDevice>setBoundsGeometry(|boundsCoordinates|)</dfn> performs the following steps:
1. If |boundsCoordinates| has fewer than 3 elements, throw a {{TypeError}}.
1. On the [=next animation frame=], set [=FakeXRDevice/device=]'s [=simulated XR device/native bounds geometry=] to |boundsCoordinates|.
</div>
The <dfn method for=FakeXRDevice>simulateResetPose()</dfn> method will, as soon as possible, behave as if the [=FakeXRDevice/device=]'s [=viewer=]'s [=native origin=] had a discontinuity, triggering appropriate {{reset}} events.
<div class="algorithm" data-algorithm="simulate-input-source-connection">
The <dfn method for=FakeXRDevice>simulateInputSourceConnection(|init|)</dfn> method creates a new [=simulated XR input source=].
When this method is invoked, the user agent MUST run the following steps:
1. Let |inputSource| be a new [=simulated XR input source=].
1. Set |inputSource|'s [=simulated XR input source/handedness=] to |init|'s {{FakeXRInputSourceInit/handedness}}.
1. Set |inputSource|'s [=simulated XR input source/targetRayMode=] to |init|'s {{FakeXRInputSourceInit/targetRayMode}}.
1. Set |inputSource|'s [=simulated XR input source/profiles=] to |init|'s {{FakeXRInputSourceInit/profiles}}
1. If |init|'s {{FakeXRInputSourceInit/gripOrigin}} is set, set |inputSource|'s [=simulated XR input source/gripOrigin=] to the result of running [=parse a rigid transform=] on |init|'s {{FakeXRInputSourceInit/gripOrigin}}
1. Set |inputSource|'s [=simulated XR input source/pointerOrigin=] to the result of running [=parse a rigid transform=] on |init|'s {{FakeXRInputSourceInit/pointerOrigin}}
1. If |init|'s {{FakeXRInputSourceInit/supportedButtons}} is set, set |inputSource|'s [=simulated XR input source/buttonState=] to the result of running [=parse supported buttons=] on |init|'s {{FakeXRInputSourceInit/supportedButtons}}
1. If |init|'s {{FakeXRInputSourceInit/selectionClicked}} is set to <code>true</code>, run [=simulate a full primary action=] on |inputSource|.
1. If |init|'s {{FakeXRInputSourceInit/selectionStarted}} is set to <code>true</code>, run [=start a primary action=] on |inputSource|.
1. By the [=next animation frame=] notify {{XRSession}} of the new [=/XR input source=].
1. Let |c| be a new {{FakeXRInputController}} object with {[=FakeXRInputController/inputSource=] as |inputSource|.
1. Return |c|.
</div>
FakeXRInputController {#fakexrinputcontroller-init}
------------
<script type="idl">
dictionary FakeXRInputSourceInit {
required XRHandedness handedness;
required XRTargetRayMode targetRayMode;
required FakeXRRigidTransformInit pointerOrigin;
required sequence<DOMString> profiles;
boolean selectionStarted = false;
boolean selectionClicked = false;
sequence<FakeXRButtonStateInit> supportedButtons;
FakeXRRigidTransformInit gripOrigin;
};
interface FakeXRInputController {
undefined setHandedness(XRHandedness handedness);
undefined setTargetRayMode(XRTargetRayMode targetRayMode);
undefined setProfiles(sequence<DOMString> profiles);
undefined setGripOrigin(FakeXRRigidTransformInit gripOrigin, optional boolean emulatedPosition = false);
undefined clearGripOrigin();
undefined setPointerOrigin(FakeXRRigidTransformInit pointerOrigin, optional boolean emulatedPosition = false);
undefined disconnect();
undefined reconnect();
undefined startSelection();
undefined endSelection();
undefined simulateSelect();
undefined setSupportedButtons(sequence<FakeXRButtonStateInit> supportedButtons);
undefined updateButtonState(FakeXRButtonStateInit buttonState);
};
enum FakeXRButtonType {
"grip",
"touchpad",
"thumbstick",
"optional-button",
"optional-thumbstick"
};
dictionary FakeXRButtonStateInit {
required FakeXRButtonType buttonType;
required boolean pressed;
required boolean touched;
required float pressedValue;
float xValue = 0.0;
float yValue = 0.0;
};
</script>
Each {{FakeXRInputController}} object has an associated <dfn for="FakeXRInputController">inputSource</dfn>, which is a [=simulated XR input source=] that it is able to control.
Since user agents may opt to send input events on a per-frame basis, the results of all {{FakeXRInputController}} methods and {{FakeXRDevice/simulateInputSourceConnection()}} are not guaranteed to be visible (via, e.g. {{XRSession/inputSources}} or {{XRSession/oninputsourceschange}} events) until the [=next animation frame=].
<div class="algorithm" data-algorithm="start-a-primary-action">
To <dfn>start a primary action</dfn> on a [=simulated XR input source=] run the following steps:
1. If [=simulated XR input source/primaryActionStarted=] is true, abort these steps.
1. Set [=simulated XR input source/primaryActionStarted=] to true.
1. By the [=next animation frame=] indicate to the {{XRSession}} that the corresponding [=/XR input source|XR input source's=] [=/primary action=] has started.
</div>
<div class="algorithm" data-algorithm="stop-a-primary-action">
To <dfn>stop a primary action</dfn> on a [=simulated XR input source=] run the following steps:
1. If [=simulated XR input source/primaryActionStarted=] is false, abort these steps.
1. Set [=simulated XR input source/primaryActionStarted=] to false.
1. By the [=next animation frame=] indicate to the {{XRSession}} that the corresponding [=/XR input source|XR input source's=] [=/primary action=] has stopped.
</div>
<div class="algorithm" data-algorithm="simulate-a-full-primary-action">
To <dfn>simulate a full primary action</dfn> on a [=simulated XR input source=] |source|, run the following steps:
1. Let |current| be the current value of [=simulated XR input source/primaryActionStarted=].
1. Run [=start a primary action=] on |source|
1. Run [=stop a primary action=] on |source|
1. If |current| is <code>true</code> run [=start a primary action=] on |source|
</div>
Note: If a gamepad is attached to the [=simulated XR input source=], then running [=start a primary action=] or [=stop a primary action=] should also ensure that the primary input's corresponding gamepad button is updated accordingly.
Note: If both [=start a primary action=] and [=stop a primary action=] are run in the same frame, then by the [=next animation frame=] It is expected that {{XRSession/onselect}} and {{XRSession/onselectend}} events will fire.
<div class="algorithm" data-algorithm="parse-supported-buttons">
To <dfn>parse supported buttons</dfn> on a sequence of {{FakeXRButtonStateInit}}s, |buttons| run the following steps:
1. Let |l| be an empty list of {{FakeXRButtonStateInit}}s
1. For each |button| in |buttons|:
1. If |l| does not contain a {{FakeXRButtonStateInit}} whose {{FakeXRButtonStateInit/buttonType}} matches |button|'s {{FakeXRButtonStateInit/buttonType}}, append |button| to |l|.
1. Return |l|
</div>
The <dfn method for=FakeXRInputController>setHandedness(|handedness|)</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/handedness=] to |handedness|.
The <dfn method for=FakeXRInputController>setTargetRayMode(|targetRayMode|)</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/targetRayMode=] to |targetRayMode|.
The <dfn method for=FakeXRInputController>setProfiles(|profiles|)</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/profiles=] to |profiles|.
The <dfn method for=FakeXRInputController>setGripOrigin(|gripOrigin|)</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/gripOrigin=] to the result of running [=parse a rigid transform=] on |gripOrigin|.
The <dfn method for=FakeXRInputController>clearGripOrigin()</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/gripOrigin=] to <code>null</code>.
The <dfn method for=FakeXRInputController>setPointerOrigin(|pointerOrigin|)</dfn> method will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/pointerOrigin=] to the result of running [=parse a rigid transform=] on |pointerOrigin|.
<div class="algorithm" data-algorithm="fakexrinputcontroller-disconnect">
The <dfn method for=FakeXRInputController>disconnect()</dfn> method will run the following steps:
1. If [=FakeXRInputController/inputSource=]'s [=simulated XR input source/connectionState=] is <code>false</code>, abort these steps.
1. Set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/connectionState=] to <code>false</code>.
1. By the [=next animation frame=], notify the {{XRSession}} that this [=/XR input source=] has been removed.
</div>
<div class="algorithm" data-algorithm="fakexrinputcontroller-reconnect">
The <dfn method for=FakeXRInputController>reconnect()</dfn> method will run the following steps:
1. If [=FakeXRInputController/inputSource=]'s [=simulated XR input source/connectionState=] is <code>true</code>, abort these steps.
1. Set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/connectionState=] to <code>true</code>.
1. By the [=next animation frame=], notify the {{XRSession}} that this [=/XR input source=] has been added.
</div>
The <dfn method for=FakeXRInputController>startSelection()</dfn> method will run [=start a primary action=] on [=FakeXRInputController/inputSource=].
The <dfn method for=FakeXRInputController>endSelection()</dfn> method will run [=stop a primary action=] on [=FakeXRInputController/inputSource=].
The <dfn method for=FakeXRInputController>simulateSelect()</dfn> method will run [=simulate a full primary action=] on [=FakeXRInputController/inputSource=].
The <dfn method for=FakeXRInputController>setSupportedButtons(|supportedButtons|)</dfn> will, by the [=next animation frame=], set [=FakeXRInputController/inputSource=]'s [=simulated XR input source/buttonState=] to the result of running [=parse supported buttons=] on |supportedButtons|.
Note: As user agents may recreate the {{XRInputSource}} or {{XRInputSource/gamepad}} objects on buttons being changed, this method SHOULD NOT be used to simulate changes to button state.
<div class="algorithm" data-algorithm="update-button-state">
The <dfn method for=FakeXRInputController>updateButtonState(|buttonState|)</dfn> will run the following steps:
1. Let |validState| equal the results of running [=validate a button state=] on |buttonState|.
1. Let |foundState| be <code>null</code>.
1. For every |state| in [=FakeXRInputController/inputSource=]'s [=simulated XR input source/buttonState=] array:
1. If |state|'s {{FakeXRButtonStateInit/buttonType}} matches |buttonState|'s {{FakeXRButtonStateInit/buttonType}}:
1. Set |foundState| to a reference of |state|
1. Break out of this loop
1. If |foundState| is <code>null</code> throw a {{NotFoundError}}
1. Update |foundState|'s attributes in [=FakeXRInputController/inputSource=]'s [=simulated XR input source/buttonState=] to match those of |validState|. Note: If {{FakeXRButtonStateInit/buttonType}} is {{FakeXRButtonType/grip}}, then [=XR input source=]'s [=/primary squeeze action=] should be updated.
</div>
<div class="algorithm" data-algorithm="validate-a-button-state">
To <dfn>validate a button state</dfn> on a {{FakeXRButtonStateInit}} |buttonState| run the following steps:
1. Let |validState| equal |buttonState|.
1. If {{FakeXRButtonStateInit/pressed}} is <code>true</code> and {{FakeXRButtonStateInit/touched}} is <code>false</code>, throw a {{TypeError}}.
1. If {{FakeXRButtonStateInit/pressedValue}} is less than <code>0.0</code>, throw a {{TypeError}}.
1. If {{FakeXRButtonStateInit/pressedValue}} is greater than <code>0.0</code> and {{FakeXRButtonStateInit/touched}} is <code>false</code> throw a {{TypeError}}.
1. If {{FakeXRButtonStateInit/buttonType}} is not one of: {{FakeXRButtonType/"touchpad"}}, {{FakeXRButtonType/"thumbstick"}}, or {{FakeXRButtonType/"optional-thumbstick"}}:
1. Set |validState|'s {{FakeXRButtonStateInit/xValue}} to <code>0.0</code>.
1. Set |validState|'s {{FakeXRButtonStateInit/yValue}} to <code>0.0</code>.
1. Return |validState|.
</div>
Hit test extensions {#hit-test-extensions}
===================
The hit test extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/hit-test/">WebXR Hit Test Module</a>.
<script type="idl">
dictionary FakeXRWorldInit {
required sequence<FakeXRRegionInit> hitTestRegions;
};
</script>
{{FakeXRWorldInit}} dictionary describes the state of the world that will be used when computing hit test results on a {{FakeXRDevice}}.
{{FakeXRWorldInit/hitTestRegions}} contains a collection of {{FakeXRRegionInit}}s that are used to describe specific regions of the fake world. The order of the regions does not matter.
<script type="idl">
dictionary FakeXRRegionInit {
required sequence<FakeXRTriangleInit> faces;
required FakeXRRegionType type;
};
</script>
{{FakeXRRegionInit}} dictionary describes the contents of a specific region of the world.
{{FakeXRRegionInit/faces}} contains a collection of {{FakeXRTriangleInit}}s that enumerate all the faces contained by the region. The order of the faces does not matter.
{{FakeXRRegionInit/type}} contains a type of the region that will be used during computation of hit test results.
<script type="idl">
dictionary FakeXRTriangleInit {
required sequence<DOMPointInit> vertices; // size = 3
};
</script>
{{FakeXRTriangleInit}} dictionary describes a single face of a region.
{{FakeXRTriangleInit/vertices}} contains a collection of {{DOMPointInit}}s that comprise the face. The face will be considered as solid when computing hit test results and as such, the winding order of the vertices does not matter.
<script type="idl">
enum FakeXRRegionType {
"point",
"plane",
"mesh"
};
</script>
{{FakeXRRegionType}} enum is used to describe a type of the world region.
DOM overlay extensions {#dom-overlay-extensions}
======================
The DOM Overlay extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/dom-overlays/">WebXR DOM Overlay Module</a>.
<script type="idl">
partial interface FakeXRInputController {
undefined setOverlayPointerPosition(float x, float y);
};
</script>
When <dfn method for=FakeXRInputController>setOverlayPointerPosition(x, y)</dfn> is called, it sets a position within the DOM overlay in DOM coordinates for the next [=XR animation frame=], and is cleared after that frame. It is intended to be used along with a [=primary action=] for that frame, simulating that the user is interacting with the DOM overlay. The UA will emit a [=beforexrselect=] event at this location before generating XR select events.
Anchors extensions {#anchors-extensions}
==================
The anchors extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/anchors/">WebXR Anchors</a>.
<script type="idl">
dictionary FakeXRAnchorCreationParameters {
FakeXRRigidTransformInit requestedAnchorOrigin;
boolean isAttachedToEntity;
};
callback FakeXRAnchorCreationCallback = Promise<boolean> (FakeXRAnchorCreationParameters parameters, FakeXRAnchorController anchorController);
partial interface FakeXRDevice {
undefined setAnchorCreationCallback(FakeXRAnchorCreationCallback? callback);
};
</script>
The {{FakeXRAnchorCreationCallback}} callback can be used by the Web Platform Tests to control the result of a call to create an anchor, and to be able to subsequently control the newly created anchor.
The {{FakeXRDevice}} interface is extended with internal <dfn for="FakeXRDevice">anchorCreationCallback</dfn>, initially set to <code>null</code>. When the device receives a request to create an anchor, it MUST run the [=determine if the anchor creation succeeded=] algorithm.
<div class="algorithm" data-algorithm="determine-if-anchor-creation-succeded">
In order to <dfn>determine if the anchor creation succeeded</dfn>, the {{FakeXRDevice}} |device| MUST run the following steps:
1. If the |device|'s [=FakeXRDevice/anchorCreationCallback=] is <code>null</code>, return <code>false</code> and abort these steps.
1. Let |promise| be the result of invoking [=FakeXRDevice/anchorCreationCallback=] with parameters set so that they reflect the parameters passed to anchor creation request.
1. [=React=] to |promise|:
- If |promise| was fulfilled with value |v|, then return |v| and abort these steps.
- If |promise| was rejected, then return <code>false</code> and abort these steps.
</div>
The WPTs can set the anchor creation callback by calling {{FakeXRDevice/setAnchorCreationCallback(callback)}}.
The {{FakeXRAnchorCreationParameters/requestedAnchorOrigin}} attribute represents a transform expressed relative to [=base reference space=] used by the device.
The {{FakeXRAnchorCreationParameters/isAttachedToEntity}} attribute will be set to <code>true</code> if the created anchor should be treated as attached to some entity. If so, the tests could emulate entity changing location by appropriately controlling the anchor via [=FakeXRAnchorCreationCallback/anchorController=].
The <dfn for=FakeXRAnchorCreationCallback>anchorController</dfn> parameter passed in to {{FakeXRAnchorCreationCallback}} can be used to update the state of the anchor, assuming that the creation request was deemed successful. Tests SHOULD store it and issue commands to it for the entire duration of controlled anchor's lifetime.
<script type="idl">
interface FakeXRAnchorController {
readonly attribute boolean deleted;
// Controlling anchor state:
undefined pauseTracking();
undefined resumeTracking();
undefined stopTracking();
// Controlling anchor location:
undefined setAnchorOrigin(FakeXRRigidTransformInit anchorOrigin);
};
</script>
Successfully created anchors can be controlled by the test through the use of {{FakeXRAnchorController}} interface.
The {{FakeXRAnchorController}} has an associated internal <dfn for=FakeXRAnchorController>anchor origin</dfn>, which is a {{FakeXRRigidTransformInit}} describing the current state of the anchor's [=XRAnchor/native origin=].
The {{FakeXRAnchorController/deleted}} attribute will be set to <code>true</code> when the application has invoked an {{XRAnchor/delete()}} method on the anchor - in that case, the changes to the fake anchor controller will be ignored.
The {{FakeXRAnchorController/pauseTracking()}} method can be used by the tests to signal that the controlled anchor is temporarily untracked (i.e. its location will be unknown). Calling this method does not modify [=FakeXRAnchorController/anchor origin=] of the controller.
The {{FakeXRAnchorController/resumeTracking()}} method can be used by the tests to signal that the controlled anchor should have its tracking resumed, if it was temporarily untracked. Calling this method does not modify [=FakeXRAnchorController/anchor origin=] of the controller.
The {{FakeXRAnchorController/stopTracking()}} method can be used by the tests to signal that the controlled anchor is no longer tracked and that anchor tracking will not be resumed. After calling this method, the other calls on anchor controller will be ignored.
The {{FakeXRAnchorController/setAnchorOrigin(anchorOrigin)}} method can be used to set the controller's [=FakeXRAnchorController/anchor origin=]. Tests can use this method to simulate updates in anchor pose.
Lighting estimation extensions {#lighting-estimation-extensions}
==================
The lighting estimation extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/lighting-estimation/">WebXR Lighting Estimation</a>.
<script type="idl">
dictionary FakeXRLightEstimateInit {
required sequence<float> sphericalHarmonicsCoefficients;
DOMPointInit primaryLightDirection;
DOMPointInit primaryLightIntensity;
};
partial interface FakeXRDevice {
undefined setLightEstimate(FakeXRLightEstimateInit init);
};
</script>
The {{FakeXRDevice}} is extended with internal <dfn for=FakeXRDevice>light estimate</dfn> which is a {{FakeXRLightEstimateInit}}, used to supply data for any requested {{XRLightEstimate}}.
<div class="algorithm" data-algorithm="set-light-estimate">
When the <dfn method for="FakeXRDevice">setLightEstimate(|init|)</dfn> method is invoked on {{FakeXRDevice}} |device|, run the following steps:
1. Let |c| be |init|'s {{FakeXRLightEstimateInit/sphericalHarmonicsCoefficients}}.
1. If |c| does not have 27 elements, throw a {{TypeError}} and abort these steps.
1. Let |d| be |init|'s {{FakeXRLightEstimateInit/primaryLightDirection}}.
1. If |d| is set and |d|'s {{DOMPointInit/w}} value does not equal <code>0</code>, throw a {{TypeError}} and abort these steps.
1. Let |i| be |init|'s {{FakeXRLightEstimateInit/primaryLightIntensity}}.
1. If |i| is set and |i|'s {{DOMPointInit/w}} value does not equal <code>1</code>, throw a {{TypeError}} and abort these steps.
1. Set |device|'s [=FakeXRDevice/light estimate=] to |init| by the [=next animation frame=].
</div>
Depth sensing extensions {#depth-sensing-extensions}
========================
The depth sensing extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/depth-sensing/">WebXR Depth Sensing Module</a>.
The {{FakeXRDevice}} is extended with internal <dfn for=FakeXRDevice>depth sensing data</dfn> which is a {{FakeXRDepthSensingDataInit}}, used to supply data for requests to [=native depth sensing=].
<script type="idl">
dictionary FakeXRDepthSensingDataInit {
required ArrayBuffer depthData;
required FakeXRRigidTransformInit normDepthBufferFromNormView;
required float rawValueToMeters;
required unsigned long width;
required unsigned long height;
};
</script>
{{FakeXRDepthSensingDataInit}} dictionary describes the state of the depth sensing data that should be used when returning latest depth information in [=creating a CPU depth information instance=] and [=creating a GPU depth information instance=] algorithms. All keys present in {{FakeXRDepthSensingDataInit}} correspond to the data required to be returned by [=native depth sensing=] capabilities of the device.
{{FakeXRDepthSensingDataInit/depthData}} corresponds to the desired depth buffer that is to be set on native depth information returned from querying the native device. Not setting {{FakeXRDepthSensingDataInit/depthData}} key in the dictionary signals that the returned native depth information should be <code>null</code>.
{{FakeXRDepthSensingDataInit/normDepthBufferFromNormView}} corresponds to the desired [=depth coordinates transformation matrix=] that is to be set on native depth information returned from querying the native device.
{{FakeXRDepthSensingDataInit/rawValueToMeters}} corresponds to the desired conversion factor that is to be set on native depth information returned from querying the native device.
{{FakeXRDepthSensingDataInit/width}} and {{FakeXRDepthSensingDataInit/height}} correspond to the desired dimensions of the depth buffer that are to be set on native depth information returned from querying the native device.
<div class="algorithm" data-algorithm="set-depth-sensing-information">
When the {{FakeXRDevice/setDepthSensingData()}} method is invoked on {{FakeXRDevice}} |device| with |depthSensingData|, run the following steps:
1. If |depthSensingData|'s {{FakeXRDepthSensingDataInit/depthData}} is <code>null</code>, throw a {{TypeError}} and abort these steps.
1. Set |device|'s [=FakeXRDevice/depth sensing data=] to |depthSensingData|.
</div>
<div class="algorithm" data-algorithm="clear-depth-sensing-information">
When the {{FakeXRDevice/clearDepthSensingData()}} method is invoked on {{FakeXRDevice}} |device|, run the following steps:
1. Set |device|'s [=FakeXRDevice/depth sensing data=] to <code>null</code>.
</div>
Raw camera access extensions {#raw-camera-access-extensions}
============================
The raw camera access extensions for test API SHOULD be implemented by all user agents that implement <a href="https://immersive-web.github.io/raw-camera-access/">WebXR Raw Camera Access Module</a>.
The {{FakeXRViewInit}} dictionary is extended with {{FakeXRViewInit/cameraImageInit}} dictionary of type {{FakeXRDeviceResolution}}. This dictionary carries information about the camera image, and is intended to affect the <code>camera image</code> variable in [=obtain camera=] algorithm. If the {{FakeXRViewInit/cameraImageInit}} key is not present in the {{FakeXRViewInit}} dictionary, the [=obtain camera=] algorithm should treat this as <code>null</code> camera image (and thus the algorithm will return <code>null</code>).
<script type="idl">
dictionary FakeXRCameraImage {
required long width;
required long height;
Uint32Array pixels;
};
</script>
The {{FakeXRCameraImage/width}} controls the width of the camera image buffer in [=obtain camera=] algorithm.
The {{FakeXRCameraImage/height}} controls the height of the camera image buffer in [=obtain camera=] algorithm.
The {{FakeXRCameraImage/pixels}} control the camera image contents in [=obtain camera=] algorithm. The pixels will be used to initialize the camera image texture.
The camera image will be initialized as if by a call to <code>gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels)</code>, where <code>width</code>, <code>height</code>, and <code>pixels</code> are, respectively, {{FakeXRCameraImage/width}}, {{FakeXRCameraImage/height}}, and {{FakeXRCameraImage/pixels}}. In case {{FakeXRCameraImage/pixels}} key is not present in the dictionary, the behavior would be as if a call to <code>gl.texImage2D()</code> variant that omits the <code>pixels</code> parameter was made.
Any time a [=simulated XR device=]'s [=simulated XR device/list of primary views=] and [=simulated XR device/list of secondary views=] is set, the user agent MUST verify that the camera images associated with the views present across both of those lists are all equal to each other. Camera images are considered equal when their {{FakeXRCameraImage/width}}s and {{FakeXRCameraImage/height}}s are equal, and their {{FakeXRCameraImage/pixels}} are the same instance (if present). If they are not equal, the user agent MUST throw an error from within the algorithm that attempted to set them.