@scenography/engine 0.2.0 → 0.3.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/dist/index.cjs +166 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -25
- package/dist/index.d.mts +133 -25
- package/dist/index.mjs +162 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,13 @@ interface FocusOptions {
|
|
|
10
10
|
widthFraction?: number;
|
|
11
11
|
/** Tween duration in ms. Default `900`. */
|
|
12
12
|
duration?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Minimum camera distance from the piece, in world units. When the framing that
|
|
15
|
+
* fits the piece would sit closer, the camera pulls back to this instead (the
|
|
16
|
+
* piece stays centered, just smaller). Default `0` (no minimum). Useful on close
|
|
17
|
+
* so a small piece doesn't leave the visitor glued to the wall.
|
|
18
|
+
*/
|
|
19
|
+
minDistance?: number;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* Public camera-framing API exposed on the gallery context as `ctx.camera`.
|
|
@@ -23,8 +30,19 @@ interface CameraApi {
|
|
|
23
30
|
centerArtwork(id: string, options?: FocusOptions): void;
|
|
24
31
|
/** Drop any active framing watch and hand control fully back to the user. */
|
|
25
32
|
release(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Animate the camera back to the scene's start position and orientation — a
|
|
35
|
+
* "home" for visitors who lose their way and want to return to the entrance.
|
|
36
|
+
*/
|
|
37
|
+
reset(): void;
|
|
26
38
|
/** Whether a framing tween is in progress (e.g. to suppress click-to-select). */
|
|
27
39
|
isAnimating(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a piece is framed and still selected (the framing watch is active).
|
|
42
|
+
* Usually paired with {@link isAnimating} to gate picking: while a piece is
|
|
43
|
+
* selected, hovering must not re-highlight it (its info panel is open).
|
|
44
|
+
*/
|
|
45
|
+
isFocused(): boolean;
|
|
28
46
|
}
|
|
29
47
|
//#endregion
|
|
30
48
|
//#region src/api/index.d.ts
|
|
@@ -93,6 +111,97 @@ interface SkyApi {
|
|
|
93
111
|
getAzimuth(): number;
|
|
94
112
|
}
|
|
95
113
|
//#endregion
|
|
114
|
+
//#region src/input/InputBus.d.ts
|
|
115
|
+
/**
|
|
116
|
+
* The shared analog-input channel: the current movement and look intent, each
|
|
117
|
+
* axis in `-1..1` and held until a producer changes it. Producers (on-screen
|
|
118
|
+
* joysticks in the overlay, a gamepad…) write to it; whatever camera system is
|
|
119
|
+
* active reads it and interprets it its own way — free-roam walks with it, a
|
|
120
|
+
* fixed camera might only use the look axes to glance around. This is what makes
|
|
121
|
+
* the joysticks reusable across camera types. Pure state — no three.js.
|
|
122
|
+
*/
|
|
123
|
+
declare class InputBus {
|
|
124
|
+
/** Forward (`+1`) / backward (`-1`). */
|
|
125
|
+
moveForward: number;
|
|
126
|
+
/** Strafe right (`+1`) / left (`-1`). */
|
|
127
|
+
moveStrafe: number;
|
|
128
|
+
/** Look right (`+1`) / left (`-1`). */
|
|
129
|
+
lookX: number;
|
|
130
|
+
/** Look up (`+1`) / down (`-1`). */
|
|
131
|
+
lookY: number;
|
|
132
|
+
/** Set the movement intent (forward, strafe). */
|
|
133
|
+
setMove(forward: number, strafe?: number): void;
|
|
134
|
+
/** Set the look intent (turn, pitch). */
|
|
135
|
+
setLook(x: number, y?: number): void;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/input/controlSettings.d.ts
|
|
139
|
+
/**
|
|
140
|
+
* The visitor's camera-control preferences: a set of orthogonal capability flags
|
|
141
|
+
* plus a derived, friendly "mode". Pure state — no three.js, no DOM. The engine
|
|
142
|
+
* wires the flags to effects (gyroscope head-look, stereo split); consumers read
|
|
143
|
+
* them (the analog look-invert axes); the overlay drives it from a mode picker
|
|
144
|
+
* (the `ScanEye` button) and the settings panel. Persistence and device
|
|
145
|
+
* feature-detection live in those callers, not here, so this stays trivially
|
|
146
|
+
* unit-testable.
|
|
147
|
+
*/
|
|
148
|
+
/**
|
|
149
|
+
* Orthogonal control capabilities. `gyro` / `splitScreen` / `joysticks` are what a
|
|
150
|
+
* mode fixes; the two `invertLook*` axes are independent look-feel preferences
|
|
151
|
+
* (some people feel the right stick inverted), kept separate from the mode.
|
|
152
|
+
*/
|
|
153
|
+
interface ControlFlags {
|
|
154
|
+
/** Orient the camera from the device gyroscope (head-look). */
|
|
155
|
+
gyro: boolean;
|
|
156
|
+
/** Split the view into a stereo left/right eye pair (phone-in-a-headset). */
|
|
157
|
+
splitScreen: boolean;
|
|
158
|
+
/** Show the on-screen virtual joysticks. */
|
|
159
|
+
joysticks: boolean;
|
|
160
|
+
/** Invert the horizontal (turn) axis of analog look. */
|
|
161
|
+
invertLookX: boolean;
|
|
162
|
+
/** Invert the vertical (pitch) axis of analog look. */
|
|
163
|
+
invertLookY: boolean;
|
|
164
|
+
}
|
|
165
|
+
/** A named preset over the mode-defining flags; `custom` when none matches. */
|
|
166
|
+
type ControlMode = "touch" | "vr" | "cardboard" | "custom";
|
|
167
|
+
/** The three flags a mode fixes (the look-invert axes stay a separate preference). */
|
|
168
|
+
type ModeFlags = Pick<ControlFlags, "gyro" | "splitScreen" | "joysticks">;
|
|
169
|
+
/**
|
|
170
|
+
* Mode presets. Touch = two joysticks, no gyro. VR = the gyro aims and a single
|
|
171
|
+
* move joystick walks you, no split. Cardboard = gyro aims, stereo split, gaze
|
|
172
|
+
* select (no joysticks). The move-only single stick in VR is derived downstream
|
|
173
|
+
* from `gyro && !splitScreen`, so it needs no flag of its own.
|
|
174
|
+
*/
|
|
175
|
+
declare const MODE_PRESETS: Record<Exclude<ControlMode, "custom">, ModeFlags>;
|
|
176
|
+
/** Default flags: plain touch, no look inversion. */
|
|
177
|
+
declare const DEFAULT_CONTROL_FLAGS: ControlFlags;
|
|
178
|
+
/** The mode a flag set represents, or `custom` when no preset matches. */
|
|
179
|
+
declare function modeOf(flags: ControlFlags): ControlMode;
|
|
180
|
+
/** Apply a mode's preset onto `flags`, leaving the look-invert axes untouched. */
|
|
181
|
+
declare function applyMode(flags: ControlFlags, mode: Exclude<ControlMode, "custom">): ControlFlags;
|
|
182
|
+
/**
|
|
183
|
+
* Live control-preferences state: holds the flags, derives the mode, and notifies
|
|
184
|
+
* subscribers on every real change. Callers apply the effects — the engine wires
|
|
185
|
+
* gyro and split, the overlay redraws — so this object is just the shared source
|
|
186
|
+
* of truth they all read and write.
|
|
187
|
+
*/
|
|
188
|
+
declare class ControlSettings {
|
|
189
|
+
private flags;
|
|
190
|
+
private readonly subscribers;
|
|
191
|
+
constructor(initial?: Partial<ControlFlags>);
|
|
192
|
+
/** The current flags (a copy — mutate through `setMode` / `setFlag`). */
|
|
193
|
+
get(): ControlFlags;
|
|
194
|
+
/** The mode the current flags represent (`custom` when hand-tuned). */
|
|
195
|
+
getMode(): ControlMode;
|
|
196
|
+
/** Switch to a mode's preset (keeps the look-invert axes). No-op if unchanged. */
|
|
197
|
+
setMode(mode: Exclude<ControlMode, "custom">): void;
|
|
198
|
+
/** Set one flag. Flipping a mode-defining flag can drop you into `custom`. */
|
|
199
|
+
setFlag<K extends keyof ControlFlags>(key: K, value: ControlFlags[K]): void;
|
|
200
|
+
/** Subscribe to changes; returns an unsubscribe. Fires on every real change. */
|
|
201
|
+
subscribe(fn: (flags: ControlFlags) => void): () => void;
|
|
202
|
+
private replace;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
96
205
|
//#region src/plugin/CapabilityRegistry.d.ts
|
|
97
206
|
/**
|
|
98
207
|
* Shared runtime contracts between plugins, keyed by name. A plugin (or the
|
|
@@ -182,30 +291,6 @@ interface EnginePlugin<T = unknown> {
|
|
|
182
291
|
controller?(elements: () => ReadonlyMap<string, Object3D>): ElementController;
|
|
183
292
|
}
|
|
184
293
|
//#endregion
|
|
185
|
-
//#region src/input/InputBus.d.ts
|
|
186
|
-
/**
|
|
187
|
-
* The shared analog-input channel: the current movement and look intent, each
|
|
188
|
-
* axis in `-1..1` and held until a producer changes it. Producers (on-screen
|
|
189
|
-
* joysticks in the overlay, a gamepad…) write to it; whatever camera system is
|
|
190
|
-
* active reads it and interprets it its own way — free-roam walks with it, a
|
|
191
|
-
* fixed camera might only use the look axes to glance around. This is what makes
|
|
192
|
-
* the joysticks reusable across camera types. Pure state — no three.js.
|
|
193
|
-
*/
|
|
194
|
-
declare class InputBus {
|
|
195
|
-
/** Forward (`+1`) / backward (`-1`). */
|
|
196
|
-
moveForward: number;
|
|
197
|
-
/** Strafe right (`+1`) / left (`-1`). */
|
|
198
|
-
moveStrafe: number;
|
|
199
|
-
/** Look right (`+1`) / left (`-1`). */
|
|
200
|
-
lookX: number;
|
|
201
|
-
/** Look up (`+1`) / down (`-1`). */
|
|
202
|
-
lookY: number;
|
|
203
|
-
/** Set the movement intent (forward, strafe). */
|
|
204
|
-
setMove(forward: number, strafe?: number): void;
|
|
205
|
-
/** Set the look intent (turn, pitch). */
|
|
206
|
-
setLook(x: number, y?: number): void;
|
|
207
|
-
}
|
|
208
|
-
//#endregion
|
|
209
294
|
//#region src/plugin/systemPlugin.d.ts
|
|
210
295
|
/**
|
|
211
296
|
* What the kernel hands a system on setup. A *system* plugin — unlike an element
|
|
@@ -227,6 +312,8 @@ interface SystemContext {
|
|
|
227
312
|
renderer: WebGLRenderer;
|
|
228
313
|
/** Shared analog input (joysticks feed it); systems interpret it. */
|
|
229
314
|
input: InputBus;
|
|
315
|
+
/** The visitor's current control flags (e.g. look-invert axes), read live. */
|
|
316
|
+
controlFlags: () => ControlFlags;
|
|
230
317
|
/** Capability registry for cross-plugin contracts (terrain, colliders…). */
|
|
231
318
|
capabilities: CapabilityRegistry;
|
|
232
319
|
/** Emit a gallery event. */
|
|
@@ -413,6 +500,25 @@ interface ControlsApi {
|
|
|
413
500
|
/** Turn right (`+1`) / left (`-1`); optional look up (`+1`) / down (`-1`). */
|
|
414
501
|
setLookInput(x: number, y?: number): void;
|
|
415
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* The visitor's camera-control preferences (view mode + capability flags). The
|
|
505
|
+
* engine applies the effects that touch rendering/input — gyroscope head-look and
|
|
506
|
+
* the stereo split — as the flags change; the overlay drives it from the mode
|
|
507
|
+
* picker and settings, and reads it to show the right joysticks. See
|
|
508
|
+
* `ControlSettings`.
|
|
509
|
+
*/
|
|
510
|
+
interface ViewControlsApi {
|
|
511
|
+
/** Current capability flags (a copy). */
|
|
512
|
+
get(): ControlFlags;
|
|
513
|
+
/** The mode the flags represent (`custom` when hand-tuned in settings). */
|
|
514
|
+
getMode(): ControlMode;
|
|
515
|
+
/** Switch to a mode's preset (keeps the look-invert axes). */
|
|
516
|
+
setMode(mode: Exclude<ControlMode, "custom">): void;
|
|
517
|
+
/** Set one capability flag (may drop the mode into `custom`). */
|
|
518
|
+
setFlag<K extends keyof ControlFlags>(key: K, value: ControlFlags[K]): void;
|
|
519
|
+
/** Subscribe to changes; returns an unsubscribe. */
|
|
520
|
+
subscribe(fn: (flags: ControlFlags) => void): () => void;
|
|
521
|
+
}
|
|
416
522
|
/** Control the HDRI sky / environment at runtime, for behavior code. */
|
|
417
523
|
interface EnvironmentApi {
|
|
418
524
|
/** Set the visible sky background brightness (`0` = black). */
|
|
@@ -431,6 +537,8 @@ interface GalleryContext {
|
|
|
431
537
|
readonly media: MediaController;
|
|
432
538
|
/** Drive the camera externally (on-screen touch controls, etc.). */
|
|
433
539
|
readonly controls: ControlsApi;
|
|
540
|
+
/** The visitor's camera-control preferences (view mode + capability flags). */
|
|
541
|
+
readonly viewControls: ViewControlsApi;
|
|
434
542
|
/** Control the HDRI sky / environment at runtime. */
|
|
435
543
|
readonly environment: EnvironmentApi;
|
|
436
544
|
/** Drive the procedural sky's time of day. */
|
|
@@ -559,5 +667,5 @@ declare class InteractionController {
|
|
|
559
667
|
private buildHighlight;
|
|
560
668
|
}
|
|
561
669
|
//#endregion
|
|
562
|
-
export { type Antialias, type BuiltElement, COLLIDERS, type CameraApi, CapabilityRegistry, type Collider, type ControlsApi, EDGES, EYE_HEIGHT, type EdgeAnchor, type EdgeIndex, type ElementController, type EngineContext, type EnginePlugin, type EnvironmentApi, type FocusOptions, type Gallery, type GalleryContext, type GalleryOptions, InputBus, InteractionController, type LightSet, type LightSetOptions, type LightsApi, type MediaController, ROAM_CONTROL, type RenderOptions, type RoamControl, SceneManager, type SkyApi, type SystemContext, type SystemInstance, type SystemPlugin, TERRAIN, type TerrainCapability, type TerrainDefinition, type VideoApi, applyTexture, createGallery, isSystemPlugin };
|
|
670
|
+
export { type Antialias, type BuiltElement, COLLIDERS, type CameraApi, CapabilityRegistry, type Collider, type ControlFlags, type ControlMode, ControlSettings, type ControlsApi, DEFAULT_CONTROL_FLAGS, EDGES, EYE_HEIGHT, type EdgeAnchor, type EdgeIndex, type ElementController, type EngineContext, type EnginePlugin, type EnvironmentApi, type FocusOptions, type Gallery, type GalleryContext, type GalleryOptions, InputBus, InteractionController, type LightSet, type LightSetOptions, type LightsApi, MODE_PRESETS, type MediaController, ROAM_CONTROL, type RenderOptions, type RoamControl, SceneManager, type SkyApi, type SystemContext, type SystemInstance, type SystemPlugin, TERRAIN, type TerrainCapability, type TerrainDefinition, type VideoApi, applyMode, applyTexture, createGallery, isSystemPlugin, modeOf };
|
|
563
671
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,13 @@ interface FocusOptions {
|
|
|
10
10
|
widthFraction?: number;
|
|
11
11
|
/** Tween duration in ms. Default `900`. */
|
|
12
12
|
duration?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Minimum camera distance from the piece, in world units. When the framing that
|
|
15
|
+
* fits the piece would sit closer, the camera pulls back to this instead (the
|
|
16
|
+
* piece stays centered, just smaller). Default `0` (no minimum). Useful on close
|
|
17
|
+
* so a small piece doesn't leave the visitor glued to the wall.
|
|
18
|
+
*/
|
|
19
|
+
minDistance?: number;
|
|
13
20
|
}
|
|
14
21
|
/**
|
|
15
22
|
* Public camera-framing API exposed on the gallery context as `ctx.camera`.
|
|
@@ -23,8 +30,19 @@ interface CameraApi {
|
|
|
23
30
|
centerArtwork(id: string, options?: FocusOptions): void;
|
|
24
31
|
/** Drop any active framing watch and hand control fully back to the user. */
|
|
25
32
|
release(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Animate the camera back to the scene's start position and orientation — a
|
|
35
|
+
* "home" for visitors who lose their way and want to return to the entrance.
|
|
36
|
+
*/
|
|
37
|
+
reset(): void;
|
|
26
38
|
/** Whether a framing tween is in progress (e.g. to suppress click-to-select). */
|
|
27
39
|
isAnimating(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a piece is framed and still selected (the framing watch is active).
|
|
42
|
+
* Usually paired with {@link isAnimating} to gate picking: while a piece is
|
|
43
|
+
* selected, hovering must not re-highlight it (its info panel is open).
|
|
44
|
+
*/
|
|
45
|
+
isFocused(): boolean;
|
|
28
46
|
}
|
|
29
47
|
//#endregion
|
|
30
48
|
//#region src/api/index.d.ts
|
|
@@ -93,6 +111,97 @@ interface SkyApi {
|
|
|
93
111
|
getAzimuth(): number;
|
|
94
112
|
}
|
|
95
113
|
//#endregion
|
|
114
|
+
//#region src/input/InputBus.d.ts
|
|
115
|
+
/**
|
|
116
|
+
* The shared analog-input channel: the current movement and look intent, each
|
|
117
|
+
* axis in `-1..1` and held until a producer changes it. Producers (on-screen
|
|
118
|
+
* joysticks in the overlay, a gamepad…) write to it; whatever camera system is
|
|
119
|
+
* active reads it and interprets it its own way — free-roam walks with it, a
|
|
120
|
+
* fixed camera might only use the look axes to glance around. This is what makes
|
|
121
|
+
* the joysticks reusable across camera types. Pure state — no three.js.
|
|
122
|
+
*/
|
|
123
|
+
declare class InputBus {
|
|
124
|
+
/** Forward (`+1`) / backward (`-1`). */
|
|
125
|
+
moveForward: number;
|
|
126
|
+
/** Strafe right (`+1`) / left (`-1`). */
|
|
127
|
+
moveStrafe: number;
|
|
128
|
+
/** Look right (`+1`) / left (`-1`). */
|
|
129
|
+
lookX: number;
|
|
130
|
+
/** Look up (`+1`) / down (`-1`). */
|
|
131
|
+
lookY: number;
|
|
132
|
+
/** Set the movement intent (forward, strafe). */
|
|
133
|
+
setMove(forward: number, strafe?: number): void;
|
|
134
|
+
/** Set the look intent (turn, pitch). */
|
|
135
|
+
setLook(x: number, y?: number): void;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/input/controlSettings.d.ts
|
|
139
|
+
/**
|
|
140
|
+
* The visitor's camera-control preferences: a set of orthogonal capability flags
|
|
141
|
+
* plus a derived, friendly "mode". Pure state — no three.js, no DOM. The engine
|
|
142
|
+
* wires the flags to effects (gyroscope head-look, stereo split); consumers read
|
|
143
|
+
* them (the analog look-invert axes); the overlay drives it from a mode picker
|
|
144
|
+
* (the `ScanEye` button) and the settings panel. Persistence and device
|
|
145
|
+
* feature-detection live in those callers, not here, so this stays trivially
|
|
146
|
+
* unit-testable.
|
|
147
|
+
*/
|
|
148
|
+
/**
|
|
149
|
+
* Orthogonal control capabilities. `gyro` / `splitScreen` / `joysticks` are what a
|
|
150
|
+
* mode fixes; the two `invertLook*` axes are independent look-feel preferences
|
|
151
|
+
* (some people feel the right stick inverted), kept separate from the mode.
|
|
152
|
+
*/
|
|
153
|
+
interface ControlFlags {
|
|
154
|
+
/** Orient the camera from the device gyroscope (head-look). */
|
|
155
|
+
gyro: boolean;
|
|
156
|
+
/** Split the view into a stereo left/right eye pair (phone-in-a-headset). */
|
|
157
|
+
splitScreen: boolean;
|
|
158
|
+
/** Show the on-screen virtual joysticks. */
|
|
159
|
+
joysticks: boolean;
|
|
160
|
+
/** Invert the horizontal (turn) axis of analog look. */
|
|
161
|
+
invertLookX: boolean;
|
|
162
|
+
/** Invert the vertical (pitch) axis of analog look. */
|
|
163
|
+
invertLookY: boolean;
|
|
164
|
+
}
|
|
165
|
+
/** A named preset over the mode-defining flags; `custom` when none matches. */
|
|
166
|
+
type ControlMode = "touch" | "vr" | "cardboard" | "custom";
|
|
167
|
+
/** The three flags a mode fixes (the look-invert axes stay a separate preference). */
|
|
168
|
+
type ModeFlags = Pick<ControlFlags, "gyro" | "splitScreen" | "joysticks">;
|
|
169
|
+
/**
|
|
170
|
+
* Mode presets. Touch = two joysticks, no gyro. VR = the gyro aims and a single
|
|
171
|
+
* move joystick walks you, no split. Cardboard = gyro aims, stereo split, gaze
|
|
172
|
+
* select (no joysticks). The move-only single stick in VR is derived downstream
|
|
173
|
+
* from `gyro && !splitScreen`, so it needs no flag of its own.
|
|
174
|
+
*/
|
|
175
|
+
declare const MODE_PRESETS: Record<Exclude<ControlMode, "custom">, ModeFlags>;
|
|
176
|
+
/** Default flags: plain touch, no look inversion. */
|
|
177
|
+
declare const DEFAULT_CONTROL_FLAGS: ControlFlags;
|
|
178
|
+
/** The mode a flag set represents, or `custom` when no preset matches. */
|
|
179
|
+
declare function modeOf(flags: ControlFlags): ControlMode;
|
|
180
|
+
/** Apply a mode's preset onto `flags`, leaving the look-invert axes untouched. */
|
|
181
|
+
declare function applyMode(flags: ControlFlags, mode: Exclude<ControlMode, "custom">): ControlFlags;
|
|
182
|
+
/**
|
|
183
|
+
* Live control-preferences state: holds the flags, derives the mode, and notifies
|
|
184
|
+
* subscribers on every real change. Callers apply the effects — the engine wires
|
|
185
|
+
* gyro and split, the overlay redraws — so this object is just the shared source
|
|
186
|
+
* of truth they all read and write.
|
|
187
|
+
*/
|
|
188
|
+
declare class ControlSettings {
|
|
189
|
+
private flags;
|
|
190
|
+
private readonly subscribers;
|
|
191
|
+
constructor(initial?: Partial<ControlFlags>);
|
|
192
|
+
/** The current flags (a copy — mutate through `setMode` / `setFlag`). */
|
|
193
|
+
get(): ControlFlags;
|
|
194
|
+
/** The mode the current flags represent (`custom` when hand-tuned). */
|
|
195
|
+
getMode(): ControlMode;
|
|
196
|
+
/** Switch to a mode's preset (keeps the look-invert axes). No-op if unchanged. */
|
|
197
|
+
setMode(mode: Exclude<ControlMode, "custom">): void;
|
|
198
|
+
/** Set one flag. Flipping a mode-defining flag can drop you into `custom`. */
|
|
199
|
+
setFlag<K extends keyof ControlFlags>(key: K, value: ControlFlags[K]): void;
|
|
200
|
+
/** Subscribe to changes; returns an unsubscribe. Fires on every real change. */
|
|
201
|
+
subscribe(fn: (flags: ControlFlags) => void): () => void;
|
|
202
|
+
private replace;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
96
205
|
//#region src/plugin/CapabilityRegistry.d.ts
|
|
97
206
|
/**
|
|
98
207
|
* Shared runtime contracts between plugins, keyed by name. A plugin (or the
|
|
@@ -182,30 +291,6 @@ interface EnginePlugin<T = unknown> {
|
|
|
182
291
|
controller?(elements: () => ReadonlyMap<string, Object3D>): ElementController;
|
|
183
292
|
}
|
|
184
293
|
//#endregion
|
|
185
|
-
//#region src/input/InputBus.d.ts
|
|
186
|
-
/**
|
|
187
|
-
* The shared analog-input channel: the current movement and look intent, each
|
|
188
|
-
* axis in `-1..1` and held until a producer changes it. Producers (on-screen
|
|
189
|
-
* joysticks in the overlay, a gamepad…) write to it; whatever camera system is
|
|
190
|
-
* active reads it and interprets it its own way — free-roam walks with it, a
|
|
191
|
-
* fixed camera might only use the look axes to glance around. This is what makes
|
|
192
|
-
* the joysticks reusable across camera types. Pure state — no three.js.
|
|
193
|
-
*/
|
|
194
|
-
declare class InputBus {
|
|
195
|
-
/** Forward (`+1`) / backward (`-1`). */
|
|
196
|
-
moveForward: number;
|
|
197
|
-
/** Strafe right (`+1`) / left (`-1`). */
|
|
198
|
-
moveStrafe: number;
|
|
199
|
-
/** Look right (`+1`) / left (`-1`). */
|
|
200
|
-
lookX: number;
|
|
201
|
-
/** Look up (`+1`) / down (`-1`). */
|
|
202
|
-
lookY: number;
|
|
203
|
-
/** Set the movement intent (forward, strafe). */
|
|
204
|
-
setMove(forward: number, strafe?: number): void;
|
|
205
|
-
/** Set the look intent (turn, pitch). */
|
|
206
|
-
setLook(x: number, y?: number): void;
|
|
207
|
-
}
|
|
208
|
-
//#endregion
|
|
209
294
|
//#region src/plugin/systemPlugin.d.ts
|
|
210
295
|
/**
|
|
211
296
|
* What the kernel hands a system on setup. A *system* plugin — unlike an element
|
|
@@ -227,6 +312,8 @@ interface SystemContext {
|
|
|
227
312
|
renderer: WebGLRenderer;
|
|
228
313
|
/** Shared analog input (joysticks feed it); systems interpret it. */
|
|
229
314
|
input: InputBus;
|
|
315
|
+
/** The visitor's current control flags (e.g. look-invert axes), read live. */
|
|
316
|
+
controlFlags: () => ControlFlags;
|
|
230
317
|
/** Capability registry for cross-plugin contracts (terrain, colliders…). */
|
|
231
318
|
capabilities: CapabilityRegistry;
|
|
232
319
|
/** Emit a gallery event. */
|
|
@@ -413,6 +500,25 @@ interface ControlsApi {
|
|
|
413
500
|
/** Turn right (`+1`) / left (`-1`); optional look up (`+1`) / down (`-1`). */
|
|
414
501
|
setLookInput(x: number, y?: number): void;
|
|
415
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* The visitor's camera-control preferences (view mode + capability flags). The
|
|
505
|
+
* engine applies the effects that touch rendering/input — gyroscope head-look and
|
|
506
|
+
* the stereo split — as the flags change; the overlay drives it from the mode
|
|
507
|
+
* picker and settings, and reads it to show the right joysticks. See
|
|
508
|
+
* `ControlSettings`.
|
|
509
|
+
*/
|
|
510
|
+
interface ViewControlsApi {
|
|
511
|
+
/** Current capability flags (a copy). */
|
|
512
|
+
get(): ControlFlags;
|
|
513
|
+
/** The mode the flags represent (`custom` when hand-tuned in settings). */
|
|
514
|
+
getMode(): ControlMode;
|
|
515
|
+
/** Switch to a mode's preset (keeps the look-invert axes). */
|
|
516
|
+
setMode(mode: Exclude<ControlMode, "custom">): void;
|
|
517
|
+
/** Set one capability flag (may drop the mode into `custom`). */
|
|
518
|
+
setFlag<K extends keyof ControlFlags>(key: K, value: ControlFlags[K]): void;
|
|
519
|
+
/** Subscribe to changes; returns an unsubscribe. */
|
|
520
|
+
subscribe(fn: (flags: ControlFlags) => void): () => void;
|
|
521
|
+
}
|
|
416
522
|
/** Control the HDRI sky / environment at runtime, for behavior code. */
|
|
417
523
|
interface EnvironmentApi {
|
|
418
524
|
/** Set the visible sky background brightness (`0` = black). */
|
|
@@ -431,6 +537,8 @@ interface GalleryContext {
|
|
|
431
537
|
readonly media: MediaController;
|
|
432
538
|
/** Drive the camera externally (on-screen touch controls, etc.). */
|
|
433
539
|
readonly controls: ControlsApi;
|
|
540
|
+
/** The visitor's camera-control preferences (view mode + capability flags). */
|
|
541
|
+
readonly viewControls: ViewControlsApi;
|
|
434
542
|
/** Control the HDRI sky / environment at runtime. */
|
|
435
543
|
readonly environment: EnvironmentApi;
|
|
436
544
|
/** Drive the procedural sky's time of day. */
|
|
@@ -559,5 +667,5 @@ declare class InteractionController {
|
|
|
559
667
|
private buildHighlight;
|
|
560
668
|
}
|
|
561
669
|
//#endregion
|
|
562
|
-
export { type Antialias, type BuiltElement, COLLIDERS, type CameraApi, CapabilityRegistry, type Collider, type ControlsApi, EDGES, EYE_HEIGHT, type EdgeAnchor, type EdgeIndex, type ElementController, type EngineContext, type EnginePlugin, type EnvironmentApi, type FocusOptions, type Gallery, type GalleryContext, type GalleryOptions, InputBus, InteractionController, type LightSet, type LightSetOptions, type LightsApi, type MediaController, ROAM_CONTROL, type RenderOptions, type RoamControl, SceneManager, type SkyApi, type SystemContext, type SystemInstance, type SystemPlugin, TERRAIN, type TerrainCapability, type TerrainDefinition, type VideoApi, applyTexture, createGallery, isSystemPlugin };
|
|
670
|
+
export { type Antialias, type BuiltElement, COLLIDERS, type CameraApi, CapabilityRegistry, type Collider, type ControlFlags, type ControlMode, ControlSettings, type ControlsApi, DEFAULT_CONTROL_FLAGS, EDGES, EYE_HEIGHT, type EdgeAnchor, type EdgeIndex, type ElementController, type EngineContext, type EnginePlugin, type EnvironmentApi, type FocusOptions, type Gallery, type GalleryContext, type GalleryOptions, InputBus, InteractionController, type LightSet, type LightSetOptions, type LightsApi, MODE_PRESETS, type MediaController, ROAM_CONTROL, type RenderOptions, type RoamControl, SceneManager, type SkyApi, type SystemContext, type SystemInstance, type SystemPlugin, TERRAIN, type TerrainCapability, type TerrainDefinition, type VideoApi, applyMode, applyTexture, createGallery, isSystemPlugin, modeOf };
|
|
563
671
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -272,6 +272,139 @@ var InputBus = class {
|
|
|
272
272
|
}
|
|
273
273
|
};
|
|
274
274
|
//#endregion
|
|
275
|
+
//#region src/input/controlSettings.ts
|
|
276
|
+
/**
|
|
277
|
+
* Mode presets. Touch = two joysticks, no gyro. VR = the gyro aims and a single
|
|
278
|
+
* move joystick walks you, no split. Cardboard = gyro aims, stereo split, gaze
|
|
279
|
+
* select (no joysticks). The move-only single stick in VR is derived downstream
|
|
280
|
+
* from `gyro && !splitScreen`, so it needs no flag of its own.
|
|
281
|
+
*/
|
|
282
|
+
const MODE_PRESETS = {
|
|
283
|
+
touch: {
|
|
284
|
+
gyro: false,
|
|
285
|
+
splitScreen: false,
|
|
286
|
+
joysticks: true
|
|
287
|
+
},
|
|
288
|
+
vr: {
|
|
289
|
+
gyro: true,
|
|
290
|
+
splitScreen: false,
|
|
291
|
+
joysticks: true
|
|
292
|
+
},
|
|
293
|
+
cardboard: {
|
|
294
|
+
gyro: true,
|
|
295
|
+
splitScreen: true,
|
|
296
|
+
joysticks: false
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
/** Default flags: plain touch, no look inversion. */
|
|
300
|
+
const DEFAULT_CONTROL_FLAGS = {
|
|
301
|
+
...MODE_PRESETS.touch,
|
|
302
|
+
invertLookX: false,
|
|
303
|
+
invertLookY: false
|
|
304
|
+
};
|
|
305
|
+
const FLAG_KEYS$1 = [
|
|
306
|
+
"gyro",
|
|
307
|
+
"splitScreen",
|
|
308
|
+
"joysticks",
|
|
309
|
+
"invertLookX",
|
|
310
|
+
"invertLookY"
|
|
311
|
+
];
|
|
312
|
+
const equalFlags = (a, b) => FLAG_KEYS$1.every((key) => a[key] === b[key]);
|
|
313
|
+
/** The mode a flag set represents, or `custom` when no preset matches. */
|
|
314
|
+
function modeOf(flags) {
|
|
315
|
+
for (const mode of [
|
|
316
|
+
"touch",
|
|
317
|
+
"vr",
|
|
318
|
+
"cardboard"
|
|
319
|
+
]) {
|
|
320
|
+
const preset = MODE_PRESETS[mode];
|
|
321
|
+
if (flags.gyro === preset.gyro && flags.splitScreen === preset.splitScreen && flags.joysticks === preset.joysticks) return mode;
|
|
322
|
+
}
|
|
323
|
+
return "custom";
|
|
324
|
+
}
|
|
325
|
+
/** Apply a mode's preset onto `flags`, leaving the look-invert axes untouched. */
|
|
326
|
+
function applyMode(flags, mode) {
|
|
327
|
+
return {
|
|
328
|
+
...flags,
|
|
329
|
+
...MODE_PRESETS[mode]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Live control-preferences state: holds the flags, derives the mode, and notifies
|
|
334
|
+
* subscribers on every real change. Callers apply the effects — the engine wires
|
|
335
|
+
* gyro and split, the overlay redraws — so this object is just the shared source
|
|
336
|
+
* of truth they all read and write.
|
|
337
|
+
*/
|
|
338
|
+
var ControlSettings = class {
|
|
339
|
+
flags;
|
|
340
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
341
|
+
constructor(initial = {}) {
|
|
342
|
+
this.flags = {
|
|
343
|
+
...DEFAULT_CONTROL_FLAGS,
|
|
344
|
+
...initial
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/** The current flags (a copy — mutate through `setMode` / `setFlag`). */
|
|
348
|
+
get() {
|
|
349
|
+
return { ...this.flags };
|
|
350
|
+
}
|
|
351
|
+
/** The mode the current flags represent (`custom` when hand-tuned). */
|
|
352
|
+
getMode() {
|
|
353
|
+
return modeOf(this.flags);
|
|
354
|
+
}
|
|
355
|
+
/** Switch to a mode's preset (keeps the look-invert axes). No-op if unchanged. */
|
|
356
|
+
setMode(mode) {
|
|
357
|
+
this.replace(applyMode(this.flags, mode));
|
|
358
|
+
}
|
|
359
|
+
/** Set one flag. Flipping a mode-defining flag can drop you into `custom`. */
|
|
360
|
+
setFlag(key, value) {
|
|
361
|
+
this.replace({
|
|
362
|
+
...this.flags,
|
|
363
|
+
[key]: value
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
/** Subscribe to changes; returns an unsubscribe. Fires on every real change. */
|
|
367
|
+
subscribe(fn) {
|
|
368
|
+
this.subscribers.add(fn);
|
|
369
|
+
return () => this.subscribers.delete(fn);
|
|
370
|
+
}
|
|
371
|
+
replace(next) {
|
|
372
|
+
if (equalFlags(this.flags, next)) return;
|
|
373
|
+
this.flags = next;
|
|
374
|
+
const snapshot = this.get();
|
|
375
|
+
for (const fn of this.subscribers) fn(snapshot);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/input/controlStorage.ts
|
|
380
|
+
/**
|
|
381
|
+
* localStorage persistence for the control flags, kept out of the pure
|
|
382
|
+
* `ControlSettings` so that stays DOM-free and unit-testable. Both functions are
|
|
383
|
+
* best-effort and guarded: no `localStorage` (SSR, privacy mode) or malformed
|
|
384
|
+
* data just falls back to defaults, never throws.
|
|
385
|
+
*/
|
|
386
|
+
const STORAGE_KEY = "scenography:controls";
|
|
387
|
+
const FLAG_KEYS = Object.keys(DEFAULT_CONTROL_FLAGS);
|
|
388
|
+
/** Read persisted control flags; `null` when absent, unavailable or malformed. */
|
|
389
|
+
function loadControlFlags() {
|
|
390
|
+
try {
|
|
391
|
+
const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
392
|
+
if (!raw) return null;
|
|
393
|
+
const parsed = JSON.parse(raw);
|
|
394
|
+
const flags = {};
|
|
395
|
+
for (const key of FLAG_KEYS) if (typeof parsed[key] === "boolean") flags[key] = parsed[key];
|
|
396
|
+
return flags;
|
|
397
|
+
} catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/** Persist control flags (best-effort; ignores a full/blocked store). */
|
|
402
|
+
function saveControlFlags(flags) {
|
|
403
|
+
try {
|
|
404
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(flags));
|
|
405
|
+
} catch {}
|
|
406
|
+
}
|
|
407
|
+
//#endregion
|
|
275
408
|
//#region src/plugin/CapabilityRegistry.ts
|
|
276
409
|
/**
|
|
277
410
|
* Shared runtime contracts between plugins, keyed by name. A plugin (or the
|
|
@@ -523,6 +656,7 @@ function createGallery(scene, options) {
|
|
|
523
656
|
const manager = new SceneManager(threeScene, elementPlugins, capabilities);
|
|
524
657
|
manager.reconcile(scene);
|
|
525
658
|
const input = new InputBus();
|
|
659
|
+
const controlSettings = new ControlSettings(loadControlFlags() ?? (options.cardboard ? MODE_PRESETS.cardboard : {}));
|
|
526
660
|
const environment = new EnvironmentController(threeScene);
|
|
527
661
|
environment.reconcile(scene.environment);
|
|
528
662
|
const listeners = /* @__PURE__ */ new Map();
|
|
@@ -546,6 +680,7 @@ function createGallery(scene, options) {
|
|
|
546
680
|
scene3d: threeScene,
|
|
547
681
|
renderer,
|
|
548
682
|
input,
|
|
683
|
+
controlFlags: () => controlSettings.get(),
|
|
549
684
|
capabilities,
|
|
550
685
|
emit,
|
|
551
686
|
resolve: (id) => manager.get(id)
|
|
@@ -557,10 +692,25 @@ function createGallery(scene, options) {
|
|
|
557
692
|
focusArtwork: () => {},
|
|
558
693
|
centerArtwork: () => {},
|
|
559
694
|
release: () => {},
|
|
560
|
-
|
|
695
|
+
reset: () => {},
|
|
696
|
+
isAnimating: () => false,
|
|
697
|
+
isFocused: () => false
|
|
561
698
|
};
|
|
562
699
|
const headTracking = apis.get(CARDBOARD_SYSTEM);
|
|
563
|
-
|
|
700
|
+
const initialFlags = controlSettings.get();
|
|
701
|
+
render.cardboard = initialFlags.splitScreen;
|
|
702
|
+
if (initialFlags.gyro) headTracking?.setEnabled(true);
|
|
703
|
+
let prevFlags = controlSettings.get();
|
|
704
|
+
controlSettings.subscribe((flags) => {
|
|
705
|
+
if (flags.gyro !== prevFlags.gyro) headTracking?.setEnabled(flags.gyro);
|
|
706
|
+
if (flags.splitScreen !== prevFlags.splitScreen) render.cardboard = flags.splitScreen;
|
|
707
|
+
prevFlags = flags;
|
|
708
|
+
saveControlFlags(flags);
|
|
709
|
+
emit("controls:change", {
|
|
710
|
+
flags,
|
|
711
|
+
mode: controlSettings.getMode()
|
|
712
|
+
});
|
|
713
|
+
});
|
|
564
714
|
const lightsApi = apis.get(CTX_SLOT.lights) ?? {
|
|
565
715
|
set: () => {},
|
|
566
716
|
dimAll: () => {},
|
|
@@ -590,7 +740,7 @@ function createGallery(scene, options) {
|
|
|
590
740
|
const gated = s.artworks ?? [];
|
|
591
741
|
return new Map(gated.filter((a) => a.infoRadius !== void 0).map((a) => [a.id, a.infoRadius]));
|
|
592
742
|
};
|
|
593
|
-
const interaction = new InteractionController(camera, canvas, threeScene, emit, () => !cameraApi.isAnimating());
|
|
743
|
+
const interaction = new InteractionController(camera, canvas, threeScene, emit, () => !cameraApi.isAnimating() && !cameraApi.isFocused());
|
|
594
744
|
interaction.setRadii(radiiOf(scene));
|
|
595
745
|
interaction.attach();
|
|
596
746
|
const clock = new Clock();
|
|
@@ -705,6 +855,13 @@ function createGallery(scene, options) {
|
|
|
705
855
|
setMoveInput: (forward, strafe) => input.setMove(forward, strafe),
|
|
706
856
|
setLookInput: (x, y) => input.setLook(x, y)
|
|
707
857
|
},
|
|
858
|
+
viewControls: {
|
|
859
|
+
get: () => controlSettings.get(),
|
|
860
|
+
getMode: () => controlSettings.getMode(),
|
|
861
|
+
setMode: (mode) => controlSettings.setMode(mode),
|
|
862
|
+
setFlag: (key, value) => controlSettings.setFlag(key, value),
|
|
863
|
+
subscribe: (fn) => controlSettings.subscribe(fn)
|
|
864
|
+
},
|
|
708
865
|
environment: { setBackgroundIntensity: (v) => environment.setBackgroundIntensity(v) },
|
|
709
866
|
sky: skyApi,
|
|
710
867
|
camera: cameraApi,
|
|
@@ -780,10 +937,7 @@ function createGallery(scene, options) {
|
|
|
780
937
|
if (next.bloom !== void 0) render.bloom = next.bloom;
|
|
781
938
|
const aaChanged = next.antialias !== void 0 && next.antialias !== render.antialias;
|
|
782
939
|
if (next.antialias !== void 0) render.antialias = next.antialias;
|
|
783
|
-
if (next.cardboard !== void 0 && next.cardboard !== render.cardboard)
|
|
784
|
-
render.cardboard = next.cardboard;
|
|
785
|
-
headTracking?.setEnabled(next.cardboard);
|
|
786
|
-
}
|
|
940
|
+
if (next.cardboard !== void 0 && next.cardboard !== render.cardboard) controlSettings.setMode(next.cardboard ? "cardboard" : "touch");
|
|
787
941
|
if (next.eyeSeparation !== void 0) {
|
|
788
942
|
render.eyeSeparation = next.eyeSeparation;
|
|
789
943
|
stereo.eyeSep = next.eyeSeparation;
|
|
@@ -843,6 +997,6 @@ const applyTexture = (material, texture) => {
|
|
|
843
997
|
return texture.ao !== void 0;
|
|
844
998
|
};
|
|
845
999
|
//#endregion
|
|
846
|
-
export { COLLIDERS, CapabilityRegistry, EDGES, EYE_HEIGHT, InputBus, InteractionController, ROAM_CONTROL, SceneManager, TERRAIN, applyTexture, createGallery, isSystemPlugin };
|
|
1000
|
+
export { COLLIDERS, CapabilityRegistry, ControlSettings, DEFAULT_CONTROL_FLAGS, EDGES, EYE_HEIGHT, InputBus, InteractionController, MODE_PRESETS, ROAM_CONTROL, SceneManager, TERRAIN, applyMode, applyTexture, createGallery, isSystemPlugin, modeOf };
|
|
847
1001
|
|
|
848
1002
|
//# sourceMappingURL=index.mjs.map
|