super-three 0.172.0 → 0.173.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/LICENSE +1 -1
- package/build/three.cjs +439 -380
- package/build/three.core.js +413 -41
- package/build/three.core.min.js +2 -2
- package/build/three.module.js +28 -344
- package/build/three.module.min.js +2 -2
- package/build/three.tsl.js +11 -3
- package/build/three.tsl.min.js +2 -2
- package/build/three.webgpu.js +7550 -1798
- package/build/three.webgpu.min.js +2 -2
- package/build/three.webgpu.nodes.js +7551 -1798
- package/build/three.webgpu.nodes.min.js +2 -2
- package/examples/jsm/animation/CCDIKSolver.js +6 -5
- package/examples/jsm/capabilities/WebGPU.js +1 -1
- package/examples/jsm/controls/ArcballControls.js +23 -21
- package/examples/jsm/exporters/GLTFExporter.js +26 -25
- package/examples/jsm/geometries/TextGeometry.js +1 -12
- package/examples/jsm/interactive/InteractiveGroup.js +108 -51
- package/examples/jsm/libs/demuxer_mp4.js +109 -0
- package/examples/jsm/loaders/GCodeLoader.js +3 -1
- package/examples/jsm/loaders/GLTFLoader.js +0 -2
- package/examples/jsm/loaders/TDSLoader.js +0 -1
- package/examples/jsm/materials/MeshGouraudMaterial.js +2 -0
- package/examples/jsm/misc/GPUComputationRenderer.js +6 -7
- package/examples/jsm/misc/ProgressiveLightMap.js +4 -3
- package/examples/jsm/misc/ProgressiveLightMapGPU.js +4 -3
- package/examples/jsm/misc/Volume.js +7 -5
- package/examples/jsm/misc/VolumeSlice.js +5 -3
- package/examples/jsm/objects/WaterMesh.js +25 -20
- package/examples/jsm/physics/JoltPhysics.js +7 -7
- package/examples/jsm/physics/RapierPhysics.js +4 -4
- package/examples/jsm/postprocessing/OutputPass.js +2 -0
- package/examples/jsm/shaders/OutputShader.js +5 -1
- package/examples/jsm/transpiler/GLSLDecoder.js +2 -2
- package/examples/jsm/transpiler/TSLEncoder.js +2 -0
- package/package.json +1 -1
- package/src/Three.Core.js +2 -0
- package/src/Three.TSL.js +9 -1
- package/src/cameras/ArrayCamera.js +1 -0
- package/src/constants.js +6 -1
- package/src/core/BufferAttribute.js +4 -0
- package/src/core/EventDispatcher.js +8 -6
- package/src/extras/ImageUtils.js +1 -11
- package/src/loaders/nodes/NodeObjectLoader.js +1 -1
- package/src/materials/nodes/MeshBasicNodeMaterial.js +1 -1
- package/src/materials/nodes/MeshPhysicalNodeMaterial.js +1 -1
- package/src/materials/nodes/NodeMaterial.js +5 -5
- package/src/materials/nodes/NodeMaterials.js +0 -1
- package/src/materials/nodes/PointsNodeMaterial.js +109 -7
- package/src/materials/nodes/SpriteNodeMaterial.js +1 -1
- package/src/materials/nodes/VolumeNodeMaterial.js +3 -3
- package/src/materials/nodes/manager/NodeMaterialObserver.js +8 -0
- package/src/nodes/Nodes.js +4 -0
- package/src/nodes/TSL.js +4 -0
- package/src/nodes/accessors/Arrays.js +30 -8
- package/src/nodes/accessors/BatchNode.js +3 -3
- package/src/nodes/accessors/BufferAttributeNode.js +1 -1
- package/src/nodes/accessors/Camera.js +64 -3
- package/src/nodes/accessors/InstanceNode.js +1 -1
- package/src/nodes/accessors/MaterialNode.js +9 -9
- package/src/nodes/accessors/ModelViewProjectionNode.js +1 -1
- package/src/nodes/accessors/Normal.js +6 -2
- package/src/nodes/accessors/Object3DNode.js +0 -1
- package/src/nodes/accessors/Position.js +6 -6
- package/src/nodes/accessors/ReferenceBaseNode.js +2 -4
- package/src/nodes/accessors/ReferenceNode.js +2 -3
- package/src/nodes/accessors/StorageBufferNode.js +42 -5
- package/src/nodes/accessors/Tangent.js +2 -2
- package/src/nodes/accessors/Texture3DNode.js +1 -1
- package/src/nodes/accessors/TextureNode.js +12 -5
- package/src/nodes/accessors/UniformArrayNode.js +9 -3
- package/src/nodes/accessors/VelocityNode.js +0 -2
- package/src/nodes/code/ScriptableNode.js +3 -3
- package/src/nodes/core/ArrayNode.js +125 -0
- package/src/nodes/core/ConstNode.js +8 -0
- package/src/nodes/core/Node.js +14 -1
- package/src/nodes/core/NodeBuilder.js +229 -19
- package/src/nodes/core/NodeUtils.js +11 -1
- package/src/nodes/core/NodeVar.js +17 -1
- package/src/nodes/core/OutputStructNode.js +19 -9
- package/src/nodes/core/StackNode.js +20 -1
- package/src/nodes/core/StructNode.js +121 -0
- package/src/nodes/core/StructType.js +13 -0
- package/src/nodes/core/StructTypeNode.js +82 -21
- package/src/nodes/core/TempNode.js +1 -1
- package/src/nodes/core/VarNode.js +94 -7
- package/src/nodes/core/VaryingNode.js +19 -3
- package/src/nodes/display/BlendModes.js +88 -1
- package/src/nodes/display/BumpMapNode.js +6 -6
- package/src/nodes/display/NormalMapNode.js +6 -6
- package/src/nodes/display/PassNode.js +3 -1
- package/src/nodes/display/ScreenNode.js +9 -0
- package/src/nodes/fog/Fog.js +17 -0
- package/src/nodes/gpgpu/AtomicFunctionNode.js +20 -4
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +8 -0
- package/src/nodes/gpgpu/WorkgroupInfoNode.js +1 -1
- package/src/nodes/lighting/ShadowNode.js +1 -1
- package/src/nodes/math/ConditionalNode.js +8 -1
- package/src/nodes/math/MathNode.js +14 -1
- package/src/nodes/math/OperatorNode.js +96 -18
- package/src/nodes/pmrem/PMREMUtils.js +1 -1
- package/src/nodes/shapes/Shapes.js +33 -0
- package/src/nodes/tsl/TSLBase.js +1 -0
- package/src/nodes/tsl/TSLCore.js +22 -2
- package/src/nodes/utils/ArrayElementNode.js +1 -1
- package/src/nodes/utils/LoopNode.js +8 -1
- package/src/nodes/utils/MemberNode.js +68 -0
- package/src/nodes/utils/StorageArrayElementNode.js +14 -0
- package/src/nodes/utils/Timer.js +21 -0
- package/src/objects/BatchedMesh.js +1 -1
- package/src/objects/Line.js +6 -6
- package/src/renderers/WebGLRenderer.js +7 -0
- package/src/renderers/common/Animation.js +23 -1
- package/src/renderers/common/Backend.js +58 -15
- package/src/renderers/common/Background.js +14 -0
- package/src/renderers/common/Binding.js +1 -1
- package/src/renderers/common/BufferUtils.js +1 -1
- package/src/renderers/common/BundleGroup.js +1 -1
- package/src/renderers/common/ChainMap.js +9 -7
- package/src/renderers/common/ClippingContext.js +1 -1
- package/src/renderers/common/Color4.js +7 -3
- package/src/renderers/common/Geometries.js +2 -23
- package/src/renderers/common/Info.js +1 -45
- package/src/renderers/common/Lighting.js +8 -6
- package/src/renderers/common/Pipelines.js +3 -3
- package/src/renderers/common/PostProcessing.js +24 -8
- package/src/renderers/common/QuadMesh.js +1 -1
- package/src/renderers/common/RenderBundles.js +9 -3
- package/src/renderers/common/RenderContext.js +37 -3
- package/src/renderers/common/RenderContexts.js +30 -17
- package/src/renderers/common/RenderList.js +2 -2
- package/src/renderers/common/RenderLists.js +9 -3
- package/src/renderers/common/RenderObject.js +76 -17
- package/src/renderers/common/RenderObjects.js +9 -7
- package/src/renderers/common/Renderer.js +86 -67
- package/src/renderers/common/SampledTexture.js +0 -1
- package/src/renderers/common/StorageBufferAttribute.js +1 -1
- package/src/renderers/common/StorageInstancedBufferAttribute.js +1 -1
- package/src/renderers/common/StorageTexture.js +1 -1
- package/src/renderers/common/Textures.js +14 -7
- package/src/renderers/common/TimestampQueryPool.js +39 -0
- package/src/renderers/common/Uniform.js +37 -1
- package/src/renderers/common/UniformsGroup.js +2 -0
- package/src/renderers/common/XRManager.js +1193 -0
- package/src/renderers/common/XRRenderTarget.js +74 -0
- package/src/renderers/common/extras/PMREMGenerator.js +53 -8
- package/src/renderers/common/nodes/NodeBuilderState.js +4 -4
- package/src/renderers/common/nodes/NodeLibrary.js +5 -5
- package/src/renderers/common/nodes/NodeSampledTexture.js +0 -1
- package/src/renderers/common/nodes/NodeUniform.js +54 -2
- package/src/renderers/common/nodes/Nodes.js +25 -18
- package/src/renderers/webgl/WebGLBackground.js +4 -0
- package/src/renderers/webgl-fallback/WebGLBackend.js +295 -110
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +347 -17
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +57 -0
- package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +28 -0
- package/src/renderers/webgl-fallback/utils/WebGLExtensions.js +45 -0
- package/src/renderers/webgl-fallback/utils/WebGLState.js +323 -11
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +167 -21
- package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +359 -0
- package/src/renderers/webgl-fallback/utils/WebGLUtils.js +43 -0
- package/src/renderers/webgpu/WebGPUBackend.js +132 -149
- package/src/renderers/webgpu/WebGPURenderer.Nodes.js +6 -4
- package/src/renderers/webgpu/WebGPURenderer.js +5 -4
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +615 -41
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +85 -6
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +85 -0
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +102 -3
- package/src/renderers/webgpu/utils/WebGPUTexturePassUtils.js +98 -1
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +257 -9
- package/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +274 -0
- package/src/renderers/webgpu/utils/WebGPUUtils.js +86 -4
- package/src/renderers/webxr/WebXRManager.js +13 -2
- package/src/textures/VideoFrameTexture.js +35 -0
- package/examples/jsm/geometries/InstancedPointsGeometry.js +0 -176
- package/examples/jsm/objects/InstancedPoints.js +0 -19
- package/src/materials/nodes/InstancedPointsNodeMaterial.js +0 -209
- package/src/nodes/accessors/InstancedPointsMaterialNode.js +0 -37
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
import { ArrayCamera } from '../../cameras/ArrayCamera.js';
|
|
2
|
+
import { EventDispatcher } from '../../core/EventDispatcher.js';
|
|
3
|
+
import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
|
|
4
|
+
import { RAD2DEG } from '../../math/MathUtils.js';
|
|
5
|
+
import { Vector2 } from '../../math/Vector2.js';
|
|
6
|
+
import { Vector3 } from '../../math/Vector3.js';
|
|
7
|
+
import { Vector4 } from '../../math/Vector4.js';
|
|
8
|
+
import { WebXRController } from '../webxr/WebXRController.js';
|
|
9
|
+
import { DepthFormat, DepthStencilFormat, RGBAFormat, UnsignedByteType, UnsignedInt248Type, UnsignedIntType } from '../../constants.js';
|
|
10
|
+
import { DepthTexture } from '../../textures/DepthTexture.js';
|
|
11
|
+
import { XRRenderTarget } from './XRRenderTarget.js';
|
|
12
|
+
|
|
13
|
+
const _cameraLPos = /*@__PURE__*/ new Vector3();
|
|
14
|
+
const _cameraRPos = /*@__PURE__*/ new Vector3();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The XR manager is built on top of the WebXR Device API to
|
|
18
|
+
* manage XR sessions with `WebGPURenderer`.
|
|
19
|
+
*
|
|
20
|
+
* XR is currently only supported with a WebGL 2 backend.
|
|
21
|
+
*
|
|
22
|
+
* @augments EventDispatcher
|
|
23
|
+
*/
|
|
24
|
+
class XRManager extends EventDispatcher {
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Constructs a new XR manager.
|
|
28
|
+
*
|
|
29
|
+
* @param {Renderer} renderer - The renderer.
|
|
30
|
+
*/
|
|
31
|
+
constructor( renderer ) {
|
|
32
|
+
|
|
33
|
+
super();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* This flag globally enables XR rendering.
|
|
37
|
+
*
|
|
38
|
+
* @type {Boolean}
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
this.enabled = false;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Whether the XR device is currently presenting or not.
|
|
45
|
+
*
|
|
46
|
+
* @type {Boolean}
|
|
47
|
+
* @default false
|
|
48
|
+
* @readonly
|
|
49
|
+
*/
|
|
50
|
+
this.isPresenting = false;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether the XR camera should automatically be updated or not.
|
|
54
|
+
*
|
|
55
|
+
* @type {Boolean}
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
this.cameraAutoUpdate = true;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The renderer.
|
|
62
|
+
*
|
|
63
|
+
* @private
|
|
64
|
+
* @type {Renderer}
|
|
65
|
+
*/
|
|
66
|
+
this._renderer = renderer;
|
|
67
|
+
|
|
68
|
+
// camera
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Represents the camera for the left eye.
|
|
72
|
+
*
|
|
73
|
+
* @private
|
|
74
|
+
* @type {PerspectiveCamera}
|
|
75
|
+
*/
|
|
76
|
+
this._cameraL = new PerspectiveCamera();
|
|
77
|
+
this._cameraL.viewport = new Vector4();
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Represents the camera for the right eye.
|
|
81
|
+
*
|
|
82
|
+
* @private
|
|
83
|
+
* @type {PerspectiveCamera}
|
|
84
|
+
*/
|
|
85
|
+
this._cameraR = new PerspectiveCamera();
|
|
86
|
+
this._cameraR.viewport = new Vector4();
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A list of cameras used for rendering the XR views.
|
|
90
|
+
*
|
|
91
|
+
* @private
|
|
92
|
+
* @type {Array<Camera>}
|
|
93
|
+
*/
|
|
94
|
+
this._cameras = [ this._cameraL, this._cameraR ];
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The main XR camera.
|
|
98
|
+
*
|
|
99
|
+
* @private
|
|
100
|
+
* @type {ArrayCamera}
|
|
101
|
+
*/
|
|
102
|
+
this._cameraXR = new ArrayCamera();
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The current near value of the XR camera.
|
|
106
|
+
*
|
|
107
|
+
* @private
|
|
108
|
+
* @type {Number?}
|
|
109
|
+
* @default null
|
|
110
|
+
*/
|
|
111
|
+
this._currentDepthNear = null;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The current far value of the XR camera.
|
|
115
|
+
*
|
|
116
|
+
* @private
|
|
117
|
+
* @type {Number?}
|
|
118
|
+
* @default null
|
|
119
|
+
*/
|
|
120
|
+
this._currentDepthFar = null;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* A list of WebXR controllers requested by the application.
|
|
124
|
+
*
|
|
125
|
+
* @private
|
|
126
|
+
* @type {Array<WebXRController>}
|
|
127
|
+
*/
|
|
128
|
+
this._controllers = [];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* A list of XR input source. Each input source belongs to
|
|
132
|
+
* an instance of WebXRController.
|
|
133
|
+
*
|
|
134
|
+
* @private
|
|
135
|
+
* @type {Array<XRInputSource?>}
|
|
136
|
+
*/
|
|
137
|
+
this._controllerInputSources = [];
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The current render target of the renderer.
|
|
141
|
+
*
|
|
142
|
+
* @private
|
|
143
|
+
* @type {RenderTarget?}
|
|
144
|
+
* @default null
|
|
145
|
+
*/
|
|
146
|
+
this._currentRenderTarget = null;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The XR render target that represents the rendering destination
|
|
150
|
+
* during an active XR session.
|
|
151
|
+
*
|
|
152
|
+
* @private
|
|
153
|
+
* @type {RenderTarget?}
|
|
154
|
+
* @default null
|
|
155
|
+
*/
|
|
156
|
+
this._xrRenderTarget = null;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The current animation context.
|
|
160
|
+
*
|
|
161
|
+
* @private
|
|
162
|
+
* @type {Window?}
|
|
163
|
+
* @default null
|
|
164
|
+
*/
|
|
165
|
+
this._currentAnimationContext = null;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The current animation loop.
|
|
169
|
+
*
|
|
170
|
+
* @private
|
|
171
|
+
* @type {Function?}
|
|
172
|
+
* @default null
|
|
173
|
+
*/
|
|
174
|
+
this._currentAnimationLoop = null;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The current pixel ratio.
|
|
178
|
+
*
|
|
179
|
+
* @private
|
|
180
|
+
* @type {Number?}
|
|
181
|
+
* @default null
|
|
182
|
+
*/
|
|
183
|
+
this._currentPixelRatio = null;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The current size of the renderer's canvas
|
|
187
|
+
* in logical pixel unit.
|
|
188
|
+
*
|
|
189
|
+
* @private
|
|
190
|
+
* @type {Vector2}
|
|
191
|
+
*/
|
|
192
|
+
this._currentSize = new Vector2();
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The default event listener for handling events inside a XR session.
|
|
196
|
+
*
|
|
197
|
+
* @private
|
|
198
|
+
* @type {Function}
|
|
199
|
+
*/
|
|
200
|
+
this._onSessionEvent = onSessionEvent.bind( this );
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The event listener for handling the end of a XR session.
|
|
204
|
+
*
|
|
205
|
+
* @private
|
|
206
|
+
* @type {Function}
|
|
207
|
+
*/
|
|
208
|
+
this._onSessionEnd = onSessionEnd.bind( this );
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The event listener for handling the `inputsourceschange` event.
|
|
212
|
+
*
|
|
213
|
+
* @private
|
|
214
|
+
* @type {Function}
|
|
215
|
+
*/
|
|
216
|
+
this._onInputSourcesChange = onInputSourcesChange.bind( this );
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The animation loop which is used as a replacement for the default
|
|
220
|
+
* animation loop of the applicatio. It is only used when a XR session
|
|
221
|
+
* is active.
|
|
222
|
+
*
|
|
223
|
+
* @private
|
|
224
|
+
* @type {Function}
|
|
225
|
+
*/
|
|
226
|
+
this._onAnimationFrame = onAnimationFrame.bind( this );
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The current XR reference space.
|
|
230
|
+
*
|
|
231
|
+
* @private
|
|
232
|
+
* @type {XRReferenceSpace?}
|
|
233
|
+
* @default null
|
|
234
|
+
*/
|
|
235
|
+
this._referenceSpace = null;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The current XR reference space type.
|
|
239
|
+
*
|
|
240
|
+
* @private
|
|
241
|
+
* @type {String}
|
|
242
|
+
* @default 'local-floor'
|
|
243
|
+
*/
|
|
244
|
+
this._referenceSpaceType = 'local-floor';
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* A custom reference space defined by the application.
|
|
248
|
+
*
|
|
249
|
+
* @private
|
|
250
|
+
* @type {XRReferenceSpace?}
|
|
251
|
+
* @default null
|
|
252
|
+
*/
|
|
253
|
+
this._customReferenceSpace = null;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The framebuffer scale factor.
|
|
257
|
+
*
|
|
258
|
+
* @private
|
|
259
|
+
* @type {Number}
|
|
260
|
+
* @default 1
|
|
261
|
+
*/
|
|
262
|
+
this._framebufferScaleFactor = 1;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The foveation factor.
|
|
266
|
+
*
|
|
267
|
+
* @private
|
|
268
|
+
* @type {Number}
|
|
269
|
+
* @default 1
|
|
270
|
+
*/
|
|
271
|
+
this._foveation = 1.0;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A reference to the current XR session.
|
|
275
|
+
*
|
|
276
|
+
* @private
|
|
277
|
+
* @type {XRSession?}
|
|
278
|
+
* @default null
|
|
279
|
+
*/
|
|
280
|
+
this._session = null;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* A reference to the current XR base layer.
|
|
284
|
+
*
|
|
285
|
+
* @private
|
|
286
|
+
* @type {XRWebGLLayer?}
|
|
287
|
+
* @default null
|
|
288
|
+
*/
|
|
289
|
+
this._glBaseLayer = null;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* A reference to the current XR binding.
|
|
293
|
+
*
|
|
294
|
+
* @private
|
|
295
|
+
* @type {XRWebGLBinding?}
|
|
296
|
+
* @default null
|
|
297
|
+
*/
|
|
298
|
+
this._glBinding = null;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* A reference to the current XR projection layer.
|
|
302
|
+
*
|
|
303
|
+
* @private
|
|
304
|
+
* @type {XRProjectionLayer?}
|
|
305
|
+
* @default null
|
|
306
|
+
*/
|
|
307
|
+
this._glProjLayer = null;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* A reference to the current XR frame.
|
|
311
|
+
*
|
|
312
|
+
* @private
|
|
313
|
+
* @type {XRFrame?}
|
|
314
|
+
* @default null
|
|
315
|
+
*/
|
|
316
|
+
this._xrFrame = null;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Whether to use the WebXR Layers API or not.
|
|
320
|
+
*
|
|
321
|
+
* @private
|
|
322
|
+
* @type {Boolean}
|
|
323
|
+
* @readonly
|
|
324
|
+
*/
|
|
325
|
+
this._useLayers = ( typeof XRWebGLBinding !== 'undefined' && 'createProjectionLayer' in XRWebGLBinding.prototype ); // eslint-disable-line compat/compat
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Returns an instance of `THREE.Group` that represents the transformation
|
|
331
|
+
* of a XR controller in target ray space. The requested controller is defined
|
|
332
|
+
* by the given index.
|
|
333
|
+
*
|
|
334
|
+
* @param {Number} index - The index of the XR controller.
|
|
335
|
+
* @return {Group} A group that represents the controller's transformation.
|
|
336
|
+
*/
|
|
337
|
+
getController( index ) {
|
|
338
|
+
|
|
339
|
+
const controller = this._getController( index );
|
|
340
|
+
|
|
341
|
+
return controller.getTargetRaySpace();
|
|
342
|
+
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Returns an instance of `THREE.Group` that represents the transformation
|
|
347
|
+
* of a XR controller in grip space. The requested controller is defined
|
|
348
|
+
* by the given index.
|
|
349
|
+
*
|
|
350
|
+
* @param {Number} index - The index of the XR controller.
|
|
351
|
+
* @return {Group} A group that represents the controller's transformation.
|
|
352
|
+
*/
|
|
353
|
+
getControllerGrip( index ) {
|
|
354
|
+
|
|
355
|
+
const controller = this._getController( index );
|
|
356
|
+
|
|
357
|
+
return controller.getGripSpace();
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Returns an instance of `THREE.Group` that represents the transformation
|
|
363
|
+
* of a XR controller in hand space. The requested controller is defined
|
|
364
|
+
* by the given index.
|
|
365
|
+
*
|
|
366
|
+
* @param {Number} index - The index of the XR controller.
|
|
367
|
+
* @return {Group} A group that represents the controller's transformation.
|
|
368
|
+
*/
|
|
369
|
+
getHand( index ) {
|
|
370
|
+
|
|
371
|
+
const controller = this._getController( index );
|
|
372
|
+
|
|
373
|
+
return controller.getHandSpace();
|
|
374
|
+
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Returns the foveation value.
|
|
379
|
+
*
|
|
380
|
+
* @return {Number|undefined} The foveation value. Returns `undefined` if no base or projection layer is defined.
|
|
381
|
+
*/
|
|
382
|
+
getFoveation() {
|
|
383
|
+
|
|
384
|
+
if ( this._glProjLayer === null && this._glBaseLayer === null ) {
|
|
385
|
+
|
|
386
|
+
return undefined;
|
|
387
|
+
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return this._foveation;
|
|
391
|
+
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Sets the foveation value.
|
|
396
|
+
*
|
|
397
|
+
* @param {Number} foveation - A number in the range `[0,1]` where `0` means no foveation (full resolution)
|
|
398
|
+
* and `1` means maximum foveation (the edges render at lower resolution).
|
|
399
|
+
*/
|
|
400
|
+
setFoveation( foveation ) {
|
|
401
|
+
|
|
402
|
+
this._foveation = foveation;
|
|
403
|
+
|
|
404
|
+
if ( this._glProjLayer !== null ) {
|
|
405
|
+
|
|
406
|
+
this._glProjLayer.fixedFoveation = foveation;
|
|
407
|
+
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if ( this._glBaseLayer !== null && this._glBaseLayer.fixedFoveation !== undefined ) {
|
|
411
|
+
|
|
412
|
+
this._glBaseLayer.fixedFoveation = foveation;
|
|
413
|
+
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Returns the framebuffer scale factor.
|
|
420
|
+
*
|
|
421
|
+
* @return {Number} The framebuffer scale factor.
|
|
422
|
+
*/
|
|
423
|
+
getFramebufferScaleFactor() {
|
|
424
|
+
|
|
425
|
+
return this._framebufferScaleFactor;
|
|
426
|
+
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Sets the framebuffer scale factor.
|
|
431
|
+
*
|
|
432
|
+
* This method can not be used during a XR session.
|
|
433
|
+
*
|
|
434
|
+
* @param {Number} factor - The framebuffer scale factor.
|
|
435
|
+
*/
|
|
436
|
+
setFramebufferScaleFactor( factor ) {
|
|
437
|
+
|
|
438
|
+
this._framebufferScaleFactor = factor;
|
|
439
|
+
|
|
440
|
+
if ( this.isPresenting === true ) {
|
|
441
|
+
|
|
442
|
+
console.warn( 'THREE.XRManager: Cannot change framebuffer scale while presenting.' );
|
|
443
|
+
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Returns the reference space type.
|
|
450
|
+
*
|
|
451
|
+
* @return {String} The reference space type.
|
|
452
|
+
*/
|
|
453
|
+
getReferenceSpaceType() {
|
|
454
|
+
|
|
455
|
+
return this._referenceSpaceType;
|
|
456
|
+
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Sets the reference space type.
|
|
461
|
+
*
|
|
462
|
+
* This method can not be used during a XR session.
|
|
463
|
+
*
|
|
464
|
+
* @param {String} type - The reference space type.
|
|
465
|
+
*/
|
|
466
|
+
setReferenceSpaceType( type ) {
|
|
467
|
+
|
|
468
|
+
this._referenceSpaceType = type;
|
|
469
|
+
|
|
470
|
+
if ( this.isPresenting === true ) {
|
|
471
|
+
|
|
472
|
+
console.warn( 'THREE.XRManager: Cannot change reference space type while presenting.' );
|
|
473
|
+
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Returns the XR reference space.
|
|
480
|
+
*
|
|
481
|
+
* @return {XRReferenceSpace} The XR reference space.
|
|
482
|
+
*/
|
|
483
|
+
getReferenceSpace() {
|
|
484
|
+
|
|
485
|
+
return this._customReferenceSpace || this._referenceSpace;
|
|
486
|
+
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Sets a custom XR reference space.
|
|
491
|
+
*
|
|
492
|
+
* @param {XRReferenceSpace} space - The XR reference space.
|
|
493
|
+
*/
|
|
494
|
+
setReferenceSpace( space ) {
|
|
495
|
+
|
|
496
|
+
this._customReferenceSpace = space;
|
|
497
|
+
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Returns the XR camera.
|
|
502
|
+
*
|
|
503
|
+
* @return {ArrayCamera} The XR camera.
|
|
504
|
+
*/
|
|
505
|
+
getCamera() {
|
|
506
|
+
|
|
507
|
+
return this._cameraXR;
|
|
508
|
+
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Returns the environment blend mode from the current XR session.
|
|
513
|
+
*
|
|
514
|
+
* @return {('opaque'|'additive'|'alpha-blend')?} The environment blend mode. Returns `null` when used outside of a XR session.
|
|
515
|
+
*/
|
|
516
|
+
getEnvironmentBlendMode() {
|
|
517
|
+
|
|
518
|
+
if ( this._session !== null ) {
|
|
519
|
+
|
|
520
|
+
return this._session.environmentBlendMode;
|
|
521
|
+
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Returns the current XR frame.
|
|
528
|
+
*
|
|
529
|
+
* @return {XRFrame?} The XR frame. Returns `null` when used outside a XR session.
|
|
530
|
+
*/
|
|
531
|
+
getFrame() {
|
|
532
|
+
|
|
533
|
+
return this._xrFrame;
|
|
534
|
+
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Returns the current XR session.
|
|
539
|
+
*
|
|
540
|
+
* @return {XRSession?} The XR session. Returns `null` when used outside a XR session.
|
|
541
|
+
*/
|
|
542
|
+
getSession() {
|
|
543
|
+
|
|
544
|
+
return this._session;
|
|
545
|
+
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* After a XR session has been requested usually with one of the `*Button` modules, it
|
|
550
|
+
* is injected into the renderer with this method. This method triggers the start of
|
|
551
|
+
* the actual XR rendering.
|
|
552
|
+
*
|
|
553
|
+
* @async
|
|
554
|
+
* @param {XRSession} session - The XR session to set.
|
|
555
|
+
* @return {Promise} A Promise that resolves when the session has been set.
|
|
556
|
+
*/
|
|
557
|
+
async setSession( session ) {
|
|
558
|
+
|
|
559
|
+
const renderer = this._renderer;
|
|
560
|
+
const backend = renderer.backend;
|
|
561
|
+
|
|
562
|
+
const gl = renderer.getContext();
|
|
563
|
+
|
|
564
|
+
this._session = session;
|
|
565
|
+
|
|
566
|
+
if ( session !== null ) {
|
|
567
|
+
|
|
568
|
+
if ( backend.isWebGPUBackend === true ) throw new Error( 'THREE.XRManager: XR is currently not supported with a WebGPU backend. Use WebGL by passing "{ forceWebGL: true }" to the constructor of the renderer.' );
|
|
569
|
+
|
|
570
|
+
this._currentRenderTarget = renderer.getRenderTarget();
|
|
571
|
+
|
|
572
|
+
session.addEventListener( 'select', this._onSessionEvent );
|
|
573
|
+
session.addEventListener( 'selectstart', this._onSessionEvent );
|
|
574
|
+
session.addEventListener( 'selectend', this._onSessionEvent );
|
|
575
|
+
session.addEventListener( 'squeeze', this._onSessionEvent );
|
|
576
|
+
session.addEventListener( 'squeezestart', this._onSessionEvent );
|
|
577
|
+
session.addEventListener( 'squeezeend', this._onSessionEvent );
|
|
578
|
+
session.addEventListener( 'end', this._onSessionEnd );
|
|
579
|
+
session.addEventListener( 'inputsourceschange', this._onInputSourcesChange );
|
|
580
|
+
|
|
581
|
+
await backend.makeXRCompatible();
|
|
582
|
+
|
|
583
|
+
this._currentPixelRatio = renderer.getPixelRatio();
|
|
584
|
+
renderer.getSize( this._currentSize );
|
|
585
|
+
|
|
586
|
+
this._currentAnimationContext = renderer._animation.getContext();
|
|
587
|
+
this._currentAnimationLoop = renderer._animation.getAnimationLoop();
|
|
588
|
+
renderer._animation.stop();
|
|
589
|
+
|
|
590
|
+
//
|
|
591
|
+
|
|
592
|
+
if ( this._useLayers === true ) {
|
|
593
|
+
|
|
594
|
+
// default path using XRWebGLBinding/XRProjectionLayer
|
|
595
|
+
|
|
596
|
+
let depthFormat = null;
|
|
597
|
+
let depthType = null;
|
|
598
|
+
let glDepthFormat = null;
|
|
599
|
+
|
|
600
|
+
if ( renderer.depth ) {
|
|
601
|
+
|
|
602
|
+
glDepthFormat = renderer.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24;
|
|
603
|
+
depthFormat = renderer.stencil ? DepthStencilFormat : DepthFormat;
|
|
604
|
+
depthType = renderer.stencil ? UnsignedInt248Type : UnsignedIntType;
|
|
605
|
+
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const projectionlayerInit = {
|
|
609
|
+
colorFormat: gl.RGBA8,
|
|
610
|
+
depthFormat: glDepthFormat,
|
|
611
|
+
scaleFactor: this._framebufferScaleFactor
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const glBinding = new XRWebGLBinding( session, gl );
|
|
615
|
+
const glProjLayer = glBinding.createProjectionLayer( projectionlayerInit );
|
|
616
|
+
|
|
617
|
+
this._glBinding = glBinding;
|
|
618
|
+
this._glProjLayer = glProjLayer;
|
|
619
|
+
|
|
620
|
+
session.updateRenderState( { layers: [ glProjLayer ] } );
|
|
621
|
+
|
|
622
|
+
renderer.setPixelRatio( 1 );
|
|
623
|
+
renderer.setSize( glProjLayer.textureWidth, glProjLayer.textureHeight, false );
|
|
624
|
+
|
|
625
|
+
this._xrRenderTarget = new XRRenderTarget(
|
|
626
|
+
glProjLayer.textureWidth,
|
|
627
|
+
glProjLayer.textureHeight,
|
|
628
|
+
{
|
|
629
|
+
format: RGBAFormat,
|
|
630
|
+
type: UnsignedByteType,
|
|
631
|
+
colorSpace: renderer.outputColorSpace,
|
|
632
|
+
depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ),
|
|
633
|
+
stencilBuffer: renderer.stencil,
|
|
634
|
+
samples: renderer.samples
|
|
635
|
+
} );
|
|
636
|
+
|
|
637
|
+
this._xrRenderTarget.hasExternalTextures = true;
|
|
638
|
+
|
|
639
|
+
} else {
|
|
640
|
+
|
|
641
|
+
// fallback to XRWebGLLayer
|
|
642
|
+
|
|
643
|
+
const layerInit = {
|
|
644
|
+
antialias: renderer.samples > 0,
|
|
645
|
+
alpha: true,
|
|
646
|
+
depth: renderer.depth,
|
|
647
|
+
stencil: renderer.stencil,
|
|
648
|
+
framebufferScaleFactor: this.getFramebufferScaleFactor()
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
const glBaseLayer = new XRWebGLLayer( session, gl, layerInit );
|
|
652
|
+
this._glBaseLayer = glBaseLayer;
|
|
653
|
+
|
|
654
|
+
session.updateRenderState( { baseLayer: glBaseLayer } );
|
|
655
|
+
|
|
656
|
+
renderer.setPixelRatio( 1 );
|
|
657
|
+
renderer.setSize( glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, false );
|
|
658
|
+
|
|
659
|
+
this._xrRenderTarget = new XRRenderTarget(
|
|
660
|
+
glBaseLayer.framebufferWidth,
|
|
661
|
+
glBaseLayer.framebufferHeight,
|
|
662
|
+
{
|
|
663
|
+
format: RGBAFormat,
|
|
664
|
+
type: UnsignedByteType,
|
|
665
|
+
colorSpace: renderer.outputColorSpace,
|
|
666
|
+
stencilBuffer: renderer.stencil
|
|
667
|
+
}
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
//
|
|
673
|
+
|
|
674
|
+
this.setFoveation( this.getFoveation() );
|
|
675
|
+
|
|
676
|
+
this._referenceSpace = await session.requestReferenceSpace( this.getReferenceSpaceType() );
|
|
677
|
+
|
|
678
|
+
renderer._animation.setAnimationLoop( this._onAnimationFrame );
|
|
679
|
+
renderer._animation.setContext( session );
|
|
680
|
+
renderer._animation.start();
|
|
681
|
+
|
|
682
|
+
this.isPresenting = true;
|
|
683
|
+
|
|
684
|
+
this.dispatchEvent( { type: 'sessionstart' } );
|
|
685
|
+
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* This method is called by the renderer per frame and updates the XR camera
|
|
692
|
+
* and it sub cameras based on the given camera. The given camera is the "user"
|
|
693
|
+
* camera created on application level and used for non-XR rendering.
|
|
694
|
+
*
|
|
695
|
+
* @param {PerspectiveCamera} camera - The camera.
|
|
696
|
+
*/
|
|
697
|
+
updateCamera( camera ) {
|
|
698
|
+
|
|
699
|
+
const session = this._session;
|
|
700
|
+
|
|
701
|
+
if ( session === null ) return;
|
|
702
|
+
|
|
703
|
+
const depthNear = camera.near;
|
|
704
|
+
const depthFar = camera.far;
|
|
705
|
+
|
|
706
|
+
const cameraXR = this._cameraXR;
|
|
707
|
+
const cameraL = this._cameraL;
|
|
708
|
+
const cameraR = this._cameraR;
|
|
709
|
+
|
|
710
|
+
cameraXR.near = cameraR.near = cameraL.near = depthNear;
|
|
711
|
+
cameraXR.far = cameraR.far = cameraL.far = depthFar;
|
|
712
|
+
|
|
713
|
+
if ( this._currentDepthNear !== cameraXR.near || this._currentDepthFar !== cameraXR.far ) {
|
|
714
|
+
|
|
715
|
+
// Note that the new renderState won't apply until the next frame. See #18320
|
|
716
|
+
|
|
717
|
+
session.updateRenderState( {
|
|
718
|
+
depthNear: cameraXR.near,
|
|
719
|
+
depthFar: cameraXR.far
|
|
720
|
+
} );
|
|
721
|
+
|
|
722
|
+
this._currentDepthNear = cameraXR.near;
|
|
723
|
+
this._currentDepthFar = cameraXR.far;
|
|
724
|
+
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
cameraL.layers.mask = camera.layers.mask | 0b010;
|
|
728
|
+
cameraR.layers.mask = camera.layers.mask | 0b100;
|
|
729
|
+
cameraXR.layers.mask = cameraL.layers.mask | cameraR.layers.mask;
|
|
730
|
+
|
|
731
|
+
const parent = camera.parent;
|
|
732
|
+
const cameras = cameraXR.cameras;
|
|
733
|
+
|
|
734
|
+
updateCamera( cameraXR, parent );
|
|
735
|
+
|
|
736
|
+
for ( let i = 0; i < cameras.length; i ++ ) {
|
|
737
|
+
|
|
738
|
+
updateCamera( cameras[ i ], parent );
|
|
739
|
+
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// update projection matrix for proper view frustum culling
|
|
743
|
+
|
|
744
|
+
if ( cameras.length === 2 ) {
|
|
745
|
+
|
|
746
|
+
setProjectionFromUnion( cameraXR, cameraL, cameraR );
|
|
747
|
+
|
|
748
|
+
} else {
|
|
749
|
+
|
|
750
|
+
// assume single camera setup (AR)
|
|
751
|
+
|
|
752
|
+
cameraXR.projectionMatrix.copy( cameraL.projectionMatrix );
|
|
753
|
+
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// update user camera and its children
|
|
757
|
+
|
|
758
|
+
updateUserCamera( camera, cameraXR, parent );
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Returns a WebXR controller for the given controller index.
|
|
765
|
+
*
|
|
766
|
+
* @private
|
|
767
|
+
* @param {Number} index - The controller index.
|
|
768
|
+
* @return {WebXRController} The XR controller.
|
|
769
|
+
*/
|
|
770
|
+
_getController( index ) {
|
|
771
|
+
|
|
772
|
+
let controller = this._controllers[ index ];
|
|
773
|
+
|
|
774
|
+
if ( controller === undefined ) {
|
|
775
|
+
|
|
776
|
+
controller = new WebXRController();
|
|
777
|
+
this._controllers[ index ] = controller;
|
|
778
|
+
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return controller;
|
|
782
|
+
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Assumes 2 cameras that are parallel and share an X-axis, and that
|
|
789
|
+
* the cameras' projection and world matrices have already been set.
|
|
790
|
+
* And that near and far planes are identical for both cameras.
|
|
791
|
+
* Visualization of this technique: https://computergraphics.stackexchange.com/a/4765
|
|
792
|
+
*
|
|
793
|
+
* @param {ArrayCamera} camera - The camera to update.
|
|
794
|
+
* @param {PerspectiveCamera} cameraL - The left camera.
|
|
795
|
+
* @param {PerspectiveCamera} cameraR - The right camera.
|
|
796
|
+
*/
|
|
797
|
+
function setProjectionFromUnion( camera, cameraL, cameraR ) {
|
|
798
|
+
|
|
799
|
+
_cameraLPos.setFromMatrixPosition( cameraL.matrixWorld );
|
|
800
|
+
_cameraRPos.setFromMatrixPosition( cameraR.matrixWorld );
|
|
801
|
+
|
|
802
|
+
const ipd = _cameraLPos.distanceTo( _cameraRPos );
|
|
803
|
+
|
|
804
|
+
const projL = cameraL.projectionMatrix.elements;
|
|
805
|
+
const projR = cameraR.projectionMatrix.elements;
|
|
806
|
+
|
|
807
|
+
// VR systems will have identical far and near planes, and
|
|
808
|
+
// most likely identical top and bottom frustum extents.
|
|
809
|
+
// Use the left camera for these values.
|
|
810
|
+
const near = projL[ 14 ] / ( projL[ 10 ] - 1 );
|
|
811
|
+
const far = projL[ 14 ] / ( projL[ 10 ] + 1 );
|
|
812
|
+
const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];
|
|
813
|
+
const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];
|
|
814
|
+
|
|
815
|
+
const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];
|
|
816
|
+
const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];
|
|
817
|
+
const left = near * leftFov;
|
|
818
|
+
const right = near * rightFov;
|
|
819
|
+
|
|
820
|
+
// Calculate the new camera's position offset from the
|
|
821
|
+
// left camera. xOffset should be roughly half `ipd`.
|
|
822
|
+
const zOffset = ipd / ( - leftFov + rightFov );
|
|
823
|
+
const xOffset = zOffset * - leftFov;
|
|
824
|
+
|
|
825
|
+
// TODO: Better way to apply this offset?
|
|
826
|
+
cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );
|
|
827
|
+
camera.translateX( xOffset );
|
|
828
|
+
camera.translateZ( zOffset );
|
|
829
|
+
camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );
|
|
830
|
+
camera.matrixWorldInverse.copy( camera.matrixWorld ).invert();
|
|
831
|
+
|
|
832
|
+
// Check if the projection uses an infinite far plane.
|
|
833
|
+
if ( projL[ 10 ] === - 1.0 ) {
|
|
834
|
+
|
|
835
|
+
// Use the projection matrix from the left eye.
|
|
836
|
+
// The camera offset is sufficient to include the view volumes
|
|
837
|
+
// of both eyes (assuming symmetric projections).
|
|
838
|
+
camera.projectionMatrix.copy( cameraL.projectionMatrix );
|
|
839
|
+
camera.projectionMatrixInverse.copy( cameraL.projectionMatrixInverse );
|
|
840
|
+
|
|
841
|
+
} else {
|
|
842
|
+
|
|
843
|
+
// Find the union of the frustum values of the cameras and scale
|
|
844
|
+
// the values so that the near plane's position does not change in world space,
|
|
845
|
+
// although must now be relative to the new union camera.
|
|
846
|
+
const near2 = near + zOffset;
|
|
847
|
+
const far2 = far + zOffset;
|
|
848
|
+
const left2 = left - xOffset;
|
|
849
|
+
const right2 = right + ( ipd - xOffset );
|
|
850
|
+
const top2 = topFov * far / far2 * near2;
|
|
851
|
+
const bottom2 = bottomFov * far / far2 * near2;
|
|
852
|
+
|
|
853
|
+
camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );
|
|
854
|
+
camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();
|
|
855
|
+
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Updates the world matrices for the given camera based on the parent 3D object.
|
|
862
|
+
*
|
|
863
|
+
* @inner
|
|
864
|
+
* @param {Camera} camera - The camera to update.
|
|
865
|
+
* @param {Object3D} parent - The parent 3D object.
|
|
866
|
+
*/
|
|
867
|
+
function updateCamera( camera, parent ) {
|
|
868
|
+
|
|
869
|
+
if ( parent === null ) {
|
|
870
|
+
|
|
871
|
+
camera.matrixWorld.copy( camera.matrix );
|
|
872
|
+
|
|
873
|
+
} else {
|
|
874
|
+
|
|
875
|
+
camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );
|
|
876
|
+
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
camera.matrixWorldInverse.copy( camera.matrixWorld ).invert();
|
|
880
|
+
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Updates the given camera with the transfomration of the XR camera and parent object.
|
|
885
|
+
*
|
|
886
|
+
* @inner
|
|
887
|
+
* @param {Camera} camera - The camera to update.
|
|
888
|
+
* @param {ArrayCamera} cameraXR - The XR camera.
|
|
889
|
+
* @param {Object3D} parent - The parent 3D object.
|
|
890
|
+
*/
|
|
891
|
+
function updateUserCamera( camera, cameraXR, parent ) {
|
|
892
|
+
|
|
893
|
+
if ( parent === null ) {
|
|
894
|
+
|
|
895
|
+
camera.matrix.copy( cameraXR.matrixWorld );
|
|
896
|
+
|
|
897
|
+
} else {
|
|
898
|
+
|
|
899
|
+
camera.matrix.copy( parent.matrixWorld );
|
|
900
|
+
camera.matrix.invert();
|
|
901
|
+
camera.matrix.multiply( cameraXR.matrixWorld );
|
|
902
|
+
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
camera.matrix.decompose( camera.position, camera.quaternion, camera.scale );
|
|
906
|
+
camera.updateMatrixWorld( true );
|
|
907
|
+
|
|
908
|
+
camera.projectionMatrix.copy( cameraXR.projectionMatrix );
|
|
909
|
+
camera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse );
|
|
910
|
+
|
|
911
|
+
if ( camera.isPerspectiveCamera ) {
|
|
912
|
+
|
|
913
|
+
camera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] );
|
|
914
|
+
camera.zoom = 1;
|
|
915
|
+
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function onSessionEvent( event ) {
|
|
921
|
+
|
|
922
|
+
const controllerIndex = this._controllerInputSources.indexOf( event.inputSource );
|
|
923
|
+
|
|
924
|
+
if ( controllerIndex === - 1 ) {
|
|
925
|
+
|
|
926
|
+
return;
|
|
927
|
+
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const controller = this._controllers[ controllerIndex ];
|
|
931
|
+
|
|
932
|
+
if ( controller !== undefined ) {
|
|
933
|
+
|
|
934
|
+
const referenceSpace = this.getReferenceSpace();
|
|
935
|
+
|
|
936
|
+
controller.update( event.inputSource, event.frame, referenceSpace );
|
|
937
|
+
controller.dispatchEvent( { type: event.type, data: event.inputSource } );
|
|
938
|
+
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function onSessionEnd() {
|
|
944
|
+
|
|
945
|
+
const session = this._session;
|
|
946
|
+
const renderer = this._renderer;
|
|
947
|
+
|
|
948
|
+
session.removeEventListener( 'select', this._onSessionEvent );
|
|
949
|
+
session.removeEventListener( 'selectstart', this._onSessionEvent );
|
|
950
|
+
session.removeEventListener( 'selectend', this._onSessionEvent );
|
|
951
|
+
session.removeEventListener( 'squeeze', this._onSessionEvent );
|
|
952
|
+
session.removeEventListener( 'squeezestart', this._onSessionEvent );
|
|
953
|
+
session.removeEventListener( 'squeezeend', this._onSessionEvent );
|
|
954
|
+
session.removeEventListener( 'end', this._onSessionEnd );
|
|
955
|
+
session.removeEventListener( 'inputsourceschange', this._onInputSourcesChange );
|
|
956
|
+
|
|
957
|
+
for ( let i = 0; i < this._controllers.length; i ++ ) {
|
|
958
|
+
|
|
959
|
+
const inputSource = this._controllerInputSources[ i ];
|
|
960
|
+
|
|
961
|
+
if ( inputSource === null ) continue;
|
|
962
|
+
|
|
963
|
+
this._controllerInputSources[ i ] = null;
|
|
964
|
+
|
|
965
|
+
this._controllers[ i ].disconnect( inputSource );
|
|
966
|
+
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
this._currentDepthNear = null;
|
|
970
|
+
this._currentDepthFar = null;
|
|
971
|
+
|
|
972
|
+
// restore framebuffer/rendering state
|
|
973
|
+
|
|
974
|
+
renderer.backend.setXRTarget( null );
|
|
975
|
+
renderer.setRenderTarget( this._currentRenderTarget );
|
|
976
|
+
|
|
977
|
+
this._session = null;
|
|
978
|
+
this._xrRenderTarget = null;
|
|
979
|
+
|
|
980
|
+
//
|
|
981
|
+
|
|
982
|
+
this.isPresenting = false;
|
|
983
|
+
|
|
984
|
+
renderer._animation.stop();
|
|
985
|
+
|
|
986
|
+
renderer._animation.setAnimationLoop( this._currentAnimationLoop );
|
|
987
|
+
renderer._animation.setContext( this._currentAnimationContext );
|
|
988
|
+
renderer._animation.start();
|
|
989
|
+
|
|
990
|
+
renderer.setPixelRatio( this._currentPixelRatio );
|
|
991
|
+
renderer.setSize( this._currentSize.width, this._currentSize.height, false );
|
|
992
|
+
|
|
993
|
+
this.dispatchEvent( { type: 'sessionend' } );
|
|
994
|
+
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function onInputSourcesChange( event ) {
|
|
998
|
+
|
|
999
|
+
const controllers = this._controllers;
|
|
1000
|
+
const controllerInputSources = this._controllerInputSources;
|
|
1001
|
+
|
|
1002
|
+
// Notify disconnected
|
|
1003
|
+
|
|
1004
|
+
for ( let i = 0; i < event.removed.length; i ++ ) {
|
|
1005
|
+
|
|
1006
|
+
const inputSource = event.removed[ i ];
|
|
1007
|
+
const index = controllerInputSources.indexOf( inputSource );
|
|
1008
|
+
|
|
1009
|
+
if ( index >= 0 ) {
|
|
1010
|
+
|
|
1011
|
+
controllerInputSources[ index ] = null;
|
|
1012
|
+
controllers[ index ].disconnect( inputSource );
|
|
1013
|
+
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Notify connected
|
|
1019
|
+
|
|
1020
|
+
for ( let i = 0; i < event.added.length; i ++ ) {
|
|
1021
|
+
|
|
1022
|
+
const inputSource = event.added[ i ];
|
|
1023
|
+
|
|
1024
|
+
let controllerIndex = controllerInputSources.indexOf( inputSource );
|
|
1025
|
+
|
|
1026
|
+
if ( controllerIndex === - 1 ) {
|
|
1027
|
+
|
|
1028
|
+
// Assign input source a controller that currently has no input source
|
|
1029
|
+
|
|
1030
|
+
for ( let i = 0; i < controllers.length; i ++ ) {
|
|
1031
|
+
|
|
1032
|
+
if ( i >= controllerInputSources.length ) {
|
|
1033
|
+
|
|
1034
|
+
controllerInputSources.push( inputSource );
|
|
1035
|
+
controllerIndex = i;
|
|
1036
|
+
break;
|
|
1037
|
+
|
|
1038
|
+
} else if ( controllerInputSources[ i ] === null ) {
|
|
1039
|
+
|
|
1040
|
+
controllerInputSources[ i ] = inputSource;
|
|
1041
|
+
controllerIndex = i;
|
|
1042
|
+
break;
|
|
1043
|
+
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// If all controllers do currently receive input we ignore new ones
|
|
1049
|
+
|
|
1050
|
+
if ( controllerIndex === - 1 ) break;
|
|
1051
|
+
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const controller = controllers[ controllerIndex ];
|
|
1055
|
+
|
|
1056
|
+
if ( controller ) {
|
|
1057
|
+
|
|
1058
|
+
controller.connect( inputSource );
|
|
1059
|
+
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function onAnimationFrame( time, frame ) {
|
|
1067
|
+
|
|
1068
|
+
if ( frame === undefined ) return;
|
|
1069
|
+
|
|
1070
|
+
const cameraXR = this._cameraXR;
|
|
1071
|
+
const renderer = this._renderer;
|
|
1072
|
+
const backend = renderer.backend;
|
|
1073
|
+
|
|
1074
|
+
const glBaseLayer = this._glBaseLayer;
|
|
1075
|
+
|
|
1076
|
+
const referenceSpace = this.getReferenceSpace();
|
|
1077
|
+
const pose = frame.getViewerPose( referenceSpace );
|
|
1078
|
+
|
|
1079
|
+
this._xrFrame = frame;
|
|
1080
|
+
|
|
1081
|
+
if ( pose !== null ) {
|
|
1082
|
+
|
|
1083
|
+
const views = pose.views;
|
|
1084
|
+
|
|
1085
|
+
if ( this._glBaseLayer !== null ) {
|
|
1086
|
+
|
|
1087
|
+
backend.setXRTarget( glBaseLayer.framebuffer );
|
|
1088
|
+
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
let cameraXRNeedsUpdate = false;
|
|
1092
|
+
|
|
1093
|
+
// check if it's necessary to rebuild cameraXR's camera list
|
|
1094
|
+
|
|
1095
|
+
if ( views.length !== cameraXR.cameras.length ) {
|
|
1096
|
+
|
|
1097
|
+
cameraXR.cameras.length = 0;
|
|
1098
|
+
cameraXRNeedsUpdate = true;
|
|
1099
|
+
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
for ( let i = 0; i < views.length; i ++ ) {
|
|
1103
|
+
|
|
1104
|
+
const view = views[ i ];
|
|
1105
|
+
|
|
1106
|
+
let viewport;
|
|
1107
|
+
|
|
1108
|
+
if ( this._useLayers === true ) {
|
|
1109
|
+
|
|
1110
|
+
const glSubImage = this._glBinding.getViewSubImage( this._glProjLayer, view );
|
|
1111
|
+
viewport = glSubImage.viewport;
|
|
1112
|
+
|
|
1113
|
+
// For side-by-side projection, we only produce a single texture for both eyes.
|
|
1114
|
+
if ( i === 0 ) {
|
|
1115
|
+
|
|
1116
|
+
backend.setXRRenderTargetTextures(
|
|
1117
|
+
this._xrRenderTarget,
|
|
1118
|
+
glSubImage.colorTexture,
|
|
1119
|
+
this._glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture
|
|
1120
|
+
);
|
|
1121
|
+
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
} else {
|
|
1125
|
+
|
|
1126
|
+
viewport = glBaseLayer.getViewport( view );
|
|
1127
|
+
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
let camera = this._cameras[ i ];
|
|
1131
|
+
|
|
1132
|
+
if ( camera === undefined ) {
|
|
1133
|
+
|
|
1134
|
+
camera = new PerspectiveCamera();
|
|
1135
|
+
camera.layers.enable( i );
|
|
1136
|
+
camera.viewport = new Vector4();
|
|
1137
|
+
this._cameras[ i ] = camera;
|
|
1138
|
+
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
camera.matrix.fromArray( view.transform.matrix );
|
|
1142
|
+
camera.matrix.decompose( camera.position, camera.quaternion, camera.scale );
|
|
1143
|
+
camera.projectionMatrix.fromArray( view.projectionMatrix );
|
|
1144
|
+
camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert();
|
|
1145
|
+
camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
|
|
1146
|
+
|
|
1147
|
+
if ( i === 0 ) {
|
|
1148
|
+
|
|
1149
|
+
cameraXR.matrix.copy( camera.matrix );
|
|
1150
|
+
cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale );
|
|
1151
|
+
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
if ( cameraXRNeedsUpdate === true ) {
|
|
1155
|
+
|
|
1156
|
+
cameraXR.cameras.push( camera );
|
|
1157
|
+
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
renderer.setRenderTarget( this._xrRenderTarget );
|
|
1163
|
+
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
//
|
|
1167
|
+
|
|
1168
|
+
for ( let i = 0; i < this._controllers.length; i ++ ) {
|
|
1169
|
+
|
|
1170
|
+
const inputSource = this._controllerInputSources[ i ];
|
|
1171
|
+
const controller = this._controllers[ i ];
|
|
1172
|
+
|
|
1173
|
+
if ( inputSource !== null && controller !== undefined ) {
|
|
1174
|
+
|
|
1175
|
+
controller.update( inputSource, frame, referenceSpace );
|
|
1176
|
+
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
if ( this._currentAnimationLoop ) this._currentAnimationLoop( time, frame );
|
|
1182
|
+
|
|
1183
|
+
if ( frame.detectedPlanes ) {
|
|
1184
|
+
|
|
1185
|
+
this.dispatchEvent( { type: 'planesdetected', data: frame } );
|
|
1186
|
+
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
this._xrFrame = null;
|
|
1190
|
+
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
export default XRManager;
|