@jgengine/shell 0.9.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 +34 -3
- 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 +4 -31
- package/dist/GamePlayerShell.js +162 -932
- package/dist/GameUiPreview.js +0 -2
- package/dist/audio/audioEngine.d.ts +8 -0
- package/dist/audio/audioEngine.js +63 -8
- package/dist/audio/audioWire.d.ts +22 -0
- package/dist/audio/audioWire.js +19 -0
- package/dist/audio/musicDirector.d.ts +31 -0
- package/dist/audio/musicDirector.js +125 -0
- package/dist/audio/musicVoices.d.ts +10 -0
- package/dist/audio/musicVoices.js +338 -0
- package/dist/audio/synthEngine.d.ts +9 -0
- package/dist/audio/synthEngine.js +57 -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 +12 -2
- package/dist/camera/GameFirstPersonCamera.js +29 -7
- package/dist/camera/GameInspectionCamera.d.ts +1 -1
- package/dist/camera/GameInspectionCamera.js +13 -2
- package/dist/camera/GameOrbitCamera.js +59 -10
- 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 +54 -6
- package/dist/camera/orbitCameraMath.js +61 -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 +4 -3
- package/dist/commandSink.d.ts +25 -0
- package/dist/commandSink.js +32 -0
- package/dist/defineGame.js +26 -5
- package/dist/devtools/ColPanel.d.ts +1 -0
- package/dist/devtools/ColPanel.js +14 -0
- package/dist/devtools/CollisionDebugWorld.js +1 -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 +9 -2
- package/dist/devtools/collisionDebugMath.js +25 -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 +29 -7
- package/dist/environment/Daylight.js +153 -13
- package/dist/environment/EnvironmentScene.js +42 -7
- package/dist/environment/RoadRibbons.d.ts +7 -0
- package/dist/environment/RoadRibbons.js +57 -0
- 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 +30 -0
- package/dist/inputSink.js +85 -0
- package/dist/materialOverride.d.ts +10 -0
- package/dist/materialOverride.js +15 -3
- package/dist/multiplayer.js +14 -4
- package/dist/postfx/PostProcessing.d.ts +10 -0
- package/dist/postfx/PostProcessing.js +120 -0
- package/dist/postfx/gradeShader.d.ts +4 -0
- package/dist/postfx/gradeShader.js +72 -0
- 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/CarvedTerrain.d.ts +4 -1
- package/dist/terrain/CarvedTerrain.js +4 -3
- 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 +31 -0
- package/dist/terrain/terrainDetailMaterial.js +157 -0
- 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 +211 -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 +52 -3
- package/dist/world/WorldHud.js +94 -9
- 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 +26 -1
- package/dist/world/worldBarSamples.js +45 -1
- package/dist/worldSky.d.ts +5 -0
- package/dist/worldSky.js +11 -0
- package/dist/worldSync.d.ts +11 -0
- package/dist/worldSync.js +20 -0
- 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 -2643
|
@@ -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);
|
|
@@ -20,5 +20,16 @@ export function GameInspectionCamera({ config: configPatch }) {
|
|
|
20
20
|
controlsRef.current?.update();
|
|
21
21
|
seededRef.current = true;
|
|
22
22
|
}, [camera, config]);
|
|
23
|
-
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!seededRef.current)
|
|
25
|
+
return;
|
|
26
|
+
const controls = controlsRef.current;
|
|
27
|
+
if (controls === null)
|
|
28
|
+
return;
|
|
29
|
+
const previous = controls.target.clone();
|
|
30
|
+
controls.target.set(config.target.x, config.target.y, config.target.z);
|
|
31
|
+
camera.position.add(controls.target.clone().sub(previous));
|
|
32
|
+
controls.update();
|
|
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: undefined, MIDDLE: MOUSE.PAN, RIGHT: MOUSE.ROTATE } }));
|
|
24
35
|
}
|
|
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { OrbitControls } from "@react-three/drei";
|
|
3
3
|
import { useFrame, useThree } from "@react-three/fiber";
|
|
4
4
|
import { useEffect, useRef } from "react";
|
|
5
|
-
import { MOUSE, PerspectiveCamera, Vector3 } from "three";
|
|
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);
|
|
@@ -16,6 +16,16 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
16
16
|
const ctx = useGameContext();
|
|
17
17
|
const playerFov = usePlayerFov();
|
|
18
18
|
const camera = useThree((state) => state.camera);
|
|
19
|
+
const scene = useThree((state) => state.scene);
|
|
20
|
+
const raycasterRef = useRef(new Raycaster());
|
|
21
|
+
const collisionDirRef = useRef(new Vector3());
|
|
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
|
+
});
|
|
19
29
|
const followId = followEntityId ?? userId;
|
|
20
30
|
useEffect(() => {
|
|
21
31
|
const entity = ctx.scene.entity.get(followId);
|
|
@@ -56,6 +66,40 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
56
66
|
camera.position.set(stepped.camera.x, stepped.camera.y, stepped.camera.z);
|
|
57
67
|
}
|
|
58
68
|
controls.update();
|
|
69
|
+
let desiredDistance = 0;
|
|
70
|
+
let pulledDistance = 0;
|
|
71
|
+
if (config.collision.enabled) {
|
|
72
|
+
const t = stepped.target;
|
|
73
|
+
const dx = camera.position.x - t.x;
|
|
74
|
+
const dy = camera.position.y - t.y;
|
|
75
|
+
const dz = camera.position.z - t.z;
|
|
76
|
+
const dist = Math.hypot(dx, dy, dz);
|
|
77
|
+
desiredDistance = dist;
|
|
78
|
+
if (dist > config.collision.minTargetDistance) {
|
|
79
|
+
const dir = collisionDirRef.current.set(dx / dist, dy / dist, dz / dist);
|
|
80
|
+
const ray = raycasterRef.current;
|
|
81
|
+
ray.set(collisionOriginRef.current.set(t.x, t.y, t.z), dir);
|
|
82
|
+
ray.near = config.collision.minTargetDistance;
|
|
83
|
+
ray.far = dist;
|
|
84
|
+
let blocked = 0;
|
|
85
|
+
for (const hit of ray.intersectObjects(scene.children, true)) {
|
|
86
|
+
const obj = hit.object;
|
|
87
|
+
if (!obj.visible)
|
|
88
|
+
continue;
|
|
89
|
+
if (obj.isSprite === true)
|
|
90
|
+
continue;
|
|
91
|
+
if (obj.userData.jgCameraTransparent === true)
|
|
92
|
+
continue;
|
|
93
|
+
blocked = hit.distance;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
if (blocked > 0) {
|
|
97
|
+
const pulled = Math.max(config.collision.minTargetDistance, blocked - config.collision.padding);
|
|
98
|
+
camera.position.set(t.x + dir.x * pulled, t.y + dir.y * pulled, t.z + dir.z * pulled);
|
|
99
|
+
pulledDistance = pulled;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
59
103
|
runtimeRef.current = {
|
|
60
104
|
target: stepped.target,
|
|
61
105
|
camera: { x: camera.position.x, y: camera.position.y, z: camera.position.z },
|
|
@@ -68,17 +112,22 @@ export function GameOrbitCamera({ yawRef, pitchRef, config: configPatch, followE
|
|
|
68
112
|
}
|
|
69
113
|
if (camera.isPerspectiveCamera === true) {
|
|
70
114
|
const perspective = camera;
|
|
71
|
-
|
|
72
|
-
|
|
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;
|
|
73
120
|
perspective.updateProjectionMatrix();
|
|
74
121
|
}
|
|
75
122
|
}
|
|
76
|
-
onCameraFollow
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
}
|
|
82
131
|
}, ORBIT_CAMERA_FRAME_PRIORITY);
|
|
83
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
|
|
84
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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export function resolveInspectionCameraConfig(config) {
|
|
2
3
|
return {
|
|
3
4
|
anchor: config?.anchor ?? "target",
|
|
@@ -24,7 +25,9 @@ export function resolveInspectionCameraConfig(config) {
|
|
|
24
25
|
dampingFactor: config?.dampingFactor ?? 0.08,
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
|
-
/** 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.
|
|
28
|
+
/** 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.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
28
31
|
export function seedInspectionCamera(config) {
|
|
29
32
|
if (config.initialPosition !== null) {
|
|
30
33
|
return { camera: config.initialPosition, target: config.target };
|
|
@@ -38,7 +41,9 @@ export function seedInspectionCamera(config) {
|
|
|
38
41
|
target: config.target,
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
|
-
/** Maps the anchor mode onto three-stdlib OrbitControls' native `zoomToCursor` flag.
|
|
44
|
+
/** Maps the anchor mode onto three-stdlib OrbitControls' native `zoomToCursor` flag.
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
42
47
|
export function resolveInspectionZoomToCursor(anchor) {
|
|
43
48
|
return anchor === "cursor";
|
|
44
49
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
/** Horizontal facing derived from an orbit camera orbiting a target on the XZ plane.
|
|
1
|
+
/** Horizontal facing derived from an orbit camera orbiting a target on the XZ plane.
|
|
2
|
+
* @internal
|
|
3
|
+
*/
|
|
2
4
|
export declare function orbitYawFromCamera(cameraX: number, cameraZ: number, targetX: number, targetZ: number): number;
|
|
3
|
-
/** Elevation the camera looks along (radians): negative = aiming down, positive = aiming up, 0 = level. Feeds aim.pitch so vertical aim tracks the camera.
|
|
5
|
+
/** Elevation the camera looks along (radians): negative = aiming down, positive = aiming up, 0 = level. Feeds aim.pitch so vertical aim tracks the camera.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
4
8
|
export declare function cameraLookPitch(camera: Vec3, target: Vec3): number;
|
|
5
9
|
export interface Vec3 {
|
|
6
10
|
x: number;
|
|
@@ -19,6 +23,12 @@ export interface OrbitCameraConfig {
|
|
|
19
23
|
targetHeight?: number;
|
|
20
24
|
initialDistance?: number;
|
|
21
25
|
initialHeight?: number;
|
|
26
|
+
/** Initial horizontal facing of the boom (radians): 0 = camera behind on -Z, PI = camera on +Z. Seeds the rig when set; unset keeps the legacy -Z placement. */
|
|
27
|
+
initialYaw?: number;
|
|
28
|
+
/** Initial boom elevation (radians): 0 = level, positive = camera above the target looking down. Seeds the rig when set; unset derives elevation from `initialHeight`/`initialDistance`. */
|
|
29
|
+
initialPitch?: number;
|
|
30
|
+
/** Signed boom-elevation clamp `[min, max]` (radians): min < 0 dips below the target, max > 0 rises overhead. Maps onto `minPolarAngle`/`maxPolarAngle` (polar = PI/2 − pitch); explicit polar fields win. */
|
|
31
|
+
pitchClamp?: readonly [number, number];
|
|
22
32
|
/** Extra offset applied on top of entity position for the orbit target. */
|
|
23
33
|
targetOffset?: Partial<Vec3>;
|
|
24
34
|
/** Keep orbit radius locked while the follow target moves. */
|
|
@@ -39,6 +49,23 @@ export interface OrbitCameraConfig {
|
|
|
39
49
|
/** Vertical clamp (three.js polar angle, radians): 0 = top-down over the head, PI/2 = level, >PI/2 = look up from below the target. Widen to allow top-down or vertical aim; leave unset for the standard chase feel. */
|
|
40
50
|
minPolarAngle?: number;
|
|
41
51
|
maxPolarAngle?: number;
|
|
52
|
+
/** Spring-arm collision: raycast target→camera each frame and pull the boom in past occluders (walls, terrain) so the camera never clips inside geometry. */
|
|
53
|
+
collision?: OrbitCollisionConfig;
|
|
54
|
+
}
|
|
55
|
+
/** Spring-arm occlusion config for the orbit rig. */
|
|
56
|
+
export interface OrbitCollisionConfig {
|
|
57
|
+
/** Enable the pull-in raycast. Default false (unchanged chase camera). */
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
/** Gap kept between the camera and the hit surface, world units. Default 0.35. */
|
|
60
|
+
padding?: number;
|
|
61
|
+
/** Ignore hits closer than this to the target — the player's own body. Default 1.2. */
|
|
62
|
+
minTargetDistance?: number;
|
|
63
|
+
}
|
|
64
|
+
/** An {@link OrbitCollisionConfig} with every field resolved to a concrete value. */
|
|
65
|
+
export interface ResolvedOrbitCollision {
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
padding: number;
|
|
68
|
+
minTargetDistance: number;
|
|
42
69
|
}
|
|
43
70
|
/** Fully resolved shell config after merging with DEFAULT_ORBIT_CAMERA. */
|
|
44
71
|
export interface ResolvedOrbitCameraConfig {
|
|
@@ -47,6 +74,8 @@ export interface ResolvedOrbitCameraConfig {
|
|
|
47
74
|
targetHeight: number;
|
|
48
75
|
initialDistance: number;
|
|
49
76
|
initialHeight: number;
|
|
77
|
+
initialYaw?: number;
|
|
78
|
+
initialPitch?: number;
|
|
50
79
|
targetOffset?: Partial<Vec3>;
|
|
51
80
|
followLock: boolean;
|
|
52
81
|
followEnabled: boolean;
|
|
@@ -58,6 +87,7 @@ export interface ResolvedOrbitCameraConfig {
|
|
|
58
87
|
distanceSmoothing: number;
|
|
59
88
|
minPolarAngle: number;
|
|
60
89
|
maxPolarAngle: number;
|
|
90
|
+
collision: ResolvedOrbitCollision;
|
|
61
91
|
}
|
|
62
92
|
export declare const DEFAULT_ORBIT_CAMERA: ResolvedOrbitCameraConfig;
|
|
63
93
|
/** Run simulation/movement before orbit follow so poses are current. */
|
|
@@ -69,22 +99,38 @@ export interface OrbitFollowRuntimeState {
|
|
|
69
99
|
camera: Vec3;
|
|
70
100
|
lockedDistance: number | null;
|
|
71
101
|
}
|
|
72
|
-
/** Frame-rate independent exponential smoothing factor in [0, 1].
|
|
102
|
+
/** Frame-rate independent exponential smoothing factor in [0, 1].
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
73
105
|
export declare function smoothBlend(deltaSeconds: number, speed: number): number;
|
|
106
|
+
/** @internal */
|
|
74
107
|
export declare function resolveOrbitCameraConfig(patch?: OrbitCameraConfig): ResolvedOrbitCameraConfig;
|
|
108
|
+
/**
|
|
109
|
+
* FOV that keeps a target's on-screen size constant as the spring-arm pulls the
|
|
110
|
+
* camera in. Widens the base FOV by the desired/pulled distance ratio; a no-op
|
|
111
|
+
* (returns the base) whenever the camera sits at or beyond its desired distance.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export declare function compensatedFov(baseFovDegrees: number, desiredDistance: number, pulledDistance: number): number;
|
|
115
|
+
/** @internal */
|
|
75
116
|
export declare function resolveTargetSmoothing(config: ResolvedOrbitCameraConfig, dragging: boolean): number;
|
|
117
|
+
/** @internal */
|
|
76
118
|
export declare function resolveFollowTargetFromPosition(position: readonly [number, number, number], config: Pick<ResolvedOrbitCameraConfig, "targetHeight" | "targetOffset">): Vec3;
|
|
119
|
+
/** @internal */
|
|
77
120
|
export declare function lerpVec3(from: Vec3, to: Vec3, blend: number): Vec3;
|
|
121
|
+
/** @internal */
|
|
78
122
|
export declare function distanceBetween(a: Vec3, b: Vec3): number;
|
|
123
|
+
/** @internal */
|
|
79
124
|
export declare function seedOrbitFollowState(input: {
|
|
80
125
|
entityPosition: readonly [number, number, number];
|
|
81
|
-
config: Pick<ResolvedOrbitCameraConfig, "targetHeight" | "targetOffset" | "initialDistance" | "initialHeight">;
|
|
126
|
+
config: Pick<ResolvedOrbitCameraConfig, "targetHeight" | "targetOffset" | "initialDistance" | "initialHeight" | "initialYaw" | "initialPitch">;
|
|
82
127
|
}): OrbitFollowRuntimeState;
|
|
83
128
|
/**
|
|
84
129
|
* Pure orbit follow step — smooth target tracking, camera carries target delta
|
|
85
130
|
* (OrbitControls alone keeps camera fixed when only target moves), optional
|
|
86
131
|
* distance lock. Call each frame before OrbitControls.update() in the shell.
|
|
87
|
-
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
88
134
|
export declare function orbitFollowStep(input: {
|
|
89
135
|
state: OrbitFollowRuntimeState;
|
|
90
136
|
desiredTarget: Vec3;
|
|
@@ -94,7 +140,9 @@ export declare function orbitFollowStep(input: {
|
|
|
94
140
|
}): OrbitFollowRuntimeState & {
|
|
95
141
|
distance: number;
|
|
96
142
|
};
|
|
97
|
-
/** @deprecated Use orbitFollowStep — kept for older call sites.
|
|
143
|
+
/** @deprecated Use orbitFollowStep — kept for older call sites.
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
98
146
|
export declare function cameraFollowStep(input: {
|
|
99
147
|
camera: Vec3;
|
|
100
148
|
target: Vec3;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
/** Horizontal facing derived from an orbit camera orbiting a target on the XZ plane.
|
|
1
|
+
/** Horizontal facing derived from an orbit camera orbiting a target on the XZ plane.
|
|
2
|
+
* @internal
|
|
3
|
+
*/
|
|
2
4
|
export function orbitYawFromCamera(cameraX, cameraZ, targetX, targetZ) {
|
|
3
5
|
const dx = targetX - cameraX;
|
|
4
6
|
const dz = targetZ - cameraZ;
|
|
5
7
|
return Math.atan2(dx, dz);
|
|
6
8
|
}
|
|
7
|
-
/** Elevation the camera looks along (radians): negative = aiming down, positive = aiming up, 0 = level. Feeds aim.pitch so vertical aim tracks the camera.
|
|
9
|
+
/** Elevation the camera looks along (radians): negative = aiming down, positive = aiming up, 0 = level. Feeds aim.pitch so vertical aim tracks the camera.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
8
12
|
export function cameraLookPitch(camera, target) {
|
|
9
13
|
const horizontal = Math.hypot(target.x - camera.x, target.z - camera.z);
|
|
10
14
|
return Math.atan2(target.y - camera.y, horizontal);
|
|
@@ -25,24 +29,54 @@ export const DEFAULT_ORBIT_CAMERA = {
|
|
|
25
29
|
distanceSmoothing: 5,
|
|
26
30
|
minPolarAngle: 0.12,
|
|
27
31
|
maxPolarAngle: Math.PI / 2.05,
|
|
32
|
+
collision: { enabled: false, padding: 0.35, minTargetDistance: 1.2 },
|
|
28
33
|
};
|
|
29
34
|
/** Run simulation/movement before orbit follow so poses are current. */
|
|
30
35
|
export const GAME_SIM_FRAME_PRIORITY = 0;
|
|
31
36
|
/** Orbit follow reads the latest entity pose after GAME_SIM_FRAME_PRIORITY. */
|
|
32
37
|
export const ORBIT_CAMERA_FRAME_PRIORITY = -1;
|
|
33
|
-
/** Frame-rate independent exponential smoothing factor in [0, 1].
|
|
38
|
+
/** Frame-rate independent exponential smoothing factor in [0, 1].
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
34
41
|
export function smoothBlend(deltaSeconds, speed) {
|
|
35
42
|
return 1 - Math.exp(-speed * deltaSeconds);
|
|
36
43
|
}
|
|
44
|
+
/** @internal */
|
|
37
45
|
export function resolveOrbitCameraConfig(patch) {
|
|
38
|
-
|
|
46
|
+
const resolved = {
|
|
47
|
+
...DEFAULT_ORBIT_CAMERA,
|
|
48
|
+
...patch,
|
|
49
|
+
collision: { ...DEFAULT_ORBIT_CAMERA.collision, ...patch?.collision },
|
|
50
|
+
};
|
|
51
|
+
if (patch?.pitchClamp !== undefined) {
|
|
52
|
+
const [minPitch, maxPitch] = patch.pitchClamp;
|
|
53
|
+
resolved.minPolarAngle = patch.minPolarAngle ?? Math.PI / 2 - maxPitch;
|
|
54
|
+
resolved.maxPolarAngle = patch.maxPolarAngle ?? Math.PI / 2 - minPitch;
|
|
55
|
+
}
|
|
56
|
+
return resolved;
|
|
39
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* FOV that keeps a target's on-screen size constant as the spring-arm pulls the
|
|
60
|
+
* camera in. Widens the base FOV by the desired/pulled distance ratio; a no-op
|
|
61
|
+
* (returns the base) whenever the camera sits at or beyond its desired distance.
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
export function compensatedFov(baseFovDegrees, desiredDistance, pulledDistance) {
|
|
65
|
+
if (pulledDistance <= 0 || desiredDistance <= 0 || pulledDistance >= desiredDistance) {
|
|
66
|
+
return baseFovDegrees;
|
|
67
|
+
}
|
|
68
|
+
const half = (baseFovDegrees * Math.PI) / 360;
|
|
69
|
+
const widened = 2 * Math.atan(Math.tan(half) * (desiredDistance / pulledDistance));
|
|
70
|
+
return Math.min((widened * 180) / Math.PI, 170);
|
|
71
|
+
}
|
|
72
|
+
/** @internal */
|
|
40
73
|
export function resolveTargetSmoothing(config, dragging) {
|
|
41
74
|
if (dragging) {
|
|
42
75
|
return config.dragTargetSmoothing;
|
|
43
76
|
}
|
|
44
77
|
return config.targetSmoothing;
|
|
45
78
|
}
|
|
79
|
+
/** @internal */
|
|
46
80
|
export function resolveFollowTargetFromPosition(position, config) {
|
|
47
81
|
const offset = config.targetOffset;
|
|
48
82
|
return {
|
|
@@ -51,6 +85,7 @@ export function resolveFollowTargetFromPosition(position, config) {
|
|
|
51
85
|
z: position[2] + (offset?.z ?? 0),
|
|
52
86
|
};
|
|
53
87
|
}
|
|
88
|
+
/** @internal */
|
|
54
89
|
export function lerpVec3(from, to, blend) {
|
|
55
90
|
return {
|
|
56
91
|
x: from.x + (to.x - from.x) * blend,
|
|
@@ -58,14 +93,31 @@ export function lerpVec3(from, to, blend) {
|
|
|
58
93
|
z: from.z + (to.z - from.z) * blend,
|
|
59
94
|
};
|
|
60
95
|
}
|
|
96
|
+
/** @internal */
|
|
61
97
|
export function distanceBetween(a, b) {
|
|
62
98
|
const dx = a.x - b.x;
|
|
63
99
|
const dy = a.y - b.y;
|
|
64
100
|
const dz = a.z - b.z;
|
|
65
101
|
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
66
102
|
}
|
|
103
|
+
/** @internal */
|
|
67
104
|
export function seedOrbitFollowState(input) {
|
|
68
105
|
const target = resolveFollowTargetFromPosition(input.entityPosition, input.config);
|
|
106
|
+
const { initialDistance, initialYaw, initialPitch } = input.config;
|
|
107
|
+
if (initialYaw !== undefined || initialPitch !== undefined) {
|
|
108
|
+
const yaw = initialYaw ?? 0;
|
|
109
|
+
const pitch = initialPitch ?? Math.atan2(input.config.initialHeight - input.config.targetHeight, initialDistance);
|
|
110
|
+
const horizontal = initialDistance * Math.cos(pitch);
|
|
111
|
+
return {
|
|
112
|
+
target,
|
|
113
|
+
camera: {
|
|
114
|
+
x: target.x - horizontal * Math.sin(yaw),
|
|
115
|
+
y: target.y + initialDistance * Math.sin(pitch),
|
|
116
|
+
z: target.z - horizontal * Math.cos(yaw),
|
|
117
|
+
},
|
|
118
|
+
lockedDistance: initialDistance,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
69
121
|
return {
|
|
70
122
|
target,
|
|
71
123
|
camera: {
|
|
@@ -80,7 +132,8 @@ export function seedOrbitFollowState(input) {
|
|
|
80
132
|
* Pure orbit follow step — smooth target tracking, camera carries target delta
|
|
81
133
|
* (OrbitControls alone keeps camera fixed when only target moves), optional
|
|
82
134
|
* distance lock. Call each frame before OrbitControls.update() in the shell.
|
|
83
|
-
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
84
137
|
export function orbitFollowStep(input) {
|
|
85
138
|
const target = { ...input.state.target };
|
|
86
139
|
const camera = { ...input.state.camera };
|
|
@@ -119,7 +172,9 @@ export function orbitFollowStep(input) {
|
|
|
119
172
|
}
|
|
120
173
|
return { target, camera, lockedDistance, distance: distanceBetween(camera, target) };
|
|
121
174
|
}
|
|
122
|
-
/** @deprecated Use orbitFollowStep — kept for older call sites.
|
|
175
|
+
/** @deprecated Use orbitFollowStep — kept for older call sites.
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
123
178
|
export function cameraFollowStep(input) {
|
|
124
179
|
const state = {
|
|
125
180
|
target: input.previousTarget ?? input.target,
|