@iwsdk/core 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/camera/camera-system.d.ts +2 -1
- package/dist/camera/camera-system.d.ts.map +1 -1
- package/dist/camera/camera-system.js +6 -6
- package/dist/camera/camera-system.js.map +1 -1
- package/dist/depth/depth-sensing-system.d.ts +2 -0
- package/dist/depth/depth-sensing-system.d.ts.map +1 -1
- package/dist/depth/depth-sensing-system.js +18 -16
- package/dist/depth/depth-sensing-system.js.map +1 -1
- package/dist/ecs/system.d.ts +5 -2
- package/dist/ecs/system.d.ts.map +1 -1
- package/dist/ecs/system.js +2 -0
- package/dist/ecs/system.js.map +1 -1
- package/dist/ecs/world.d.ts +26 -4
- package/dist/ecs/world.d.ts.map +1 -1
- package/dist/ecs/world.js +2 -2
- package/dist/ecs/world.js.map +1 -1
- package/dist/environment-raycast/environment-raycast.js +2 -2
- package/dist/environment-raycast/environment-raycast.js.map +1 -1
- package/dist/grab/grab-system.d.ts.map +1 -1
- package/dist/grab/grab-system.js +20 -7
- package/dist/grab/grab-system.js.map +1 -1
- package/dist/grab/grabbed.d.ts +21 -0
- package/dist/grab/grabbed.d.ts.map +1 -0
- package/dist/grab/grabbed.js +29 -0
- package/dist/grab/grabbed.js.map +1 -0
- package/dist/grab/index.d.ts +1 -0
- package/dist/grab/index.d.ts.map +1 -1
- package/dist/grab/index.js +1 -0
- package/dist/grab/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/init/player-camera.d.ts +9 -0
- package/dist/init/player-camera.d.ts.map +1 -0
- package/dist/init/player-camera.js +14 -0
- package/dist/init/player-camera.js.map +1 -0
- package/dist/init/world-initializer.d.ts +30 -4
- package/dist/init/world-initializer.d.ts.map +1 -1
- package/dist/init/world-initializer.js +114 -24
- package/dist/init/world-initializer.js.map +1 -1
- package/dist/init/xr.d.ts.map +1 -1
- package/dist/init/xr.js +1 -0
- package/dist/init/xr.js.map +1 -1
- package/dist/input/canvas-pointer-system.d.ts +38 -0
- package/dist/input/canvas-pointer-system.d.ts.map +1 -0
- package/dist/input/canvas-pointer-system.js +47 -0
- package/dist/input/canvas-pointer-system.js.map +1 -0
- package/dist/input/index.d.ts +5 -0
- package/dist/input/index.d.ts.map +1 -1
- package/dist/input/input-actions.d.ts +139 -0
- package/dist/input/input-actions.d.ts.map +1 -0
- package/dist/input/input-actions.js +357 -0
- package/dist/input/input-actions.js.map +1 -0
- package/dist/input/input-manager.d.ts +56 -0
- package/dist/input/input-manager.d.ts.map +1 -0
- package/dist/input/input-manager.js +89 -0
- package/dist/input/input-manager.js.map +1 -0
- package/dist/input/input-system.d.ts +149 -137
- package/dist/input/input-system.d.ts.map +1 -1
- package/dist/input/input-system.js +30 -66
- package/dist/input/input-system.js.map +1 -1
- package/dist/input/stateful-browser-gamepad.d.ts +40 -0
- package/dist/input/stateful-browser-gamepad.d.ts.map +1 -0
- package/dist/input/stateful-browser-gamepad.js +139 -0
- package/dist/input/stateful-browser-gamepad.js.map +1 -0
- package/dist/input/stateful-keyboard.d.ts +23 -0
- package/dist/input/stateful-keyboard.d.ts.map +1 -0
- package/dist/input/stateful-keyboard.js +63 -0
- package/dist/input/stateful-keyboard.js.map +1 -0
- package/dist/layers/index.d.ts +10 -0
- package/dist/layers/index.d.ts.map +1 -0
- package/dist/layers/xr-cylinder-layer.d.ts +62 -0
- package/dist/layers/xr-cylinder-layer.d.ts.map +1 -0
- package/dist/layers/xr-cylinder-layer.js +48 -0
- package/dist/layers/xr-cylinder-layer.js.map +1 -0
- package/dist/layers/xr-layer-system.d.ts +296 -0
- package/dist/layers/xr-layer-system.d.ts.map +1 -0
- package/dist/layers/xr-layer-system.js +378 -0
- package/dist/layers/xr-layer-system.js.map +1 -0
- package/dist/layers/xr-quad-layer.d.ts +57 -0
- package/dist/layers/xr-quad-layer.d.ts.map +1 -0
- package/dist/layers/xr-quad-layer.js +46 -0
- package/dist/layers/xr-quad-layer.js.map +1 -0
- package/dist/locomotion/index.d.ts +1 -0
- package/dist/locomotion/index.d.ts.map +1 -1
- package/dist/locomotion/locomotion-input-provider.d.ts +39 -0
- package/dist/locomotion/locomotion-input-provider.d.ts.map +1 -0
- package/dist/locomotion/locomotion-input-provider.js +109 -0
- package/dist/locomotion/locomotion-input-provider.js.map +1 -0
- package/dist/locomotion/locomotion.d.ts +11 -11
- package/dist/locomotion/locomotion.d.ts.map +1 -1
- package/dist/locomotion/locomotion.js +10 -10
- package/dist/locomotion/locomotion.js.map +1 -1
- package/dist/locomotion/slide.d.ts +12 -12
- package/dist/locomotion/slide.d.ts.map +1 -1
- package/dist/locomotion/slide.js +11 -15
- package/dist/locomotion/slide.js.map +1 -1
- package/dist/locomotion/teleport.d.ts +11 -1
- package/dist/locomotion/teleport.d.ts.map +1 -1
- package/dist/locomotion/teleport.js +10 -30
- package/dist/locomotion/teleport.js.map +1 -1
- package/dist/locomotion/turn.d.ts +11 -0
- package/dist/locomotion/turn.d.ts.map +1 -1
- package/dist/locomotion/turn.js +14 -25
- package/dist/locomotion/turn.js.map +1 -1
- package/dist/mcp/ecs-debug-tools.d.ts.map +1 -1
- package/dist/mcp/ecs-debug-tools.js +20 -13
- package/dist/mcp/ecs-debug-tools.js.map +1 -1
- package/dist/physics/physics-system.d.ts.map +1 -1
- package/dist/physics/physics-system.js +2 -2
- package/dist/ui/ui.d.ts +5 -18
- package/dist/ui/ui.d.ts.map +1 -1
- package/dist/ui/ui.js +6 -25
- package/dist/ui/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { Types } from '../ecs/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Internal component storing per-entity runtime state for active XR layers.
|
|
10
|
+
* Attached by {@link XRLayerSystem} when an entity qualifies; removed on
|
|
11
|
+
* teardown. Keeping state in a component rather than system-side maps makes
|
|
12
|
+
* the system stateless with respect to entities, which improves HMR behavior
|
|
13
|
+
* and allows runtime MCP tools to inspect layer state.
|
|
14
|
+
*
|
|
15
|
+
* @category Layers
|
|
16
|
+
*/
|
|
17
|
+
export declare const XRLayerState: import("elics").Component<{
|
|
18
|
+
isQuad: {
|
|
19
|
+
type: Types.Boolean;
|
|
20
|
+
default: true;
|
|
21
|
+
};
|
|
22
|
+
mesh: {
|
|
23
|
+
type: Types.Object;
|
|
24
|
+
default: null;
|
|
25
|
+
};
|
|
26
|
+
renderTarget: {
|
|
27
|
+
type: Types.Object;
|
|
28
|
+
default: null;
|
|
29
|
+
};
|
|
30
|
+
fallbackMaterial: {
|
|
31
|
+
type: Types.Object;
|
|
32
|
+
default: null;
|
|
33
|
+
};
|
|
34
|
+
xrLayer: {
|
|
35
|
+
type: Types.Object;
|
|
36
|
+
default: null;
|
|
37
|
+
};
|
|
38
|
+
pixelWidth: {
|
|
39
|
+
type: Types.Int16;
|
|
40
|
+
default: number;
|
|
41
|
+
};
|
|
42
|
+
pixelHeight: {
|
|
43
|
+
type: Types.Int16;
|
|
44
|
+
default: number;
|
|
45
|
+
};
|
|
46
|
+
stencil: {
|
|
47
|
+
type: Types.Boolean;
|
|
48
|
+
default: false;
|
|
49
|
+
};
|
|
50
|
+
cachedTransform: {
|
|
51
|
+
type: Types.Object;
|
|
52
|
+
default: null;
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
55
|
+
declare const XRLayerSystem_base: import("elics").SystemConstructor<import("elics").SystemSchema, {
|
|
56
|
+
quadLayers: {
|
|
57
|
+
required: import("elics").Component<{
|
|
58
|
+
width: {
|
|
59
|
+
type: Types.Float32;
|
|
60
|
+
default: number;
|
|
61
|
+
};
|
|
62
|
+
height: {
|
|
63
|
+
type: Types.Float32;
|
|
64
|
+
default: number;
|
|
65
|
+
};
|
|
66
|
+
pixelWidth: {
|
|
67
|
+
type: Types.Int16;
|
|
68
|
+
default: number;
|
|
69
|
+
};
|
|
70
|
+
pixelHeight: {
|
|
71
|
+
type: Types.Int16;
|
|
72
|
+
default: number;
|
|
73
|
+
};
|
|
74
|
+
renderCallback: {
|
|
75
|
+
type: Types.Object;
|
|
76
|
+
default: null;
|
|
77
|
+
};
|
|
78
|
+
stencil: {
|
|
79
|
+
type: Types.Boolean;
|
|
80
|
+
default: false;
|
|
81
|
+
};
|
|
82
|
+
}>[];
|
|
83
|
+
};
|
|
84
|
+
cylinderLayers: {
|
|
85
|
+
required: import("elics").Component<{
|
|
86
|
+
radius: {
|
|
87
|
+
type: Types.Float32;
|
|
88
|
+
default: number;
|
|
89
|
+
};
|
|
90
|
+
centralAngle: {
|
|
91
|
+
type: Types.Float32;
|
|
92
|
+
default: number;
|
|
93
|
+
};
|
|
94
|
+
aspectRatio: {
|
|
95
|
+
type: Types.Float32;
|
|
96
|
+
default: number;
|
|
97
|
+
};
|
|
98
|
+
pixelWidth: {
|
|
99
|
+
type: Types.Int16;
|
|
100
|
+
default: number;
|
|
101
|
+
};
|
|
102
|
+
pixelHeight: {
|
|
103
|
+
type: Types.Int16;
|
|
104
|
+
default: number;
|
|
105
|
+
};
|
|
106
|
+
renderCallback: {
|
|
107
|
+
type: Types.Object;
|
|
108
|
+
default: null;
|
|
109
|
+
};
|
|
110
|
+
stencil: {
|
|
111
|
+
type: Types.Boolean;
|
|
112
|
+
default: false;
|
|
113
|
+
};
|
|
114
|
+
}>[];
|
|
115
|
+
};
|
|
116
|
+
activeLayers: {
|
|
117
|
+
required: import("elics").Component<{
|
|
118
|
+
isQuad: {
|
|
119
|
+
type: Types.Boolean;
|
|
120
|
+
default: true;
|
|
121
|
+
};
|
|
122
|
+
mesh: {
|
|
123
|
+
type: Types.Object;
|
|
124
|
+
default: null;
|
|
125
|
+
};
|
|
126
|
+
renderTarget: {
|
|
127
|
+
type: Types.Object;
|
|
128
|
+
default: null;
|
|
129
|
+
};
|
|
130
|
+
fallbackMaterial: {
|
|
131
|
+
type: Types.Object;
|
|
132
|
+
default: null;
|
|
133
|
+
};
|
|
134
|
+
xrLayer: {
|
|
135
|
+
type: Types.Object;
|
|
136
|
+
default: null;
|
|
137
|
+
};
|
|
138
|
+
pixelWidth: {
|
|
139
|
+
type: Types.Int16;
|
|
140
|
+
default: number;
|
|
141
|
+
};
|
|
142
|
+
pixelHeight: {
|
|
143
|
+
type: Types.Int16;
|
|
144
|
+
default: number;
|
|
145
|
+
};
|
|
146
|
+
stencil: {
|
|
147
|
+
type: Types.Boolean;
|
|
148
|
+
default: false;
|
|
149
|
+
};
|
|
150
|
+
cachedTransform: {
|
|
151
|
+
type: Types.Object;
|
|
152
|
+
default: null;
|
|
153
|
+
};
|
|
154
|
+
}>[];
|
|
155
|
+
};
|
|
156
|
+
}, import("../ecs/world.js").World, import("../ecs/system.js").System<import("elics").SystemSchema, {
|
|
157
|
+
quadLayers: {
|
|
158
|
+
required: import("elics").Component<{
|
|
159
|
+
width: {
|
|
160
|
+
type: Types.Float32;
|
|
161
|
+
default: number;
|
|
162
|
+
};
|
|
163
|
+
height: {
|
|
164
|
+
type: Types.Float32;
|
|
165
|
+
default: number;
|
|
166
|
+
};
|
|
167
|
+
pixelWidth: {
|
|
168
|
+
type: Types.Int16;
|
|
169
|
+
default: number;
|
|
170
|
+
};
|
|
171
|
+
pixelHeight: {
|
|
172
|
+
type: Types.Int16;
|
|
173
|
+
default: number;
|
|
174
|
+
};
|
|
175
|
+
renderCallback: {
|
|
176
|
+
type: Types.Object;
|
|
177
|
+
default: null;
|
|
178
|
+
};
|
|
179
|
+
stencil: {
|
|
180
|
+
type: Types.Boolean;
|
|
181
|
+
default: false;
|
|
182
|
+
};
|
|
183
|
+
}>[];
|
|
184
|
+
};
|
|
185
|
+
cylinderLayers: {
|
|
186
|
+
required: import("elics").Component<{
|
|
187
|
+
radius: {
|
|
188
|
+
type: Types.Float32;
|
|
189
|
+
default: number;
|
|
190
|
+
};
|
|
191
|
+
centralAngle: {
|
|
192
|
+
type: Types.Float32;
|
|
193
|
+
default: number;
|
|
194
|
+
};
|
|
195
|
+
aspectRatio: {
|
|
196
|
+
type: Types.Float32;
|
|
197
|
+
default: number;
|
|
198
|
+
};
|
|
199
|
+
pixelWidth: {
|
|
200
|
+
type: Types.Int16;
|
|
201
|
+
default: number;
|
|
202
|
+
};
|
|
203
|
+
pixelHeight: {
|
|
204
|
+
type: Types.Int16;
|
|
205
|
+
default: number;
|
|
206
|
+
};
|
|
207
|
+
renderCallback: {
|
|
208
|
+
type: Types.Object;
|
|
209
|
+
default: null;
|
|
210
|
+
};
|
|
211
|
+
stencil: {
|
|
212
|
+
type: Types.Boolean;
|
|
213
|
+
default: false;
|
|
214
|
+
};
|
|
215
|
+
}>[];
|
|
216
|
+
};
|
|
217
|
+
activeLayers: {
|
|
218
|
+
required: import("elics").Component<{
|
|
219
|
+
isQuad: {
|
|
220
|
+
type: Types.Boolean;
|
|
221
|
+
default: true;
|
|
222
|
+
};
|
|
223
|
+
mesh: {
|
|
224
|
+
type: Types.Object;
|
|
225
|
+
default: null;
|
|
226
|
+
};
|
|
227
|
+
renderTarget: {
|
|
228
|
+
type: Types.Object;
|
|
229
|
+
default: null;
|
|
230
|
+
};
|
|
231
|
+
fallbackMaterial: {
|
|
232
|
+
type: Types.Object;
|
|
233
|
+
default: null;
|
|
234
|
+
};
|
|
235
|
+
xrLayer: {
|
|
236
|
+
type: Types.Object;
|
|
237
|
+
default: null;
|
|
238
|
+
};
|
|
239
|
+
pixelWidth: {
|
|
240
|
+
type: Types.Int16;
|
|
241
|
+
default: number;
|
|
242
|
+
};
|
|
243
|
+
pixelHeight: {
|
|
244
|
+
type: Types.Int16;
|
|
245
|
+
default: number;
|
|
246
|
+
};
|
|
247
|
+
stencil: {
|
|
248
|
+
type: Types.Boolean;
|
|
249
|
+
default: false;
|
|
250
|
+
};
|
|
251
|
+
cachedTransform: {
|
|
252
|
+
type: Types.Object;
|
|
253
|
+
default: null;
|
|
254
|
+
};
|
|
255
|
+
}>[];
|
|
256
|
+
};
|
|
257
|
+
}>>;
|
|
258
|
+
/**
|
|
259
|
+
* XRLayerSystem manages WebXR quad and cylinder composition layers.
|
|
260
|
+
*
|
|
261
|
+
* For each entity with an {@link XRQuadLayer} or {@link XRCylinderLayer}
|
|
262
|
+
* component, the system:
|
|
263
|
+
* - Creates a mesh (plane or cylinder) as a child of the entity's object3D
|
|
264
|
+
* - Renders layer content each frame via the component's `renderCallback`
|
|
265
|
+
* - In XR sessions with layer support, creates native XR composition layers
|
|
266
|
+
* and switches the mesh to a punch-through material that makes the
|
|
267
|
+
* projection layer transparent where the content layer appears
|
|
268
|
+
* - Falls back to rendering the content onto a textured mesh outside XR
|
|
269
|
+
*
|
|
270
|
+
* @category Layers
|
|
271
|
+
*/
|
|
272
|
+
export declare class XRLayerSystem extends XRLayerSystem_base {
|
|
273
|
+
private sessionUsesLayers;
|
|
274
|
+
init(): void;
|
|
275
|
+
update(): void;
|
|
276
|
+
private setupLayer;
|
|
277
|
+
private teardownLayer;
|
|
278
|
+
private handleSessionStart;
|
|
279
|
+
private handleSessionEnd;
|
|
280
|
+
private activateNativeLayer;
|
|
281
|
+
private deactivateNativeLayer;
|
|
282
|
+
/**
|
|
283
|
+
* Rebuild the session's render state layers array.
|
|
284
|
+
* Keeps the projection layer at index 0, then appends content layers.
|
|
285
|
+
*/
|
|
286
|
+
private updateSessionLayers;
|
|
287
|
+
private renderLayers;
|
|
288
|
+
/**
|
|
289
|
+
* Update the native XR layer's transform only when the mesh has moved,
|
|
290
|
+
* avoiding per-frame XRRigidTransform allocations for static layers.
|
|
291
|
+
*/
|
|
292
|
+
private syncLayerTransform;
|
|
293
|
+
private createRenderTarget;
|
|
294
|
+
}
|
|
295
|
+
export {};
|
|
296
|
+
//# sourceMappingURL=xr-layer-system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xr-layer-system.d.ts","sourceRoot":"","sources":["../../src/layers/xr-layer-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAyC,KAAK,EAAE,MAAM,iBAAiB,CAAC;AA2C/E;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEF;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,SAAQ,kBAIjC;IACA,OAAO,CAAC,iBAAiB,CAAS;IAElC,IAAI;IAyBJ,MAAM;IAWN,OAAO,CAAC,UAAU;IAwElB,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,mBAAmB;IAmF3B,OAAO,CAAC,qBAAqB;IAqC7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,YAAY;IAiEpB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA6B1B,OAAO,CAAC,kBAAkB;CAwB3B"}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { createSystem } from '../ecs/system.js';
|
|
2
|
+
import '../ecs/world.js';
|
|
3
|
+
import { createComponent, Types } from 'elics';
|
|
4
|
+
import '../ecs/entity.js';
|
|
5
|
+
import '../ecs/component-registry.js';
|
|
6
|
+
import '../runtime/three.js';
|
|
7
|
+
import { XRCylinderLayer } from './xr-cylinder-layer.js';
|
|
8
|
+
import { XRQuadLayer } from './xr-quad-layer.js';
|
|
9
|
+
import { Vector3, Quaternion, Color, PlaneGeometry, CylinderGeometry, MeshBasicMaterial, Mesh, FrontSide, BackSide, CustomBlending, AddEquation, ZeroFactor, SRGBColorSpace, WebGLRenderTarget, DepthTexture, UnsignedByteType, RGBAFormat, UnsignedInt248Type, UnsignedIntType, DepthStencilFormat, DepthFormat } from 'three';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the MIT license found in the
|
|
15
|
+
* LICENSE file in the root directory of this source tree.
|
|
16
|
+
*/
|
|
17
|
+
const _position = new Vector3();
|
|
18
|
+
const _quaternion = new Quaternion();
|
|
19
|
+
const _clearColor = new Color();
|
|
20
|
+
function toRigidTransform(pos, quat) {
|
|
21
|
+
return new XRRigidTransform({ x: pos.x, y: pos.y, z: pos.z }, { x: quat.x, y: quat.y, z: quat.z, w: quat.w });
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Internal component storing per-entity runtime state for active XR layers.
|
|
25
|
+
* Attached by {@link XRLayerSystem} when an entity qualifies; removed on
|
|
26
|
+
* teardown. Keeping state in a component rather than system-side maps makes
|
|
27
|
+
* the system stateless with respect to entities, which improves HMR behavior
|
|
28
|
+
* and allows runtime MCP tools to inspect layer state.
|
|
29
|
+
*
|
|
30
|
+
* @category Layers
|
|
31
|
+
*/
|
|
32
|
+
const XRLayerState = createComponent('XRLayerState', {
|
|
33
|
+
isQuad: { type: Types.Boolean, default: true },
|
|
34
|
+
mesh: { type: Types.Object, default: null },
|
|
35
|
+
renderTarget: { type: Types.Object, default: null },
|
|
36
|
+
fallbackMaterial: { type: Types.Object, default: null },
|
|
37
|
+
xrLayer: { type: Types.Object, default: null },
|
|
38
|
+
pixelWidth: { type: Types.Int16, default: 0 },
|
|
39
|
+
pixelHeight: { type: Types.Int16, default: 0 },
|
|
40
|
+
stencil: { type: Types.Boolean, default: false },
|
|
41
|
+
cachedTransform: { type: Types.Object, default: null },
|
|
42
|
+
}, 'Internal state for active XR layer entities');
|
|
43
|
+
/**
|
|
44
|
+
* XRLayerSystem manages WebXR quad and cylinder composition layers.
|
|
45
|
+
*
|
|
46
|
+
* For each entity with an {@link XRQuadLayer} or {@link XRCylinderLayer}
|
|
47
|
+
* component, the system:
|
|
48
|
+
* - Creates a mesh (plane or cylinder) as a child of the entity's object3D
|
|
49
|
+
* - Renders layer content each frame via the component's `renderCallback`
|
|
50
|
+
* - In XR sessions with layer support, creates native XR composition layers
|
|
51
|
+
* and switches the mesh to a punch-through material that makes the
|
|
52
|
+
* projection layer transparent where the content layer appears
|
|
53
|
+
* - Falls back to rendering the content onto a textured mesh outside XR
|
|
54
|
+
*
|
|
55
|
+
* @category Layers
|
|
56
|
+
*/
|
|
57
|
+
class XRLayerSystem extends createSystem({
|
|
58
|
+
quadLayers: { required: [XRQuadLayer] },
|
|
59
|
+
cylinderLayers: { required: [XRCylinderLayer] },
|
|
60
|
+
activeLayers: { required: [XRLayerState] },
|
|
61
|
+
}) {
|
|
62
|
+
constructor() {
|
|
63
|
+
super(...arguments);
|
|
64
|
+
this.sessionUsesLayers = false;
|
|
65
|
+
}
|
|
66
|
+
init() {
|
|
67
|
+
const onSessionStart = () => this.handleSessionStart();
|
|
68
|
+
const onSessionEnd = () => this.handleSessionEnd();
|
|
69
|
+
this.xrManager.addEventListener('sessionstart', onSessionStart);
|
|
70
|
+
this.xrManager.addEventListener('sessionend', onSessionEnd);
|
|
71
|
+
this.cleanupFuncs.push(() => this.xrManager.removeEventListener('sessionstart', onSessionStart), () => this.xrManager.removeEventListener('sessionend', onSessionEnd));
|
|
72
|
+
this.queries.quadLayers.subscribe('qualify', (entity) => this.setupLayer(entity, 'quad'));
|
|
73
|
+
this.queries.quadLayers.subscribe('disqualify', (entity) => this.teardownLayer(entity));
|
|
74
|
+
this.queries.cylinderLayers.subscribe('qualify', (entity) => this.setupLayer(entity, 'cylinder'));
|
|
75
|
+
this.queries.cylinderLayers.subscribe('disqualify', (entity) => this.teardownLayer(entity));
|
|
76
|
+
}
|
|
77
|
+
update() {
|
|
78
|
+
if (this.queries.activeLayers.entities.size === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.renderLayers();
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Layer setup / teardown
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
setupLayer(entity, type) {
|
|
87
|
+
var _a;
|
|
88
|
+
const isQuad = type === 'quad';
|
|
89
|
+
const component = isQuad ? XRQuadLayer : XRCylinderLayer;
|
|
90
|
+
const pixelWidth = entity.getValue(component, 'pixelWidth');
|
|
91
|
+
const pixelHeight = entity.getValue(component, 'pixelHeight');
|
|
92
|
+
const stencil = entity.getValue(component, 'stencil');
|
|
93
|
+
let geometry;
|
|
94
|
+
let side;
|
|
95
|
+
if (isQuad) {
|
|
96
|
+
const width = entity.getValue(XRQuadLayer, 'width');
|
|
97
|
+
const height = entity.getValue(XRQuadLayer, 'height');
|
|
98
|
+
geometry = new PlaneGeometry(width, height);
|
|
99
|
+
side = FrontSide;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const radius = entity.getValue(XRCylinderLayer, 'radius');
|
|
103
|
+
const centralAngle = entity.getValue(XRCylinderLayer, 'centralAngle');
|
|
104
|
+
const aspectRatio = entity.getValue(XRCylinderLayer, 'aspectRatio');
|
|
105
|
+
geometry = new CylinderGeometry(radius, radius, (radius * centralAngle) / aspectRatio, 64, 64, true, Math.PI - centralAngle / 2, centralAngle);
|
|
106
|
+
side = BackSide;
|
|
107
|
+
}
|
|
108
|
+
const renderTarget = this.createRenderTarget(pixelWidth, pixelHeight, stencil);
|
|
109
|
+
const fallbackMaterial = new MeshBasicMaterial({
|
|
110
|
+
color: 0xffffff,
|
|
111
|
+
side,
|
|
112
|
+
transparent: true,
|
|
113
|
+
});
|
|
114
|
+
fallbackMaterial.map = renderTarget.texture;
|
|
115
|
+
fallbackMaterial.map.offset.y = 1;
|
|
116
|
+
fallbackMaterial.map.repeat.y = -1;
|
|
117
|
+
const mesh = new Mesh(geometry, fallbackMaterial);
|
|
118
|
+
(_a = entity.object3D) === null || _a === void 0 ? void 0 : _a.add(mesh);
|
|
119
|
+
entity.addComponent(XRLayerState, {
|
|
120
|
+
isQuad,
|
|
121
|
+
mesh,
|
|
122
|
+
renderTarget,
|
|
123
|
+
fallbackMaterial,
|
|
124
|
+
pixelWidth,
|
|
125
|
+
pixelHeight,
|
|
126
|
+
stencil,
|
|
127
|
+
});
|
|
128
|
+
if (this.sessionUsesLayers) {
|
|
129
|
+
this.activateNativeLayer(entity);
|
|
130
|
+
this.updateSessionLayers();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
teardownLayer(entity) {
|
|
134
|
+
const mesh = XRLayerState.data.mesh[entity.index];
|
|
135
|
+
if (!mesh) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const fallbackMaterial = XRLayerState.data.fallbackMaterial[entity.index];
|
|
139
|
+
const renderTarget = XRLayerState.data.renderTarget[entity.index];
|
|
140
|
+
const xrLayer = XRLayerState.data.xrLayer[entity.index];
|
|
141
|
+
if (xrLayer && this.sessionUsesLayers) {
|
|
142
|
+
entity.setValue(XRLayerState, 'xrLayer', null);
|
|
143
|
+
this.updateSessionLayers();
|
|
144
|
+
}
|
|
145
|
+
mesh.geometry.dispose();
|
|
146
|
+
fallbackMaterial.dispose();
|
|
147
|
+
if (mesh.material !== fallbackMaterial) {
|
|
148
|
+
mesh.material.dispose();
|
|
149
|
+
}
|
|
150
|
+
renderTarget.dispose();
|
|
151
|
+
mesh.removeFromParent();
|
|
152
|
+
entity.removeComponent(XRLayerState);
|
|
153
|
+
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// XR session lifecycle
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
handleSessionStart() {
|
|
158
|
+
var _a;
|
|
159
|
+
const session = this.xrManager.getSession();
|
|
160
|
+
const layers = (_a = session === null || session === void 0 ? void 0 : session.renderState) === null || _a === void 0 ? void 0 : _a.layers;
|
|
161
|
+
// Need both a layers array AND a projection layer at index 0.
|
|
162
|
+
// The IWER emulator exposes an empty layers array which is truthy
|
|
163
|
+
// but has no projection layer -- treat that as "no layers support".
|
|
164
|
+
if (!(layers === null || layers === void 0 ? void 0 : layers.length)) {
|
|
165
|
+
this.sessionUsesLayers = false;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
this.sessionUsesLayers = true;
|
|
169
|
+
for (const entity of this.queries.activeLayers.entities) {
|
|
170
|
+
this.activateNativeLayer(entity);
|
|
171
|
+
}
|
|
172
|
+
this.updateSessionLayers();
|
|
173
|
+
}
|
|
174
|
+
handleSessionEnd() {
|
|
175
|
+
for (const entity of this.queries.activeLayers.entities) {
|
|
176
|
+
this.deactivateNativeLayer(entity);
|
|
177
|
+
}
|
|
178
|
+
this.sessionUsesLayers = false;
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Native layer management
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
activateNativeLayer(entity) {
|
|
184
|
+
const binding = this.xrManager.getBinding();
|
|
185
|
+
const session = this.xrManager.getSession();
|
|
186
|
+
if (!binding || !session) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const isQuad = !!XRLayerState.data.isQuad[entity.index];
|
|
190
|
+
const mesh = XRLayerState.data.mesh[entity.index];
|
|
191
|
+
const pixelWidth = XRLayerState.data.pixelWidth[entity.index];
|
|
192
|
+
const pixelHeight = XRLayerState.data.pixelHeight[entity.index];
|
|
193
|
+
const renderTarget = XRLayerState.data.renderTarget[entity.index];
|
|
194
|
+
const refSpace = this.xrManager.getReferenceSpace();
|
|
195
|
+
mesh.updateMatrixWorld(true);
|
|
196
|
+
mesh.getWorldPosition(_position);
|
|
197
|
+
mesh.getWorldQuaternion(_quaternion);
|
|
198
|
+
const transform = toRigidTransform(_position, _quaternion);
|
|
199
|
+
let xrLayer;
|
|
200
|
+
if (isQuad) {
|
|
201
|
+
const width = entity.getValue(XRQuadLayer, 'width');
|
|
202
|
+
const height = entity.getValue(XRQuadLayer, 'height');
|
|
203
|
+
xrLayer = binding.createQuadLayer({
|
|
204
|
+
transform,
|
|
205
|
+
width: width / 2,
|
|
206
|
+
height: height / 2,
|
|
207
|
+
space: refSpace,
|
|
208
|
+
viewPixelWidth: pixelWidth,
|
|
209
|
+
viewPixelHeight: pixelHeight,
|
|
210
|
+
clearOnAccess: false,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const radius = entity.getValue(XRCylinderLayer, 'radius');
|
|
215
|
+
const centralAngle = entity.getValue(XRCylinderLayer, 'centralAngle');
|
|
216
|
+
const aspectRatio = entity.getValue(XRCylinderLayer, 'aspectRatio');
|
|
217
|
+
xrLayer = binding.createCylinderLayer({
|
|
218
|
+
transform,
|
|
219
|
+
radius,
|
|
220
|
+
centralAngle,
|
|
221
|
+
aspectRatio,
|
|
222
|
+
space: refSpace,
|
|
223
|
+
viewPixelWidth: pixelWidth,
|
|
224
|
+
viewPixelHeight: pixelHeight,
|
|
225
|
+
clearOnAccess: false,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
entity.setValue(XRLayerState, 'xrLayer', xrLayer);
|
|
229
|
+
// Switch to punch-through material so the projection layer is
|
|
230
|
+
// transparent where this content layer appears.
|
|
231
|
+
const side = isQuad ? FrontSide : BackSide;
|
|
232
|
+
const punchMaterial = new MeshBasicMaterial({ color: 0xffffff, side });
|
|
233
|
+
punchMaterial.blending = CustomBlending;
|
|
234
|
+
punchMaterial.blendEquation = AddEquation;
|
|
235
|
+
punchMaterial.blendSrc = ZeroFactor;
|
|
236
|
+
punchMaterial.blendDst = ZeroFactor;
|
|
237
|
+
mesh.material = punchMaterial;
|
|
238
|
+
// Mark render target for external-texture usage and set colorSpace so
|
|
239
|
+
// that WebGLPrograms can derive the correct output encoding (super-three
|
|
240
|
+
// reads renderTarget.texture.colorSpace when isXRRenderTarget is true).
|
|
241
|
+
renderTarget.isXRRenderTarget = true;
|
|
242
|
+
renderTarget.texture.colorSpace = SRGBColorSpace;
|
|
243
|
+
entity.setValue(XRLayerState, 'cachedTransform', {
|
|
244
|
+
position: _position.clone(),
|
|
245
|
+
quaternion: _quaternion.clone(),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
deactivateNativeLayer(entity) {
|
|
249
|
+
const mesh = XRLayerState.data.mesh[entity.index];
|
|
250
|
+
const fallbackMaterial = XRLayerState.data.fallbackMaterial[entity.index];
|
|
251
|
+
const pixelWidth = XRLayerState.data.pixelWidth[entity.index];
|
|
252
|
+
const pixelHeight = XRLayerState.data.pixelHeight[entity.index];
|
|
253
|
+
const stencil = !!XRLayerState.data.stencil[entity.index];
|
|
254
|
+
// Dispose punch-through material
|
|
255
|
+
if (mesh.material !== fallbackMaterial) {
|
|
256
|
+
mesh.material.dispose();
|
|
257
|
+
}
|
|
258
|
+
// Recreate render target so stale __hasExternalTextures state is cleared
|
|
259
|
+
const oldRenderTarget = XRLayerState.data.renderTarget[entity.index];
|
|
260
|
+
oldRenderTarget.dispose();
|
|
261
|
+
const newRenderTarget = this.createRenderTarget(pixelWidth, pixelHeight, stencil);
|
|
262
|
+
entity.setValue(XRLayerState, 'renderTarget', newRenderTarget);
|
|
263
|
+
// Restore fallback material with the fresh render target texture
|
|
264
|
+
fallbackMaterial.map = newRenderTarget.texture;
|
|
265
|
+
fallbackMaterial.map.offset.y = 1;
|
|
266
|
+
fallbackMaterial.map.repeat.y = -1;
|
|
267
|
+
mesh.material = fallbackMaterial;
|
|
268
|
+
entity.setValue(XRLayerState, 'xrLayer', null);
|
|
269
|
+
entity.setValue(XRLayerState, 'cachedTransform', null);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Rebuild the session's render state layers array.
|
|
273
|
+
* Keeps the projection layer at index 0, then appends content layers.
|
|
274
|
+
*/
|
|
275
|
+
updateSessionLayers() {
|
|
276
|
+
const session = this.xrManager.getSession();
|
|
277
|
+
if (!(session === null || session === void 0 ? void 0 : session.renderState.layers)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Projection layer is always the first entry
|
|
281
|
+
const projLayer = session.renderState.layers[0];
|
|
282
|
+
const layers = projLayer ? [projLayer] : [];
|
|
283
|
+
for (const entity of this.queries.activeLayers.entities) {
|
|
284
|
+
const xrLayer = XRLayerState.data.xrLayer[entity.index];
|
|
285
|
+
if (xrLayer) {
|
|
286
|
+
layers.push(xrLayer);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
session.updateRenderState({ layers });
|
|
290
|
+
}
|
|
291
|
+
// ---------------------------------------------------------------------------
|
|
292
|
+
// Per-frame rendering
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
renderLayers() {
|
|
295
|
+
const renderer = this.renderer;
|
|
296
|
+
const savedRenderTarget = renderer.getRenderTarget();
|
|
297
|
+
// Disable XR so renderer.render() uses the layer's own camera
|
|
298
|
+
// instead of the headset-tracked XR cameras.
|
|
299
|
+
const savedXrEnabled = renderer.xr.enabled;
|
|
300
|
+
renderer.xr.enabled = false;
|
|
301
|
+
// Save clear color so we can clear layers to transparent
|
|
302
|
+
renderer.getClearColor(_clearColor);
|
|
303
|
+
const savedClearAlpha = renderer.getClearAlpha();
|
|
304
|
+
renderer.setClearColor(0x000000, 0);
|
|
305
|
+
for (const entity of this.queries.activeLayers.entities) {
|
|
306
|
+
const isQuad = !!XRLayerState.data.isQuad[entity.index];
|
|
307
|
+
const component = isQuad ? XRQuadLayer : XRCylinderLayer;
|
|
308
|
+
const callback = entity.getValue(component, 'renderCallback');
|
|
309
|
+
if (!callback) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const xrLayer = XRLayerState.data.xrLayer[entity.index];
|
|
313
|
+
const mesh = XRLayerState.data.mesh[entity.index];
|
|
314
|
+
const renderTarget = XRLayerState.data.renderTarget[entity.index];
|
|
315
|
+
if (this.sessionUsesLayers && xrLayer) {
|
|
316
|
+
// XR path: sync native layer transform and render to its texture
|
|
317
|
+
const binding = this.xrManager.getBinding();
|
|
318
|
+
const frame = this.xrFrame;
|
|
319
|
+
if (!binding || !frame) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
this.syncLayerTransform(xrLayer, mesh, entity);
|
|
323
|
+
const subImage = binding.getSubImage(xrLayer, frame);
|
|
324
|
+
renderer.setRenderTargetTextures(renderTarget, subImage.colorTexture, undefined);
|
|
325
|
+
renderer.setRenderTarget(renderTarget);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
// Fallback path: render to the internal render target
|
|
329
|
+
renderer.setRenderTarget(renderTarget);
|
|
330
|
+
}
|
|
331
|
+
callback();
|
|
332
|
+
}
|
|
333
|
+
// Restore renderer state
|
|
334
|
+
renderer.setClearColor(_clearColor, savedClearAlpha);
|
|
335
|
+
renderer.xr.enabled = savedXrEnabled;
|
|
336
|
+
renderer.setRenderTarget(savedRenderTarget);
|
|
337
|
+
}
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
// Helpers
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
/**
|
|
342
|
+
* Update the native XR layer's transform only when the mesh has moved,
|
|
343
|
+
* avoiding per-frame XRRigidTransform allocations for static layers.
|
|
344
|
+
*/
|
|
345
|
+
syncLayerTransform(xrLayer, mesh, entity) {
|
|
346
|
+
mesh.updateMatrixWorld(true);
|
|
347
|
+
mesh.getWorldPosition(_position);
|
|
348
|
+
mesh.getWorldQuaternion(_quaternion);
|
|
349
|
+
const cached = XRLayerState.data.cachedTransform[entity.index];
|
|
350
|
+
if ((cached === null || cached === void 0 ? void 0 : cached.position.equals(_position)) &&
|
|
351
|
+
cached.quaternion.equals(_quaternion)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
xrLayer.transform = toRigidTransform(_position, _quaternion);
|
|
355
|
+
if (cached) {
|
|
356
|
+
cached.position.copy(_position);
|
|
357
|
+
cached.quaternion.copy(_quaternion);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
entity.setValue(XRLayerState, 'cachedTransform', {
|
|
361
|
+
position: _position.clone(),
|
|
362
|
+
quaternion: _quaternion.clone(),
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
createRenderTarget(width, height, stencil) {
|
|
367
|
+
return new WebGLRenderTarget(width, height, {
|
|
368
|
+
format: RGBAFormat,
|
|
369
|
+
type: UnsignedByteType,
|
|
370
|
+
depthTexture: new DepthTexture(width, height, stencil ? UnsignedInt248Type : UnsignedIntType, undefined, undefined, undefined, undefined, undefined, undefined, stencil ? DepthStencilFormat : DepthFormat),
|
|
371
|
+
samples: 4,
|
|
372
|
+
stencilBuffer: stencil,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export { XRLayerState, XRLayerSystem };
|
|
378
|
+
//# sourceMappingURL=xr-layer-system.js.map
|