@jgengine/shell 0.10.0 → 0.11.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/CHANGELOG.md +13 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/GamePhaseStamp.d.ts +1 -0
- package/dist/GamePhaseStamp.js +9 -0
- package/dist/GamePlayerShell.d.ts +0 -12
- package/dist/GamePlayerShell.js +88 -1043
- package/dist/GameUiPreview.js +0 -2
- package/dist/audio/audioEngine.js +30 -8
- package/dist/audio/audioWire.d.ts +22 -0
- package/dist/audio/audioWire.js +19 -0
- package/dist/behaviour.d.ts +2 -1
- package/dist/behaviour.js +2 -1
- package/dist/behaviourAttach.d.ts +1 -0
- package/dist/behaviourAttach.js +1 -0
- package/dist/behaviourDriver.d.ts +1 -0
- package/dist/behaviourDriver.js +1 -0
- package/dist/boundActionDispatch.d.ts +13 -0
- package/dist/boundActionDispatch.js +37 -0
- package/dist/camera/GameCameraRig.d.ts +5 -2
- package/dist/camera/GameCameraRig.js +2 -2
- package/dist/camera/GameFirstPersonCamera.d.ts +9 -2
- package/dist/camera/GameFirstPersonCamera.js +8 -3
- package/dist/camera/GameInspectionCamera.d.ts +1 -1
- package/dist/camera/GameInspectionCamera.js +2 -2
- package/dist/camera/GameOrbitCamera.js +24 -9
- package/dist/camera/cameraBlendMath.d.ts +3 -0
- package/dist/camera/cameraBlendMath.js +3 -0
- package/dist/camera/fovPreference.d.ts +6 -1
- package/dist/camera/fovPreference.js +6 -1
- package/dist/camera/index.d.ts +4 -7
- package/dist/camera/index.js +4 -7
- package/dist/camera/inspectionCameraMath.d.ts +7 -2
- package/dist/camera/inspectionCameraMath.js +7 -2
- package/dist/camera/orbitCameraMath.d.ts +36 -6
- package/dist/camera/orbitCameraMath.js +56 -6
- package/dist/camera/rigMath.d.ts +94 -24
- package/dist/camera/rigMath.js +100 -24
- package/dist/camera/rigResolve.d.ts +4 -2
- package/dist/camera/rigResolve.js +4 -2
- package/dist/camera/shakeChannel.d.ts +6 -2
- package/dist/camera/shakeChannel.js +6 -2
- package/dist/camera/shakeChannelMath.d.ts +1 -0
- package/dist/camera/shakeChannelMath.js +1 -0
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/cartridge.d.ts +1 -1
- package/dist/cartridge.js +3 -3
- package/dist/commandSink.d.ts +8 -3
- package/dist/commandSink.js +8 -3
- package/dist/defineGame.js +25 -6
- package/dist/devtools/ColPanel.d.ts +1 -0
- package/dist/devtools/ColPanel.js +14 -0
- package/dist/devtools/DevtoolsOverlay.d.ts +7 -3
- package/dist/devtools/DevtoolsOverlay.js +19 -450
- package/dist/devtools/KeysPanel.d.ts +4 -0
- package/dist/devtools/KeysPanel.js +31 -0
- package/dist/devtools/LogsPanel.d.ts +1 -0
- package/dist/devtools/LogsPanel.js +12 -0
- package/dist/devtools/NetPanel.d.ts +4 -0
- package/dist/devtools/NetPanel.js +10 -0
- package/dist/devtools/PerfPanel.d.ts +4 -0
- package/dist/devtools/PerfPanel.js +53 -0
- package/dist/devtools/TunePanel.d.ts +3 -0
- package/dist/devtools/TunePanel.js +309 -0
- package/dist/devtools/agentBridge.d.ts +41 -0
- package/dist/devtools/agentBridge.js +262 -0
- package/dist/devtools/collisionDebug.d.ts +6 -0
- package/dist/devtools/collisionDebug.js +6 -0
- package/dist/devtools/collisionDebugMath.d.ts +8 -2
- package/dist/devtools/collisionDebugMath.js +24 -5
- package/dist/devtools/devtoolsOverrides.d.ts +6 -0
- package/dist/devtools/devtoolsOverrides.js +48 -0
- package/dist/devtools/panelAtoms.d.ts +9 -0
- package/dist/devtools/panelAtoms.js +10 -0
- package/dist/devtools/perfDiagnose.d.ts +2 -0
- package/dist/devtools/perfDiagnose.js +43 -0
- package/dist/diagnostics/RuntimeDiagnostics.d.ts +29 -0
- package/dist/diagnostics/RuntimeDiagnostics.js +69 -0
- package/dist/drivers/FrameDriver.d.ts +37 -0
- package/dist/drivers/FrameDriver.js +232 -0
- package/dist/drivers/HudOnlyDriver.d.ts +16 -0
- package/dist/drivers/HudOnlyDriver.js +60 -0
- package/dist/environment/Daylight.d.ts +23 -7
- package/dist/environment/Daylight.js +106 -13
- package/dist/environment/EnvironmentScene.js +32 -6
- package/dist/environment/VolumetricClouds.d.ts +17 -0
- package/dist/environment/VolumetricClouds.js +202 -0
- package/dist/environment/daylightCycle.d.ts +3 -1
- package/dist/environment/daylightCycle.js +3 -1
- package/dist/environment/groundPadMath.d.ts +3 -0
- package/dist/environment/groundPadMath.js +3 -0
- package/dist/environment/index.d.ts +1 -0
- package/dist/environment/index.js +1 -0
- package/dist/environment/skyLightingPolicy.d.ts +2 -0
- package/dist/environment/skyLightingPolicy.js +2 -0
- package/dist/hotbarActions.d.ts +26 -0
- package/dist/hotbarActions.js +27 -0
- package/dist/inputSink.d.ts +16 -4
- package/dist/inputSink.js +57 -7
- package/dist/materialOverride.d.ts +10 -0
- package/dist/materialOverride.js +15 -3
- package/dist/multiplayer.js +8 -2
- package/dist/postfx/PostProcessing.js +47 -9
- package/dist/postfx/gradeShader.js +9 -1
- package/dist/registry.d.ts +3 -2
- package/dist/render/EnvironmentLighting.d.ts +10 -0
- package/dist/render/EnvironmentLighting.js +34 -0
- package/dist/render/SceneLighting.d.ts +7 -0
- package/dist/render/SceneLighting.js +18 -0
- package/dist/render/SceneModels.d.ts +14 -0
- package/dist/render/SceneModels.js +337 -0
- package/dist/render/defaultSurface.d.ts +29 -0
- package/dist/render/defaultSurface.js +101 -0
- package/dist/render/modelLoad.d.ts +24 -0
- package/dist/render/modelLoad.js +73 -0
- package/dist/render/modelRender.d.ts +11 -0
- package/dist/render/modelRender.js +15 -0
- package/dist/render/resolveModel.d.ts +39 -3
- package/dist/render/resolveModel.js +79 -2
- package/dist/render/useEntityRenderCues.d.ts +13 -0
- package/dist/render/useEntityRenderCues.js +62 -0
- package/dist/scatter/InstancedScatter.d.ts +24 -0
- package/dist/scatter/InstancedScatter.js +130 -0
- package/dist/scatter/scatterModels.d.ts +25 -0
- package/dist/scatter/scatterModels.js +61 -0
- package/dist/scatter/scatterProxies.d.ts +6 -0
- package/dist/scatter/scatterProxies.js +94 -0
- package/dist/scatter.d.ts +1 -0
- package/dist/scatter.js +1 -0
- package/dist/scene/AuthoredScene.d.ts +82 -0
- package/dist/scene/AuthoredScene.js +173 -0
- package/dist/scene/GeneratedAssetRenderer.d.ts +14 -0
- package/dist/scene/GeneratedAssetRenderer.js +59 -0
- package/dist/scene/GrassFieldRenderer.d.ts +2 -0
- package/dist/scene/GrassFieldRenderer.js +25 -0
- package/dist/scene/SoilPatchRenderer.d.ts +2 -0
- package/dist/scene/SoilPatchRenderer.js +21 -0
- package/dist/scene/StudioStage.d.ts +37 -0
- package/dist/scene/StudioStage.js +42 -0
- package/dist/scene/WaterRenderer.d.ts +2 -0
- package/dist/scene/WaterRenderer.js +37 -0
- package/dist/scene/builtinSceneKindRenderers.d.ts +2 -0
- package/dist/scene/builtinSceneKindRenderers.js +22 -0
- package/dist/scene/sceneKindRenderers.d.ts +28 -0
- package/dist/scene/sceneKindRenderers.js +17 -0
- package/dist/scene.d.ts +1 -0
- package/dist/scene.js +1 -0
- package/dist/settings/appliedSettings.d.ts +5 -0
- package/dist/settings/appliedSettings.js +9 -0
- package/dist/settings/settingsController.d.ts +2 -0
- package/dist/settings/settingsController.js +22 -1
- package/dist/shellConstants.d.ts +4 -0
- package/dist/shellConstants.js +4 -0
- package/dist/shellMovement.d.ts +8 -0
- package/dist/shellMovement.js +11 -0
- package/dist/shellPointer.d.ts +20 -0
- package/dist/shellPointer.js +28 -0
- package/dist/structures/PlacementGhost.d.ts +4 -0
- package/dist/structures/PlacementGhost.js +4 -0
- package/dist/structures/TransformGizmo.d.ts +43 -0
- package/dist/structures/TransformGizmo.js +55 -0
- package/dist/structures/index.d.ts +1 -0
- package/dist/structures/index.js +1 -0
- package/dist/terrain/GrassField.js +1 -1
- package/dist/terrain/TerraformBrushCursor.js +3 -0
- package/dist/terrain/grassBudget.d.ts +11 -2
- package/dist/terrain/grassBudget.js +20 -4
- package/dist/terrain/grassGeometry.d.ts +3 -0
- package/dist/terrain/grassGeometry.js +3 -0
- package/dist/terrain/grassMaterial.d.ts +2 -0
- package/dist/terrain/grassMaterial.js +2 -0
- package/dist/terrain/random.d.ts +3 -0
- package/dist/terrain/random.js +3 -0
- package/dist/terrain/soilPatchMaterial.d.ts +10 -0
- package/dist/terrain/soilPatchMaterial.js +109 -0
- package/dist/terrain/terrainDetailMaterial.d.ts +19 -2
- package/dist/terrain/terrainDetailMaterial.js +72 -5
- package/dist/terrain/terrainMath.d.ts +8 -1
- package/dist/terrain/terrainMath.js +8 -1
- package/dist/touch/TouchControlsOverlay.d.ts +9 -5
- package/dist/touch/TouchControlsOverlay.js +201 -22
- package/dist/useShellMultiplayerSync.d.ts +9 -0
- package/dist/useShellMultiplayerSync.js +98 -0
- package/dist/visibility/CullingProvider.js +1 -1
- package/dist/water/OceanConfig.d.ts +2 -0
- package/dist/water/OceanConfig.js +2 -0
- package/dist/weather/fireSpreadPose.d.ts +1 -0
- package/dist/weather/fireSpreadPose.js +1 -0
- package/dist/weather/weatherGeometry.d.ts +1 -0
- package/dist/weather/weatherGeometry.js +1 -0
- package/dist/weather/weatherMath.d.ts +3 -0
- package/dist/weather/weatherMath.js +3 -0
- package/dist/weather/weatherUniforms.d.ts +3 -0
- package/dist/weather/weatherUniforms.js +3 -0
- package/dist/world/RetainedVfx.d.ts +10 -0
- package/dist/world/RetainedVfx.js +136 -0
- package/dist/world/WorldHud.d.ts +49 -2
- package/dist/world/WorldHud.js +87 -2
- package/dist/world/WorldScene.d.ts +21 -0
- package/dist/world/WorldScene.js +159 -0
- package/dist/world/WorldVfx.d.ts +9 -0
- package/dist/world/WorldVfx.js +184 -0
- package/dist/world/entityPose.d.ts +2 -0
- package/dist/world/entityPose.js +2 -0
- package/dist/world/floatTextStyle.d.ts +1 -0
- package/dist/world/floatTextStyle.js +1 -0
- package/dist/world/telegraphPulse.d.ts +1 -0
- package/dist/world/telegraphPulse.js +1 -0
- package/dist/world/worldBarSamples.d.ts +25 -0
- package/dist/world/worldBarSamples.js +38 -0
- package/dist/worldSky.d.ts +5 -0
- package/dist/worldSky.js +11 -0
- package/dist/worldSync.d.ts +2 -1
- package/dist/worldSync.js +2 -1
- package/package.json +6 -6
- package/skills/jgengine/SKILL.md +51 -0
- package/skills/jgengine/api.md +434 -0
- package/skills/jgengine/capabilities.md +29 -0
- package/skills/jgengine/reference-cartridge.md +72 -0
- package/skills/jgengine-ui/SKILL.md +36 -0
- package/skills/jgengine-ui/api.md +1852 -0
- package/skills/jgengine-ui/capabilities.md +142 -0
- package/skills/jgengine-ui/reference.md +500 -0
- package/skills/jgengine-ui/references/visual-scorecard.md +73 -0
- package/skills/jgengine-world/SKILL.md +38 -0
- package/skills/jgengine-world/api.md +2652 -0
- package/skills/jgengine-world/capabilities.md +370 -0
- package/skills/jgengine-world/reference.md +222 -0
- package/llms.txt +0 -2761
package/dist/GameUiPreview.js
CHANGED
|
@@ -37,7 +37,6 @@ export const defaultUiScenario = (ctx, playable) => {
|
|
|
37
37
|
export function GameUiPreview({ playable, scenario = defaultUiScenario, }) {
|
|
38
38
|
const [ctx, setCtx] = useState(null);
|
|
39
39
|
useEffect(() => {
|
|
40
|
-
playable.game.scene.clear();
|
|
41
40
|
const context = createGameContext({
|
|
42
41
|
definition: playable.game,
|
|
43
42
|
content: playable.content,
|
|
@@ -53,7 +52,6 @@ export function GameUiPreview({ playable, scenario = defaultUiScenario, }) {
|
|
|
53
52
|
}
|
|
54
53
|
setCtx(context);
|
|
55
54
|
return () => {
|
|
56
|
-
playable.game.scene.clear();
|
|
57
55
|
setCtx(null);
|
|
58
56
|
};
|
|
59
57
|
}, [playable, scenario]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { distance3, resolveEmitterGain } from "@jgengine/core/audio/audioFalloff";
|
|
2
|
+
import { createDisposer } from "@jgengine/core/game/defineGame";
|
|
2
3
|
import { MusicDirector } from "./musicDirector.js";
|
|
3
4
|
import { createNoiseBuffer, realizeSynthPatch } from "./synthEngine.js";
|
|
4
5
|
function createNoopEngine() {
|
|
@@ -74,6 +75,7 @@ export function createAudioEngine(config = {}) {
|
|
|
74
75
|
return pending;
|
|
75
76
|
}
|
|
76
77
|
let listenerPosition = { x: 0, y: 0, z: 0 };
|
|
78
|
+
const activeSpatialUpdaters = new Set();
|
|
77
79
|
function playInternal(soundId, position, loop) {
|
|
78
80
|
const sound = sounds[soundId];
|
|
79
81
|
if (sound === undefined)
|
|
@@ -89,35 +91,54 @@ export function createAudioEngine(config = {}) {
|
|
|
89
91
|
}
|
|
90
92
|
if (sound.url === undefined)
|
|
91
93
|
return null;
|
|
92
|
-
|
|
93
|
-
let
|
|
94
|
+
const disposer = createDisposer();
|
|
95
|
+
let liveGain = null;
|
|
96
|
+
function updateGain() {
|
|
97
|
+
if (liveGain !== null) {
|
|
98
|
+
liveGain.gain.value = resolveEmitterGain(distance3(currentPosition, listenerPosition), sound, 1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
94
101
|
void loadBuffer(sound.url).then((buffer) => {
|
|
95
|
-
if (
|
|
102
|
+
if (buffer === null)
|
|
96
103
|
return;
|
|
97
104
|
const sourceNode = context.createBufferSource();
|
|
98
105
|
sourceNode.buffer = buffer;
|
|
99
106
|
sourceNode.loop = loop || (sound.loop ?? false);
|
|
100
|
-
gainNode = context.createGain();
|
|
107
|
+
const gainNode = context.createGain();
|
|
101
108
|
gainNode.gain.value = resolveEmitterGain(distance3(currentPosition, listenerPosition), sound, 1);
|
|
102
109
|
sourceNode.connect(gainNode);
|
|
103
110
|
gainNode.connect(bus);
|
|
104
111
|
sourceNode.start();
|
|
112
|
+
liveGain = gainNode;
|
|
113
|
+
disposer.onDispose(() => {
|
|
114
|
+
try {
|
|
115
|
+
sourceNode.stop();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
}
|
|
119
|
+
sourceNode.disconnect();
|
|
120
|
+
gainNode.disconnect();
|
|
121
|
+
liveGain = null;
|
|
122
|
+
});
|
|
105
123
|
});
|
|
124
|
+
if (loop)
|
|
125
|
+
activeSpatialUpdaters.add(updateGain);
|
|
106
126
|
return {
|
|
107
127
|
setPosition(next) {
|
|
108
128
|
currentPosition = next;
|
|
109
|
-
|
|
110
|
-
gainNode.gain.value = resolveEmitterGain(distance3(currentPosition, listenerPosition), sound, 1);
|
|
111
|
-
}
|
|
129
|
+
updateGain();
|
|
112
130
|
},
|
|
113
131
|
stop() {
|
|
114
|
-
|
|
132
|
+
activeSpatialUpdaters.delete(updateGain);
|
|
133
|
+
disposer.dispose();
|
|
115
134
|
},
|
|
116
135
|
};
|
|
117
136
|
}
|
|
118
137
|
return {
|
|
119
138
|
setListenerPose(position) {
|
|
120
139
|
listenerPosition = position;
|
|
140
|
+
for (const updateGain of activeSpatialUpdaters)
|
|
141
|
+
updateGain();
|
|
121
142
|
},
|
|
122
143
|
playOneShot(soundId, position) {
|
|
123
144
|
void context.resume().catch(() => undefined);
|
|
@@ -140,6 +161,7 @@ export function createAudioEngine(config = {}) {
|
|
|
140
161
|
void context.resume().catch(() => undefined);
|
|
141
162
|
},
|
|
142
163
|
dispose() {
|
|
164
|
+
activeSpatialUpdaters.clear();
|
|
143
165
|
director?.dispose();
|
|
144
166
|
void context.close().catch(() => undefined);
|
|
145
167
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AudioEngine } from "./audioEngine.js";
|
|
2
|
+
type AudioPlayPayload = {
|
|
3
|
+
sound: string;
|
|
4
|
+
at?: readonly [number, number, number];
|
|
5
|
+
};
|
|
6
|
+
type AudioMusicPayload = {
|
|
7
|
+
theme: string | null;
|
|
8
|
+
transpose?: number;
|
|
9
|
+
};
|
|
10
|
+
/** Minimal event bus the shell wires to the audio engine. @internal */
|
|
11
|
+
export type AudioEventBus = {
|
|
12
|
+
on(event: "audio.play", handler: (payload: AudioPlayPayload) => void): () => void;
|
|
13
|
+
on(event: "audio.music", handler: (payload: AudioMusicPayload) => void): () => void;
|
|
14
|
+
on(event: "audio.resume", handler: () => void): () => void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Subscribe game audio events to the shell audio engine.
|
|
18
|
+
* Returns a single unsubscribe that tears all three listeners down.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
export declare function attachAudioEventWire(events: AudioEventBus, audioEngine: Pick<AudioEngine, "playOneShot" | "playMusic" | "resume">): () => void;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscribe game audio events to the shell audio engine.
|
|
3
|
+
* Returns a single unsubscribe that tears all three listeners down.
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export function attachAudioEventWire(events, audioEngine) {
|
|
7
|
+
const offPlay = events.on("audio.play", ({ sound, at }) => {
|
|
8
|
+
audioEngine.playOneShot(sound, at === undefined ? undefined : { x: at[0], y: at[1], z: at[2] });
|
|
9
|
+
});
|
|
10
|
+
const offMusic = events.on("audio.music", ({ theme, transpose }) => {
|
|
11
|
+
audioEngine.playMusic(theme, transpose === undefined ? undefined : { transpose });
|
|
12
|
+
});
|
|
13
|
+
const offResume = events.on("audio.resume", () => audioEngine.resume());
|
|
14
|
+
return () => {
|
|
15
|
+
offPlay();
|
|
16
|
+
offMusic();
|
|
17
|
+
offResume();
|
|
18
|
+
};
|
|
19
|
+
}
|
package/dist/behaviour.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export { attachObject3D, Object3DBehaviour } from "./behaviourAttach.js";
|
|
|
8
8
|
* receive — pass the game clock's scaling to keep behaviours on game time, or
|
|
9
9
|
* omit for real-time seconds. Games driving `world.update` themselves from
|
|
10
10
|
* `loop.onTick` should not also render this.
|
|
11
|
-
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
12
13
|
export declare function useBehaviourWorld(world: BehaviourWorld, scaleDt?: (rawDt: number) => number): void;
|
package/dist/behaviour.js
CHANGED
|
@@ -10,7 +10,8 @@ export { attachObject3D, Object3DBehaviour } from "./behaviourAttach.js";
|
|
|
10
10
|
* receive — pass the game clock's scaling to keep behaviours on game time, or
|
|
11
11
|
* omit for real-time seconds. Games driving `world.update` themselves from
|
|
12
12
|
* `loop.onTick` should not also render this.
|
|
13
|
-
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
14
15
|
export function useBehaviourWorld(world, scaleDt) {
|
|
15
16
|
const driverRef = useRef(createBehaviourWorldDriver(world));
|
|
16
17
|
useEffect(() => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Object3D } from "three";
|
|
2
2
|
import { Behaviour, type BehaviourWorld } from "@jgengine/core/behaviour/behaviour";
|
|
3
|
+
/** @internal */
|
|
3
4
|
export declare class Object3DBehaviour extends Behaviour {
|
|
4
5
|
/** @internal */ _object: Object3D | null;
|
|
5
6
|
get object(): Object3D;
|
package/dist/behaviourAttach.js
CHANGED
package/dist/behaviourDriver.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ActionStateTracker } from "@jgengine/core/input/actionBindings";
|
|
2
|
+
import type { Aim } from "@jgengine/core/scene/spatial";
|
|
3
|
+
import type { GameContext } from "@jgengine/core/runtime/gameContext";
|
|
4
|
+
import type { ActionCodesMap } from "@jgengine/core/input/actionBindings";
|
|
5
|
+
import { type CommandSink } from "./commandSink.js";
|
|
6
|
+
/** Actions the shell consumes natively and never routes to a same-named command. @internal */
|
|
7
|
+
export declare const RESERVED_INPUT_ACTIONS: ReadonlySet<string>;
|
|
8
|
+
/** Actions from `input` currently held down, for `ctx.input.publish` (#164.1); includes reserved movement/jump actions. @internal */
|
|
9
|
+
export declare function heldActionsFor(tracker: Pick<ActionStateTracker<string>, "isDown">, actions: readonly string[]): string[];
|
|
10
|
+
/** Whether a bound action should fire this frame: on press, or on repeat interval while held (shared by `FrameDriver` and `HudOnlyDriver`). @internal */
|
|
11
|
+
export declare function shouldFireBoundAction(tracker: Pick<ActionStateTracker<string>, "isDown" | "wasPressed">, action: string, input: ActionCodesMap | undefined, repeatFiredAt: ReadonlyMap<string, number>, now: number): boolean;
|
|
12
|
+
/** Resolves and runs the command bound to `action` via the shell's action→command convention (shared by `FrameDriver` and `HudOnlyDriver`). @internal */
|
|
13
|
+
export declare function dispatchBoundAction(ctx: GameContext, action: string, yaw: number, pitch: number, aim: Aim, reserved?: ReadonlySet<string>, sink?: CommandSink): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { actionRepeatMs, resolveActionCommand, shouldDispatchAction, } from "@jgengine/core/input/actionBindings";
|
|
2
|
+
import { localCommandSink } from "./commandSink.js";
|
|
3
|
+
/** Actions the shell consumes natively and never routes to a same-named command. @internal */
|
|
4
|
+
export const RESERVED_INPUT_ACTIONS = new Set([
|
|
5
|
+
"moveForward",
|
|
6
|
+
"moveBack",
|
|
7
|
+
"moveLeft",
|
|
8
|
+
"moveRight",
|
|
9
|
+
"turnLeft",
|
|
10
|
+
"turnRight",
|
|
11
|
+
"sprint",
|
|
12
|
+
"jump",
|
|
13
|
+
"tabTarget",
|
|
14
|
+
"clearTarget",
|
|
15
|
+
"useAbility",
|
|
16
|
+
"interact",
|
|
17
|
+
]);
|
|
18
|
+
/** Actions from `input` currently held down, for `ctx.input.publish` (#164.1); includes reserved movement/jump actions. @internal */
|
|
19
|
+
export function heldActionsFor(tracker, actions) {
|
|
20
|
+
return actions.filter((action) => tracker.isDown(action));
|
|
21
|
+
}
|
|
22
|
+
/** Whether a bound action should fire this frame: on press, or on repeat interval while held (shared by `FrameDriver` and `HudOnlyDriver`). @internal */
|
|
23
|
+
export function shouldFireBoundAction(tracker, action, input, repeatFiredAt, now) {
|
|
24
|
+
return shouldDispatchAction({
|
|
25
|
+
pressed: tracker.wasPressed(action),
|
|
26
|
+
down: tracker.isDown(action),
|
|
27
|
+
repeatMs: actionRepeatMs(input?.[action]),
|
|
28
|
+
lastFiredAt: repeatFiredAt.get(action) ?? null,
|
|
29
|
+
now,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** Resolves and runs the command bound to `action` via the shell's action→command convention (shared by `FrameDriver` and `HudOnlyDriver`). @internal */
|
|
33
|
+
export function dispatchBoundAction(ctx, action, yaw, pitch, aim, reserved = RESERVED_INPUT_ACTIONS, sink = localCommandSink(ctx)) {
|
|
34
|
+
const command = resolveActionCommand(action, (name) => ctx.game.commands.has(name), reserved);
|
|
35
|
+
if (command !== null)
|
|
36
|
+
sink.run(command, { yaw, pitch, aim });
|
|
37
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type MutableRefObject } from "react";
|
|
1
|
+
import { type ComponentType, type MutableRefObject } from "react";
|
|
2
2
|
import type { GameCameraConfig } from "@jgengine/core/game/playableGame";
|
|
3
3
|
import type { CameraDirector } from "@jgengine/core/runtime/cameraDirector";
|
|
4
|
+
import { type ViewmodelProps } from "./GameFirstPersonCamera.js";
|
|
4
5
|
export { resolveRigKind } from "./rigResolve.js";
|
|
5
6
|
export interface GameCameraRigProps {
|
|
6
7
|
yawRef: MutableRefObject<number>;
|
|
@@ -12,5 +13,7 @@ export interface GameCameraRigProps {
|
|
|
12
13
|
panKeysEnabled?: boolean;
|
|
13
14
|
/** Runtime follow/cinematic override (#196.2). While present it wins over the static `config` for any field it reports non-`undefined`/non-`null`; absent (or an all-`undefined` snapshot) is a no-op. */
|
|
14
15
|
director?: CameraDirector;
|
|
16
|
+
/** Custom first-person viewmodel (#542); read only when the resolved rig is `"first"`. */
|
|
17
|
+
viewmodel?: ComponentType<ViewmodelProps>;
|
|
15
18
|
}
|
|
16
|
-
export declare function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, }: GameCameraRigProps): import("react").JSX.Element;
|
|
19
|
+
export declare function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, viewmodel, }: GameCameraRigProps): import("react").JSX.Element;
|
|
@@ -8,7 +8,7 @@ import { resolveDirectedCamera } from "./rigMath.js";
|
|
|
8
8
|
import { resolveRigKind, turntableAsObserver } from "./rigResolve.js";
|
|
9
9
|
import { CameraShakeContext, createCameraShakeChannel } from "./shakeChannel.js";
|
|
10
10
|
export { resolveRigKind } from "./rigResolve.js";
|
|
11
|
-
export function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, }) {
|
|
11
|
+
export function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerControls, panKeysEnabled, director, viewmodel, }) {
|
|
12
12
|
const channel = useMemo(() => createCameraShakeChannel(config?.shake?.decayPerSecond), [config?.shake?.decayPerSecond]);
|
|
13
13
|
const [, notifyDirectorChange] = useReducer((count) => count + 1, 0);
|
|
14
14
|
useEffect(() => {
|
|
@@ -27,7 +27,7 @@ export function GameCameraRig({ yawRef, pitchRef, config, onDragChange, pointerC
|
|
|
27
27
|
const kind = resolveRigKind(config);
|
|
28
28
|
switch (kind) {
|
|
29
29
|
case "first":
|
|
30
|
-
return (_jsx(GameFirstPersonCamera, { yawRef: yawRef, pitchRef: pitchRef, config: config?.firstPerson, followEntityId: followEntityId ?? undefined }));
|
|
30
|
+
return (_jsx(GameFirstPersonCamera, { yawRef: yawRef, pitchRef: pitchRef, config: config?.firstPerson, followEntityId: followEntityId ?? undefined, viewmodel: viewmodel }));
|
|
31
31
|
case "topDown":
|
|
32
32
|
return _jsx(TopDownRig, { yawRef: yawRef, pitchRef: pitchRef, config: config, followEntityId: followEntityId });
|
|
33
33
|
case "rts":
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { type MutableRefObject } from "react";
|
|
1
|
+
import { type ComponentType, type MutableRefObject } from "react";
|
|
2
2
|
import * as THREE from "three";
|
|
3
3
|
import type { FirstPersonCameraConfig } from "@jgengine/core/game/playableGame";
|
|
4
|
+
import type { EntityRenderCues } from "@jgengine/core/combat/renderCues";
|
|
4
5
|
/** World position of the first-person weapon muzzle, or false when no viewmodel is mounted. */
|
|
5
6
|
export declare function readFirstPersonMuzzle(target: THREE.Vector3): boolean;
|
|
7
|
+
/** Props handed to a custom viewmodel component (#542): a live cue ref (velocity/bob/firing/reloading/recoil/hit) for the followed entity, driven from your own `useFrame` — read `cuesRef.current` there rather than storing it as render state. */
|
|
8
|
+
export interface ViewmodelProps {
|
|
9
|
+
cuesRef: MutableRefObject<EntityRenderCues>;
|
|
10
|
+
}
|
|
6
11
|
export interface GameFirstPersonCameraProps {
|
|
7
12
|
yawRef: MutableRefObject<number>;
|
|
8
13
|
pitchRef: MutableRefObject<number>;
|
|
9
14
|
config?: FirstPersonCameraConfig;
|
|
10
15
|
followEntityId?: string;
|
|
16
|
+
/** Custom viewmodel component replacing the built-in three-mesh gun, rendered inside the same camera-locked, muzzle-tracked anchor. Ignored when `config.viewmodel === false`. */
|
|
17
|
+
viewmodel?: ComponentType<ViewmodelProps>;
|
|
11
18
|
}
|
|
12
|
-
export declare function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, }: GameFirstPersonCameraProps): import("react").JSX.Element | null;
|
|
19
|
+
export declare function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, }: GameFirstPersonCameraProps): import("react").JSX.Element | null;
|
|
@@ -5,6 +5,7 @@ import * as THREE from "three";
|
|
|
5
5
|
import { DEFAULT_EYE_HEIGHT } from "@jgengine/core/combat/shotOrigin";
|
|
6
6
|
import { useGameContext } from "@jgengine/react/provider";
|
|
7
7
|
import { usePlayer } from "@jgengine/react/hooks";
|
|
8
|
+
import { useEntityRenderCues } from "../render/useEntityRenderCues.js";
|
|
8
9
|
import { usePlayerFov } from "./PlayerFov.js";
|
|
9
10
|
import { GAME_SIM_FRAME_PRIORITY, ORBIT_CAMERA_FRAME_PRIORITY } from "./orbitCameraMath.js";
|
|
10
11
|
const DEFAULT_SENSITIVITY = 0.0025;
|
|
@@ -21,7 +22,7 @@ export function readFirstPersonMuzzle(target) {
|
|
|
21
22
|
target.copy(muzzleWorld);
|
|
22
23
|
return true;
|
|
23
24
|
}
|
|
24
|
-
export function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, }) {
|
|
25
|
+
export function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId, viewmodel, }) {
|
|
25
26
|
const eyeHeight = config?.eyeHeight ?? DEFAULT_EYE_HEIGHT;
|
|
26
27
|
const sensitivity = config?.sensitivity ?? DEFAULT_SENSITIVITY;
|
|
27
28
|
const maxPitch = config?.maxPitch ?? DEFAULT_MAX_PITCH;
|
|
@@ -32,6 +33,7 @@ export function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId
|
|
|
32
33
|
const domElement = useThree((state) => state.gl.domElement);
|
|
33
34
|
const followId = followEntityId ?? userId;
|
|
34
35
|
const seededRef = useRef(false);
|
|
36
|
+
const cuesRef = useEntityRenderCues(followId);
|
|
35
37
|
useEffect(() => {
|
|
36
38
|
const requestLock = () => {
|
|
37
39
|
if (window.matchMedia?.("(pointer: coarse)").matches)
|
|
@@ -73,9 +75,9 @@ export function GameFirstPersonCamera({ yawRef, pitchRef, config, followEntityId
|
|
|
73
75
|
}, ORBIT_CAMERA_FRAME_PRIORITY);
|
|
74
76
|
if (config?.viewmodel === false)
|
|
75
77
|
return null;
|
|
76
|
-
return _jsx(FirstPersonViewmodel, { camera: camera });
|
|
78
|
+
return _jsx(FirstPersonViewmodel, { camera: camera, viewmodel: viewmodel, cuesRef: cuesRef });
|
|
77
79
|
}
|
|
78
|
-
function FirstPersonViewmodel({ camera }) {
|
|
80
|
+
function FirstPersonViewmodel({ camera, viewmodel: Viewmodel, cuesRef, }) {
|
|
79
81
|
const groupRef = useRef(null);
|
|
80
82
|
useEffect(() => () => {
|
|
81
83
|
muzzleTracked = false;
|
|
@@ -92,5 +94,8 @@ function FirstPersonViewmodel({ camera }) {
|
|
|
92
94
|
muzzleWorld.copy(MUZZLE_OFFSET).applyQuaternion(camera.quaternion).add(camera.position);
|
|
93
95
|
muzzleTracked = true;
|
|
94
96
|
}, GAME_SIM_FRAME_PRIORITY);
|
|
97
|
+
if (Viewmodel !== undefined) {
|
|
98
|
+
return (_jsx("group", { ref: groupRef, children: _jsx(Viewmodel, { cuesRef: cuesRef }) }));
|
|
99
|
+
}
|
|
95
100
|
return (_jsxs("group", { ref: groupRef, children: [_jsxs("mesh", { position: [0, 0, -0.22], children: [_jsx("boxGeometry", { args: [0.09, 0.11, 0.55] }), _jsx("meshStandardMaterial", { color: "#22262d", metalness: 0.6, roughness: 0.35 })] }), _jsxs("mesh", { position: [0, -0.13, 0.04], rotation: [0.35, 0, 0], children: [_jsx("boxGeometry", { args: [0.08, 0.18, 0.11] }), _jsx("meshStandardMaterial", { color: "#33373f", metalness: 0.4, roughness: 0.5 })] }), _jsxs("mesh", { position: [0, 0.03, -0.52], rotation: [Math.PI / 2, 0, 0], children: [_jsx("cylinderGeometry", { args: [0.022, 0.022, 0.18, 10] }), _jsx("meshStandardMaterial", { color: "#0e0f12", metalness: 0.7, roughness: 0.3 })] })] }));
|
|
96
101
|
}
|
|
@@ -2,5 +2,5 @@ import { type InspectionCameraConfig } from "./inspectionCameraMath.js";
|
|
|
2
2
|
export interface GameInspectionCameraProps {
|
|
3
3
|
config?: InspectionCameraConfig;
|
|
4
4
|
}
|
|
5
|
-
/** Model-viewer
|
|
5
|
+
/** Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurable anchor. Orbits a fixed `target`; never reads player/entity state. */
|
|
6
6
|
export declare function GameInspectionCamera({ config: configPatch }: GameInspectionCameraProps): import("react").JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { useThree } from "@react-three/fiber";
|
|
|
4
4
|
import { useEffect, useRef } from "react";
|
|
5
5
|
import { MOUSE } from "three";
|
|
6
6
|
import { resolveInspectionCameraConfig, resolveInspectionZoomToCursor, seedInspectionCamera, } from "./inspectionCameraMath.js";
|
|
7
|
-
/** Model-viewer
|
|
7
|
+
/** Model-viewer / editor rig (#207.7, #866): left-click selects (editor), middle-drag pans, right-drag orbits, scroll zooms toward a configurable anchor. Orbits a fixed `target`; never reads player/entity state. */
|
|
8
8
|
export function GameInspectionCamera({ config: configPatch }) {
|
|
9
9
|
const config = resolveInspectionCameraConfig(configPatch);
|
|
10
10
|
const controlsRef = useRef(null);
|
|
@@ -31,5 +31,5 @@ export function GameInspectionCamera({ config: configPatch }) {
|
|
|
31
31
|
camera.position.add(controls.target.clone().sub(previous));
|
|
32
32
|
controls.update();
|
|
33
33
|
}, [camera, config.target.x, config.target.y, config.target.z]);
|
|
34
|
-
return (_jsx(OrbitControls, { ref: controlsRef, makeDefault: true, enableDamping: true, dampingFactor: config.dampingFactor, rotateSpeed: config.rotateSpeed, zoomSpeed: config.zoomSpeed, zoomToCursor: resolveInspectionZoomToCursor(config.anchor), enablePan: config.pan, enableRotate: true, enableZoom: true, minDistance: config.minDistance, maxDistance: config.maxDistance, minPolarAngle: config.minPolarAngle, maxPolarAngle: config.maxPolarAngle, screenSpacePanning: true, mouseButtons: { LEFT:
|
|
34
|
+
return (_jsx(OrbitControls, { ref: controlsRef, makeDefault: true, enableDamping: true, dampingFactor: config.dampingFactor, rotateSpeed: config.rotateSpeed, zoomSpeed: config.zoomSpeed, zoomToCursor: resolveInspectionZoomToCursor(config.anchor), enablePan: config.pan, enableRotate: true, enableZoom: true, minDistance: config.minDistance, maxDistance: config.maxDistance, minPolarAngle: config.minPolarAngle, maxPolarAngle: config.maxPolarAngle, screenSpacePanning: true, mouseButtons: { LEFT: undefined, MIDDLE: MOUSE.PAN, RIGHT: MOUSE.ROTATE } }));
|
|
35
35
|
}
|
|
@@ -5,7 +5,7 @@ import { useEffect, useRef } from "react";
|
|
|
5
5
|
import { MOUSE, PerspectiveCamera, Raycaster, Vector3 } from "three";
|
|
6
6
|
import { useGameContext } from "@jgengine/react/provider";
|
|
7
7
|
import { usePlayer } from "@jgengine/react/hooks";
|
|
8
|
-
import { cameraLookPitch, ORBIT_CAMERA_FRAME_PRIORITY, orbitFollowStep, orbitYawFromCamera, resolveFollowTargetFromPosition, resolveOrbitCameraConfig, seedOrbitFollowState, } from "./orbitCameraMath.js";
|
|
8
|
+
import { cameraLookPitch, compensatedFov, ORBIT_CAMERA_FRAME_PRIORITY, orbitFollowStep, orbitYawFromCamera, resolveFollowTargetFromPosition, resolveOrbitCameraConfig, seedOrbitFollowState, } from "./orbitCameraMath.js";
|
|
9
9
|
import { usePlayerFov } from "./PlayerFov.js";
|
|
10
10
|
export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followEntityId, resolveFollowTarget, onDragChange, onCameraFollow, pointerControls = false, }) {
|
|
11
11
|
const config = resolveOrbitCameraConfig(configPatch);
|
|
@@ -20,6 +20,12 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
20
20
|
const raycasterRef = useRef(new Raycaster());
|
|
21
21
|
const collisionDirRef = useRef(new Vector3());
|
|
22
22
|
const collisionOriginRef = useRef(new Vector3());
|
|
23
|
+
const followPayloadRef = useRef({
|
|
24
|
+
entityId: "",
|
|
25
|
+
target: { x: 0, y: 0, z: 0 },
|
|
26
|
+
camera: { x: 0, y: 0, z: 0 },
|
|
27
|
+
distance: 0,
|
|
28
|
+
});
|
|
23
29
|
const followId = followEntityId ?? userId;
|
|
24
30
|
useEffect(() => {
|
|
25
31
|
const entity = ctx.scene.entity.get(followId);
|
|
@@ -60,12 +66,15 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
60
66
|
camera.position.set(stepped.camera.x, stepped.camera.y, stepped.camera.z);
|
|
61
67
|
}
|
|
62
68
|
controls.update();
|
|
69
|
+
let desiredDistance = 0;
|
|
70
|
+
let pulledDistance = 0;
|
|
63
71
|
if (config.collision.enabled) {
|
|
64
72
|
const t = stepped.target;
|
|
65
73
|
const dx = camera.position.x - t.x;
|
|
66
74
|
const dy = camera.position.y - t.y;
|
|
67
75
|
const dz = camera.position.z - t.z;
|
|
68
76
|
const dist = Math.hypot(dx, dy, dz);
|
|
77
|
+
desiredDistance = dist;
|
|
69
78
|
if (dist > config.collision.minTargetDistance) {
|
|
70
79
|
const dir = collisionDirRef.current.set(dx / dist, dy / dist, dz / dist);
|
|
71
80
|
const ray = raycasterRef.current;
|
|
@@ -87,6 +96,7 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
87
96
|
if (blocked > 0) {
|
|
88
97
|
const pulled = Math.max(config.collision.minTargetDistance, blocked - config.collision.padding);
|
|
89
98
|
camera.position.set(t.x + dir.x * pulled, t.y + dir.y * pulled, t.z + dir.z * pulled);
|
|
99
|
+
pulledDistance = pulled;
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
102
|
}
|
|
@@ -102,17 +112,22 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
102
112
|
}
|
|
103
113
|
if (camera.isPerspectiveCamera === true) {
|
|
104
114
|
const perspective = camera;
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
const targetFov = pulledDistance > 0 && pulledDistance < desiredDistance
|
|
116
|
+
? compensatedFov(playerFov.fov, desiredDistance, pulledDistance)
|
|
117
|
+
: playerFov.fov;
|
|
118
|
+
if (Math.abs(perspective.fov - targetFov) > 0.001) {
|
|
119
|
+
perspective.fov = targetFov;
|
|
107
120
|
perspective.updateProjectionMatrix();
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
|
-
onCameraFollow
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
if (onCameraFollow !== undefined) {
|
|
124
|
+
const payload = followPayloadRef.current;
|
|
125
|
+
payload.entityId = followId;
|
|
126
|
+
payload.target = stepped.target;
|
|
127
|
+
payload.camera = runtimeRef.current.camera;
|
|
128
|
+
payload.distance = runtimeRef.current.lockedDistance ?? stepped.distance;
|
|
129
|
+
onCameraFollow(payload);
|
|
130
|
+
}
|
|
116
131
|
}, ORBIT_CAMERA_FRAME_PRIORITY);
|
|
117
132
|
return (_jsx(OrbitControls, { ref: controlsRef, enableDamping: true, dampingFactor: config.dampingFactor, rotateSpeed: config.rotateSpeed, zoomSpeed: config.zoomSpeed, enablePan: false, enableRotate: true, enableZoom: true, minDistance: config.minDistance, maxDistance: config.maxDistance, maxPolarAngle: config.maxPolarAngle, minPolarAngle: config.minPolarAngle, screenSpacePanning: false, mouseButtons: pointerControls
|
|
118
133
|
? { LEFT: undefined, MIDDLE: MOUSE.ROTATE, RIGHT: undefined }
|
|
@@ -8,6 +8,9 @@ export interface CameraBlendScratch {
|
|
|
8
8
|
elapsed: number;
|
|
9
9
|
duration: number;
|
|
10
10
|
}
|
|
11
|
+
/** @internal */
|
|
11
12
|
export declare function createCameraBlendScratch(Vector3Ctor: new () => Vector3, QuaternionCtor: new () => Quaternion): CameraBlendScratch;
|
|
13
|
+
/** @internal */
|
|
12
14
|
export declare function captureCameraBlendFrom(scratch: CameraBlendScratch, position: Vector3, quaternion: Quaternion, fov: number, duration: number): void;
|
|
15
|
+
/** @internal */
|
|
13
16
|
export declare function applyCameraBlendStep(scratch: CameraBlendScratch, camera: Camera, targetFov: number, dt: number): boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { smoothstep } from "./rigMath.js";
|
|
2
|
+
/** @internal */
|
|
2
3
|
export function createCameraBlendScratch(Vector3Ctor, QuaternionCtor) {
|
|
3
4
|
return {
|
|
4
5
|
fromPos: new Vector3Ctor(),
|
|
@@ -10,6 +11,7 @@ export function createCameraBlendScratch(Vector3Ctor, QuaternionCtor) {
|
|
|
10
11
|
duration: 0,
|
|
11
12
|
};
|
|
12
13
|
}
|
|
14
|
+
/** @internal */
|
|
13
15
|
export function captureCameraBlendFrom(scratch, position, quaternion, fov, duration) {
|
|
14
16
|
scratch.fromPos.copy(position);
|
|
15
17
|
scratch.fromQuat.copy(quaternion);
|
|
@@ -20,6 +22,7 @@ export function captureCameraBlendFrom(scratch, position, quaternion, fov, durat
|
|
|
20
22
|
function isPerspective(camera) {
|
|
21
23
|
return camera.isPerspectiveCamera === true;
|
|
22
24
|
}
|
|
25
|
+
/** @internal */
|
|
23
26
|
export function applyCameraBlendStep(scratch, camera, targetFov, dt) {
|
|
24
27
|
scratch.elapsed += dt;
|
|
25
28
|
const t = scratch.duration <= 0 ? 1 : Math.min(scratch.elapsed / scratch.duration, 1);
|
|
@@ -7,13 +7,17 @@ export interface PlayerFovBounds {
|
|
|
7
7
|
max: number;
|
|
8
8
|
defaultFov: number;
|
|
9
9
|
}
|
|
10
|
+
/** @internal */
|
|
10
11
|
export declare function resolvePlayerFovBounds(options?: {
|
|
11
12
|
min?: number;
|
|
12
13
|
max?: number;
|
|
13
14
|
default?: number;
|
|
14
15
|
}): PlayerFovBounds;
|
|
16
|
+
/** @internal */
|
|
15
17
|
export declare function clampPlayerFov(value: unknown, min?: number, max?: number): number;
|
|
18
|
+
/** @internal */
|
|
16
19
|
export declare function loadPlayerFov(bounds?: PlayerFovBounds, storage?: Pick<Storage, "getItem"> | null | undefined): number;
|
|
20
|
+
/** @internal */
|
|
17
21
|
export declare function savePlayerFov(value: number, bounds?: PlayerFovBounds, storage?: Pick<Storage, "setItem"> | null | undefined): number;
|
|
18
22
|
/**
|
|
19
23
|
* Composition model for perspective rigs:
|
|
@@ -21,5 +25,6 @@ export declare function savePlayerFov(value: number, bounds?: PlayerFovBounds, s
|
|
|
21
25
|
* - poseFov is the rig's authored FOV (includes chase-speed modulation, ADS zoom, transitions)
|
|
22
26
|
* - relative mode shifts the authored FOV by (preference − default) so mods still stack
|
|
23
27
|
* - absolute mode (cinematic keyframes) keeps the authored FOV as-is
|
|
24
|
-
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
25
30
|
export declare function composePlayerFov(preference: number, poseFov: number, mode?: "relative" | "absolute", bounds?: PlayerFovBounds): number;
|
|
@@ -3,6 +3,7 @@ export const PLAYER_FOV_DEFAULT = CAMERA_FRUSTUM_DEFAULTS.fov;
|
|
|
3
3
|
export const PLAYER_FOV_MIN = 40;
|
|
4
4
|
export const PLAYER_FOV_MAX = 120;
|
|
5
5
|
export const PLAYER_FOV_STORAGE_KEY = "jgengine:player-fov";
|
|
6
|
+
/** @internal */
|
|
6
7
|
export function resolvePlayerFovBounds(options) {
|
|
7
8
|
const min = Number.isFinite(options?.min) ? options.min : PLAYER_FOV_MIN;
|
|
8
9
|
const max = Number.isFinite(options?.max) ? options.max : PLAYER_FOV_MAX;
|
|
@@ -11,6 +12,7 @@ export function resolvePlayerFovBounds(options) {
|
|
|
11
12
|
const defaultFov = clampPlayerFov(options?.default ?? PLAYER_FOV_DEFAULT, lo, hi);
|
|
12
13
|
return { min: lo, max: hi, defaultFov };
|
|
13
14
|
}
|
|
15
|
+
/** @internal */
|
|
14
16
|
export function clampPlayerFov(value, min = PLAYER_FOV_MIN, max = PLAYER_FOV_MAX) {
|
|
15
17
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
16
18
|
return clampPlayerFov(PLAYER_FOV_DEFAULT, min, max);
|
|
@@ -20,6 +22,7 @@ export function clampPlayerFov(value, min = PLAYER_FOV_MIN, max = PLAYER_FOV_MAX
|
|
|
20
22
|
return max;
|
|
21
23
|
return value;
|
|
22
24
|
}
|
|
25
|
+
/** @internal */
|
|
23
26
|
export function loadPlayerFov(bounds = resolvePlayerFovBounds(), storage = defaultStorage()) {
|
|
24
27
|
if (storage == null)
|
|
25
28
|
return bounds.defaultFov;
|
|
@@ -33,6 +36,7 @@ export function loadPlayerFov(bounds = resolvePlayerFovBounds(), storage = defau
|
|
|
33
36
|
return bounds.defaultFov;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
/** @internal */
|
|
36
40
|
export function savePlayerFov(value, bounds = resolvePlayerFovBounds(), storage = defaultStorage()) {
|
|
37
41
|
const next = clampPlayerFov(value, bounds.min, bounds.max);
|
|
38
42
|
if (storage == null)
|
|
@@ -51,7 +55,8 @@ export function savePlayerFov(value, bounds = resolvePlayerFovBounds(), storage
|
|
|
51
55
|
* - poseFov is the rig's authored FOV (includes chase-speed modulation, ADS zoom, transitions)
|
|
52
56
|
* - relative mode shifts the authored FOV by (preference − default) so mods still stack
|
|
53
57
|
* - absolute mode (cinematic keyframes) keeps the authored FOV as-is
|
|
54
|
-
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
55
60
|
export function composePlayerFov(preference, poseFov, mode = "relative", bounds = resolvePlayerFovBounds()) {
|
|
56
61
|
if (mode === "absolute")
|
|
57
62
|
return clampPlayerFov(poseFov, bounds.min, bounds.max);
|
package/dist/camera/index.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
/** Public camera surface for games. Pure rig math lives in sibling modules (package-internal). */
|
|
1
2
|
export { GameOrbitCamera, type CameraFollowListener, type GameOrbitCameraProps } from "./GameOrbitCamera.js";
|
|
2
|
-
export { GameFirstPersonCamera, type GameFirstPersonCameraProps } from "./GameFirstPersonCamera.js";
|
|
3
|
+
export { GameFirstPersonCamera, readFirstPersonMuzzle, type GameFirstPersonCameraProps, type ViewmodelProps, } from "./GameFirstPersonCamera.js";
|
|
3
4
|
export { GameInspectionCamera, type GameInspectionCameraProps } from "./GameInspectionCamera.js";
|
|
4
5
|
export { GameCameraRig, resolveRigKind, type GameCameraRigProps } from "./GameCameraRig.js";
|
|
5
6
|
export { PlayerFovProvider, PlayerFovSlider, usePlayerFov, type PlayerFovState, } from "./PlayerFov.js";
|
|
6
|
-
export { PLAYER_FOV_DEFAULT, PLAYER_FOV_MAX, PLAYER_FOV_MIN, PLAYER_FOV_STORAGE_KEY, clampPlayerFov, composePlayerFov, loadPlayerFov, resolvePlayerFovBounds, savePlayerFov, type PlayerFovBounds, } from "./fovPreference.js";
|
|
7
|
-
export { CAMERA_POST_FRAME_PRIORITY, CAMERA_RIG_FRAME_PRIORITY, ChaseRig, CinematicRig, LockOnRig, ObserverRig, RtsRig, ShoulderRig, SideScrollRig, TopDownRig, type RigProps, } from "./cameraRigs.js";
|
|
8
|
-
export { applyCameraBlendStep, captureCameraBlendFrom, createCameraBlendScratch, type CameraBlendScratch, } from "./cameraBlendMath.js";
|
|
9
7
|
export { CameraShakeContext, cameraShake, createCameraShakeChannel, defaultCameraShakeChannel, useCameraShake, type CameraShakeChannel, } from "./shakeChannel.js";
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { resolveInspectionCameraConfig, resolveInspectionZoomToCursor, seedInspectionCamera, type InspectionCameraConfig, type InspectionZoomAnchor, type ResolvedInspectionCameraConfig, } from "./inspectionCameraMath.js";
|
|
8
|
+
export { rtsPanKeysConflict } from "./rigMath.js";
|
|
9
|
+
export { GAME_SIM_FRAME_PRIORITY } from "./orbitCameraMath.js";
|
package/dist/camera/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
/** Public camera surface for games. Pure rig math lives in sibling modules (package-internal). */
|
|
1
2
|
export { GameOrbitCamera } from "./GameOrbitCamera.js";
|
|
2
|
-
export { GameFirstPersonCamera } from "./GameFirstPersonCamera.js";
|
|
3
|
+
export { GameFirstPersonCamera, readFirstPersonMuzzle, } from "./GameFirstPersonCamera.js";
|
|
3
4
|
export { GameInspectionCamera } from "./GameInspectionCamera.js";
|
|
4
5
|
export { GameCameraRig, resolveRigKind } from "./GameCameraRig.js";
|
|
5
6
|
export { PlayerFovProvider, PlayerFovSlider, usePlayerFov, } from "./PlayerFov.js";
|
|
6
|
-
export { PLAYER_FOV_DEFAULT, PLAYER_FOV_MAX, PLAYER_FOV_MIN, PLAYER_FOV_STORAGE_KEY, clampPlayerFov, composePlayerFov, loadPlayerFov, resolvePlayerFovBounds, savePlayerFov, } from "./fovPreference.js";
|
|
7
|
-
export { CAMERA_POST_FRAME_PRIORITY, CAMERA_RIG_FRAME_PRIORITY, ChaseRig, CinematicRig, LockOnRig, ObserverRig, RtsRig, ShoulderRig, SideScrollRig, TopDownRig, } from "./cameraRigs.js";
|
|
8
|
-
export { applyCameraBlendStep, captureCameraBlendFrom, createCameraBlendScratch, } from "./cameraBlendMath.js";
|
|
9
7
|
export { CameraShakeContext, cameraShake, createCameraShakeChannel, defaultCameraShakeChannel, useCameraShake, } from "./shakeChannel.js";
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { resolveInspectionCameraConfig, resolveInspectionZoomToCursor, seedInspectionCamera, } from "./inspectionCameraMath.js";
|
|
8
|
+
export { rtsPanKeysConflict } from "./rigMath.js";
|
|
9
|
+
export { GAME_SIM_FRAME_PRIORITY } from "./orbitCameraMath.js";
|
|
@@ -15,11 +15,16 @@ export interface ResolvedInspectionCameraConfig {
|
|
|
15
15
|
zoomSpeed: number;
|
|
16
16
|
dampingFactor: number;
|
|
17
17
|
}
|
|
18
|
+
/** @internal */
|
|
18
19
|
export declare function resolveInspectionCameraConfig(config?: InspectionCameraConfig): ResolvedInspectionCameraConfig;
|
|
19
|
-
/** Seeds the camera/target world position before OrbitControls mounts. Falls back to `initialDistance` behind `target` on the -Z axis, raised by 40% of that distance, when `initialPosition` is unset.
|
|
20
|
+
/** Seeds the camera/target world position before OrbitControls mounts. Falls back to `initialDistance` behind `target` on the -Z axis, raised by 40% of that distance, when `initialPosition` is unset.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
20
23
|
export declare function seedInspectionCamera(config: ResolvedInspectionCameraConfig): {
|
|
21
24
|
camera: Vec3;
|
|
22
25
|
target: Vec3;
|
|
23
26
|
};
|
|
24
|
-
/** Maps the anchor mode onto three-stdlib OrbitControls' native `zoomToCursor` flag.
|
|
27
|
+
/** Maps the anchor mode onto three-stdlib OrbitControls' native `zoomToCursor` flag.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
25
30
|
export declare function resolveInspectionZoomToCursor(anchor: InspectionZoomAnchor): boolean;
|