@iwsdk/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/camera/camera-source.d.ts +51 -0
- package/dist/camera/camera-source.d.ts.map +1 -0
- package/dist/camera/camera-source.js +32 -0
- package/dist/camera/camera-source.js.map +1 -0
- package/dist/camera/camera-system.d.ts +114 -0
- package/dist/camera/camera-system.d.ts.map +1 -0
- package/dist/camera/camera-system.js +208 -0
- package/dist/camera/camera-system.js.map +1 -0
- package/dist/camera/camera-utils.d.ts +63 -0
- package/dist/camera/camera-utils.d.ts.map +1 -0
- package/dist/camera/camera-utils.js +140 -0
- package/dist/camera/camera-utils.js.map +1 -0
- package/dist/camera/index.d.ts +11 -0
- package/dist/camera/index.d.ts.map +1 -0
- package/dist/camera/types.d.ts +21 -0
- package/dist/camera/types.d.ts.map +1 -0
- package/dist/camera/types.js +19 -0
- package/dist/camera/types.js.map +1 -0
- package/dist/grab/distance-grabbable.d.ts +6 -0
- package/dist/grab/distance-grabbable.d.ts.map +1 -1
- package/dist/grab/distance-grabbable.js +6 -0
- package/dist/grab/distance-grabbable.js.map +1 -1
- package/dist/grab/pointer-events-patch.d.ts.map +1 -1
- package/dist/grab/pointer-events-patch.js +34 -7
- package/dist/grab/pointer-events-patch.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/init/world-initializer.d.ts +2 -0
- package/dist/init/world-initializer.d.ts.map +1 -1
- package/dist/init/world-initializer.js +12 -2
- package/dist/init/world-initializer.js.map +1 -1
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.1.7_super-three@0.177.0/node_modules/@pmndrs/uikitml/dist/interpreter/html.js +128 -0
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.1.7_super-three@0.177.0/node_modules/@pmndrs/uikitml/dist/interpreter/html.js.map +1 -0
- package/dist/node_modules/.pnpm/{@pmndrs_uikitml@0.0.10_@pmndrs_uikit@1.0.32_super-three@0.177.0_ → @pmndrs_uikitml@0.1.7_super-three@0.177.0}/node_modules/@pmndrs/uikitml/dist/interpreter/index.js +34 -27
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.1.7_super-three@0.177.0/node_modules/@pmndrs/uikitml/dist/interpreter/index.js.map +1 -0
- package/dist/physics/physics-system.d.ts +33 -1
- package/dist/physics/physics-system.d.ts.map +1 -1
- package/dist/physics/physics-system.js +17 -8
- package/dist/physics/physics-system.js.map +1 -1
- package/dist/physics/physicsBody.d.ts +9 -0
- package/dist/physics/physicsBody.d.ts.map +1 -1
- package/dist/physics/physicsBody.js +4 -1
- package/dist/physics/physicsBody.js.map +1 -1
- package/dist/transform/transform.d.ts.map +1 -1
- package/dist/transform/transform.js +1 -0
- package/dist/transform/transform.js.map +1 -1
- package/dist/ui/ui.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -6
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.0.10_@pmndrs_uikit@1.0.32_super-three@0.177.0_/node_modules/@pmndrs/uikitml/dist/index.js +0 -13
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.0.10_@pmndrs_uikit@1.0.32_super-three@0.177.0_/node_modules/@pmndrs/uikitml/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@pmndrs_uikitml@0.0.10_@pmndrs_uikit@1.0.32_super-three@0.177.0_/node_modules/@pmndrs/uikitml/dist/interpreter/index.js.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { Types } from '../ecs/index.js';
|
|
8
|
+
import { CameraState } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* CameraSource - Component for accessing device cameras
|
|
11
|
+
* Provides VideoTexture for rendering and HTMLVideoElement for advanced use
|
|
12
|
+
*/
|
|
13
|
+
export declare const CameraSource: import("elics").Component<{
|
|
14
|
+
deviceId: {
|
|
15
|
+
type: Types.String;
|
|
16
|
+
default: string;
|
|
17
|
+
};
|
|
18
|
+
facing: {
|
|
19
|
+
type: Types.String;
|
|
20
|
+
default: string;
|
|
21
|
+
};
|
|
22
|
+
width: {
|
|
23
|
+
type: Types.Int16;
|
|
24
|
+
default: number;
|
|
25
|
+
};
|
|
26
|
+
height: {
|
|
27
|
+
type: Types.Int16;
|
|
28
|
+
default: number;
|
|
29
|
+
};
|
|
30
|
+
frameRate: {
|
|
31
|
+
type: Types.Int16;
|
|
32
|
+
default: number;
|
|
33
|
+
};
|
|
34
|
+
state: {
|
|
35
|
+
type: Types.String;
|
|
36
|
+
default: CameraState;
|
|
37
|
+
};
|
|
38
|
+
texture: {
|
|
39
|
+
type: Types.Object;
|
|
40
|
+
default: null;
|
|
41
|
+
};
|
|
42
|
+
videoElement: {
|
|
43
|
+
type: Types.Object;
|
|
44
|
+
default: null;
|
|
45
|
+
};
|
|
46
|
+
stream: {
|
|
47
|
+
type: Types.Object;
|
|
48
|
+
default: null;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
51
|
+
//# sourceMappingURL=camera-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-source.d.ts","sourceRoot":"","sources":["../../src/camera/camera-source.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,EAAmB,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBxB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import '@preact/signals-core';
|
|
2
|
+
import '../ecs/world.js';
|
|
3
|
+
import { createComponent, Types } from 'elics';
|
|
4
|
+
import '../ecs/component-registry.js';
|
|
5
|
+
import { CameraState } from './types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
9
|
+
*
|
|
10
|
+
* This source code is licensed under the MIT license found in the
|
|
11
|
+
* LICENSE file in the root directory of this source tree.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* CameraSource - Component for accessing device cameras
|
|
15
|
+
* Provides VideoTexture for rendering and HTMLVideoElement for advanced use
|
|
16
|
+
*/
|
|
17
|
+
const CameraSource = createComponent('CameraSource', {
|
|
18
|
+
// Input (set by user)
|
|
19
|
+
deviceId: { type: Types.String, default: '' }, // Empty = auto-select based on facing
|
|
20
|
+
facing: { type: Types.String, default: 'back' }, // 'front' | 'back' - used for auto-selection
|
|
21
|
+
width: { type: Types.Int16, default: 1920 },
|
|
22
|
+
height: { type: Types.Int16, default: 1080 },
|
|
23
|
+
frameRate: { type: Types.Int16, default: 30 },
|
|
24
|
+
// Output (managed by system, read-only for users)
|
|
25
|
+
state: { type: Types.String, default: CameraState.Inactive }, // 'inactive' | 'starting' | 'active' | 'error'
|
|
26
|
+
texture: { type: Types.Object, default: null }, // VideoTexture
|
|
27
|
+
videoElement: { type: Types.Object, default: null }, // HTMLVideoElement (for advanced use)
|
|
28
|
+
stream: { type: Types.Object, default: null }, // MediaStream (internal)
|
|
29
|
+
}, 'Camera video source with texture output');
|
|
30
|
+
|
|
31
|
+
export { CameraSource };
|
|
32
|
+
//# sourceMappingURL=camera-source.js.map
|
|
@@ -0,0 +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,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;IAC/C,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,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE;AAC5D,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;;;;"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { CameraState } from './types.js';
|
|
8
|
+
declare const CameraSystem_base: import("elics").SystemConstructor<import("elics").SystemSchema, {
|
|
9
|
+
cameras: {
|
|
10
|
+
required: import("elics").Component<{
|
|
11
|
+
deviceId: {
|
|
12
|
+
type: import("elics/lib/types.js").Types.String;
|
|
13
|
+
default: string;
|
|
14
|
+
};
|
|
15
|
+
facing: {
|
|
16
|
+
type: import("elics/lib/types.js").Types.String;
|
|
17
|
+
default: string;
|
|
18
|
+
};
|
|
19
|
+
width: {
|
|
20
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
21
|
+
default: number;
|
|
22
|
+
};
|
|
23
|
+
height: {
|
|
24
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
25
|
+
default: number;
|
|
26
|
+
};
|
|
27
|
+
frameRate: {
|
|
28
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
29
|
+
default: number;
|
|
30
|
+
};
|
|
31
|
+
state: {
|
|
32
|
+
type: import("elics/lib/types.js").Types.String;
|
|
33
|
+
default: CameraState;
|
|
34
|
+
};
|
|
35
|
+
texture: {
|
|
36
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
37
|
+
default: null;
|
|
38
|
+
};
|
|
39
|
+
videoElement: {
|
|
40
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
41
|
+
default: null;
|
|
42
|
+
};
|
|
43
|
+
stream: {
|
|
44
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
45
|
+
default: null;
|
|
46
|
+
};
|
|
47
|
+
}>[];
|
|
48
|
+
};
|
|
49
|
+
}, import("../ecs/world.js").World, import("../ecs/system.js").System<import("elics").SystemSchema, {
|
|
50
|
+
cameras: {
|
|
51
|
+
required: import("elics").Component<{
|
|
52
|
+
deviceId: {
|
|
53
|
+
type: import("elics/lib/types.js").Types.String;
|
|
54
|
+
default: string;
|
|
55
|
+
};
|
|
56
|
+
facing: {
|
|
57
|
+
type: import("elics/lib/types.js").Types.String;
|
|
58
|
+
default: string;
|
|
59
|
+
};
|
|
60
|
+
width: {
|
|
61
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
62
|
+
default: number;
|
|
63
|
+
};
|
|
64
|
+
height: {
|
|
65
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
66
|
+
default: number;
|
|
67
|
+
};
|
|
68
|
+
frameRate: {
|
|
69
|
+
type: import("elics/lib/types.js").Types.Int16;
|
|
70
|
+
default: number;
|
|
71
|
+
};
|
|
72
|
+
state: {
|
|
73
|
+
type: import("elics/lib/types.js").Types.String;
|
|
74
|
+
default: CameraState;
|
|
75
|
+
};
|
|
76
|
+
texture: {
|
|
77
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
78
|
+
default: null;
|
|
79
|
+
};
|
|
80
|
+
videoElement: {
|
|
81
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
82
|
+
default: null;
|
|
83
|
+
};
|
|
84
|
+
stream: {
|
|
85
|
+
type: import("elics/lib/types.js").Types.Object;
|
|
86
|
+
default: null;
|
|
87
|
+
};
|
|
88
|
+
}>[];
|
|
89
|
+
};
|
|
90
|
+
}>>;
|
|
91
|
+
/**
|
|
92
|
+
* CameraSystem - Manages camera stream lifecycle for CameraSource components
|
|
93
|
+
* Automatically starts streams when XR session is active, stops when inactive
|
|
94
|
+
*
|
|
95
|
+
* System is stateless - all state is stored in CameraSource components
|
|
96
|
+
*/
|
|
97
|
+
export declare class CameraSystem extends CameraSystem_base {
|
|
98
|
+
init(): void;
|
|
99
|
+
update(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Start camera stream for an entity
|
|
102
|
+
* Async operation - sets state to Starting, then Active when complete
|
|
103
|
+
* Users should check state or null-check texture/videoElement before using
|
|
104
|
+
*
|
|
105
|
+
* Checks state after each async operation to abort if stopCamera was called
|
|
106
|
+
*/
|
|
107
|
+
private startCamera;
|
|
108
|
+
/**
|
|
109
|
+
* Stop camera stream for an entity
|
|
110
|
+
*/
|
|
111
|
+
private stopCamera;
|
|
112
|
+
}
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=camera-system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-system.d.ts","sourceRoot":"","sources":["../../src/camera/camera-system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEzC;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,iBAEhC;IACA,IAAI;IAWJ,MAAM;IAkBN;;;;;;OAMG;YACW,WAAW;IAuJzB;;OAEG;IACH,OAAO,CAAC,UAAU;CAoCnB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createSystem } from '../ecs/system.js';
|
|
2
|
+
import { VisibilityState } from '../ecs/world.js';
|
|
3
|
+
import 'elics';
|
|
4
|
+
import '../ecs/component-registry.js';
|
|
5
|
+
import '../runtime/three.js';
|
|
6
|
+
import { CameraSource } from './camera-source.js';
|
|
7
|
+
import { CameraUtils } from './camera-utils.js';
|
|
8
|
+
import { CameraState } from './types.js';
|
|
9
|
+
import { VideoTexture, LinearFilter } from 'three';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the MIT license found in the
|
|
15
|
+
* LICENSE file in the root directory of this source tree.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* CameraSystem - Manages camera stream lifecycle for CameraSource components
|
|
19
|
+
* Automatically starts streams when XR session is active, stops when inactive
|
|
20
|
+
*
|
|
21
|
+
* System is stateless - all state is stored in CameraSource components
|
|
22
|
+
*/
|
|
23
|
+
class CameraSystem extends createSystem({
|
|
24
|
+
cameras: { required: [CameraSource] },
|
|
25
|
+
}) {
|
|
26
|
+
init() {
|
|
27
|
+
// Stop all cameras when leaving XR
|
|
28
|
+
this.world.visibilityState.subscribe((state) => {
|
|
29
|
+
if (state !== VisibilityState.Visible) {
|
|
30
|
+
for (const entity of this.queries.cameras.entities) {
|
|
31
|
+
this.stopCamera(entity);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
update() {
|
|
37
|
+
// Only manage cameras when XR is active
|
|
38
|
+
const isXRActive = this.world.visibilityState.value === VisibilityState.Visible;
|
|
39
|
+
if (!isXRActive) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const entity of this.queries.cameras.entities) {
|
|
43
|
+
const state = entity.getValue(CameraSource, 'state');
|
|
44
|
+
// Start camera if inactive (not started or errored)
|
|
45
|
+
if (state === CameraState.Inactive || state === CameraState.Error) {
|
|
46
|
+
this.startCamera(entity);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Start camera stream for an entity
|
|
52
|
+
* Async operation - sets state to Starting, then Active when complete
|
|
53
|
+
* Users should check state or null-check texture/videoElement before using
|
|
54
|
+
*
|
|
55
|
+
* Checks state after each async operation to abort if stopCamera was called
|
|
56
|
+
*/
|
|
57
|
+
async startCamera(entity) {
|
|
58
|
+
// Set state to Starting to prevent duplicate attempts
|
|
59
|
+
entity.setValue(CameraSource, 'state', CameraState.Starting);
|
|
60
|
+
try {
|
|
61
|
+
let deviceId = entity.getValue(CameraSource, 'deviceId');
|
|
62
|
+
// Auto-select camera if no deviceId provided
|
|
63
|
+
if (!deviceId) {
|
|
64
|
+
const devices = await CameraUtils.getDevices();
|
|
65
|
+
// Check if we should abort (stopCamera was called during async operation)
|
|
66
|
+
const currentState = entity.getValue(CameraSource, 'state');
|
|
67
|
+
if (currentState !== CameraState.Starting) {
|
|
68
|
+
return; // Aborted
|
|
69
|
+
}
|
|
70
|
+
const facing = entity.getValue(CameraSource, 'facing');
|
|
71
|
+
const selected = CameraUtils.findByFacing(devices, facing);
|
|
72
|
+
if (selected) {
|
|
73
|
+
deviceId = selected.deviceId;
|
|
74
|
+
entity.setValue(CameraSource, 'deviceId', deviceId);
|
|
75
|
+
}
|
|
76
|
+
else if (devices.length > 0) {
|
|
77
|
+
// Fallback to first camera
|
|
78
|
+
deviceId = devices[0].deviceId;
|
|
79
|
+
entity.setValue(CameraSource, 'deviceId', deviceId);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.error('No cameras available');
|
|
83
|
+
entity.setValue(CameraSource, 'state', CameraState.Error);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Get stream constraints
|
|
88
|
+
const width = entity.getValue(CameraSource, 'width');
|
|
89
|
+
const height = entity.getValue(CameraSource, 'height');
|
|
90
|
+
const frameRate = entity.getValue(CameraSource, 'frameRate');
|
|
91
|
+
// Request camera stream
|
|
92
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
93
|
+
video: {
|
|
94
|
+
deviceId: { exact: deviceId },
|
|
95
|
+
width: { ideal: width },
|
|
96
|
+
height: { ideal: height },
|
|
97
|
+
frameRate: { ideal: frameRate },
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
// Check if we should abort after getting stream
|
|
101
|
+
const currentState = entity.getValue(CameraSource, 'state');
|
|
102
|
+
if (currentState !== CameraState.Starting) {
|
|
103
|
+
// Aborted - clean up the stream we just created
|
|
104
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Create video element
|
|
108
|
+
const video = document.createElement('video');
|
|
109
|
+
video.setAttribute('playsinline', '');
|
|
110
|
+
video.setAttribute('autoplay', '');
|
|
111
|
+
video.muted = true;
|
|
112
|
+
video.srcObject = stream;
|
|
113
|
+
// Wait for video to be ready
|
|
114
|
+
await new Promise((resolve, reject) => {
|
|
115
|
+
const onCanPlay = () => {
|
|
116
|
+
video.removeEventListener('canplay', onCanPlay);
|
|
117
|
+
video.removeEventListener('error', onError);
|
|
118
|
+
resolve();
|
|
119
|
+
};
|
|
120
|
+
const onError = (error) => {
|
|
121
|
+
video.removeEventListener('canplay', onCanPlay);
|
|
122
|
+
video.removeEventListener('error', onError);
|
|
123
|
+
reject(error);
|
|
124
|
+
};
|
|
125
|
+
video.addEventListener('canplay', onCanPlay);
|
|
126
|
+
video.addEventListener('error', onError);
|
|
127
|
+
});
|
|
128
|
+
// Check if we should abort after video ready
|
|
129
|
+
const finalState = entity.getValue(CameraSource, 'state');
|
|
130
|
+
if (finalState !== CameraState.Starting) {
|
|
131
|
+
// Aborted - clean up everything
|
|
132
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
133
|
+
video.srcObject = null;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Start playback
|
|
137
|
+
await video.play();
|
|
138
|
+
// Create VideoTexture
|
|
139
|
+
const texture = new VideoTexture(video);
|
|
140
|
+
texture.minFilter = LinearFilter;
|
|
141
|
+
texture.magFilter = LinearFilter;
|
|
142
|
+
// Final check before committing - ensure state is still Starting
|
|
143
|
+
const committingState = entity.getValue(CameraSource, 'state');
|
|
144
|
+
if (committingState !== CameraState.Starting) {
|
|
145
|
+
// Aborted at the last moment - clean up everything
|
|
146
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
147
|
+
video.srcObject = null;
|
|
148
|
+
texture.dispose();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Commit all resources atomically
|
|
152
|
+
entity.setValue(CameraSource, 'stream', stream);
|
|
153
|
+
entity.setValue(CameraSource, 'videoElement', video);
|
|
154
|
+
entity.setValue(CameraSource, 'texture', texture);
|
|
155
|
+
entity.setValue(CameraSource, 'state', CameraState.Active);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error('Failed to start camera:', error);
|
|
159
|
+
// Clean up any partial state
|
|
160
|
+
const stream = entity.getValue(CameraSource, 'stream');
|
|
161
|
+
if (stream) {
|
|
162
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
163
|
+
}
|
|
164
|
+
const video = entity.getValue(CameraSource, 'videoElement');
|
|
165
|
+
if (video) {
|
|
166
|
+
video.pause();
|
|
167
|
+
video.srcObject = null;
|
|
168
|
+
}
|
|
169
|
+
const texture = entity.getValue(CameraSource, 'texture');
|
|
170
|
+
if (texture) {
|
|
171
|
+
texture.dispose();
|
|
172
|
+
}
|
|
173
|
+
entity.setValue(CameraSource, 'stream', null);
|
|
174
|
+
entity.setValue(CameraSource, 'videoElement', null);
|
|
175
|
+
entity.setValue(CameraSource, 'texture', null);
|
|
176
|
+
entity.setValue(CameraSource, 'state', CameraState.Error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Stop camera stream for an entity
|
|
181
|
+
*/
|
|
182
|
+
stopCamera(entity) {
|
|
183
|
+
const stream = entity.getValue(CameraSource, 'stream');
|
|
184
|
+
const video = entity.getValue(CameraSource, 'videoElement');
|
|
185
|
+
const texture = entity.getValue(CameraSource, 'texture');
|
|
186
|
+
// Stop stream
|
|
187
|
+
if (stream) {
|
|
188
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
189
|
+
}
|
|
190
|
+
// Clean up video element
|
|
191
|
+
if (video) {
|
|
192
|
+
video.pause();
|
|
193
|
+
video.srcObject = null;
|
|
194
|
+
}
|
|
195
|
+
// Dispose texture
|
|
196
|
+
if (texture) {
|
|
197
|
+
texture.dispose();
|
|
198
|
+
}
|
|
199
|
+
// Clear component values and set state to Inactive
|
|
200
|
+
entity.setValue(CameraSource, 'stream', null);
|
|
201
|
+
entity.setValue(CameraSource, 'videoElement', null);
|
|
202
|
+
entity.setValue(CameraSource, 'texture', null);
|
|
203
|
+
entity.setValue(CameraSource, 'state', CameraState.Inactive);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export { CameraSystem };
|
|
208
|
+
//# sourceMappingURL=camera-system.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-system.js","sources":["../../src/camera/camera-system.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { Entity } from '../ecs/index.js';
|
|
8
|
+
import type { CameraDeviceInfo } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* CameraUtils - Static utilities for camera discovery and selection
|
|
11
|
+
* Handles permission requests and device enumeration with caching
|
|
12
|
+
*/
|
|
13
|
+
export declare class CameraUtils {
|
|
14
|
+
private static cachedDevices;
|
|
15
|
+
/**
|
|
16
|
+
* Get available camera devices
|
|
17
|
+
*
|
|
18
|
+
* On first call or if refresh=true:
|
|
19
|
+
* - Requests camera permission (if not granted)
|
|
20
|
+
* - Enumerates all video input devices
|
|
21
|
+
* - Caches results for subsequent calls
|
|
22
|
+
*
|
|
23
|
+
* On subsequent calls with refresh=false:
|
|
24
|
+
* - Returns cached results immediately
|
|
25
|
+
*
|
|
26
|
+
* @param refresh - Force re-enumeration (useful for hot-plugged devices)
|
|
27
|
+
* @returns Array of available camera devices
|
|
28
|
+
*/
|
|
29
|
+
static getDevices(refresh?: boolean): Promise<CameraDeviceInfo[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Find camera by facing direction from device list
|
|
32
|
+
*/
|
|
33
|
+
static findByFacing(devices: CameraDeviceInfo[], facing: 'front' | 'back'): CameraDeviceInfo | null;
|
|
34
|
+
/**
|
|
35
|
+
* Check if camera permission is granted (doesn't request)
|
|
36
|
+
*/
|
|
37
|
+
static hasPermission(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Capture current video frame as a canvas
|
|
40
|
+
* Returns canvas at full video resolution, ready for processing
|
|
41
|
+
*
|
|
42
|
+
* @param entity - Entity with CameraSource component
|
|
43
|
+
* @returns HTMLCanvasElement or null if camera not ready
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Simple texture creation
|
|
47
|
+
* const canvas = CameraUtils.captureFrame(cameraEntity);
|
|
48
|
+
* if (canvas) {
|
|
49
|
+
* const texture = new CanvasTexture(canvas);
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // With post-processing
|
|
54
|
+
* const canvas = CameraUtils.captureFrame(cameraEntity);
|
|
55
|
+
* if (canvas) {
|
|
56
|
+
* const ctx = canvas.getContext('2d');
|
|
57
|
+
* ctx.filter = 'blur(2px)';
|
|
58
|
+
* const texture = new CanvasTexture(canvas);
|
|
59
|
+
* }
|
|
60
|
+
*/
|
|
61
|
+
static captureFrame(entity: Entity): HTMLCanvasElement | null;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=camera-utils.d.ts.map
|
|
@@ -0,0 +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,iBAAiB,CAAC;AAEzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;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,OAAO,GAAG,MAAM,GACvB,gBAAgB,GAAG,IAAI;IAI1B;;OAEG;WACU,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAY9C;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;CA+B9D"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { CameraSource } from './camera-source.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* CameraUtils - Static utilities for camera discovery and selection
|
|
11
|
+
* Handles permission requests and device enumeration with caching
|
|
12
|
+
*/
|
|
13
|
+
class CameraUtils {
|
|
14
|
+
/**
|
|
15
|
+
* Get available camera devices
|
|
16
|
+
*
|
|
17
|
+
* On first call or if refresh=true:
|
|
18
|
+
* - Requests camera permission (if not granted)
|
|
19
|
+
* - Enumerates all video input devices
|
|
20
|
+
* - Caches results for subsequent calls
|
|
21
|
+
*
|
|
22
|
+
* On subsequent calls with refresh=false:
|
|
23
|
+
* - Returns cached results immediately
|
|
24
|
+
*
|
|
25
|
+
* @param refresh - Force re-enumeration (useful for hot-plugged devices)
|
|
26
|
+
* @returns Array of available camera devices
|
|
27
|
+
*/
|
|
28
|
+
static async getDevices(refresh = false) {
|
|
29
|
+
// Return cache if available and not forcing refresh
|
|
30
|
+
if (this.cachedDevices && !refresh) {
|
|
31
|
+
return this.cachedDevices;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
// Request permission by getting a temporary stream
|
|
35
|
+
const tempStream = await navigator.mediaDevices.getUserMedia({
|
|
36
|
+
video: true,
|
|
37
|
+
});
|
|
38
|
+
// Immediately stop the stream - we just needed permission
|
|
39
|
+
tempStream.getTracks().forEach((track) => track.stop());
|
|
40
|
+
// Enumerate all video devices
|
|
41
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
42
|
+
const videoDevices = devices.filter((d) => d.kind === 'videoinput');
|
|
43
|
+
// Map to our interface with facing detection
|
|
44
|
+
this.cachedDevices = videoDevices.map((device) => {
|
|
45
|
+
const label = device.label.toLowerCase();
|
|
46
|
+
let facing = 'unknown';
|
|
47
|
+
if (label.includes('back') ||
|
|
48
|
+
label.includes('environment') ||
|
|
49
|
+
label.includes('rear')) {
|
|
50
|
+
facing = 'back';
|
|
51
|
+
}
|
|
52
|
+
else if (label.includes('front') ||
|
|
53
|
+
label.includes('user') ||
|
|
54
|
+
label.includes('face')) {
|
|
55
|
+
facing = 'front';
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
deviceId: device.deviceId,
|
|
59
|
+
label: device.label,
|
|
60
|
+
facing,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
return this.cachedDevices;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Failed to enumerate camera devices:', error);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Find camera by facing direction from device list
|
|
72
|
+
*/
|
|
73
|
+
static findByFacing(devices, facing) {
|
|
74
|
+
return devices.find((d) => d.facing === facing) || null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if camera permission is granted (doesn't request)
|
|
78
|
+
*/
|
|
79
|
+
static async hasPermission() {
|
|
80
|
+
try {
|
|
81
|
+
const result = await navigator.permissions.query({
|
|
82
|
+
name: 'camera',
|
|
83
|
+
});
|
|
84
|
+
return result.state === 'granted';
|
|
85
|
+
}
|
|
86
|
+
catch (_a) {
|
|
87
|
+
// Permissions API not supported
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Capture current video frame as a canvas
|
|
93
|
+
* Returns canvas at full video resolution, ready for processing
|
|
94
|
+
*
|
|
95
|
+
* @param entity - Entity with CameraSource component
|
|
96
|
+
* @returns HTMLCanvasElement or null if camera not ready
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // Simple texture creation
|
|
100
|
+
* const canvas = CameraUtils.captureFrame(cameraEntity);
|
|
101
|
+
* if (canvas) {
|
|
102
|
+
* const texture = new CanvasTexture(canvas);
|
|
103
|
+
* }
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // With post-processing
|
|
107
|
+
* const canvas = CameraUtils.captureFrame(cameraEntity);
|
|
108
|
+
* if (canvas) {
|
|
109
|
+
* const ctx = canvas.getContext('2d');
|
|
110
|
+
* ctx.filter = 'blur(2px)';
|
|
111
|
+
* const texture = new CanvasTexture(canvas);
|
|
112
|
+
* }
|
|
113
|
+
*/
|
|
114
|
+
static captureFrame(entity) {
|
|
115
|
+
// Get video element from CameraSource component
|
|
116
|
+
const videoElement = entity.getValue(CameraSource, 'videoElement');
|
|
117
|
+
if (!videoElement) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
// Validate video has valid dimensions
|
|
121
|
+
if (videoElement.videoWidth === 0 || videoElement.videoHeight === 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
// Create canvas at video resolution
|
|
125
|
+
const canvas = document.createElement('canvas');
|
|
126
|
+
canvas.width = videoElement.videoWidth;
|
|
127
|
+
canvas.height = videoElement.videoHeight;
|
|
128
|
+
// Draw current video frame to canvas
|
|
129
|
+
const ctx = canvas.getContext('2d', { colorSpace: 'srgb' });
|
|
130
|
+
if (!ctx) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
ctx.drawImage(videoElement, 0, 0);
|
|
134
|
+
return canvas;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
CameraUtils.cachedDevices = null;
|
|
138
|
+
|
|
139
|
+
export { CameraUtils };
|
|
140
|
+
//# sourceMappingURL=camera-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-utils.js","sources":["../../src/camera/camera-utils.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
export * from './camera-utils.js';
|
|
9
|
+
export * from './camera-source.js';
|
|
10
|
+
export * from './camera-system.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/camera/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC"}
|