xrblocks 0.6.0 → 0.7.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/README.md CHANGED
@@ -74,8 +74,8 @@ code below:
74
74
  <script type="importmap">
75
75
  {
76
76
  "imports": {
77
- "three": "https://cdn.jsdelivr.net/npm/three@0.181.0/build/three.module.js",
78
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.181.0/examples/jsm/",
77
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
78
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
79
79
  "xrblocks": "https://cdn.jsdelivr.net/gh/google/xrblocks@build/xrblocks.js",
80
80
  "xrblocks/addons/": "https://cdn.jsdelivr.net/gh/google/xrblocks@build/addons/"
81
81
  }
@@ -14,6 +14,9 @@ export declare class ScriptsManager {
14
14
  callKeyUpBound: (event: KeyEvent) => void;
15
15
  /** The set of scripts currently being initialized. */
16
16
  private initializingScripts;
17
+ private seenScripts;
18
+ private syncPromises;
19
+ private checkScriptBound;
17
20
  constructor(initScriptFunction: (script: Script) => Promise<void>);
18
21
  /**
19
22
  * Initializes a script and adds it to the set of scripts which will receive
@@ -29,13 +32,17 @@ export declare class ScriptsManager {
29
32
  * @param script - The script to uninitialize.
30
33
  */
31
34
  uninitScript(script: Script): void;
35
+ /**
36
+ * Helper for scene traversal to avoid closure allocation.
37
+ */
38
+ private checkScript;
32
39
  /**
33
40
  * Finds all scripts in the scene and initializes them or uninitailizes them.
34
41
  * Returns a promise which resolves when all new scripts are finished
35
42
  * initalizing.
36
43
  * @param scene - The main scene which is used to find scripts.
37
44
  */
38
- syncScriptsWithScene(scene: THREE.Scene): Promise<void>;
45
+ syncScriptsWithScene(scene: THREE.Scene): Promise<PromiseSettledResult<void>[]>;
39
46
  callSelectStart(event: SelectEvent): void;
40
47
  callSelectEnd(event: SelectEvent): void;
41
48
  callSelect(event: SelectEvent): void;
@@ -19,7 +19,7 @@ export declare class Depth {
19
19
  options: DepthOptions;
20
20
  width: number;
21
21
  height: number;
22
- rawValueToMeters: number;
22
+ get rawValueToMeters(): number;
23
23
  occludableShaders: Set<Shader>;
24
24
  private occlusionPass?;
25
25
  private depthClientsInitialized;
@@ -2,14 +2,14 @@ import * as THREE from 'three';
2
2
  import { DepthOptions } from './DepthOptions';
3
3
  export declare class DepthTextures {
4
4
  private options;
5
- private uint16Arrays;
5
+ private float32Arrays;
6
6
  private uint8Arrays;
7
7
  private dataTextures;
8
8
  private nativeTextures;
9
9
  depthData: XRCPUDepthInformation[];
10
10
  constructor(options: DepthOptions);
11
11
  private createDataDepthTextures;
12
- updateData(depthData: XRCPUDepthInformation, view_id: number): void;
13
- updateNativeTexture(depthData: XRWebGLDepthInformation, renderer: THREE.WebGLRenderer, view_id: number): void;
14
- get(view_id: number): THREE.DataTexture | THREE.ExternalTexture;
12
+ updateData(depthData: XRCPUDepthInformation, viewId: number): void;
13
+ updateNativeTexture(depthData: XRWebGLDepthInformation, renderer: THREE.WebGLRenderer, viewId: number): void;
14
+ get(viewId: number): THREE.DataTexture | THREE.ExternalTexture;
15
15
  }
package/build/xrblocks.js CHANGED
@@ -14,15 +14,15 @@
14
14
  * limitations under the License.
15
15
  *
16
16
  * @file xrblocks.js
17
- * @version v0.6.0
18
- * @commitid 64e2279
19
- * @builddate 2025-12-19T21:53:04.057Z
17
+ * @version v0.7.0
18
+ * @commitid 950b1e5
19
+ * @builddate 2026-01-03T00:05:55.605Z
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:
23
23
  * 1. Include the following importmap for maximum compatibility:
24
- "three": "https://cdn.jsdelivr.net/npm/three@0.181.0/build/three.module.js",
25
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.181.0/examples/jsm/",
24
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
25
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
26
26
  "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
27
27
  "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
28
28
  "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
@@ -2255,10 +2255,6 @@ class DepthMesh extends MeshScript {
2255
2255
  const depthY = Math.round(clamp((1.0 - v) * height, 0, height - 1));
2256
2256
  const rawDepth = depthArray[depthY * width + depthX];
2257
2257
  let depth = depthData.rawValueToMeters * rawDepth;
2258
- // Workaround for b/382679381.
2259
- if (this.depthOptions.useFloat32) {
2260
- depth = rawDepth;
2261
- }
2262
2258
  // Finds global min/max.
2263
2259
  if (depth > 0) {
2264
2260
  if (depth < this.minDepth) {
@@ -2502,68 +2498,63 @@ const xrDepthMeshPhysicsOptions = deepFreeze(new DepthOptions({
2502
2498
  class DepthTextures {
2503
2499
  constructor(options) {
2504
2500
  this.options = options;
2505
- this.uint16Arrays = [];
2501
+ this.float32Arrays = [];
2506
2502
  this.uint8Arrays = [];
2507
2503
  this.dataTextures = [];
2508
2504
  this.nativeTextures = [];
2509
2505
  this.depthData = [];
2510
2506
  }
2511
- createDataDepthTextures(depthData, view_id) {
2512
- if (this.dataTextures[view_id]) {
2513
- this.dataTextures[view_id].dispose();
2507
+ createDataDepthTextures(depthData, viewId) {
2508
+ if (this.dataTextures[viewId]) {
2509
+ this.dataTextures[viewId].dispose();
2514
2510
  }
2515
2511
  if (this.options.useFloat32) {
2516
- const typedArray = new Uint16Array(depthData.width * depthData.height);
2512
+ const typedArray = new Float32Array(depthData.width * depthData.height);
2517
2513
  const format = THREE.RedFormat;
2518
- const type = THREE.HalfFloatType;
2519
- this.uint16Arrays[view_id] = typedArray;
2520
- this.dataTextures[view_id] = new THREE.DataTexture(typedArray, depthData.width, depthData.height, format, type);
2514
+ const type = THREE.FloatType;
2515
+ this.float32Arrays[viewId] = typedArray;
2516
+ this.dataTextures[viewId] = new THREE.DataTexture(typedArray, depthData.width, depthData.height, format, type);
2521
2517
  }
2522
2518
  else {
2523
2519
  const typedArray = new Uint8Array(depthData.width * depthData.height * 2);
2524
2520
  const format = THREE.RGFormat;
2525
2521
  const type = THREE.UnsignedByteType;
2526
- this.uint8Arrays[view_id] = typedArray;
2527
- this.dataTextures[view_id] = new THREE.DataTexture(typedArray, depthData.width, depthData.height, format, type);
2522
+ this.uint8Arrays[viewId] = typedArray;
2523
+ this.dataTextures[viewId] = new THREE.DataTexture(typedArray, depthData.width, depthData.height, format, type);
2528
2524
  }
2529
2525
  }
2530
- updateData(depthData, view_id) {
2531
- if (this.dataTextures.length < view_id + 1 ||
2532
- this.dataTextures[view_id].image.width !== depthData.width ||
2533
- this.dataTextures[view_id].image.height !== depthData.height) {
2534
- this.createDataDepthTextures(depthData, view_id);
2526
+ updateData(depthData, viewId) {
2527
+ if (this.dataTextures.length < viewId + 1 ||
2528
+ this.dataTextures[viewId].image.width !== depthData.width ||
2529
+ this.dataTextures[viewId].image.height !== depthData.height) {
2530
+ this.createDataDepthTextures(depthData, viewId);
2535
2531
  }
2536
2532
  if (this.options.useFloat32) {
2537
- const float32Data = new Float32Array(depthData.data);
2538
- const float16Data = new Uint16Array(float32Data.length);
2539
- for (let i = 0; i < float16Data.length; i++) {
2540
- float16Data[i] = THREE.DataUtils.toHalfFloat(float32Data[i]);
2541
- }
2542
- this.uint16Arrays[view_id].set(float16Data);
2533
+ this.float32Arrays[viewId].set(new Float32Array(depthData.data));
2543
2534
  }
2544
2535
  else {
2545
- this.uint8Arrays[view_id].set(new Uint8Array(depthData.data));
2536
+ this.uint8Arrays[viewId].set(new Uint8Array(depthData.data));
2546
2537
  }
2547
- this.dataTextures[view_id].needsUpdate = true;
2548
- this.depthData[view_id] = depthData;
2538
+ this.dataTextures[viewId].needsUpdate = true;
2539
+ this.depthData[viewId] = depthData;
2549
2540
  }
2550
- updateNativeTexture(depthData, renderer, view_id) {
2551
- if (this.dataTextures.length < view_id + 1) {
2552
- this.nativeTextures[view_id] = new THREE.ExternalTexture(depthData.texture);
2541
+ updateNativeTexture(depthData, renderer, viewId) {
2542
+ if (this.dataTextures.length < viewId + 1) {
2543
+ this.nativeTextures[viewId] = new THREE.ExternalTexture(depthData.texture);
2553
2544
  }
2554
2545
  else {
2555
- this.nativeTextures[view_id].sourceTexture = depthData.texture;
2546
+ this.nativeTextures[viewId].sourceTexture = depthData.texture;
2556
2547
  }
2557
2548
  // fixed in newer revision of three
2558
- const textureProperties = renderer.properties.get(this.nativeTextures[view_id]);
2549
+ const textureProperties = renderer.properties.get(this.nativeTextures[viewId]);
2559
2550
  textureProperties.__webglTexture = depthData.texture;
2560
2551
  textureProperties.__version = 1;
2561
2552
  }
2562
- get(view_id) {
2553
+ get(viewId) {
2563
2554
  if (this.dataTextures.length > 0) {
2564
- return this.dataTextures[view_id];
2555
+ return this.dataTextures[viewId];
2565
2556
  }
2566
- return this.nativeTextures[view_id];
2557
+ return this.nativeTextures[viewId];
2567
2558
  }
2568
2559
  }
2569
2560
 
@@ -3057,6 +3048,15 @@ const DEFAULT_DEPTH_WIDTH = 160;
3057
3048
  const DEFAULT_DEPTH_HEIGHT = DEFAULT_DEPTH_WIDTH;
3058
3049
  const clipSpacePosition = new THREE.Vector3();
3059
3050
  class Depth {
3051
+ get rawValueToMeters() {
3052
+ if (this.cpuDepthData.length) {
3053
+ return this.cpuDepthData[0].rawValueToMeters;
3054
+ }
3055
+ else if (this.gpuDepthData.length) {
3056
+ return this.gpuDepthData[0].rawValueToMeters;
3057
+ }
3058
+ return 0;
3059
+ }
3060
3060
  /**
3061
3061
  * Depth is a lightweight manager based on three.js to simply prototyping
3062
3062
  * with Depth in WebXR.
@@ -3070,7 +3070,6 @@ class Depth {
3070
3070
  this.options = new DepthOptions();
3071
3071
  this.width = DEFAULT_DEPTH_WIDTH;
3072
3072
  this.height = DEFAULT_DEPTH_HEIGHT;
3073
- this.rawValueToMeters = 0.0010000000474974513;
3074
3073
  this.occludableShaders = new Set();
3075
3074
  // Whether we're counting the number of depth clients.
3076
3075
  this.depthClientsInitialized = false;
@@ -3180,11 +3179,6 @@ class Depth {
3180
3179
  }
3181
3180
  updateCPUDepthData(depthData, viewId = 0) {
3182
3181
  this.cpuDepthData[viewId] = depthData;
3183
- // Workaround for b/382679381.
3184
- this.rawValueToMeters = depthData.rawValueToMeters;
3185
- if (this.options.useFloat32) {
3186
- this.rawValueToMeters = 1.0;
3187
- }
3188
3182
  // Updates Depth Array.
3189
3183
  if (this.depthArray[viewId] == null) {
3190
3184
  this.depthArray[viewId] = this.options.useFloat32
@@ -3210,11 +3204,6 @@ class Depth {
3210
3204
  }
3211
3205
  updateGPUDepthData(depthData, viewId = 0) {
3212
3206
  this.gpuDepthData[viewId] = depthData;
3213
- // Workaround for b/382679381.
3214
- this.rawValueToMeters = depthData.rawValueToMeters;
3215
- if (this.options.useFloat32) {
3216
- this.rawValueToMeters = 1.0;
3217
- }
3218
3207
  // For now, assume that we need cpu depth only if depth mesh is enabled.
3219
3208
  // In the future, add a separate option.
3220
3209
  const needCpuDepth = this.options.depthMesh.enabled;
@@ -3291,22 +3280,22 @@ class Depth {
3291
3280
  if (xrRefSpace) {
3292
3281
  const pose = frame.getViewerPose(xrRefSpace);
3293
3282
  if (pose) {
3294
- for (let view_id = 0; view_id < pose.views.length; ++view_id) {
3295
- const view = pose.views[view_id];
3296
- this.view[view_id] = view;
3283
+ for (let viewId = 0; viewId < pose.views.length; ++viewId) {
3284
+ const view = pose.views[viewId];
3285
+ this.view[viewId] = view;
3297
3286
  if (session.depthUsage === 'gpu-optimized') {
3298
3287
  const depthData = binding.getDepthInformation(view);
3299
3288
  if (!depthData) {
3300
3289
  return;
3301
3290
  }
3302
- this.updateGPUDepthData(depthData, view_id);
3291
+ this.updateGPUDepthData(depthData, viewId);
3303
3292
  }
3304
3293
  else {
3305
3294
  const depthData = frame.getDepthInformation(view);
3306
3295
  if (!depthData) {
3307
3296
  return;
3308
3297
  }
3309
- this.updateCPUDepthData(depthData, view_id);
3298
+ this.updateCPUDepthData(depthData, viewId);
3310
3299
  }
3311
3300
  }
3312
3301
  }
@@ -4185,6 +4174,9 @@ class ScriptsManager {
4185
4174
  this.callKeyUpBound = this.callKeyUp.bind(this);
4186
4175
  /** The set of scripts currently being initialized. */
4187
4176
  this.initializingScripts = new Set();
4177
+ this.seenScripts = new Set();
4178
+ this.syncPromises = [];
4179
+ this.checkScriptBound = this.checkScript.bind(this);
4188
4180
  }
4189
4181
  /**
4190
4182
  * Initializes a script and adds it to the set of scripts which will receive
@@ -4215,29 +4207,33 @@ class ScriptsManager {
4215
4207
  this.scripts.delete(script);
4216
4208
  this.initializingScripts.delete(script);
4217
4209
  }
4210
+ /**
4211
+ * Helper for scene traversal to avoid closure allocation.
4212
+ */
4213
+ checkScript(obj) {
4214
+ if (obj.isXRScript) {
4215
+ const script = obj;
4216
+ this.syncPromises.push(this.initScript(script));
4217
+ this.seenScripts.add(script);
4218
+ }
4219
+ }
4218
4220
  /**
4219
4221
  * Finds all scripts in the scene and initializes them or uninitailizes them.
4220
4222
  * Returns a promise which resolves when all new scripts are finished
4221
4223
  * initalizing.
4222
4224
  * @param scene - The main scene which is used to find scripts.
4223
4225
  */
4224
- async syncScriptsWithScene(scene) {
4225
- const seenScripts = new Set();
4226
- const promises = [];
4227
- scene.traverse((obj) => {
4228
- if (obj.isXRScript) {
4229
- const script = obj;
4230
- promises.push(this.initScript(script));
4231
- seenScripts.add(script);
4232
- }
4233
- });
4234
- await Promise.allSettled(promises);
4226
+ syncScriptsWithScene(scene) {
4227
+ this.seenScripts.clear();
4228
+ this.syncPromises.length = 0;
4229
+ scene.traverse(this.checkScriptBound);
4235
4230
  // Delete missing scripts.
4236
4231
  for (const script of this.scripts) {
4237
- if (!seenScripts.has(script)) {
4232
+ if (!this.seenScripts.has(script)) {
4238
4233
  this.uninitScript(script);
4239
4234
  }
4240
4235
  }
4236
+ return Promise.allSettled(this.syncPromises);
4241
4237
  }
4242
4238
  callSelectStart(event) {
4243
4239
  for (const script of this.scripts) {
@@ -5830,6 +5826,7 @@ function traverseUtil(node, callback) {
5830
5826
  return false;
5831
5827
  }
5832
5828
 
5829
+ const tempBox = new THREE.Box3();
5833
5830
  /**
5834
5831
  * User is an embodied instance to manage hands, controllers, speech, and
5835
5832
  * avatars. It extends Script to update human-world interaction.
@@ -6192,8 +6189,8 @@ class User extends Script {
6192
6189
  const currentlyTouchedMeshes = [];
6193
6190
  this.scene.traverse((object) => {
6194
6191
  if (object.isMesh && object.visible) {
6195
- const boundingBox = new THREE.Box3().setFromObject(object);
6196
- if (boundingBox.containsPoint(indexTipPosition)) {
6192
+ tempBox.setFromObject(object);
6193
+ if (tempBox.containsPoint(indexTipPosition)) {
6197
6194
  currentlyTouchedMeshes.push(object);
6198
6195
  }
6199
6196
  }
@@ -8315,12 +8312,15 @@ class SimulatorDepth {
8315
8312
  this.simulatorScene.overrideMaterial = null;
8316
8313
  this.renderer.setRenderTarget(originalRenderTarget);
8317
8314
  }
8318
- updateDepth() {
8315
+ async updateDepth() {
8319
8316
  // We preventively unbind the PIXEL_PACK_BUFFER before reading from the
8320
8317
  // render target in case external libraries (Spark.js) left it bound.
8321
8318
  const context = this.renderer.getContext();
8322
8319
  context.bindBuffer(context.PIXEL_PACK_BUFFER, null);
8323
- this.renderer.readRenderTargetPixels(this.depthRenderTarget, 0, 0, this.depthWidth, this.depthHeight, this.depthBuffer);
8320
+ // Cache the projection matrix and transform of the rendered depth.
8321
+ const projectionMatrix = this.depthCamera.projectionMatrix.clone();
8322
+ const transform = new XRRigidTransform(this.depthCamera.position, this.depthCamera.quaternion);
8323
+ await this.renderer.readRenderTargetPixelsAsync(this.depthRenderTarget, 0, 0, this.depthWidth, this.depthHeight, this.depthBuffer);
8324
8324
  // Flip the depth buffer.
8325
8325
  if (this.depthBufferSlice.length != this.depthWidth) {
8326
8326
  this.depthBufferSlice = new Float32Array(this.depthWidth);
@@ -8336,14 +8336,14 @@ class SimulatorDepth {
8336
8336
  // Copy the temp slice (original row i) to row j
8337
8337
  this.depthBuffer.set(this.depthBufferSlice, j_offset);
8338
8338
  }
8339
- this.depthCamera.projectionMatrix.toArray(this.projectionMatrixArray);
8339
+ projectionMatrix.toArray(this.projectionMatrixArray);
8340
8340
  const depthData = {
8341
8341
  width: this.depthWidth,
8342
8342
  height: this.depthHeight,
8343
8343
  data: this.depthBuffer.buffer,
8344
8344
  rawValueToMeters: 1.0,
8345
8345
  projectionMatrix: this.projectionMatrixArray,
8346
- transform: new XRRigidTransform(this.depthCamera.position, this.depthCamera.quaternion),
8346
+ transform: transform,
8347
8347
  };
8348
8348
  this.depth.updateCPUDepthData(depthData, 0);
8349
8349
  }
@@ -12459,6 +12459,15 @@ class VideoView extends View {
12459
12459
  /** The cross-origin setting for the video element. Default is 'anonymous'. */
12460
12460
  this.crossOrigin = 'anonymous';
12461
12461
  this.videoAspectRatio = 0.0;
12462
+ // set video options if passed in
12463
+ this.autoplay = options.autoplay ?? this.autoplay;
12464
+ this.muted = options.muted ?? this.muted;
12465
+ this.loop = options.loop ?? this.loop;
12466
+ this.playsInline = options.playsInline ?? this.playsInline;
12467
+ if (options.crossOrigin)
12468
+ this.crossOrigin = options.crossOrigin;
12469
+ if (options.mode)
12470
+ this.mode = options.mode;
12462
12471
  const videoGeometry = new THREE.PlaneGeometry(1, 1);
12463
12472
  const videoMaterial = new THREE.MeshBasicMaterial({
12464
12473
  transparent: true,