xrblocks 0.4.0 → 0.5.1
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/build/camera/CameraOptions.d.ts +88 -1
- package/build/camera/CameraUtils.d.ts +0 -18
- package/build/camera/XRDeviceCamera.d.ts +4 -3
- package/build/core/Core.d.ts +2 -0
- package/build/core/Options.d.ts +8 -5
- package/build/core/components/PermissionsManager.d.ts +54 -0
- package/build/core/components/XRButton.d.ts +8 -1
- package/build/depth/DepthOptions.d.ts +8 -0
- package/build/simulator/Simulator.d.ts +2 -0
- package/build/simulator/SimulatorOptions.d.ts +5 -1
- package/build/ui/components/TextButton.d.ts +2 -0
- package/build/ui/components/TextView.d.ts +1 -1
- package/build/world/objects/ObjectDetector.d.ts +8 -3
- package/build/xrblocks.js +410 -79
- package/build/xrblocks.js.map +1 -1
- package/build/xrblocks.min.js +1 -1
- package/build/xrblocks.min.js.map +1 -1
- package/package.json +1 -1
package/build/xrblocks.js
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*
|
|
16
16
|
* @file xrblocks.js
|
|
17
|
-
* @version v0.
|
|
18
|
-
* @commitid
|
|
19
|
-
* @builddate 2025-
|
|
17
|
+
* @version v0.5.1
|
|
18
|
+
* @commitid 896d66f
|
|
19
|
+
* @builddate 2025-12-06T06:19:44.629Z
|
|
20
20
|
* @description XR Blocks SDK, built from source with the above commit ID.
|
|
21
21
|
* @agent When using with Gemini to create XR apps, use **Gemini Canvas** mode,
|
|
22
22
|
* and follow rules below:
|
|
@@ -1849,6 +1849,27 @@ function deepMerge(obj1, obj2) {
|
|
|
1849
1849
|
}
|
|
1850
1850
|
}
|
|
1851
1851
|
|
|
1852
|
+
/**
|
|
1853
|
+
* Default parameters for rgb to depth projection.
|
|
1854
|
+
* For RGB and depth, 4:3 and 1:1, respectively.
|
|
1855
|
+
*/
|
|
1856
|
+
const DEFAULT_RGB_TO_DEPTH_PARAMS = {
|
|
1857
|
+
scale: 1,
|
|
1858
|
+
scaleX: 0.75,
|
|
1859
|
+
scaleY: 0.63,
|
|
1860
|
+
translateU: 0.2,
|
|
1861
|
+
translateV: -0.02,
|
|
1862
|
+
k1: -0.046,
|
|
1863
|
+
k2: 0,
|
|
1864
|
+
k3: 0,
|
|
1865
|
+
p1: 0,
|
|
1866
|
+
p2: 0,
|
|
1867
|
+
xc: 0,
|
|
1868
|
+
yc: 0,
|
|
1869
|
+
};
|
|
1870
|
+
/**
|
|
1871
|
+
* Configuration options for the device camera.
|
|
1872
|
+
*/
|
|
1852
1873
|
class DeviceCameraOptions {
|
|
1853
1874
|
constructor(options) {
|
|
1854
1875
|
this.enabled = false;
|
|
@@ -1856,10 +1877,14 @@ class DeviceCameraOptions {
|
|
|
1856
1877
|
* Hint for performance optimization on frequent captures.
|
|
1857
1878
|
*/
|
|
1858
1879
|
this.willCaptureFrequently = false;
|
|
1880
|
+
/**
|
|
1881
|
+
* Parameters for RGB to depth UV mapping given different aspect ratios.
|
|
1882
|
+
*/
|
|
1883
|
+
this.rgbToDepthParams = { ...DEFAULT_RGB_TO_DEPTH_PARAMS };
|
|
1859
1884
|
deepMerge(this, options);
|
|
1860
1885
|
}
|
|
1861
1886
|
}
|
|
1862
|
-
// Base configuration for all common capture settings
|
|
1887
|
+
// Base configuration for all common capture settings.
|
|
1863
1888
|
const baseCaptureOptions = {
|
|
1864
1889
|
enabled: true,
|
|
1865
1890
|
videoConstraints: {
|
|
@@ -2408,6 +2433,8 @@ class DepthOptions {
|
|
|
2408
2433
|
// Occlusion pass.
|
|
2409
2434
|
this.occlusion = { enabled: false };
|
|
2410
2435
|
this.useFloat32 = true;
|
|
2436
|
+
this.depthTypeRequest = ['raw'];
|
|
2437
|
+
this.matchDepthView = true;
|
|
2411
2438
|
deepMerge(this, options);
|
|
2412
2439
|
}
|
|
2413
2440
|
}
|
|
@@ -3314,24 +3341,6 @@ const aspectRatios = {
|
|
|
3314
3341
|
depth: 1.0,
|
|
3315
3342
|
RGB: 4 / 3,
|
|
3316
3343
|
};
|
|
3317
|
-
/**
|
|
3318
|
-
* Parameters for RGB to depth UV mapping (manually calibrated for aspect
|
|
3319
|
-
* ratios. For RGB and depth, 4:3 and 1:1, respectively.
|
|
3320
|
-
*/
|
|
3321
|
-
const rgbToDepthParams = {
|
|
3322
|
-
scale: 1,
|
|
3323
|
-
scaleX: 0.75,
|
|
3324
|
-
scaleY: 0.63,
|
|
3325
|
-
translateU: 0.2,
|
|
3326
|
-
translateV: -0.02,
|
|
3327
|
-
k1: -0.046,
|
|
3328
|
-
k2: 0,
|
|
3329
|
-
k3: 0,
|
|
3330
|
-
p1: 0,
|
|
3331
|
-
p2: 0,
|
|
3332
|
-
xc: 0,
|
|
3333
|
-
yc: 0,
|
|
3334
|
-
};
|
|
3335
3344
|
/**
|
|
3336
3345
|
* Maps a UV coordinate from a RGB space to a destination depth space,
|
|
3337
3346
|
* applying Brown-Conrady distortion and affine transformations based on
|
|
@@ -3366,42 +3375,40 @@ function transformRgbToDepthUv(rgbUv, xrDeviceCamera) {
|
|
|
3366
3375
|
console.error('Invalid aspect ratios provided.');
|
|
3367
3376
|
return null;
|
|
3368
3377
|
}
|
|
3369
|
-
|
|
3378
|
+
const params = xrDeviceCamera?.rgbToDepthParams ?? DEFAULT_RGB_TO_DEPTH_PARAMS;
|
|
3379
|
+
// Determine the relative scaling required to fit the overlay within the base.
|
|
3370
3380
|
let relativeScaleX, relativeScaleY;
|
|
3371
3381
|
if (aspectRatios.depth > aspectRatios.RGB) {
|
|
3372
|
-
// Base is wider than overlay ("letterboxing")
|
|
3382
|
+
// Base is wider than overlay ("letterboxing").
|
|
3373
3383
|
relativeScaleY = 1.0;
|
|
3374
3384
|
relativeScaleX = aspectRatios.RGB / aspectRatios.depth;
|
|
3375
3385
|
}
|
|
3376
3386
|
else {
|
|
3377
|
-
// Base is narrower than overlay ("pillarboxing")
|
|
3387
|
+
// Base is narrower than overlay ("pillarboxing").
|
|
3378
3388
|
relativeScaleX = 1.0;
|
|
3379
3389
|
relativeScaleY = aspectRatios.depth / aspectRatios.RGB;
|
|
3380
3390
|
}
|
|
3381
|
-
// Convert input source UV [0, 1] to
|
|
3391
|
+
// Convert input source UV [0, 1] to normalized coordinates in [-0.5, 0.5].
|
|
3382
3392
|
const u_norm = rgbUv.u - 0.5;
|
|
3383
3393
|
const v_norm = rgbUv.v - 0.5;
|
|
3384
|
-
// Apply the FORWARD Brown-Conrady distortion model
|
|
3385
|
-
const u_centered = u_norm -
|
|
3386
|
-
const v_centered = v_norm -
|
|
3394
|
+
// Apply the FORWARD Brown-Conrady distortion model.
|
|
3395
|
+
const u_centered = u_norm - params.xc;
|
|
3396
|
+
const v_centered = v_norm - params.yc;
|
|
3387
3397
|
const r2 = u_centered * u_centered + v_centered * v_centered;
|
|
3388
|
-
const radial = 1 +
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
const
|
|
3395
|
-
|
|
3396
|
-
const
|
|
3397
|
-
const
|
|
3398
|
-
// Apply
|
|
3399
|
-
const
|
|
3400
|
-
const
|
|
3401
|
-
//
|
|
3402
|
-
const finalNormX = u_fitted * rgbToDepthParams.scale * rgbToDepthParams.scaleX;
|
|
3403
|
-
const finalNormY = v_fitted * rgbToDepthParams.scale * rgbToDepthParams.scaleY;
|
|
3404
|
-
// Convert the final normalized coordinate back to a UV coordinate [0, 1]
|
|
3398
|
+
const radial = 1 + params.k1 * r2 + params.k2 * r2 * r2 + params.k3 * r2 * r2 * r2;
|
|
3399
|
+
const tanX = 2 * params.p1 * u_centered * v_centered +
|
|
3400
|
+
params.p2 * (r2 + 2 * u_centered * u_centered);
|
|
3401
|
+
const tanY = params.p1 * (r2 + 2 * v_centered * v_centered) +
|
|
3402
|
+
2 * params.p2 * u_centered * v_centered;
|
|
3403
|
+
const u_distorted = u_centered * radial + tanX + params.xc;
|
|
3404
|
+
const v_distorted = v_centered * radial + tanY + params.yc;
|
|
3405
|
+
// Apply initial aspect ratio scaling and translation.
|
|
3406
|
+
const u_fitted = u_distorted * relativeScaleX + params.translateU;
|
|
3407
|
+
const v_fitted = v_distorted * relativeScaleY + params.translateV;
|
|
3408
|
+
// Apply the final user-controlled scaling (zoom and stretch).
|
|
3409
|
+
const finalNormX = u_fitted * params.scale * params.scaleX;
|
|
3410
|
+
const finalNormY = v_fitted * params.scale * params.scaleY;
|
|
3411
|
+
// Convert the final normalized coordinate back to a UV coordinate [0, 1].
|
|
3405
3412
|
const finalU = finalNormX + 0.5;
|
|
3406
3413
|
const finalV = finalNormY + 0.5;
|
|
3407
3414
|
return { u: finalU, v: 1.0 - finalV };
|
|
@@ -3665,12 +3672,13 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3665
3672
|
/**
|
|
3666
3673
|
* @param options - The configuration options.
|
|
3667
3674
|
*/
|
|
3668
|
-
constructor({ videoConstraints = { facingMode: 'environment' }, willCaptureFrequently = false, } = {}) {
|
|
3675
|
+
constructor({ videoConstraints = { facingMode: 'environment' }, willCaptureFrequently = false, rgbToDepthParams = DEFAULT_RGB_TO_DEPTH_PARAMS, } = {}) {
|
|
3669
3676
|
super({ willCaptureFrequently });
|
|
3670
3677
|
this.isInitializing_ = false;
|
|
3671
3678
|
this.availableDevices_ = [];
|
|
3672
3679
|
this.currentDeviceIndex_ = -1;
|
|
3673
3680
|
this.videoConstraints_ = { ...videoConstraints };
|
|
3681
|
+
this.rgbToDepthParams = rgbToDepthParams;
|
|
3674
3682
|
}
|
|
3675
3683
|
/**
|
|
3676
3684
|
* Retrieves the list of available video input devices.
|
|
@@ -3722,7 +3730,7 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3722
3730
|
return;
|
|
3723
3731
|
this.isInitializing_ = true;
|
|
3724
3732
|
this.setState_(StreamState.INITIALIZING);
|
|
3725
|
-
// Reset state for the new stream
|
|
3733
|
+
// Reset state for the new stream.
|
|
3726
3734
|
this.currentTrackSettings_ = undefined;
|
|
3727
3735
|
this.currentDeviceIndex_ = -1;
|
|
3728
3736
|
try {
|
|
@@ -3756,7 +3764,7 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3756
3764
|
if (!videoTracks.length) {
|
|
3757
3765
|
throw new Error('MediaStream has no video tracks.');
|
|
3758
3766
|
}
|
|
3759
|
-
// After the stream is active, we can get the ID
|
|
3767
|
+
// After the stream is active, we can get the track ID.
|
|
3760
3768
|
const activeTrack = videoTracks[0];
|
|
3761
3769
|
this.currentTrackSettings_ = activeTrack.getSettings();
|
|
3762
3770
|
console.debug('Active track settings:', this.currentTrackSettings_);
|
|
@@ -3766,10 +3774,10 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3766
3774
|
else {
|
|
3767
3775
|
console.warn('Stream started without deviceId as it was unavailable');
|
|
3768
3776
|
}
|
|
3769
|
-
this.stop_(); // Stop any previous stream before starting new one
|
|
3777
|
+
this.stop_(); // Stop any previous stream before starting new one.
|
|
3770
3778
|
this.stream_ = stream;
|
|
3771
3779
|
this.video_.srcObject = stream;
|
|
3772
|
-
this.video_.src = ''; // Required for some browsers to reset the src
|
|
3780
|
+
this.video_.src = ''; // Required for some browsers to reset the src.
|
|
3773
3781
|
await new Promise((resolve, reject) => {
|
|
3774
3782
|
this.video_.onloadedmetadata = () => {
|
|
3775
3783
|
this.handleVideoStreamLoadedMetadata(resolve, reject, true);
|
|
@@ -3781,7 +3789,7 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3781
3789
|
};
|
|
3782
3790
|
this.video_.play();
|
|
3783
3791
|
});
|
|
3784
|
-
// Once
|
|
3792
|
+
// Once stream is loaded and dimensions are known, set the final state.
|
|
3785
3793
|
const details = {
|
|
3786
3794
|
width: this.width,
|
|
3787
3795
|
height: this.height,
|
|
@@ -3803,7 +3811,7 @@ class XRDeviceCamera extends VideoStream {
|
|
|
3803
3811
|
/**
|
|
3804
3812
|
* Sets the active camera by its device ID. Removes potentially conflicting
|
|
3805
3813
|
* constraints such as facingMode.
|
|
3806
|
-
* @param deviceId - Device
|
|
3814
|
+
* @param deviceId - Device ID
|
|
3807
3815
|
*/
|
|
3808
3816
|
async setDeviceId(deviceId) {
|
|
3809
3817
|
const newIndex = this.availableDevices_.findIndex((device) => device.deviceId === deviceId);
|
|
@@ -4402,13 +4410,19 @@ class WebXRSessionManager extends THREE.EventDispatcher {
|
|
|
4402
4410
|
const XRBUTTON_WRAPPER_ID = 'XRButtonWrapper';
|
|
4403
4411
|
const XRBUTTON_CLASS = 'XRButton';
|
|
4404
4412
|
class XRButton {
|
|
4405
|
-
constructor(sessionManager, startText = 'ENTER XR', endText = 'END XR', invalidText = 'XR NOT SUPPORTED', startSimulatorText = 'START SIMULATOR', showEnterSimulatorButton = false, startSimulator = () => { }
|
|
4413
|
+
constructor(sessionManager, permissionsManager, startText = 'ENTER XR', endText = 'END XR', invalidText = 'XR NOT SUPPORTED', startSimulatorText = 'START SIMULATOR', showEnterSimulatorButton = false, startSimulator = () => { }, permissions = {
|
|
4414
|
+
geolocation: false,
|
|
4415
|
+
camera: false,
|
|
4416
|
+
microphone: false,
|
|
4417
|
+
}) {
|
|
4406
4418
|
this.sessionManager = sessionManager;
|
|
4419
|
+
this.permissionsManager = permissionsManager;
|
|
4407
4420
|
this.startText = startText;
|
|
4408
4421
|
this.endText = endText;
|
|
4409
4422
|
this.invalidText = invalidText;
|
|
4410
4423
|
this.startSimulatorText = startSimulatorText;
|
|
4411
4424
|
this.startSimulator = startSimulator;
|
|
4425
|
+
this.permissions = permissions;
|
|
4412
4426
|
this.domElement = document.createElement('div');
|
|
4413
4427
|
this.simulatorButtonElement = document.createElement('button');
|
|
4414
4428
|
this.xrButtonElement = document.createElement('button');
|
|
@@ -4443,7 +4457,17 @@ class XRButton {
|
|
|
4443
4457
|
button.innerHTML = this.startText;
|
|
4444
4458
|
button.disabled = false;
|
|
4445
4459
|
button.onclick = () => {
|
|
4446
|
-
this.
|
|
4460
|
+
this.permissionsManager
|
|
4461
|
+
.checkAndRequestPermissions(this.permissions)
|
|
4462
|
+
.then((result) => {
|
|
4463
|
+
if (result.granted) {
|
|
4464
|
+
this.sessionManager.startSession();
|
|
4465
|
+
}
|
|
4466
|
+
else {
|
|
4467
|
+
this.xrButtonElement.textContent =
|
|
4468
|
+
'Error:' + result.error + '\nPlease try again.';
|
|
4469
|
+
}
|
|
4470
|
+
});
|
|
4447
4471
|
};
|
|
4448
4472
|
}
|
|
4449
4473
|
showXRNotSupported() {
|
|
@@ -7172,6 +7196,7 @@ class SimulatorOptions {
|
|
|
7172
7196
|
constructor(options) {
|
|
7173
7197
|
this.initialCameraPosition = { x: 0, y: 1.5, z: 0 };
|
|
7174
7198
|
this.scenePath = XR_BLOCKS_ASSETS_PATH + 'simulator/scenes/XREmulatorsceneV5_livingRoom.glb';
|
|
7199
|
+
this.videoPath = undefined;
|
|
7175
7200
|
this.initialScenePosition = { x: -1.6, y: 0.3, z: 0 };
|
|
7176
7201
|
this.defaultMode = SimulatorMode.USER;
|
|
7177
7202
|
this.defaultHand = Handedness.LEFT;
|
|
@@ -7188,7 +7213,10 @@ class SimulatorOptions {
|
|
|
7188
7213
|
enabled: true,
|
|
7189
7214
|
element: 'xrblocks-simulator-hand-pose-panel',
|
|
7190
7215
|
};
|
|
7191
|
-
this.
|
|
7216
|
+
this.geminiLivePanel = {
|
|
7217
|
+
enabled: false,
|
|
7218
|
+
element: 'xrblocks-simulator-geminilive',
|
|
7219
|
+
};
|
|
7192
7220
|
this.stereo = {
|
|
7193
7221
|
enabled: false,
|
|
7194
7222
|
};
|
|
@@ -7436,6 +7464,14 @@ class Options {
|
|
|
7436
7464
|
// Whether to autostart the simulator even if WebXR is available.
|
|
7437
7465
|
alwaysAutostartSimulator: false,
|
|
7438
7466
|
};
|
|
7467
|
+
/**
|
|
7468
|
+
* Which permissions to request before entering the XR session.
|
|
7469
|
+
*/
|
|
7470
|
+
this.permissions = {
|
|
7471
|
+
geolocation: false,
|
|
7472
|
+
camera: false,
|
|
7473
|
+
microphone: false,
|
|
7474
|
+
};
|
|
7439
7475
|
deepMerge(this, options);
|
|
7440
7476
|
}
|
|
7441
7477
|
/**
|
|
@@ -7476,6 +7512,7 @@ class Options {
|
|
|
7476
7512
|
* @returns The instance for chaining.
|
|
7477
7513
|
*/
|
|
7478
7514
|
enableObjectDetection() {
|
|
7515
|
+
this.permissions.camera = true;
|
|
7479
7516
|
this.world.enableObjectDetection();
|
|
7480
7517
|
return this;
|
|
7481
7518
|
}
|
|
@@ -7486,6 +7523,7 @@ class Options {
|
|
|
7486
7523
|
* @returns The instance for chaining.
|
|
7487
7524
|
*/
|
|
7488
7525
|
enableCamera(facingMode = 'environment') {
|
|
7526
|
+
this.permissions.camera = true;
|
|
7489
7527
|
this.deviceCamera = new DeviceCameraOptions(facingMode === 'environment'
|
|
7490
7528
|
? xrDeviceCameraEnvironmentOptions
|
|
7491
7529
|
: xrDeviceCameraUserOptions);
|
|
@@ -7516,14 +7554,6 @@ class Options {
|
|
|
7516
7554
|
this.controllers.visualizeRays = true;
|
|
7517
7555
|
return this;
|
|
7518
7556
|
}
|
|
7519
|
-
/**
|
|
7520
|
-
* Enables the Gemini Live feature.
|
|
7521
|
-
* @returns The instance for chaining.
|
|
7522
|
-
*/
|
|
7523
|
-
enableGeminiLive() {
|
|
7524
|
-
this.simulator.geminilive = true;
|
|
7525
|
-
return this;
|
|
7526
|
-
}
|
|
7527
7557
|
/**
|
|
7528
7558
|
* Enables a standard set of AI features, including Gemini Live.
|
|
7529
7559
|
* @returns The instance for chaining.
|
|
@@ -9785,8 +9815,8 @@ class SimulatorInterface {
|
|
|
9785
9815
|
}
|
|
9786
9816
|
}
|
|
9787
9817
|
showGeminiLivePanel(simulatorOptions) {
|
|
9788
|
-
if (simulatorOptions.
|
|
9789
|
-
const element = document.createElement(
|
|
9818
|
+
if (simulatorOptions.geminiLivePanel.enabled) {
|
|
9819
|
+
const element = document.createElement(simulatorOptions.geminiLivePanel.element);
|
|
9790
9820
|
document.body.appendChild(element);
|
|
9791
9821
|
this.elements.push(element);
|
|
9792
9822
|
}
|
|
@@ -9830,6 +9860,9 @@ class SimulatorScene extends THREE.Scene {
|
|
|
9830
9860
|
}
|
|
9831
9861
|
async init(simulatorOptions) {
|
|
9832
9862
|
this.addLights();
|
|
9863
|
+
if (simulatorOptions.videoPath) {
|
|
9864
|
+
return;
|
|
9865
|
+
}
|
|
9833
9866
|
if (simulatorOptions.scenePath) {
|
|
9834
9867
|
await this.loadGLTF(simulatorOptions.scenePath, new THREE.Vector3(simulatorOptions.initialScenePosition.x, simulatorOptions.initialScenePosition.y, simulatorOptions.initialScenePosition.z));
|
|
9835
9868
|
}
|
|
@@ -9967,6 +10000,21 @@ class Simulator extends Script {
|
|
|
9967
10000
|
if (this.options.stereo.enabled) {
|
|
9968
10001
|
this.setupStereoCameras(camera);
|
|
9969
10002
|
}
|
|
10003
|
+
if (this.options.videoPath) {
|
|
10004
|
+
this.videoElement = document.createElement('video');
|
|
10005
|
+
this.videoElement.src = this.options.videoPath;
|
|
10006
|
+
this.videoElement.loop = true;
|
|
10007
|
+
this.videoElement.muted = true;
|
|
10008
|
+
this.videoElement.play().catch((e) => {
|
|
10009
|
+
console.error(`Simulator: Failed to play video at ${this.options.videoPath}`, e);
|
|
10010
|
+
});
|
|
10011
|
+
this.videoElement.addEventListener('error', () => {
|
|
10012
|
+
console.error(`Simulator: Error loading video at ${this.options.videoPath}`, this.videoElement?.error);
|
|
10013
|
+
});
|
|
10014
|
+
const videoTexture = new THREE.VideoTexture(this.videoElement);
|
|
10015
|
+
videoTexture.colorSpace = THREE.SRGBColorSpace;
|
|
10016
|
+
this.backgroundVideoQuad = new FullScreenQuad(new THREE.MeshBasicMaterial({ map: videoTexture }));
|
|
10017
|
+
}
|
|
9970
10018
|
this.virtualSceneRenderTarget = new THREE.WebGLRenderTarget(renderer.domElement.width, renderer.domElement.height, { stencilBuffer: options.stencil });
|
|
9971
10019
|
const virtualSceneMaterial = new THREE.MeshBasicMaterial({
|
|
9972
10020
|
map: this.virtualSceneRenderTarget.texture,
|
|
@@ -10076,6 +10124,9 @@ class Simulator extends Script {
|
|
|
10076
10124
|
this.sparkRenderer.defaultView.encodeLinear = false;
|
|
10077
10125
|
}
|
|
10078
10126
|
this.renderer.setRenderTarget(null);
|
|
10127
|
+
if (this.backgroundVideoQuad) {
|
|
10128
|
+
this.backgroundVideoQuad.render(this.renderer);
|
|
10129
|
+
}
|
|
10079
10130
|
this.renderer.render(this.simulatorScene, camera);
|
|
10080
10131
|
this.renderer.clearDepth();
|
|
10081
10132
|
}
|
|
@@ -11751,7 +11802,7 @@ class TextView extends View {
|
|
|
11751
11802
|
}
|
|
11752
11803
|
setTextColor(color) {
|
|
11753
11804
|
if (Text && this.textObj instanceof Text) {
|
|
11754
|
-
this.textObj.color = color;
|
|
11805
|
+
this.textObj.color = getColorHex(color);
|
|
11755
11806
|
}
|
|
11756
11807
|
}
|
|
11757
11808
|
/**
|
|
@@ -12141,7 +12192,7 @@ class TextButton extends TextView {
|
|
|
12141
12192
|
*/
|
|
12142
12193
|
constructor(options = {}) {
|
|
12143
12194
|
const geometry = new THREE.PlaneGeometry(1, 1);
|
|
12144
|
-
const colorVec4 = getVec4ByColorString(options.backgroundColor ?? '#
|
|
12195
|
+
const colorVec4 = getVec4ByColorString(options.backgroundColor ?? '#000000');
|
|
12145
12196
|
const { opacity = 0.0, radius = SquircleShader.uniforms.uRadius.value, boxSize = SquircleShader.uniforms.uBoxSize.value, } = options;
|
|
12146
12197
|
const uniforms = {
|
|
12147
12198
|
...SquircleShader.uniforms,
|
|
@@ -12191,6 +12242,9 @@ class TextButton extends TextView {
|
|
|
12191
12242
|
// Applies our own overrides to the default values.
|
|
12192
12243
|
this.fontSize = options.fontSize ?? this.fontSize;
|
|
12193
12244
|
this.fontColor = options.fontColor ?? this.fontColor;
|
|
12245
|
+
this.hoverColor = options.hoverColor ?? this.hoverColor;
|
|
12246
|
+
this.selectedFontColor =
|
|
12247
|
+
options.selectedFontColor ?? this.selectedFontColor;
|
|
12194
12248
|
this.width = options.width ?? this.width;
|
|
12195
12249
|
this.height = options.height ?? this.height;
|
|
12196
12250
|
}
|
|
@@ -12213,20 +12267,19 @@ class TextButton extends TextView {
|
|
|
12213
12267
|
if (!this.textObj) {
|
|
12214
12268
|
return;
|
|
12215
12269
|
}
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
}
|
|
12270
|
+
// Update render order to ensure text appears on top of the button mesh
|
|
12271
|
+
this.textObj.renderOrder = this.renderOrder + 1;
|
|
12219
12272
|
const ux = this.ux;
|
|
12220
12273
|
if (ux.isHovered()) {
|
|
12221
12274
|
if (ux.isSelected()) {
|
|
12222
|
-
this.setTextColor(
|
|
12275
|
+
this.setTextColor(this.selectedFontColor);
|
|
12223
12276
|
}
|
|
12224
12277
|
else {
|
|
12225
|
-
this.setTextColor(
|
|
12278
|
+
this.setTextColor(this.hoverColor);
|
|
12226
12279
|
}
|
|
12227
12280
|
}
|
|
12228
12281
|
else {
|
|
12229
|
-
this.setTextColor(
|
|
12282
|
+
this.setTextColor(this.fontColor);
|
|
12230
12283
|
this.uniforms.uOpacity.value = this.defaultOpacity * this.opacity;
|
|
12231
12284
|
}
|
|
12232
12285
|
}
|
|
@@ -13842,6 +13895,7 @@ class ObjectDetector extends Script {
|
|
|
13842
13895
|
}
|
|
13843
13896
|
if (this.options.objects.showDebugVisualizations) {
|
|
13844
13897
|
this._visualizeBoundingBoxesOnImage(base64Image, parsedResponse);
|
|
13898
|
+
this._visualizeDepthMap(cachedDepthArray);
|
|
13845
13899
|
}
|
|
13846
13900
|
const detectionPromises = parsedResponse.map(async (item) => {
|
|
13847
13901
|
const { ymin, xmin, ymax, xmax, objectName, ...additionalData } = item || {};
|
|
@@ -13890,7 +13944,7 @@ class ObjectDetector extends Script {
|
|
|
13890
13944
|
* Retrieves a list of currently detected objects.
|
|
13891
13945
|
*
|
|
13892
13946
|
* @param label - The semantic label to filter by (e.g., 'chair'). If null,
|
|
13893
|
-
*
|
|
13947
|
+
* all objects are returned.
|
|
13894
13948
|
* @returns An array of `Object` instances.
|
|
13895
13949
|
*/
|
|
13896
13950
|
get(label = null) {
|
|
@@ -13927,8 +13981,7 @@ class ObjectDetector extends Script {
|
|
|
13927
13981
|
* Draws the detected bounding boxes on the input image and triggers a
|
|
13928
13982
|
* download for debugging.
|
|
13929
13983
|
* @param base64Image - The base64 encoded input image.
|
|
13930
|
-
* @param detections - The array of detected objects from the
|
|
13931
|
-
* AI response.
|
|
13984
|
+
* @param detections - The array of detected objects from the AI response.
|
|
13932
13985
|
*/
|
|
13933
13986
|
_visualizeBoundingBoxesOnImage(base64Image, detections) {
|
|
13934
13987
|
const img = new Image();
|
|
@@ -13977,6 +14030,71 @@ class ObjectDetector extends Script {
|
|
|
13977
14030
|
};
|
|
13978
14031
|
img.src = base64Image;
|
|
13979
14032
|
}
|
|
14033
|
+
/**
|
|
14034
|
+
* Generates a visual representation of the depth map, normalized to 0-1 range,
|
|
14035
|
+
* and triggers a download for debugging.
|
|
14036
|
+
* @param depthArray - The raw depth data array.
|
|
14037
|
+
*/
|
|
14038
|
+
_visualizeDepthMap(depthArray) {
|
|
14039
|
+
const width = this.depth.width;
|
|
14040
|
+
const height = this.depth.height;
|
|
14041
|
+
if (!width || !height || depthArray.length === 0) {
|
|
14042
|
+
console.warn('Cannot visualize depth map: missing dimensions or data.');
|
|
14043
|
+
return;
|
|
14044
|
+
}
|
|
14045
|
+
// 1. Find Min/Max for normalization (ignoring 0/invalid depth).
|
|
14046
|
+
let min = Infinity;
|
|
14047
|
+
let max = -Infinity;
|
|
14048
|
+
for (let i = 0; i < depthArray.length; ++i) {
|
|
14049
|
+
const val = depthArray[i];
|
|
14050
|
+
if (val > 0) {
|
|
14051
|
+
if (val < min)
|
|
14052
|
+
min = val;
|
|
14053
|
+
if (val > max)
|
|
14054
|
+
max = val;
|
|
14055
|
+
}
|
|
14056
|
+
}
|
|
14057
|
+
// Handle edge case where no valid depth exists.
|
|
14058
|
+
if (min === Infinity) {
|
|
14059
|
+
min = 0;
|
|
14060
|
+
max = 1;
|
|
14061
|
+
}
|
|
14062
|
+
if (min === max) {
|
|
14063
|
+
max = min + 1; // Avoid divide by zero
|
|
14064
|
+
}
|
|
14065
|
+
// 2. Create Canvas.
|
|
14066
|
+
const canvas = document.createElement('canvas');
|
|
14067
|
+
canvas.width = width;
|
|
14068
|
+
canvas.height = height;
|
|
14069
|
+
const ctx = canvas.getContext('2d');
|
|
14070
|
+
const imageData = ctx.createImageData(width, height);
|
|
14071
|
+
const data = imageData.data;
|
|
14072
|
+
// 3. Fill Pixels.
|
|
14073
|
+
for (let i = 0; i < depthArray.length; ++i) {
|
|
14074
|
+
const raw = depthArray[i];
|
|
14075
|
+
// Normalize to 0-1.
|
|
14076
|
+
// Typically 0 means invalid/sky in some depth APIs, so we keep it black.
|
|
14077
|
+
// Otherwise, map [min, max] to [0, 1].
|
|
14078
|
+
const normalized = raw === 0 ? 0 : (raw - min) / (max - min);
|
|
14079
|
+
const byteVal = Math.floor(normalized * 255);
|
|
14080
|
+
const stride = i * 4;
|
|
14081
|
+
data[stride] = byteVal; // R
|
|
14082
|
+
data[stride + 1] = byteVal; // G
|
|
14083
|
+
data[stride + 2] = byteVal; // B
|
|
14084
|
+
data[stride + 3] = 255; // Alpha
|
|
14085
|
+
}
|
|
14086
|
+
ctx.putImageData(imageData, 0, 0);
|
|
14087
|
+
// 4. Download.
|
|
14088
|
+
const timestamp = new Date()
|
|
14089
|
+
.toISOString()
|
|
14090
|
+
.slice(0, 19)
|
|
14091
|
+
.replace('T', '_')
|
|
14092
|
+
.replace(/:/g, '-');
|
|
14093
|
+
const link = document.createElement('a');
|
|
14094
|
+
link.download = `depth_debug_${timestamp}.png`;
|
|
14095
|
+
link.href = canvas.toDataURL('image/png');
|
|
14096
|
+
link.click();
|
|
14097
|
+
}
|
|
13980
14098
|
/**
|
|
13981
14099
|
* Creates a simple debug visualization for an object based on its position
|
|
13982
14100
|
* (center of its 2D detection bounding box).
|
|
@@ -14397,6 +14515,216 @@ class XRTransition extends MeshScript {
|
|
|
14397
14515
|
}
|
|
14398
14516
|
}
|
|
14399
14517
|
|
|
14518
|
+
/**
|
|
14519
|
+
* A utility class to manage and request browser permissions for
|
|
14520
|
+
* Location, Camera, and Microphone.
|
|
14521
|
+
*/
|
|
14522
|
+
class PermissionsManager {
|
|
14523
|
+
/**
|
|
14524
|
+
* Requests permission to access the user's geolocation.
|
|
14525
|
+
* Note: This actually attempts to fetch the position to trigger the prompt.
|
|
14526
|
+
*/
|
|
14527
|
+
async requestLocationPermission() {
|
|
14528
|
+
if (!('geolocation' in navigator)) {
|
|
14529
|
+
return {
|
|
14530
|
+
granted: false,
|
|
14531
|
+
status: 'error',
|
|
14532
|
+
error: 'Geolocation is not supported by this browser.',
|
|
14533
|
+
};
|
|
14534
|
+
}
|
|
14535
|
+
return new Promise((resolve) => {
|
|
14536
|
+
navigator.geolocation.getCurrentPosition(() => {
|
|
14537
|
+
resolve({ granted: true, status: 'granted' });
|
|
14538
|
+
}, (error) => {
|
|
14539
|
+
let errorMsg = 'Unknown error';
|
|
14540
|
+
switch (error.code) {
|
|
14541
|
+
case error.PERMISSION_DENIED:
|
|
14542
|
+
errorMsg = 'User denied the request.';
|
|
14543
|
+
break;
|
|
14544
|
+
case error.POSITION_UNAVAILABLE:
|
|
14545
|
+
errorMsg = 'Location information is unavailable.';
|
|
14546
|
+
break;
|
|
14547
|
+
case error.TIMEOUT:
|
|
14548
|
+
errorMsg = 'The request to get user location timed out.';
|
|
14549
|
+
break;
|
|
14550
|
+
}
|
|
14551
|
+
resolve({ granted: false, status: 'denied', error: errorMsg });
|
|
14552
|
+
}, { timeout: 10000 } // 10 second timeout
|
|
14553
|
+
);
|
|
14554
|
+
});
|
|
14555
|
+
}
|
|
14556
|
+
/**
|
|
14557
|
+
* Requests permission to access the microphone.
|
|
14558
|
+
* Opens a stream to trigger the prompt, then immediately closes it.
|
|
14559
|
+
*/
|
|
14560
|
+
async requestMicrophonePermission() {
|
|
14561
|
+
return this.requestMediaPermission({ audio: true });
|
|
14562
|
+
}
|
|
14563
|
+
/**
|
|
14564
|
+
* Requests permission to access the camera.
|
|
14565
|
+
* Opens a stream to trigger the prompt, then immediately closes it.
|
|
14566
|
+
*/
|
|
14567
|
+
async requestCameraPermission() {
|
|
14568
|
+
return this.requestMediaPermission({ video: true });
|
|
14569
|
+
}
|
|
14570
|
+
/**
|
|
14571
|
+
* Requests permission for both camera and microphone simultaneously.
|
|
14572
|
+
*/
|
|
14573
|
+
async requestAVPermission() {
|
|
14574
|
+
return this.requestMediaPermission({ video: true, audio: true });
|
|
14575
|
+
}
|
|
14576
|
+
/**
|
|
14577
|
+
* Internal helper to handle getUserMedia requests.
|
|
14578
|
+
* Crucially, this stops the tracks immediately after permission is granted
|
|
14579
|
+
* so the hardware doesn't remain active.
|
|
14580
|
+
*/
|
|
14581
|
+
async requestMediaPermission(constraints) {
|
|
14582
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
14583
|
+
return {
|
|
14584
|
+
granted: false,
|
|
14585
|
+
status: 'error',
|
|
14586
|
+
error: 'Media Devices API is not supported by this browser.',
|
|
14587
|
+
};
|
|
14588
|
+
}
|
|
14589
|
+
try {
|
|
14590
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
14591
|
+
// Permission granted. Now stop the stream to release hardware.
|
|
14592
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
14593
|
+
return { granted: true, status: 'granted' };
|
|
14594
|
+
}
|
|
14595
|
+
catch (err) {
|
|
14596
|
+
// Handle common getUserMedia errors
|
|
14597
|
+
const status = 'denied';
|
|
14598
|
+
let errorMessage = 'Permission denied';
|
|
14599
|
+
if (err instanceof Error) {
|
|
14600
|
+
if (err.name === 'NotFoundError' ||
|
|
14601
|
+
err.name === 'DevicesNotFoundError') {
|
|
14602
|
+
return {
|
|
14603
|
+
granted: false,
|
|
14604
|
+
status: 'error',
|
|
14605
|
+
error: 'Hardware not found.',
|
|
14606
|
+
};
|
|
14607
|
+
}
|
|
14608
|
+
errorMessage = err.message || errorMessage;
|
|
14609
|
+
}
|
|
14610
|
+
return { granted: false, status: status, error: errorMessage };
|
|
14611
|
+
}
|
|
14612
|
+
}
|
|
14613
|
+
/**
|
|
14614
|
+
* Requests multiple permissions sequentially.
|
|
14615
|
+
* Returns a single result: granted is true only if ALL requested permissions are granted.
|
|
14616
|
+
*/
|
|
14617
|
+
async checkAndRequestPermissions({ geolocation = false, camera = false, microphone = false, }) {
|
|
14618
|
+
const results = [];
|
|
14619
|
+
// 1. Handle Location
|
|
14620
|
+
if (geolocation) {
|
|
14621
|
+
const status = await this.checkPermissionStatus('geolocation');
|
|
14622
|
+
if (status === 'granted') {
|
|
14623
|
+
results.push({ granted: true, status: 'granted' });
|
|
14624
|
+
}
|
|
14625
|
+
else {
|
|
14626
|
+
results.push(await this.requestLocationPermission());
|
|
14627
|
+
}
|
|
14628
|
+
}
|
|
14629
|
+
// 2. Handle Media (Camera & Mic)
|
|
14630
|
+
// We group these because requestAVPermission can ask for both in one prompt
|
|
14631
|
+
if (camera && microphone) {
|
|
14632
|
+
const camStatus = await this.checkPermissionStatus('camera');
|
|
14633
|
+
const micStatus = await this.checkPermissionStatus('microphone');
|
|
14634
|
+
if (camStatus === 'granted' && micStatus === 'granted') {
|
|
14635
|
+
results.push({ granted: true, status: 'granted' });
|
|
14636
|
+
}
|
|
14637
|
+
else if (camStatus === 'granted') {
|
|
14638
|
+
// Only need mic
|
|
14639
|
+
results.push(await this.requestMicrophonePermission());
|
|
14640
|
+
}
|
|
14641
|
+
else if (micStatus === 'granted') {
|
|
14642
|
+
// Only need camera
|
|
14643
|
+
results.push(await this.requestCameraPermission());
|
|
14644
|
+
}
|
|
14645
|
+
else {
|
|
14646
|
+
// Need both
|
|
14647
|
+
results.push(await this.requestAVPermission());
|
|
14648
|
+
}
|
|
14649
|
+
}
|
|
14650
|
+
else if (camera) {
|
|
14651
|
+
const status = await this.checkPermissionStatus('camera');
|
|
14652
|
+
if (status === 'granted') {
|
|
14653
|
+
results.push({ granted: true, status: 'granted' });
|
|
14654
|
+
}
|
|
14655
|
+
else {
|
|
14656
|
+
results.push(await this.requestCameraPermission());
|
|
14657
|
+
}
|
|
14658
|
+
}
|
|
14659
|
+
else if (microphone) {
|
|
14660
|
+
const status = await this.checkPermissionStatus('microphone');
|
|
14661
|
+
if (status === 'granted') {
|
|
14662
|
+
results.push({ granted: true, status: 'granted' });
|
|
14663
|
+
}
|
|
14664
|
+
else {
|
|
14665
|
+
results.push(await this.requestMicrophonePermission());
|
|
14666
|
+
}
|
|
14667
|
+
}
|
|
14668
|
+
// 3. Aggregate results
|
|
14669
|
+
if (results.length === 0) {
|
|
14670
|
+
return { granted: true, status: 'granted' };
|
|
14671
|
+
}
|
|
14672
|
+
const allGranted = results.every((r) => r.granted);
|
|
14673
|
+
const anyDenied = results.find((r) => r.status === 'denied');
|
|
14674
|
+
const anyError = results.find((r) => r.status === 'error');
|
|
14675
|
+
// Aggregate errors
|
|
14676
|
+
const errors = results
|
|
14677
|
+
.filter((r) => r.error)
|
|
14678
|
+
.map((r) => r.error)
|
|
14679
|
+
.join(' | ');
|
|
14680
|
+
let finalStatus = 'granted';
|
|
14681
|
+
if (anyError)
|
|
14682
|
+
finalStatus = 'error';
|
|
14683
|
+
else if (anyDenied)
|
|
14684
|
+
finalStatus = 'denied';
|
|
14685
|
+
return {
|
|
14686
|
+
granted: allGranted,
|
|
14687
|
+
status: finalStatus,
|
|
14688
|
+
error: errors || undefined,
|
|
14689
|
+
};
|
|
14690
|
+
}
|
|
14691
|
+
/**
|
|
14692
|
+
* Checks the current status of a permission without triggering a prompt.
|
|
14693
|
+
* Useful for UI state (e.g., disabling buttons if already denied).
|
|
14694
|
+
* * @param permissionName - 'geolocation', 'camera', or 'microphone'
|
|
14695
|
+
*/
|
|
14696
|
+
async checkPermissionStatus(permissionName) {
|
|
14697
|
+
if (!navigator.permissions || !navigator.permissions.query) {
|
|
14698
|
+
return 'unknown';
|
|
14699
|
+
}
|
|
14700
|
+
try {
|
|
14701
|
+
let queryName;
|
|
14702
|
+
// Map friendly names to API PermissionName types
|
|
14703
|
+
// Note: 'camera' and 'microphone' are part of the newer spec,
|
|
14704
|
+
// but strictly Typed TypeScript might expect specific descriptor objects.
|
|
14705
|
+
if (permissionName === 'geolocation') {
|
|
14706
|
+
queryName = 'geolocation';
|
|
14707
|
+
}
|
|
14708
|
+
else if (permissionName === 'camera' ||
|
|
14709
|
+
permissionName === 'microphone') {
|
|
14710
|
+
const descriptor = { name: permissionName };
|
|
14711
|
+
const result = await navigator.permissions.query(descriptor);
|
|
14712
|
+
return result.state;
|
|
14713
|
+
}
|
|
14714
|
+
else {
|
|
14715
|
+
return 'unknown';
|
|
14716
|
+
}
|
|
14717
|
+
const result = await navigator.permissions.query({ name: queryName });
|
|
14718
|
+
return result.state;
|
|
14719
|
+
}
|
|
14720
|
+
catch (error) {
|
|
14721
|
+
// Firefox and Safari have incomplete Permissions API support
|
|
14722
|
+
console.warn(`Error checking permission status for ${permissionName}`, error);
|
|
14723
|
+
return 'unknown';
|
|
14724
|
+
}
|
|
14725
|
+
}
|
|
14726
|
+
}
|
|
14727
|
+
|
|
14400
14728
|
/**
|
|
14401
14729
|
* Core is the central engine of the XR Blocks framework, acting as a
|
|
14402
14730
|
* singleton manager for all XR subsystems. Its primary goal is to abstract
|
|
@@ -14456,6 +14784,7 @@ class Core {
|
|
|
14456
14784
|
await script.initPhysics(this.physics);
|
|
14457
14785
|
}
|
|
14458
14786
|
});
|
|
14787
|
+
this.permissionsManager = new PermissionsManager();
|
|
14459
14788
|
if (Core.instance) {
|
|
14460
14789
|
return Core.instance;
|
|
14461
14790
|
}
|
|
@@ -14564,6 +14893,8 @@ class Core {
|
|
|
14564
14893
|
dataFormatPreference: [
|
|
14565
14894
|
this.options.depth.useFloat32 ? 'float32' : 'luminance-alpha',
|
|
14566
14895
|
],
|
|
14896
|
+
depthTypeRequest: options.depth.depthTypeRequest,
|
|
14897
|
+
matchDepthView: options.depth.matchDepthView,
|
|
14567
14898
|
};
|
|
14568
14899
|
this.depth.init(this.camera, options.depth, this.renderer, this.registry, this.scene);
|
|
14569
14900
|
}
|
|
@@ -14600,7 +14931,7 @@ class Core {
|
|
|
14600
14931
|
// Sets up xrButton.
|
|
14601
14932
|
let shouldAutostartSimulator = this.options.xrButton.alwaysAutostartSimulator;
|
|
14602
14933
|
if (!shouldAutostartSimulator && options.xrButton.enabled) {
|
|
14603
|
-
this.xrButton = new XRButton(this.webXRSessionManager, options.xrButton?.startText, options.xrButton?.endText, options.xrButton?.invalidText, options.xrButton?.startSimulatorText, options.xrButton?.showEnterSimulatorButton, this.startSimulator.bind(this));
|
|
14934
|
+
this.xrButton = new XRButton(this.webXRSessionManager, this.permissionsManager, options.xrButton?.startText, options.xrButton?.endText, options.xrButton?.invalidText, options.xrButton?.startSimulatorText, options.xrButton?.showEnterSimulatorButton, this.startSimulator.bind(this), options.permissions);
|
|
14604
14935
|
document.body.appendChild(this.xrButton.domElement);
|
|
14605
14936
|
}
|
|
14606
14937
|
this.webXRSessionManager.addEventListener(WebXRSessionEventType.UNSUPPORTED, () => {
|
|
@@ -16828,5 +17159,5 @@ class VideoFileStream extends VideoStream {
|
|
|
16828
17159
|
}
|
|
16829
17160
|
}
|
|
16830
17161
|
|
|
16831
|
-
export { AI, AIOptions, AVERAGE_IPD_METERS, ActiveControllers, Agent, AnimatableNumber, AudioListener, AudioPlayer, BACK, BackgroundMusic, CategoryVolumes, Col, Core, CoreSound, DEFAULT_DEVICE_CAMERA_HEIGHT, DEFAULT_DEVICE_CAMERA_WIDTH, DOWN, Depth, DepthMesh, DepthMeshOptions, DepthOptions, DepthTextures, DetectedObject, DetectedPlane, DeviceCameraOptions, DragManager, DragMode, ExitButton, FORWARD, FreestandingSlider, GazeController, Gemini, GeminiOptions, GenerateSkyboxTool, GestureRecognition, GestureRecognitionOptions, GetWeatherTool, Grid, HAND_BONE_IDX_CONNECTION_MAP, HAND_JOINT_COUNT, HAND_JOINT_IDX_CONNECTION_MAP, HAND_JOINT_NAMES, Handedness, Hands, HandsOptions, HorizontalPager, IconButton, IconView, ImageView, Input, InputOptions, Keycodes, LEFT, LEFT_VIEW_ONLY_LAYER, LabelView, Lighting, LightingOptions, LoadingSpinnerManager, MaterialSymbolsView, MeshScript, ModelLoader, ModelViewer, MouseController, NEXT_SIMULATOR_MODE, NUM_HANDS, OCCLUDABLE_ITEMS_LAYER, ObjectDetector, ObjectsOptions, OcclusionPass, OcclusionUtils, OpenAI, OpenAIOptions, Options, PageIndicator, Pager, PagerState, Panel, PanelMesh, Physics, PhysicsOptions, PinchOnButtonAction, PlaneDetector, PlanesOptions, RIGHT, RIGHT_VIEW_ONLY_LAYER, Registry, Reticle, ReticleOptions, RotationRaycastMesh, Row, SIMULATOR_HAND_POSE_NAMES, SIMULATOR_HAND_POSE_TO_JOINTS_LEFT, SIMULATOR_HAND_POSE_TO_JOINTS_RIGHT, SOUND_PRESETS, ScreenshotSynthesizer, Script, ScriptMixin, ScriptsManager, ScrollingTroikaTextView, SetSimulatorModeEvent, ShowHandsAction, Simulator, SimulatorCamera, SimulatorControlMode, SimulatorControllerState, SimulatorControls, SimulatorDepth, SimulatorDepthMaterial, SimulatorHandPose, SimulatorHandPoseChangeRequestEvent, SimulatorHands, SimulatorInterface, SimulatorMediaDeviceInfo, SimulatorMode, SimulatorOptions, SimulatorRenderMode, SimulatorScene, SimulatorUser, SimulatorUserAction, SketchPanel, SkyboxAgent, SoundOptions, SoundSynthesizer, SpatialAudio, SpatialPanel, SpeechRecognizer, SpeechRecognizerOptions, SpeechSynthesizer, SpeechSynthesizerOptions, SplatAnchor, StreamState, TextButton, TextScrollerState, TextView, Tool, UI, UI_OVERLAY_LAYER, UP, UX, User, VIEW_DEPTH_GAP, VerticalPager, VideoFileStream, VideoStream, VideoView, View, VolumeCategory, WaitFrame, WalkTowardsPanelAction, World, WorldOptions, XRButton, XRDeviceCamera, XREffects, XRPass, XRTransitionOptions, XR_BLOCKS_ASSETS_PATH, ZERO_VECTOR3, add, ai, aspectRatios, callInitWithDependencyInjection, clamp, clampRotationToAngle, core, cropImage, extractYaw, getColorHex, getDeltaTime, getUrlParamBool, getUrlParamFloat, getUrlParamInt, getUrlParameter, getVec4ByColorString, getXrCameraLeft, getXrCameraRight, init, initScript, lerp, loadStereoImageAsTextures, loadingSpinnerManager, lookAtRotation, objectIsDescendantOf, parseBase64DataURL, placeObjectAtIntersectionFacingTarget, print,
|
|
17162
|
+
export { AI, AIOptions, AVERAGE_IPD_METERS, ActiveControllers, Agent, AnimatableNumber, AudioListener, AudioPlayer, BACK, BackgroundMusic, CategoryVolumes, Col, Core, CoreSound, DEFAULT_DEVICE_CAMERA_HEIGHT, DEFAULT_DEVICE_CAMERA_WIDTH, DEFAULT_RGB_TO_DEPTH_PARAMS, DOWN, Depth, DepthMesh, DepthMeshOptions, DepthOptions, DepthTextures, DetectedObject, DetectedPlane, DeviceCameraOptions, DragManager, DragMode, ExitButton, FORWARD, FreestandingSlider, GazeController, Gemini, GeminiOptions, GenerateSkyboxTool, GestureRecognition, GestureRecognitionOptions, GetWeatherTool, Grid, HAND_BONE_IDX_CONNECTION_MAP, HAND_JOINT_COUNT, HAND_JOINT_IDX_CONNECTION_MAP, HAND_JOINT_NAMES, Handedness, Hands, HandsOptions, HorizontalPager, IconButton, IconView, ImageView, Input, InputOptions, Keycodes, LEFT, LEFT_VIEW_ONLY_LAYER, LabelView, Lighting, LightingOptions, LoadingSpinnerManager, MaterialSymbolsView, MeshScript, ModelLoader, ModelViewer, MouseController, NEXT_SIMULATOR_MODE, NUM_HANDS, OCCLUDABLE_ITEMS_LAYER, ObjectDetector, ObjectsOptions, OcclusionPass, OcclusionUtils, OpenAI, OpenAIOptions, Options, PageIndicator, Pager, PagerState, Panel, PanelMesh, Physics, PhysicsOptions, PinchOnButtonAction, PlaneDetector, PlanesOptions, RIGHT, RIGHT_VIEW_ONLY_LAYER, Registry, Reticle, ReticleOptions, RotationRaycastMesh, Row, SIMULATOR_HAND_POSE_NAMES, SIMULATOR_HAND_POSE_TO_JOINTS_LEFT, SIMULATOR_HAND_POSE_TO_JOINTS_RIGHT, SOUND_PRESETS, ScreenshotSynthesizer, Script, ScriptMixin, ScriptsManager, ScrollingTroikaTextView, SetSimulatorModeEvent, ShowHandsAction, Simulator, SimulatorCamera, SimulatorControlMode, SimulatorControllerState, SimulatorControls, SimulatorDepth, SimulatorDepthMaterial, SimulatorHandPose, SimulatorHandPoseChangeRequestEvent, SimulatorHands, SimulatorInterface, SimulatorMediaDeviceInfo, SimulatorMode, SimulatorOptions, SimulatorRenderMode, SimulatorScene, SimulatorUser, SimulatorUserAction, SketchPanel, SkyboxAgent, SoundOptions, SoundSynthesizer, SpatialAudio, SpatialPanel, SpeechRecognizer, SpeechRecognizerOptions, SpeechSynthesizer, SpeechSynthesizerOptions, SplatAnchor, StreamState, TextButton, TextScrollerState, TextView, Tool, UI, UI_OVERLAY_LAYER, UP, UX, User, VIEW_DEPTH_GAP, VerticalPager, VideoFileStream, VideoStream, VideoView, View, VolumeCategory, WaitFrame, WalkTowardsPanelAction, World, WorldOptions, XRButton, XRDeviceCamera, XREffects, XRPass, XRTransitionOptions, XR_BLOCKS_ASSETS_PATH, ZERO_VECTOR3, add, ai, aspectRatios, callInitWithDependencyInjection, clamp, clampRotationToAngle, core, cropImage, extractYaw, getColorHex, getDeltaTime, getUrlParamBool, getUrlParamFloat, getUrlParamInt, getUrlParameter, getVec4ByColorString, getXrCameraLeft, getXrCameraRight, init, initScript, lerp, loadStereoImageAsTextures, loadingSpinnerManager, lookAtRotation, objectIsDescendantOf, parseBase64DataURL, placeObjectAtIntersectionFacingTarget, print, scene, showOnlyInLeftEye, showOnlyInRightEye, showReticleOnDepthMesh, transformRgbToDepthUv, transformRgbUvToWorld, traverseUtil, uninitScript, urlParams, user, world, xrDepthMeshOptions, xrDepthMeshPhysicsOptions, xrDepthMeshVisualizationOptions, xrDeviceCameraEnvironmentContinuousOptions, xrDeviceCameraEnvironmentOptions, xrDeviceCameraUserContinuousOptions, xrDeviceCameraUserOptions };
|
|
16832
17163
|
//# sourceMappingURL=xrblocks.js.map
|