@iwsdk/core 0.5.3 → 1.0.0-rc.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/README.md +22 -1
- package/dist/asset/asset-manager.d.ts.map +1 -1
- package/dist/asset/asset-manager.js +3 -2
- package/dist/asset/asset-manager.js.map +1 -1
- package/dist/asset/cache-manager.js.map +1 -1
- package/dist/asset/loaders/audio-loader.js.map +1 -1
- package/dist/asset/loaders/cached-asset-load.js.map +1 -1
- package/dist/asset/loaders/gltf-loader.js.map +1 -1
- package/dist/asset/loaders/hdr-texture-loader.js.map +1 -1
- package/dist/asset/loaders/texture-loader.js.map +1 -1
- package/dist/asset/loaders/uikitml-loader.js.map +1 -1
- package/dist/audio/audio-pool.js.map +1 -1
- package/dist/audio/audio-system.js.map +1 -1
- package/dist/audio/audio-utils.js.map +1 -1
- package/dist/audio/audio.js.map +1 -1
- package/dist/camera/camera-source.js.map +1 -1
- package/dist/camera/camera-system.d.ts +7 -3
- package/dist/camera/camera-system.d.ts.map +1 -1
- package/dist/camera/camera-system.js +81 -50
- package/dist/camera/camera-system.js.map +1 -1
- package/dist/camera/camera-utils.d.ts +5 -0
- package/dist/camera/camera-utils.d.ts.map +1 -1
- package/dist/camera/camera-utils.js +8 -1
- package/dist/camera/camera-utils.js.map +1 -1
- package/dist/camera/types.js.map +1 -1
- package/dist/depth/depth-occludable.js.map +1 -1
- package/dist/depth/depth-sensing-system.d.ts +3 -0
- package/dist/depth/depth-sensing-system.d.ts.map +1 -1
- package/dist/depth/depth-sensing-system.js +55 -1
- package/dist/depth/depth-sensing-system.js.map +1 -1
- package/dist/depth/depth-textures.js.map +1 -1
- package/dist/depth/occlusion/preprocessing-pass.js.map +1 -1
- package/dist/depth/occlusion/preprocessing-shader.glsl.js.map +1 -1
- package/dist/ecs/builtin-components.js.map +1 -1
- package/dist/ecs/component-editor-metadata.js.map +1 -1
- package/dist/ecs/component-manifest.js.map +1 -1
- package/dist/ecs/entity.js.map +1 -1
- package/dist/ecs/system.js.map +1 -1
- package/dist/ecs/world.d.ts +2 -0
- package/dist/ecs/world.d.ts.map +1 -1
- package/dist/ecs/world.js +4 -0
- package/dist/ecs/world.js.map +1 -1
- package/dist/environment/dome-gradient.js.map +1 -1
- package/dist/environment/dome-texture.js.map +1 -1
- package/dist/environment/environment-system.js.map +1 -1
- package/dist/environment/gradient-environment.js.map +1 -1
- package/dist/environment/ibl-gradient.js.map +1 -1
- package/dist/environment/ibl-texture.js.map +1 -1
- package/dist/environment/scene-ar-environment.js.map +1 -1
- package/dist/environment-raycast/environment-raycast.js.map +1 -1
- package/dist/environment-raycast/raycast-target.js.map +1 -1
- package/dist/grab/distance-grabbable.d.ts +5 -0
- package/dist/grab/distance-grabbable.d.ts.map +1 -1
- package/dist/grab/distance-grabbable.js +5 -0
- package/dist/grab/distance-grabbable.js.map +1 -1
- package/dist/grab/grab-helpers.js.map +1 -1
- package/dist/grab/grab-system.js.map +1 -1
- package/dist/grab/grab-warnings.js.map +1 -1
- package/dist/grab/grabbed.js.map +1 -1
- package/dist/grab/handles.d.ts +6 -0
- package/dist/grab/handles.d.ts.map +1 -1
- package/dist/grab/handles.js +46 -12
- package/dist/grab/handles.js.map +1 -1
- package/dist/grab/index.js.map +1 -1
- package/dist/grab/one-hand-grabbable.d.ts +5 -0
- package/dist/grab/one-hand-grabbable.d.ts.map +1 -1
- package/dist/grab/one-hand-grabbable.js +5 -0
- package/dist/grab/one-hand-grabbable.js.map +1 -1
- package/dist/grab/pointer-events-patch.js.map +1 -1
- package/dist/grab/two-hands-grabbable.d.ts +5 -0
- package/dist/grab/two-hands-grabbable.d.ts.map +1 -1
- package/dist/grab/two-hands-grabbable.js +5 -0
- package/dist/grab/two-hands-grabbable.js.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/init/browser-camera.js.map +1 -1
- package/dist/init/index.d.ts +2 -2
- package/dist/init/index.d.ts.map +1 -1
- package/dist/init/player-camera.js.map +1 -1
- package/dist/init/session-grant.d.ts +34 -0
- package/dist/init/session-grant.d.ts.map +1 -0
- package/dist/init/session-grant.js +89 -0
- package/dist/init/session-grant.js.map +1 -0
- package/dist/init/world-initializer.d.ts +87 -4
- package/dist/init/world-initializer.d.ts.map +1 -1
- package/dist/init/world-initializer.js +45 -41
- package/dist/init/world-initializer.js.map +1 -1
- package/dist/init/xr.d.ts +19 -0
- package/dist/init/xr.d.ts.map +1 -1
- package/dist/init/xr.js +136 -41
- package/dist/init/xr.js.map +1 -1
- package/dist/input/canvas-pointer-system.d.ts +2 -0
- package/dist/input/canvas-pointer-system.d.ts.map +1 -1
- package/dist/input/canvas-pointer-system.js +33 -13
- package/dist/input/canvas-pointer-system.js.map +1 -1
- package/dist/input/input-actions.js.map +1 -1
- package/dist/input/input-manager.js.map +1 -1
- package/dist/input/input-system.d.ts.map +1 -1
- package/dist/input/input-system.js +5 -2
- package/dist/input/input-system.js.map +1 -1
- package/dist/input/state-tags.js.map +1 -1
- package/dist/input/stateful-browser-gamepad.js.map +1 -1
- package/dist/input/stateful-keyboard.js.map +1 -1
- package/dist/layers/xr-cylinder-layer.d.ts +2 -1
- package/dist/layers/xr-cylinder-layer.d.ts.map +1 -1
- package/dist/layers/xr-cylinder-layer.js +2 -1
- package/dist/layers/xr-cylinder-layer.js.map +1 -1
- package/dist/layers/xr-layer-init.d.ts +19 -0
- package/dist/layers/xr-layer-init.d.ts.map +1 -0
- package/dist/layers/xr-layer-init.js +20 -0
- package/dist/layers/xr-layer-init.js.map +1 -0
- package/dist/layers/xr-layer-state.js.map +1 -1
- package/dist/layers/xr-layer-system.d.ts +41 -1
- package/dist/layers/xr-layer-system.d.ts.map +1 -1
- package/dist/layers/xr-layer-system.js +506 -132
- package/dist/layers/xr-layer-system.js.map +1 -1
- package/dist/layers/xr-layer-transform.d.ts +18 -0
- package/dist/layers/xr-layer-transform.d.ts.map +1 -0
- package/dist/layers/xr-layer-transform.js +115 -0
- package/dist/layers/xr-layer-transform.js.map +1 -0
- package/dist/layers/xr-quad-layer.d.ts +2 -1
- package/dist/layers/xr-quad-layer.d.ts.map +1 -1
- package/dist/layers/xr-quad-layer.js +2 -1
- package/dist/layers/xr-quad-layer.js.map +1 -1
- package/dist/level/level-component-applier.d.ts +2 -0
- package/dist/level/level-component-applier.d.ts.map +1 -1
- package/dist/level/level-component-applier.js +24 -5
- package/dist/level/level-component-applier.js.map +1 -1
- package/dist/level/level-player-rig.js.map +1 -1
- package/dist/level/level-root.js.map +1 -1
- package/dist/level/level-scene-environment.js.map +1 -1
- package/dist/level/level-scene-json-importer.js.map +1 -1
- package/dist/level/level-scene-material.js.map +1 -1
- package/dist/level/level-scene-object.js.map +1 -1
- package/dist/level/level-scene-primitive.js.map +1 -1
- package/dist/level/level-system.js.map +1 -1
- package/dist/level/level-tag.js.map +1 -1
- package/dist/lighting/light-binding.js.map +1 -1
- package/dist/lighting/light-components.js.map +1 -1
- package/dist/lighting/light-system.js.map +1 -1
- package/dist/locomotion/geometries/beveled-cylinder.js.map +1 -1
- package/dist/locomotion/locomotion-environment.js.map +1 -1
- package/dist/locomotion/locomotion-input-provider.js.map +1 -1
- package/dist/locomotion/locomotion.js.map +1 -1
- package/dist/locomotion/materials/line.js.map +1 -1
- package/dist/locomotion/slide.js.map +1 -1
- package/dist/locomotion/teleport.js.map +1 -1
- package/dist/locomotion/turn.js.map +1 -1
- package/dist/mcp/ecs-debug-tools.d.ts +4 -1
- package/dist/mcp/ecs-debug-tools.d.ts.map +1 -1
- package/dist/mcp/ecs-debug-tools.js +113 -10
- package/dist/mcp/ecs-debug-tools.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/mcp-runtime.d.ts +2 -0
- package/dist/mcp/mcp-runtime.d.ts.map +1 -1
- package/dist/mcp/mcp-runtime.js +41 -0
- package/dist/mcp/mcp-runtime.js.map +1 -1
- package/dist/mcp/scene-tools.js.map +1 -1
- package/dist/mcp/ui-debug-tools.d.ts +56 -0
- package/dist/mcp/ui-debug-tools.d.ts.map +1 -0
- package/dist/mcp/ui-debug-tools.js +412 -0
- package/dist/mcp/ui-debug-tools.js.map +1 -0
- package/dist/physics/index.js +1 -1
- package/dist/physics/physics-runtime.d.ts +22 -0
- package/dist/physics/physics-runtime.d.ts.map +1 -0
- package/dist/physics/physics-runtime.js +423 -0
- package/dist/physics/physics-runtime.js.map +1 -0
- package/dist/physics/physics-system.d.ts +84 -108
- package/dist/physics/physics-system.d.ts.map +1 -1
- package/dist/physics/physics-system.js +686 -555
- package/dist/physics/physics-system.js.map +1 -1
- package/dist/physics/physics-transport.d.ts +20 -0
- package/dist/physics/physics-transport.d.ts.map +1 -0
- package/dist/physics/physics-transport.js +111 -0
- package/dist/physics/physics-transport.js.map +1 -0
- package/dist/physics/physics-worker-protocol.d.ts +144 -0
- package/dist/physics/physics-worker-protocol.d.ts.map +1 -0
- package/dist/physics/physics-worker-protocol.js +41 -0
- package/dist/physics/physics-worker-protocol.js.map +1 -0
- package/dist/physics/physics-worker-utils.d.ts +8 -0
- package/dist/physics/physics-worker-utils.d.ts.map +1 -0
- package/dist/physics/physics-worker-utils.js +12 -0
- package/dist/physics/physics-worker-utils.js.map +1 -0
- package/dist/physics/physics-worker.d.ts +8 -0
- package/dist/physics/physics-worker.d.ts.map +1 -0
- package/dist/physics/physics-worker.js +66 -0
- package/dist/physics/physics-worker.js.map +1 -0
- package/dist/physics/physicsBody.js.map +1 -1
- package/dist/physics/physicsManipulation.js.map +1 -1
- package/dist/physics/physicsShape.js.map +1 -1
- package/dist/physics/utils.js.map +1 -1
- package/dist/project/normalize.js +7 -1
- package/dist/project/normalize.js.map +1 -1
- package/dist/project/paths.js.map +1 -1
- package/dist/project/types.d.ts +7 -1
- package/dist/project/types.d.ts.map +1 -1
- package/dist/project/types.js.map +1 -1
- package/dist/project/validation.js +36 -2
- package/dist/project/validation.js.map +1 -1
- package/dist/runtime/three.js.map +1 -1
- package/dist/scene-understanding/anchor.js.map +1 -1
- package/dist/scene-understanding/mesh.js.map +1 -1
- package/dist/scene-understanding/plane.js.map +1 -1
- package/dist/scene-understanding/scene-understanding-system.d.ts +42 -6
- package/dist/scene-understanding/scene-understanding-system.d.ts.map +1 -1
- package/dist/scene-understanding/scene-understanding-system.js +391 -106
- package/dist/scene-understanding/scene-understanding-system.js.map +1 -1
- package/dist/schemas/iwsdk-project.v1.schema.json +20 -1
- package/dist/transform/synced-euler.js.map +1 -1
- package/dist/transform/synced-quaternion.js.map +1 -1
- package/dist/transform/synced-vector3.js.map +1 -1
- package/dist/transform/transform-component.js.map +1 -1
- package/dist/transform/transform-utils.js.map +1 -1
- package/dist/transform/transform.js.map +1 -1
- package/dist/ui/document.js.map +1 -1
- package/dist/ui/follow-component.js.map +1 -1
- package/dist/ui/follow.d.ts.map +1 -1
- package/dist/ui/follow.js +0 -1
- package/dist/ui/follow.js.map +1 -1
- package/dist/ui/panel-components.js.map +1 -1
- package/dist/ui/screenspace-component.js.map +1 -1
- package/dist/ui/screenspace.js.map +1 -1
- package/dist/ui/ui.js.map +1 -1
- package/dist/ui/uikitml-asset.js.map +1 -1
- package/dist/ui/uikitml.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/visibility/visibility-component.js.map +1 -1
- package/dist/visibility/visibility.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-utils.js","sources":["../../src/audio/audio-utils.ts"],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"audio-utils.js","sources":["../../src/audio/audio-utils.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { Entity } from '../ecs/entity.js';\nimport type { World } from '../ecs/world.js';\nimport { AudioSource as AudioComponent } from './audio.js';\n\n/** Utility helpers to control {@link AudioSource} without touching Three audio.\n * @category Audio\n */\nexport class AudioUtils {\n /**\n * Play audio on an entity\n * @param entity - Entity with Audio component\n * @param fadeIn - Fade in duration in seconds\n */\n static play(entity: Entity, fadeIn = 0): void {\n const index = entity.index;\n // Check if entity has audio by checking if data exists\n if (AudioComponent.data.src[index] === undefined) {\n console.warn('Entity does not have Audio component');\n return;\n }\n\n AudioComponent.data._playRequested[index] = 1;\n AudioComponent.data._fadeIn[index] = fadeIn;\n }\n\n /**\n * Pause audio on an entity\n * @param entity - Entity with Audio component\n * @param fadeOut - Fade out duration in seconds\n */\n static pause(entity: Entity, fadeOut = 0): void {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n console.warn('Entity does not have Audio component');\n return;\n }\n\n AudioComponent.data._pauseRequested[index] = 1;\n AudioComponent.data._fadeOut[index] = fadeOut;\n }\n\n /**\n * Stop audio on an entity\n * @param entity - Entity with Audio component\n */\n static stop(entity: Entity): void {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n console.warn('Entity does not have Audio component');\n return;\n }\n\n AudioComponent.data._stopRequested[index] = 1;\n }\n\n /**\n * Check if audio is playing on an entity\n * @param entity - Entity with Audio component\n * @returns True if audio is playing\n */\n static isPlaying(entity: Entity): boolean {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n return false;\n }\n\n return AudioComponent.data._isPlaying[index] === 1;\n }\n\n /**\n * Set volume on an entity's audio\n * @param entity - Entity with Audio component\n * @param volume - Volume level (0.0 to 1.0)\n */\n static setVolume(entity: Entity, volume: number): void {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n console.warn('Entity does not have Audio component');\n return;\n }\n\n AudioComponent.data.volume[index] = Math.max(0, Math.min(1, volume));\n }\n\n /**\n * Get volume from an entity's audio\n * @param entity - Entity with Audio component\n * @returns Current volume level\n */\n static getVolume(entity: Entity): number {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n return 0;\n }\n\n return AudioComponent.data.volume[index];\n }\n\n /**\n * Preload audio for an entity\n * @param entity - Entity with Audio component\n * @returns Promise that resolves when audio is loaded\n */\n static async preload(entity: Entity): Promise<void> {\n const index = entity.index;\n if (AudioComponent.data.src[index] === undefined) {\n throw new Error('Entity does not have Audio component');\n }\n\n // Wait for loading to complete\n return new Promise<void>((resolve) => {\n const checkLoaded = () => {\n if (AudioComponent.data._loaded[index]) {\n resolve();\n } else if (\n !AudioComponent.data._loading[index] &&\n !AudioComponent.data._loaded[index]\n ) {\n // Not loading and not loaded, might need to wait for system update\n setTimeout(checkLoaded, 16);\n } else {\n // Currently loading\n setTimeout(checkLoaded, 16);\n }\n };\n\n checkLoaded();\n });\n }\n\n /**\n * Create a one-shot audio entity that auto-removes after playing\n * @param world - World instance\n * @param src - Audio source path\n * @param options - Additional audio options\n * @returns Created entity\n */\n static createOneShot(\n world: World,\n src: string,\n options: {\n volume?: number;\n positional?: boolean;\n position?: { x: number; y: number; z: number };\n } = {},\n ): Entity {\n const entity = world.createEntity();\n\n entity.addComponent(AudioComponent, {\n src,\n volume: options.volume ?? 1.0,\n positional: options.positional ?? false,\n autoplay: true,\n loop: false,\n });\n\n if (options.positional && options.position) {\n // Would need Transform component\n // entity.addComponent(Transform, { position: options.position });\n }\n\n // Auto-remove entity when sound ends\n // This would be handled by the AudioSystem when detecting ended non-looping sounds\n\n return entity;\n }\n}\n"],"names":["AudioComponent"],"mappings":"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"}
|
package/dist/audio/audio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.js","sources":["../../src/audio/audio.ts"],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"audio.js","sources":["../../src/audio/audio.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n// See audio-system.ts for rationale — import from the subfile, not the\n// `../ecs/index.js` barrel, to keep this module free of the world.js cycle.\nimport { Types, createComponent } from '../ecs/component.js';\n\n/**\n * Playback behavior when a new play is requested.\n *\n * @category Audio\n */\nexport const PlaybackMode = {\n Restart: 'restart',\n Overlap: 'overlap',\n Ignore: 'ignore',\n FadeRestart: 'fade-restart',\n} as const;\n\n/**\n * Policy used when the instance pool is full and a new play is requested.\n *\n * @category Audio\n */\nexport const InstanceStealPolicy = {\n Oldest: 'oldest',\n Quietest: 'quietest',\n Furthest: 'furthest',\n} as const;\n\n/**\n * Distance attenuation model for positional audio.\n *\n * @category Audio\n */\nexport const DistanceModel = {\n Linear: 'linear',\n Inverse: 'inverse',\n Exponential: 'exponential',\n} as const;\n\n/**\n * Configurable audio playback component for positional and ambient sounds.\n *\n * @remarks\n * - The {@link AudioSystem} loads sources via {@link AssetManager} and manages\n * instance pooling, crossfades, and XR session lifecycle.\n * - For positional audio, attach the component to an entity with a valid\n * Object3D; non-positional audio plays from the listener.\n * - Use `_playRequested/_pauseRequested/_stopRequested` to trigger actions\n * without directly reaching into Three.js audio objects.\n *\n * @category Audio\n */\nexport const AudioSource = createComponent(\n 'AudioSource',\n {\n // Core properties PathByFilePickerAttribute\n src: {\n type: Types.FilePath,\n default: '',\n fileTypes: '.mp3,.wav,.ogg,.m4a,.aac',\n subfolder: 'audio',\n }, // URL or cache key for an audio buffer\n volume: { type: Types.Float32, default: 1.0 }, // Linear gain [0..1]\n loop: { type: Types.Boolean, default: false }, // When true, newly created instances loop\n autoplay: { type: Types.Boolean, default: false }, // Trigger a single play on first update after load\n\n // Spatial properties\n positional: { type: Types.Boolean, default: true }, // true: PositionalAudio; false: ambient Audio\n refDistance: { type: Types.Float32, default: 1 }, // Distance at which volume is 1.0\n rolloffFactor: { type: Types.Float32, default: 1 }, // Rolloff curve steepness\n maxDistance: { type: Types.Float32, default: 10000 }, // Distance after which volume is 0\n distanceModel: {\n type: Types.Enum,\n enum: DistanceModel,\n default: DistanceModel.Inverse,\n },\n coneInnerAngle: { type: Types.Float32, default: 360 }, // Degrees of full-volume cone\n coneOuterAngle: { type: Types.Float32, default: 360 }, // Degrees until falloff reaches outer gain\n coneOuterGain: { type: Types.Float32, default: 0 }, // Gain at the edge/outside of the cone\n\n // Playback behavior control\n playbackMode: {\n type: Types.Enum,\n enum: PlaybackMode,\n default: PlaybackMode.Restart,\n }, // Behavior when play is triggered while another instance is active\n maxInstances: { type: Types.Int8, default: 1 }, // Max simultaneous instances in pool\n crossfadeDuration: { type: Types.Float32, default: 0.1 }, // Seconds used for FadeRestart\n instanceStealPolicy: {\n type: Types.Enum,\n enum: InstanceStealPolicy,\n default: InstanceStealPolicy.Oldest,\n }, // Which instance to replace when pool is full\n\n // Playback control flags (internal)\n _playRequested: { type: Types.Boolean, default: false }, // Set true to request play on next update\n _pauseRequested: { type: Types.Boolean, default: false }, // Set true to request pause (optional fade)\n _stopRequested: { type: Types.Boolean, default: false }, // Set true to stop and clear instances\n _fadeIn: { type: Types.Float32, default: 0 }, // Seconds to fade in on next play\n _fadeOut: { type: Types.Float32, default: 0 }, // Seconds to fade out on pause\n\n // Runtime state (managed by AudioSystem)\n _pool: { type: Types.Object, default: undefined }, // Assigned AudioPool for this entity\n _instances: { type: Types.Object, default: undefined }, // Active instance list (internal)\n _isPlaying: { type: Types.Boolean, default: false }, // True if any instance is currently playing\n _buffer: { type: Types.Object, default: undefined }, // Loaded AudioBuffer\n _loaded: { type: Types.Boolean, default: false }, // True once buffer is loaded\n _loading: { type: Types.Boolean, default: false }, // True while buffer is loading\n },\n 'AudioSource playback component for positional and non-positional sounds',\n);\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;AACA;AAGA;;;;AAIG;AACI,MAAM,YAAY,GAAG;AAC1B,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,WAAW,EAAE,cAAc;;AAG7B;;;;AAIG;AACI,MAAM,mBAAmB,GAAG;AACjC,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,QAAQ,EAAE,UAAU;AACpB,IAAA,QAAQ,EAAE,UAAU;;AAGtB;;;;AAIG;AACI,MAAM,aAAa,GAAG;AAC3B,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,WAAW,EAAE,aAAa;;AAG5B;;;;;;;;;;;;AAYG;AACI,MAAM,WAAW,GAAG,eAAe,CACxC,aAAa,EACb;;AAEE,IAAA,GAAG,EAAE;QACH,IAAI,EAAE,KAAK,CAAC,QAAQ;AACpB,QAAA,OAAO,EAAE,EAAE;AACX,QAAA,SAAS,EAAE,0BAA0B;AACrC,QAAA,SAAS,EAAE,OAAO;AACnB,KAAA;AACD,IAAA,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;AAC7C,IAAA,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AAC7C,IAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;;AAGjD,IAAA,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;AAClD,IAAA,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAChD,IAAA,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAClD,IAAA,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACpD,IAAA,aAAa,EAAE;QACb,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,aAAa,CAAC,OAAO;AAC/B,KAAA;AACD,IAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;AACrD,IAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;AACrD,IAAA,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;;AAGlD,IAAA,YAAY,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,YAAY,CAAC,OAAO;AAC9B,KAAA;AACD,IAAA,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE;AAC9C,IAAA,iBAAiB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE;AACxD,IAAA,mBAAmB,EAAE;QACnB,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,mBAAmB,CAAC,MAAM;AACpC,KAAA;;AAGD,IAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACvD,IAAA,eAAe,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACxD,IAAA,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACvD,IAAA,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAC5C,IAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;;AAG7C,IAAA,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;AACjD,IAAA,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;AACtD,IAAA,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AACnD,IAAA,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;AACnD,IAAA,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;AAChD,IAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;CAClD,EACD,yEAAyE;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera-source.js","sources":["../../src/camera/camera-source.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAAA;;;;;AAKG;AAKH;;;AAGG;AACI,MAAM,YAAY,GAAG,eAAe,CACzC,cAAc,EACd;;AAEE,IAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;AAC7C,IAAA,MAAM,EAAE;QACN,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,YAAY,CAAC,OAAO;AAC9B,KAAA;IACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;IAC5C,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;;AAG7C,IAAA,KAAK,EAAE;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,WAAW,CAAC,QAAQ;AAC9B,KAAA;AACD,IAAA,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;AAC9C,IAAA,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;AACnD,IAAA,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;CAC9C,EACD,yCAAyC;;;;"}
|
|
1
|
+
{"version":3,"file":"camera-source.js","sources":["../../src/camera/camera-source.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { Types, createComponent } from '../ecs/component.js';\nimport { CameraFacing, CameraState } from './types.js';\n\n/**\n * CameraSource - Component for accessing device cameras\n * Provides VideoTexture for rendering and HTMLVideoElement for advanced use\n */\nexport const CameraSource = createComponent(\n 'CameraSource',\n {\n // Input (set by user)\n deviceId: { type: Types.String, default: '' }, // Empty = auto-select based on facing\n facing: {\n type: Types.Enum,\n enum: CameraFacing,\n default: CameraFacing.Unknown,\n }, // 'front' | 'back' | 'unknown' - used for auto-selection (unknown = any camera)\n width: { type: Types.Int16, default: 1920 },\n height: { type: Types.Int16, default: 1080 },\n frameRate: { type: Types.Int16, default: 30 },\n\n // Output (managed by system, read-only for users)\n state: {\n type: Types.Enum,\n enum: CameraState,\n default: CameraState.Inactive,\n }, // 'inactive' | 'starting' | 'active' | 'error'\n texture: { type: Types.Object, default: null }, // VideoTexture\n videoElement: { type: Types.Object, default: null }, // HTMLVideoElement (for advanced use)\n stream: { type: Types.Object, default: null }, // MediaStream (internal)\n },\n 'Camera video source with texture output',\n);\n"],"names":[],"mappings":";;;AAAA;;;;;AAKG;AAKH;;;AAGG;AACI,MAAM,YAAY,GAAG,eAAe,CACzC,cAAc,EACd;;AAEE,IAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;AAC7C,IAAA,MAAM,EAAE;QACN,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,YAAY,CAAC,OAAO;AAC9B,KAAA;IACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;IAC5C,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;;AAG7C,IAAA,KAAK,EAAE;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,WAAW,CAAC,QAAQ;AAC9B,KAAA;AACD,IAAA,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;AAC9C,IAAA,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;AACnD,IAAA,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;CAC9C,EACD,yCAAyC;;;;"}
|
|
@@ -113,10 +113,9 @@ declare const CameraSystem_base: import("elics").SystemConstructor<import("elics
|
|
|
113
113
|
* CameraSystem - Manages camera stream lifecycle for CameraSource components
|
|
114
114
|
* Automatically starts streams when the world is visible, including browser
|
|
115
115
|
* non-immersive mode and immersive XR sessions.
|
|
116
|
-
*
|
|
117
|
-
* System is stateless - all state is stored in CameraSource components
|
|
118
116
|
*/
|
|
119
117
|
export declare class CameraSystem extends CameraSystem_base {
|
|
118
|
+
private readonly operationIds;
|
|
120
119
|
init(): void;
|
|
121
120
|
update(): void;
|
|
122
121
|
/**
|
|
@@ -124,13 +123,18 @@ export declare class CameraSystem extends CameraSystem_base {
|
|
|
124
123
|
* Async operation - sets state to Starting, then Active when complete
|
|
125
124
|
* Users should check state or null-check texture/videoElement before using
|
|
126
125
|
*
|
|
127
|
-
* Checks
|
|
126
|
+
* Checks an operation generation after each async operation so a restarted
|
|
127
|
+
* camera cannot be overwritten by an older request that resolves later.
|
|
128
128
|
*/
|
|
129
129
|
private startCamera;
|
|
130
130
|
/**
|
|
131
131
|
* Stop camera stream for an entity
|
|
132
132
|
*/
|
|
133
133
|
private stopCamera;
|
|
134
|
+
private releaseCameraResources;
|
|
135
|
+
private stopAllCameras;
|
|
136
|
+
private beginOperation;
|
|
137
|
+
private isCurrentStart;
|
|
134
138
|
/**
|
|
135
139
|
* Clean up camera resources (stream, video element, texture)
|
|
136
140
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera-system.d.ts","sourceRoot":"","sources":["../../src/camera/camera-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeH
|
|
1
|
+
{"version":3,"file":"camera-system.d.ts","sourceRoot":"","sources":["../../src/camera/camera-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeH;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,iBAEhC;IACA,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiC;IAE9D,IAAI;IAkCJ,MAAM;IAqBN;;;;;;;OAOG;YACW,WAAW;IA0IzB;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,sBAAsB;IAqB9B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,cAAc;IAOtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAgB/B"}
|
|
@@ -16,33 +16,49 @@ import { VideoTexture, LinearFilter } from 'three';
|
|
|
16
16
|
* CameraSystem - Manages camera stream lifecycle for CameraSource components
|
|
17
17
|
* Automatically starts streams when the world is visible, including browser
|
|
18
18
|
* non-immersive mode and immersive XR sessions.
|
|
19
|
-
*
|
|
20
|
-
* System is stateless - all state is stored in CameraSource components
|
|
21
19
|
*/
|
|
22
20
|
class CameraSystem extends createSystem({
|
|
23
21
|
cameras: { required: [CameraSource] },
|
|
24
22
|
}) {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.operationIds = new WeakMap();
|
|
26
|
+
}
|
|
25
27
|
init() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const stopIfDocumentHidden = () => {
|
|
29
|
+
if (document.visibilityState === 'hidden') {
|
|
30
|
+
this.stopAllCameras();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const hasDocumentVisibility = typeof document !== 'undefined' &&
|
|
34
|
+
typeof document.addEventListener === 'function';
|
|
35
|
+
if (hasDocumentVisibility) {
|
|
36
|
+
document.addEventListener('visibilitychange', stopIfDocumentHidden);
|
|
37
|
+
}
|
|
29
38
|
this.cleanupFuncs.push(this.world.visibilityState.subscribe((state) => {
|
|
30
39
|
if (state === VisibilityState.Hidden) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
this.stopAllCameras();
|
|
41
|
+
}
|
|
42
|
+
}), this.queries.cameras.subscribe('disqualify', (entity) => {
|
|
43
|
+
this.stopCamera(entity);
|
|
44
|
+
}), () => {
|
|
45
|
+
if (hasDocumentVisibility) {
|
|
46
|
+
document.removeEventListener('visibilitychange', stopIfDocumentHidden);
|
|
34
47
|
}
|
|
35
|
-
|
|
48
|
+
this.stopAllCameras();
|
|
49
|
+
});
|
|
36
50
|
}
|
|
37
51
|
update() {
|
|
38
52
|
// CameraSource uses browser media APIs and works outside immersive XR.
|
|
39
|
-
|
|
53
|
+
const documentHidden = typeof document !== 'undefined' && document.visibilityState === 'hidden';
|
|
54
|
+
if (documentHidden ||
|
|
55
|
+
this.world.visibilityState.value === VisibilityState.Hidden) {
|
|
40
56
|
return;
|
|
41
57
|
}
|
|
42
58
|
for (const entity of this.queries.cameras.entities) {
|
|
43
59
|
const state = entity.getValue(CameraSource, 'state');
|
|
44
|
-
//
|
|
45
|
-
if (state === CameraState.Inactive
|
|
60
|
+
// Error is terminal until callers explicitly set the source inactive.
|
|
61
|
+
if (state === CameraState.Inactive) {
|
|
46
62
|
this.startCamera(entity);
|
|
47
63
|
}
|
|
48
64
|
}
|
|
@@ -52,19 +68,23 @@ class CameraSystem extends createSystem({
|
|
|
52
68
|
* Async operation - sets state to Starting, then Active when complete
|
|
53
69
|
* Users should check state or null-check texture/videoElement before using
|
|
54
70
|
*
|
|
55
|
-
* Checks
|
|
71
|
+
* Checks an operation generation after each async operation so a restarted
|
|
72
|
+
* camera cannot be overwritten by an older request that resolves later.
|
|
56
73
|
*/
|
|
57
74
|
async startCamera(entity) {
|
|
75
|
+
const operationId = this.beginOperation(entity);
|
|
76
|
+
this.releaseCameraResources(entity);
|
|
58
77
|
// Set state to Starting to prevent duplicate attempts
|
|
59
78
|
entity.setValue(CameraSource, 'state', CameraState.Starting);
|
|
79
|
+
let stream = null;
|
|
80
|
+
let video = null;
|
|
81
|
+
let texture = null;
|
|
60
82
|
try {
|
|
61
83
|
let deviceId = entity.getValue(CameraSource, 'deviceId');
|
|
62
84
|
// Auto-select camera if no deviceId provided
|
|
63
85
|
if (!deviceId) {
|
|
64
86
|
const devices = await CameraUtils.getDevices();
|
|
65
|
-
|
|
66
|
-
const currentState = entity.getValue(CameraSource, 'state');
|
|
67
|
-
if (currentState !== CameraState.Starting) {
|
|
87
|
+
if (!this.isCurrentStart(entity, operationId)) {
|
|
68
88
|
return; // Aborted
|
|
69
89
|
}
|
|
70
90
|
const facing = entity.getValue(CameraSource, 'facing');
|
|
@@ -100,7 +120,7 @@ class CameraSystem extends createSystem({
|
|
|
100
120
|
const height = entity.getValue(CameraSource, 'height');
|
|
101
121
|
const frameRate = entity.getValue(CameraSource, 'frameRate');
|
|
102
122
|
// Request camera stream
|
|
103
|
-
|
|
123
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
104
124
|
video: {
|
|
105
125
|
deviceId: { exact: deviceId },
|
|
106
126
|
width: { ideal: width },
|
|
@@ -108,50 +128,45 @@ class CameraSystem extends createSystem({
|
|
|
108
128
|
frameRate: { ideal: frameRate },
|
|
109
129
|
},
|
|
110
130
|
});
|
|
111
|
-
|
|
112
|
-
const currentState = entity.getValue(CameraSource, 'state');
|
|
113
|
-
if (currentState !== CameraState.Starting) {
|
|
131
|
+
if (!this.isCurrentStart(entity, operationId)) {
|
|
114
132
|
// Aborted - clean up the stream we just created
|
|
115
133
|
this.cleanupCameraResources(stream, null, null);
|
|
116
134
|
return;
|
|
117
135
|
}
|
|
118
136
|
// Create video element
|
|
119
|
-
const
|
|
120
|
-
video
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
137
|
+
const createdVideo = document.createElement('video');
|
|
138
|
+
video = createdVideo;
|
|
139
|
+
createdVideo.setAttribute('playsinline', '');
|
|
140
|
+
createdVideo.setAttribute('autoplay', '');
|
|
141
|
+
createdVideo.muted = true;
|
|
142
|
+
createdVideo.srcObject = stream;
|
|
124
143
|
// Wait for video to be ready
|
|
125
144
|
await new Promise((resolve, reject) => {
|
|
126
145
|
const onCanPlay = () => {
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
createdVideo.removeEventListener('canplay', onCanPlay);
|
|
147
|
+
createdVideo.removeEventListener('error', onError);
|
|
129
148
|
resolve();
|
|
130
149
|
};
|
|
131
150
|
const onError = (error) => {
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
createdVideo.removeEventListener('canplay', onCanPlay);
|
|
152
|
+
createdVideo.removeEventListener('error', onError);
|
|
134
153
|
reject(error);
|
|
135
154
|
};
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
createdVideo.addEventListener('canplay', onCanPlay);
|
|
156
|
+
createdVideo.addEventListener('error', onError);
|
|
138
157
|
});
|
|
139
|
-
|
|
140
|
-
const finalState = entity.getValue(CameraSource, 'state');
|
|
141
|
-
if (finalState !== CameraState.Starting) {
|
|
158
|
+
if (!this.isCurrentStart(entity, operationId)) {
|
|
142
159
|
// Aborted - clean up everything
|
|
143
160
|
this.cleanupCameraResources(stream, video, null);
|
|
144
161
|
return;
|
|
145
162
|
}
|
|
146
163
|
// Start playback
|
|
147
|
-
await
|
|
164
|
+
await createdVideo.play();
|
|
148
165
|
// Create VideoTexture
|
|
149
|
-
|
|
166
|
+
texture = new VideoTexture(createdVideo);
|
|
150
167
|
texture.minFilter = LinearFilter;
|
|
151
168
|
texture.magFilter = LinearFilter;
|
|
152
|
-
|
|
153
|
-
const committingState = entity.getValue(CameraSource, 'state');
|
|
154
|
-
if (committingState !== CameraState.Starting) {
|
|
169
|
+
if (!this.isCurrentStart(entity, operationId)) {
|
|
155
170
|
// Aborted at the last moment - clean up everything
|
|
156
171
|
this.cleanupCameraResources(stream, video, texture);
|
|
157
172
|
return;
|
|
@@ -163,31 +178,47 @@ class CameraSystem extends createSystem({
|
|
|
163
178
|
entity.setValue(CameraSource, 'state', CameraState.Active);
|
|
164
179
|
}
|
|
165
180
|
catch (error) {
|
|
166
|
-
console.error('Failed to start camera:', error);
|
|
167
|
-
// Clean up any partial state
|
|
168
|
-
const stream = entity.getValue(CameraSource, 'stream');
|
|
169
|
-
const video = entity.getValue(CameraSource, 'videoElement');
|
|
170
|
-
const texture = entity.getValue(CameraSource, 'texture');
|
|
171
181
|
this.cleanupCameraResources(stream, video, texture);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
if (this.isCurrentStart(entity, operationId)) {
|
|
183
|
+
console.error('Failed to start camera:', error);
|
|
184
|
+
entity.setValue(CameraSource, 'stream', null);
|
|
185
|
+
entity.setValue(CameraSource, 'videoElement', null);
|
|
186
|
+
entity.setValue(CameraSource, 'texture', null);
|
|
187
|
+
entity.setValue(CameraSource, 'state', CameraState.Error);
|
|
188
|
+
}
|
|
176
189
|
}
|
|
177
190
|
}
|
|
178
191
|
/**
|
|
179
192
|
* Stop camera stream for an entity
|
|
180
193
|
*/
|
|
181
194
|
stopCamera(entity) {
|
|
195
|
+
this.beginOperation(entity);
|
|
196
|
+
this.releaseCameraResources(entity);
|
|
197
|
+
entity.setValue(CameraSource, 'state', CameraState.Inactive);
|
|
198
|
+
}
|
|
199
|
+
releaseCameraResources(entity) {
|
|
182
200
|
const stream = entity.getValue(CameraSource, 'stream');
|
|
183
201
|
const video = entity.getValue(CameraSource, 'videoElement');
|
|
184
202
|
const texture = entity.getValue(CameraSource, 'texture');
|
|
185
203
|
this.cleanupCameraResources(stream, video, texture);
|
|
186
|
-
// Clear component values and set state to Inactive
|
|
187
204
|
entity.setValue(CameraSource, 'stream', null);
|
|
188
205
|
entity.setValue(CameraSource, 'videoElement', null);
|
|
189
206
|
entity.setValue(CameraSource, 'texture', null);
|
|
190
|
-
|
|
207
|
+
}
|
|
208
|
+
stopAllCameras() {
|
|
209
|
+
for (const entity of this.queries.cameras.entities) {
|
|
210
|
+
this.stopCamera(entity);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
beginOperation(entity) {
|
|
214
|
+
var _a;
|
|
215
|
+
const operationId = ((_a = this.operationIds.get(entity)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
216
|
+
this.operationIds.set(entity, operationId);
|
|
217
|
+
return operationId;
|
|
218
|
+
}
|
|
219
|
+
isCurrentStart(entity, operationId) {
|
|
220
|
+
return (this.operationIds.get(entity) === operationId &&
|
|
221
|
+
entity.getValue(CameraSource, 'state') === CameraState.Starting);
|
|
191
222
|
}
|
|
192
223
|
/**
|
|
193
224
|
* Clean up camera resources (stream, video element, texture)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera-system.js","sources":["../../src/camera/camera-system.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"camera-system.js","sources":["../../src/camera/camera-system.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { Entity } from '../ecs/entity.js';\nimport { createSystem } from '../ecs/system.js';\nimport { VisibilityState } from '../ecs/world.js';\nimport { LinearFilter, VideoTexture } from '../runtime/three.js';\nimport { CameraSource } from './camera-source.js';\nimport { CameraUtils } from './camera-utils.js';\nimport {\n CameraFacing,\n CameraState,\n type CameraFacingType,\n type CameraStateType,\n} from './types.js';\n\n/**\n * CameraSystem - Manages camera stream lifecycle for CameraSource components\n * Automatically starts streams when the world is visible, including browser\n * non-immersive mode and immersive XR sessions.\n */\nexport class CameraSystem extends createSystem({\n cameras: { required: [CameraSource] },\n}) {\n private readonly operationIds = new WeakMap<Entity, number>();\n\n init() {\n const stopIfDocumentHidden = () => {\n if (document.visibilityState === 'hidden') {\n this.stopAllCameras();\n }\n };\n const hasDocumentVisibility =\n typeof document !== 'undefined' &&\n typeof document.addEventListener === 'function';\n if (hasDocumentVisibility) {\n document.addEventListener('visibilitychange', stopIfDocumentHidden);\n }\n\n this.cleanupFuncs.push(\n this.world.visibilityState.subscribe((state) => {\n if (state === VisibilityState.Hidden) {\n this.stopAllCameras();\n }\n }),\n this.queries.cameras.subscribe('disqualify', (entity) => {\n this.stopCamera(entity);\n }),\n () => {\n if (hasDocumentVisibility) {\n document.removeEventListener(\n 'visibilitychange',\n stopIfDocumentHidden,\n );\n }\n this.stopAllCameras();\n },\n );\n }\n\n update() {\n // CameraSource uses browser media APIs and works outside immersive XR.\n const documentHidden =\n typeof document !== 'undefined' && document.visibilityState === 'hidden';\n if (\n documentHidden ||\n this.world.visibilityState.value === VisibilityState.Hidden\n ) {\n return;\n }\n\n for (const entity of this.queries.cameras.entities) {\n const state = entity.getValue(CameraSource, 'state') as CameraStateType;\n\n // Error is terminal until callers explicitly set the source inactive.\n if (state === CameraState.Inactive) {\n this.startCamera(entity);\n }\n }\n }\n\n /**\n * Start camera stream for an entity\n * Async operation - sets state to Starting, then Active when complete\n * Users should check state or null-check texture/videoElement before using\n *\n * Checks an operation generation after each async operation so a restarted\n * camera cannot be overwritten by an older request that resolves later.\n */\n private async startCamera(entity: Entity) {\n const operationId = this.beginOperation(entity);\n this.releaseCameraResources(entity);\n\n // Set state to Starting to prevent duplicate attempts\n entity.setValue(CameraSource, 'state', CameraState.Starting);\n let stream: MediaStream | null = null;\n let video: HTMLVideoElement | null = null;\n let texture: VideoTexture | null = null;\n\n try {\n let deviceId = entity.getValue(CameraSource, 'deviceId') as string;\n\n // Auto-select camera if no deviceId provided\n if (!deviceId) {\n const devices = await CameraUtils.getDevices();\n\n if (!this.isCurrentStart(entity, operationId)) {\n return; // Aborted\n }\n\n const facing = entity.getValue(\n CameraSource,\n 'facing',\n ) as CameraFacingType;\n\n if (facing === CameraFacing.Unknown) {\n // Unknown = any camera is fine, use first available\n if (devices.length > 0) {\n deviceId = devices[0].deviceId;\n entity.setValue(CameraSource, 'deviceId', deviceId);\n } else {\n console.error('No cameras available');\n entity.setValue(CameraSource, 'state', CameraState.Error);\n return;\n }\n } else {\n // Specific facing requested - must match or fail\n const selected = CameraUtils.findByFacing(devices, facing);\n\n if (selected) {\n deviceId = selected.deviceId;\n entity.setValue(CameraSource, 'deviceId', deviceId);\n } else {\n const facingStr = facing === CameraFacing.Back ? 'back' : 'front';\n console.error(\n `No ${facingStr}-facing camera available (found ${devices.length} camera(s))`,\n );\n entity.setValue(CameraSource, 'state', CameraState.Error);\n return;\n }\n }\n }\n\n // Get stream constraints\n const width = entity.getValue(CameraSource, 'width');\n const height = entity.getValue(CameraSource, 'height');\n const frameRate = entity.getValue(CameraSource, 'frameRate');\n\n // Request camera stream\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n deviceId: { exact: deviceId },\n width: { ideal: width as number | undefined },\n height: { ideal: height as number | undefined },\n frameRate: { ideal: frameRate as number | undefined },\n },\n });\n\n if (!this.isCurrentStart(entity, operationId)) {\n // Aborted - clean up the stream we just created\n this.cleanupCameraResources(stream, null, null);\n return;\n }\n\n // Create video element\n const createdVideo = document.createElement('video');\n video = createdVideo;\n createdVideo.setAttribute('playsinline', '');\n createdVideo.setAttribute('autoplay', '');\n createdVideo.muted = true;\n createdVideo.srcObject = stream;\n\n // Wait for video to be ready\n await new Promise<void>((resolve, reject) => {\n const onCanPlay = () => {\n createdVideo.removeEventListener('canplay', onCanPlay);\n createdVideo.removeEventListener('error', onError);\n resolve();\n };\n\n const onError = (error: Event) => {\n createdVideo.removeEventListener('canplay', onCanPlay);\n createdVideo.removeEventListener('error', onError);\n reject(error);\n };\n\n createdVideo.addEventListener('canplay', onCanPlay);\n createdVideo.addEventListener('error', onError);\n });\n\n if (!this.isCurrentStart(entity, operationId)) {\n // Aborted - clean up everything\n this.cleanupCameraResources(stream, video, null);\n return;\n }\n\n // Start playback\n await createdVideo.play();\n\n // Create VideoTexture\n texture = new VideoTexture(createdVideo);\n texture.minFilter = LinearFilter;\n texture.magFilter = LinearFilter;\n\n if (!this.isCurrentStart(entity, operationId)) {\n // Aborted at the last moment - clean up everything\n this.cleanupCameraResources(stream, video, texture);\n return;\n }\n\n // Commit all resources atomically\n entity.setValue(CameraSource, 'stream', stream);\n entity.setValue(CameraSource, 'videoElement', video);\n entity.setValue(CameraSource, 'texture', texture);\n entity.setValue(CameraSource, 'state', CameraState.Active);\n } catch (error) {\n this.cleanupCameraResources(stream, video, texture);\n if (this.isCurrentStart(entity, operationId)) {\n console.error('Failed to start camera:', error);\n entity.setValue(CameraSource, 'stream', null);\n entity.setValue(CameraSource, 'videoElement', null);\n entity.setValue(CameraSource, 'texture', null);\n entity.setValue(CameraSource, 'state', CameraState.Error);\n }\n }\n }\n\n /**\n * Stop camera stream for an entity\n */\n private stopCamera(entity: Entity) {\n this.beginOperation(entity);\n this.releaseCameraResources(entity);\n entity.setValue(CameraSource, 'state', CameraState.Inactive);\n }\n\n private releaseCameraResources(entity: Entity): void {\n const stream = entity.getValue(\n CameraSource,\n 'stream',\n ) as MediaStream | null;\n const video = entity.getValue(\n CameraSource,\n 'videoElement',\n ) as HTMLVideoElement | null;\n const texture = entity.getValue(\n CameraSource,\n 'texture',\n ) as VideoTexture | null;\n\n this.cleanupCameraResources(stream, video, texture);\n\n entity.setValue(CameraSource, 'stream', null);\n entity.setValue(CameraSource, 'videoElement', null);\n entity.setValue(CameraSource, 'texture', null);\n }\n\n private stopAllCameras(): void {\n for (const entity of this.queries.cameras.entities) {\n this.stopCamera(entity);\n }\n }\n\n private beginOperation(entity: Entity): number {\n const operationId = (this.operationIds.get(entity) ?? 0) + 1;\n this.operationIds.set(entity, operationId);\n return operationId;\n }\n\n private isCurrentStart(entity: Entity, operationId: number): boolean {\n return (\n this.operationIds.get(entity) === operationId &&\n entity.getValue(CameraSource, 'state') === CameraState.Starting\n );\n }\n\n /**\n * Clean up camera resources (stream, video element, texture)\n */\n private cleanupCameraResources(\n stream: MediaStream | null,\n video: HTMLVideoElement | null,\n texture: VideoTexture | null,\n ) {\n if (stream) {\n stream.getTracks().forEach((track) => track.stop());\n }\n if (video) {\n video.pause();\n video.srcObject = null;\n }\n if (texture) {\n texture.dispose();\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -35,6 +35,11 @@ export declare class CameraUtils {
|
|
|
35
35
|
* Check if camera permission is granted (doesn't request)
|
|
36
36
|
*/
|
|
37
37
|
static hasPermission(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Restart a camera source after changing its configuration or handling an
|
|
40
|
+
* error. CameraSystem releases the previous resources before starting again.
|
|
41
|
+
*/
|
|
42
|
+
static restart(entity: Entity): void;
|
|
38
43
|
/**
|
|
39
44
|
* Capture current video frame as a canvas
|
|
40
45
|
* Returns canvas at full video resolution, ready for processing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera-utils.d.ts","sourceRoot":"","sources":["../../src/camera/camera-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,
|
|
1
|
+
{"version":3,"file":"camera-utils.d.ts","sourceRoot":"","sources":["../../src/camera/camera-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAmC;IAE/D;;;;;;;;;;;;;OAaG;WACU,UAAU,CAAC,OAAO,UAAQ,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAoDrE;;OAEG;IACH,MAAM,CAAC,YAAY,CACjB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,MAAM,EAAE,gBAAgB,GACvB,gBAAgB,GAAG,IAAI;IAI1B;;OAEG;WACU,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAY9C;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIpC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;CA+B9D"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CameraSource } from './camera-source.js';
|
|
2
|
-
import { CameraFacing } from './types.js';
|
|
2
|
+
import { CameraFacing, CameraState } from './types.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -89,6 +89,13 @@ class CameraUtils {
|
|
|
89
89
|
return false;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Restart a camera source after changing its configuration or handling an
|
|
94
|
+
* error. CameraSystem releases the previous resources before starting again.
|
|
95
|
+
*/
|
|
96
|
+
static restart(entity) {
|
|
97
|
+
entity.setValue(CameraSource, 'state', CameraState.Inactive);
|
|
98
|
+
}
|
|
92
99
|
/**
|
|
93
100
|
* Capture current video frame as a canvas
|
|
94
101
|
* Returns canvas at full video resolution, ready for processing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera-utils.js","sources":["../../src/camera/camera-utils.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAAA;;;;;AAKG;
|
|
1
|
+
{"version":3,"file":"camera-utils.js","sources":["../../src/camera/camera-utils.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { Entity } from '../ecs/entity.js';\nimport { CameraSource } from './camera-source.js';\nimport {\n CameraFacing,\n CameraState,\n type CameraDeviceInfo,\n type CameraFacingType,\n} from './types.js';\n\n/**\n * CameraUtils - Static utilities for camera discovery and selection\n * Handles permission requests and device enumeration with caching\n */\nexport class CameraUtils {\n private static cachedDevices: CameraDeviceInfo[] | null = null;\n\n /**\n * Get available camera devices\n *\n * On first call or if refresh=true:\n * - Requests camera permission (if not granted)\n * - Enumerates all video input devices\n * - Caches results for subsequent calls\n *\n * On subsequent calls with refresh=false:\n * - Returns cached results immediately\n *\n * @param refresh - Force re-enumeration (useful for hot-plugged devices)\n * @returns Array of available camera devices\n */\n static async getDevices(refresh = false): Promise<CameraDeviceInfo[]> {\n // Return cache if available and not forcing refresh\n if (this.cachedDevices && !refresh) {\n return this.cachedDevices;\n }\n\n try {\n // Request permission by getting a temporary stream\n const tempStream = await navigator.mediaDevices.getUserMedia({\n video: true,\n });\n\n // Immediately stop the stream - we just needed permission\n tempStream.getTracks().forEach((track) => track.stop());\n\n // Enumerate all video devices\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((d) => d.kind === 'videoinput');\n\n // Map to our interface with facing detection\n this.cachedDevices = videoDevices.map((device) => {\n const label = device.label.toLowerCase();\n let facing: CameraFacingType = CameraFacing.Unknown;\n\n if (\n label.includes('back') ||\n label.includes('environment') ||\n label.includes('rear')\n ) {\n facing = CameraFacing.Back;\n } else if (\n label.includes('front') ||\n label.includes('user') ||\n label.includes('face')\n ) {\n facing = CameraFacing.Front;\n }\n\n return {\n deviceId: device.deviceId,\n label: device.label,\n facing,\n };\n });\n\n return this.cachedDevices;\n } catch (error) {\n console.error('Failed to enumerate camera devices:', error);\n throw error;\n }\n }\n\n /**\n * Find camera by facing direction from device list\n */\n static findByFacing(\n devices: CameraDeviceInfo[],\n facing: CameraFacingType,\n ): CameraDeviceInfo | null {\n return devices.find((d) => d.facing === facing) || null;\n }\n\n /**\n * Check if camera permission is granted (doesn't request)\n */\n static async hasPermission(): Promise<boolean> {\n try {\n const result = await navigator.permissions.query({\n name: 'camera' as PermissionName,\n });\n return result.state === 'granted';\n } catch {\n // Permissions API not supported\n return false;\n }\n }\n\n /**\n * Restart a camera source after changing its configuration or handling an\n * error. CameraSystem releases the previous resources before starting again.\n */\n static restart(entity: Entity): void {\n entity.setValue(CameraSource, 'state', CameraState.Inactive);\n }\n\n /**\n * Capture current video frame as a canvas\n * Returns canvas at full video resolution, ready for processing\n *\n * @param entity - Entity with CameraSource component\n * @returns HTMLCanvasElement or null if camera not ready\n *\n * @example\n * // Simple texture creation\n * const canvas = CameraUtils.captureFrame(cameraEntity);\n * if (canvas) {\n * const texture = new CanvasTexture(canvas);\n * }\n *\n * @example\n * // With post-processing\n * const canvas = CameraUtils.captureFrame(cameraEntity);\n * if (canvas) {\n * const ctx = canvas.getContext('2d');\n * ctx.filter = 'blur(2px)';\n * const texture = new CanvasTexture(canvas);\n * }\n */\n static captureFrame(entity: Entity): HTMLCanvasElement | null {\n // Get video element from CameraSource component\n const videoElement = entity.getValue(\n CameraSource,\n 'videoElement',\n ) as HTMLVideoElement | null;\n\n if (!videoElement) {\n return null;\n }\n\n // Validate video has valid dimensions\n if (videoElement.videoWidth === 0 || videoElement.videoHeight === 0) {\n return null;\n }\n\n // Create canvas at video resolution\n const canvas = document.createElement('canvas');\n canvas.width = videoElement.videoWidth;\n canvas.height = videoElement.videoHeight;\n\n // Draw current video frame to canvas\n const ctx = canvas.getContext('2d', { colorSpace: 'srgb' });\n if (!ctx) {\n return null;\n }\n\n ctx.drawImage(videoElement, 0, 0);\n\n return canvas;\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/camera/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/camera/types.ts"],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/camera/types.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nexport const CameraFacing = {\n Back: 'back',\n Front: 'front',\n Unknown: 'unknown',\n} as const;\n\nexport type CameraFacingType = (typeof CameraFacing)[keyof typeof CameraFacing];\n\nexport interface CameraDeviceInfo {\n deviceId: string;\n label: string;\n facing: CameraFacingType;\n}\n\n/**\n * Camera stream state lifecycle\n */\nexport const CameraState = {\n Inactive: 'inactive', // Not started\n Starting: 'starting', // Async initialization in progress\n Active: 'active', // Stream running\n Error: 'error', // Failed to start\n} as const;\n\nexport type CameraStateType = (typeof CameraState)[keyof typeof CameraState];\n"],"names":[],"mappings":"AAAA;;;;;AAKG;AAEI,MAAM,YAAY,GAAG;AAC1B,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,OAAO,EAAE,SAAS;;AAWpB;;AAEG;AACI,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"depth-occludable.js","sources":["../../src/depth/depth-occludable.ts"],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"depth-occludable.js","sources":["../../src/depth/depth-occludable.ts"],"sourcesContent":["/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { Types, createComponent } from '../ecs/component.js';\n/** Occlusion shader mode for {@link DepthOccludable}. @category Depth Sensing */\nexport const OcclusionShadersMode = {\n /** Soft occlusion with 13-tap blur sampling for smooth edges. */\n SoftOcclusion: 'SoftOcclusion',\n /** Hard occlusion with a single depth sample per fragment. */\n HardOcclusion: 'HardOcclusion',\n /** MinMax soft occlusion with depth preprocessing for edge-aware smooth edges. */\n MinMaxSoftOcclusion: 'MinMaxSoftOcclusion',\n};\n\n/**\n * Component for entities that should be occluded by real-world depth.\n * Add this component to entities that should be hidden when behind real-world surfaces.\n * NOTE: The depth occlusion feature may not be compatible with custom shaders.\n *\n * @example\n * ```ts\n * // Create an entity with soft occlusion (default)\n * const entity = world.createTransformEntity(mesh);\n * entity.addComponent(DepthOccludable);\n *\n * // Create an entity with hard occlusion (no blur)\n * entity.addComponent(DepthOccludable, { mode: OcclusionShadersMode.HardOcclusion });\n * ```\n *\n * @category Depth Sensing\n */\nexport const DepthOccludable = createComponent(\n 'DepthOccludable',\n {\n mode: {\n type: Types.Enum,\n enum: OcclusionShadersMode,\n default: OcclusionShadersMode.SoftOcclusion,\n },\n },\n 'Entity that can be occluded by real-world depth',\n);\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAGH;AACO,MAAM,oBAAoB,GAAG;;AAElC,IAAA,aAAa,EAAE,eAAe;;AAE9B,IAAA,aAAa,EAAE,eAAe;;AAE9B,IAAA,mBAAmB,EAAE,qBAAqB;;AAG5C;;;;;;;;;;;;;;;;AAgBG;AACI,MAAM,eAAe,GAAG,eAAe,CAC5C,iBAAiB,EACjB;AACE,IAAA,IAAI,EAAE;QACJ,IAAI,EAAE,KAAK,CAAC,IAAI;AAChB,QAAA,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,oBAAoB,CAAC,aAAa;AAC5C,KAAA;CACF,EACD,iDAAiD;;;;"}
|
|
@@ -107,6 +107,7 @@ export declare class DepthSensingSystem extends DepthSensingSystem_base {
|
|
|
107
107
|
private depthFeatureEnabled;
|
|
108
108
|
cpuDepthData: XRCPUDepthInformation[];
|
|
109
109
|
gpuDepthData: XRWebGLDepthInformation[];
|
|
110
|
+
private hasCurrentFrameDepthData;
|
|
110
111
|
private depthTextures?;
|
|
111
112
|
private entityShaderMap;
|
|
112
113
|
private readonly viewportSize;
|
|
@@ -123,6 +124,8 @@ export declare class DepthSensingSystem extends DepthSensingSystem_base {
|
|
|
123
124
|
*/
|
|
124
125
|
private attachOcclusionToEntity;
|
|
125
126
|
private detachOcclusionFromEntity;
|
|
127
|
+
private clearOcclusionUniforms;
|
|
128
|
+
private clearAllOcclusionUniforms;
|
|
126
129
|
/**
|
|
127
130
|
* Modifies a material's shader in-place to incorporate inline depth-based
|
|
128
131
|
* occlusion. Compares the virtual fragment's view-space depth against the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"depth-sensing-system.d.ts","sourceRoot":"","sources":["../../src/depth/depth-sensing-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiB5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,kBAAmB,SAAQ,uBAUvC;IACC,OAAO,CAAC,mBAAmB,CAAsB;IAGjD,YAAY,EAAE,qBAAqB,EAAE,CAAM;IAC3C,YAAY,EAAE,uBAAuB,EAAE,CAAM;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAgB;IAGtC,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiB;IAC9C,OAAO,CAAC,iBAAiB,CAAC,CAAyB;IACnD,OAAO,CAAC,iBAAiB,CAAK;IAE9B;;OAEG;IACH,IAAI,gBAAgB,IAAI,MAAM,CAO7B;IAED,IAAI,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"depth-sensing-system.d.ts","sourceRoot":"","sources":["../../src/depth/depth-sensing-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiB5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,kBAAmB,SAAQ,uBAUvC;IACC,OAAO,CAAC,mBAAmB,CAAsB;IAGjD,YAAY,EAAE,qBAAqB,EAAE,CAAM;IAC3C,YAAY,EAAE,uBAAuB,EAAE,CAAM;IAC7C,OAAO,CAAC,wBAAwB,CAAS;IACzC,OAAO,CAAC,aAAa,CAAC,CAAgB;IAGtC,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiB;IAC9C,OAAO,CAAC,iBAAiB,CAAC,CAAyB;IACnD,OAAO,CAAC,iBAAiB,CAAK;IAE9B;;OAEG;IACH,IAAI,gBAAgB,IAAI,MAAM,CAO7B;IAED,IAAI,IAAI,IAAI;IAmEZ,OAAO,CAAC,uBAAuB;IAI/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAwC/B,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,yBAAyB;IAMjC;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAsInC,OAAO,CAAC,OAAO;IAgBf,OAAO,CAAC,qBAAqB;IAe7B,MAAM,IAAI,IAAI;IA8Bd;;;;OAIG;IACH,OAAO,KAAK,UAAU,GAGrB;IAED,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,sBAAsB;IA8C9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA4CxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;CAwChC"}
|