@jgengine/shell 0.10.0 → 0.12.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 +39 -2
- package/LICENSE +203 -661
- package/README.md +1 -1
- package/dist/CombatPresentation.d.ts +5 -0
- package/dist/CombatPresentation.js +8 -0
- package/dist/GameHost.d.ts +25 -1
- package/dist/GameHost.js +60 -4
- package/dist/GamePhaseStamp.d.ts +1 -0
- package/dist/GamePhaseStamp.js +9 -0
- package/dist/GamePlayer.d.ts +5 -0
- package/dist/GamePlayer.js +5 -0
- package/dist/GamePlayerShell.d.ts +5 -12
- package/dist/GamePlayerShell.js +67 -1332
- package/dist/GameUiPreview.js +3 -2
- package/dist/Shell3dPresentation.d.ts +63 -0
- package/dist/Shell3dPresentation.js +302 -0
- package/dist/ShellChrome.d.ts +51 -0
- package/dist/ShellChrome.js +51 -0
- package/dist/ShellHudPresentation.d.ts +41 -0
- package/dist/ShellHudPresentation.js +22 -0
- package/dist/audio/audioEngine.d.ts +4 -0
- package/dist/audio/audioEngine.js +128 -21
- package/dist/audio/audioWire.d.ts +40 -0
- package/dist/audio/audioWire.js +62 -0
- package/dist/audio/loopParams.d.ts +20 -0
- package/dist/audio/loopParams.js +28 -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 +8 -3
- 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/cameraRigs.d.ts +3 -1
- package/dist/camera/cameraRigs.js +21 -3
- 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 +114 -24
- package/dist/camera/rigMath.js +129 -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/commandSink.d.ts +8 -3
- package/dist/commandSink.js +8 -3
- package/dist/defineGame.d.ts +15 -1
- package/dist/defineGame.js +50 -9
- package/dist/devtools/ColPanel.d.ts +1 -0
- package/dist/devtools/ColPanel.js +14 -0
- package/dist/devtools/DevtoolsOverlay.d.ts +11 -7
- package/dist/devtools/DevtoolsOverlay.js +47 -462
- 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 +266 -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 +36 -0
- package/dist/diagnostics/RuntimeDiagnostics.js +80 -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 +44 -9
- 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/environment/terrainGroundColor.d.ts +9 -0
- package/dist/environment/terrainGroundColor.js +22 -0
- package/dist/gameKit.d.ts +27 -0
- package/dist/gameKit.js +29 -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/map/MapMarkerBeacons.d.ts +3 -2
- package/dist/materialOverride.d.ts +10 -0
- package/dist/materialOverride.js +15 -3
- package/dist/multiplayer.js +8 -2
- package/dist/postfx/PostProcessing.d.ts +9 -1
- package/dist/postfx/PostProcessing.js +56 -11
- package/dist/postfx/gradeShader.js +9 -1
- package/dist/presentationResolve.d.ts +35 -0
- package/dist/presentationResolve.js +51 -0
- package/dist/registry.d.ts +4 -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/assetBase.d.ts +17 -0
- package/dist/render/assetBase.js +35 -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 +76 -0
- package/dist/render/modelRender.d.ts +11 -0
- package/dist/render/modelRender.js +15 -0
- package/dist/render/resolveModel.d.ts +54 -3
- package/dist/render/resolveModel.js +116 -4
- 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 +135 -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 +118 -0
- package/dist/scatter.d.ts +1 -0
- package/dist/scatter.js +1 -0
- package/dist/scene/AuthoredScene.d.ts +84 -0
- package/dist/scene/AuthoredScene.js +178 -0
- package/dist/scene/CityRenderer.d.ts +2 -0
- package/dist/scene/CityRenderer.js +1031 -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 +73 -0
- package/dist/scene/PoleLineRenderer.d.ts +2 -0
- package/dist/scene/PoleLineRenderer.js +287 -0
- package/dist/scene/SoilPatchRenderer.d.ts +2 -0
- package/dist/scene/SoilPatchRenderer.js +84 -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 +61 -0
- package/dist/scene/builtinSceneKindRenderers.d.ts +2 -0
- package/dist/scene/builtinSceneKindRenderers.js +26 -0
- package/dist/scene/cityWindowMaterial.d.ts +16 -0
- package/dist/scene/cityWindowMaterial.js +81 -0
- package/dist/scene/sceneKindRenderers.d.ts +30 -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 +7 -1
- package/dist/settings/appliedSettings.js +13 -2
- 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 +53 -0
- package/dist/structures/TransformGizmo.js +59 -0
- package/dist/structures/index.d.ts +1 -0
- package/dist/structures/index.js +1 -0
- package/dist/terrain/GrassField.d.ts +11 -3
- package/dist/terrain/GrassField.js +16 -6
- 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 +35 -0
- package/dist/terrain/grassGeometry.js +85 -20
- package/dist/terrain/grassMaterial.d.ts +20 -0
- package/dist/terrain/grassMaterial.js +68 -13
- package/dist/terrain/heightfieldGeometry.d.ts +34 -0
- package/dist/terrain/heightfieldGeometry.js +109 -0
- package/dist/terrain/index.d.ts +1 -0
- package/dist/terrain/index.js +1 -0
- package/dist/terrain/random.d.ts +3 -0
- package/dist/terrain/random.js +3 -0
- package/dist/terrain/soilPatchMaterial.d.ts +23 -0
- package/dist/terrain/soilPatchMaterial.js +147 -0
- package/dist/terrain/terrainDetailMaterial.d.ts +19 -2
- package/dist/terrain/terrainDetailMaterial.js +87 -7
- 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/Ocean.d.ts +7 -1
- package/dist/water/Ocean.js +16 -3
- package/dist/water/OceanConfig.d.ts +11 -0
- package/dist/water/OceanConfig.js +8 -0
- package/dist/water/OceanMaterial.d.ts +12 -0
- package/dist/water/OceanMaterial.js +9 -0
- package/dist/water/OceanShader.d.ts +2 -2
- package/dist/water/OceanShader.js +78 -10
- 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/PlaceScene.d.ts +11 -0
- package/dist/world/PlaceScene.js +27 -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 +186 -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 +11 -0
- package/dist/worldSky.js +21 -0
- package/dist/worldSync.d.ts +2 -1
- package/dist/worldSync.js +2 -1
- package/package.json +6 -6
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +54 -0
- package/skills/jgengine/api.md +527 -0
- package/skills/jgengine/capabilities.md +131 -0
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-ui/SKILL.md +41 -0
- package/skills/jgengine-ui/api.md +2007 -0
- package/skills/jgengine-ui/capabilities.md +211 -0
- package/skills/jgengine-ui/recipes/portable-minimap.md +150 -0
- package/skills/jgengine-ui/reference.md +513 -0
- package/skills/jgengine-ui/references/visual-scorecard.md +73 -0
- package/skills/jgengine-world/SKILL.md +51 -0
- package/skills/jgengine-world/api.md +3056 -0
- package/skills/jgengine-world/capabilities.md +476 -0
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +261 -0
- package/skills/level-design/SKILL.md +54 -0
- package/skills/level-design/agents/openai.yaml +4 -0
- package/skills/level-design/references/application-playbook.md +192 -0
- package/skills/level-design/references/field-guide.md +337 -0
- package/skills/level-design/references/live-editor-loop.md +179 -0
- package/dist/cartridge.d.ts +0 -145
- package/dist/cartridge.js +0 -246
- package/llms.txt +0 -2761
package/dist/camera/rigMath.d.ts
CHANGED
|
@@ -6,12 +6,19 @@ export interface CameraPose {
|
|
|
6
6
|
lookAt: Vec3;
|
|
7
7
|
fov: number;
|
|
8
8
|
}
|
|
9
|
+
/** @internal */
|
|
9
10
|
export declare function rtsPanKeysConflict(input: ActionCodesMap | undefined): boolean;
|
|
11
|
+
/** @internal */
|
|
10
12
|
export declare function clamp(value: number, min: number, max: number): number;
|
|
13
|
+
/** @internal */
|
|
11
14
|
export declare function lerp(from: number, to: number, blend: number): number;
|
|
15
|
+
/** @internal */
|
|
12
16
|
export declare function smoothstep(t: number): number;
|
|
17
|
+
/** @internal */
|
|
13
18
|
export declare function forwardVector(yaw: number): Vec3;
|
|
14
|
-
/** Screen-right of a yaw: `forward × up` with up = +Y.
|
|
19
|
+
/** Screen-right of a yaw: `forward × up` with up = +Y.
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
15
22
|
export declare function rightVector(yaw: number): Vec3;
|
|
16
23
|
export interface ResolvedTopDown {
|
|
17
24
|
height: number;
|
|
@@ -20,15 +27,19 @@ export interface ResolvedTopDown {
|
|
|
20
27
|
offset: Vec3;
|
|
21
28
|
followSmoothing: number;
|
|
22
29
|
}
|
|
30
|
+
/** @internal */
|
|
23
31
|
export declare function resolveTopDown(config: TopDownCameraConfig | undefined): ResolvedTopDown;
|
|
24
32
|
/**
|
|
25
33
|
* Camera pose for a fixed top-down / isometric rig. `pitch` is the elevation of
|
|
26
34
|
* the camera→target ray above the ground (PI/2 = straight down); `yaw` is the
|
|
27
35
|
* fixed azimuth (PI/4 reads isometric). Height sets zoom; horizontal boom is
|
|
28
36
|
* derived so the look angle stays constant as height changes.
|
|
29
|
-
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
30
39
|
export declare function topDownPose(follow: Vec3, resolved: ResolvedTopDown, fov: number): CameraPose;
|
|
31
|
-
/** Exponential spring-arm approach toward a desired point (frame-rate independent).
|
|
40
|
+
/** Exponential spring-arm approach toward a desired point (frame-rate independent).
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
32
43
|
export declare function springArmStep(current: Vec3, desired: Vec3, damping: number, dt: number): Vec3;
|
|
33
44
|
export interface ResolvedSideScroll {
|
|
34
45
|
axis: "x" | "z";
|
|
@@ -37,26 +48,38 @@ export interface ResolvedSideScroll {
|
|
|
37
48
|
lookHeight: number;
|
|
38
49
|
followSmoothing: number;
|
|
39
50
|
}
|
|
51
|
+
/** @internal */
|
|
40
52
|
export declare function resolveSideScroll(config: SideScrollCameraConfig | undefined): ResolvedSideScroll;
|
|
41
|
-
/** Frame-rate independent follow blend for the side-scroll rig; `followSmoothing <= 0` hard-locks (blend 1) instead of freezing.
|
|
53
|
+
/** Frame-rate independent follow blend for the side-scroll rig; `followSmoothing <= 0` hard-locks (blend 1) instead of freezing.
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
42
56
|
export declare function sideScrollFollowBlend(followSmoothing: number, dt: number): number;
|
|
43
57
|
/**
|
|
44
58
|
* Fixed lateral 2.5D follow pose: the camera sits perpendicular to the travel
|
|
45
59
|
* axis at `distance`, above the entity by `height`, and looks at the entity
|
|
46
60
|
* raised by `lookHeight`. Axis "x" watches from +z; axis "z" watches from +x.
|
|
47
|
-
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
48
63
|
export declare function resolveSideScrollPose(entityPos: Vec3, resolved: ResolvedSideScroll, fov: number): CameraPose;
|
|
49
|
-
/** Speed→FOV curve: FOV climbs from base to max as speed rises to `speedForMax`.
|
|
64
|
+
/** Speed→FOV curve: FOV climbs from base to max as speed rises to `speedForMax`.
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
50
67
|
export declare function speedToFov(speed: number, curve: {
|
|
51
68
|
base?: number;
|
|
52
69
|
max?: number;
|
|
53
70
|
speedForMax?: number;
|
|
54
71
|
} | undefined): number;
|
|
55
|
-
/** Yaw that points from `from` toward `to` on the XZ plane (matches shell forward = (sin, cos)).
|
|
72
|
+
/** Yaw that points from `from` toward `to` on the XZ plane (matches shell forward = (sin, cos)).
|
|
73
|
+
* @internal
|
|
74
|
+
*/
|
|
56
75
|
export declare function yawTo(from: Vec3, to: Vec3): number;
|
|
57
|
-
/** Shortest signed angular delta from `a` to `b`, wrapped to (-PI, PI].
|
|
76
|
+
/** Shortest signed angular delta from `a` to `b`, wrapped to (-PI, PI].
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
58
79
|
export declare function angleDelta(a: number, b: number): number;
|
|
59
|
-
/** Exponential yaw smoothing that respects wrap-around.
|
|
80
|
+
/** Exponential yaw smoothing that respects wrap-around.
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
60
83
|
export declare function smoothYaw(current: number, desired: number, speed: number, dt: number): number;
|
|
61
84
|
export interface ResolvedShoulder {
|
|
62
85
|
shoulderOffset: number;
|
|
@@ -64,20 +87,25 @@ export interface ResolvedShoulder {
|
|
|
64
87
|
distance: number;
|
|
65
88
|
fov: number;
|
|
66
89
|
}
|
|
90
|
+
/** @internal */
|
|
67
91
|
export declare function resolveShoulder(config: ShoulderCameraConfig | undefined, aiming: boolean): ResolvedShoulder;
|
|
68
|
-
/** Blend two shoulder framings by an ADS factor in [0,1].
|
|
92
|
+
/** Blend two shoulder framings by an ADS factor in [0,1].
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
69
95
|
export declare function blendShoulder(hip: ResolvedShoulder, ads: ResolvedShoulder, t: number): ResolvedShoulder;
|
|
70
96
|
/**
|
|
71
97
|
* Over-the-shoulder camera pose. The boom sits behind the follow point along
|
|
72
98
|
* `yaw`, raised by `heightOffset`, and pushed sideways by `shoulderOffset`
|
|
73
99
|
* (sign set by `sideSign`: +1 right, -1 left). `pitch` tilts the look point.
|
|
74
|
-
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
75
102
|
export declare function shoulderPose(follow: Vec3, yaw: number, pitch: number, sideSign: number, shoulder: ResolvedShoulder): CameraPose;
|
|
76
103
|
/**
|
|
77
104
|
* Lock-on pose: camera sits behind the player along the player→target vector so
|
|
78
105
|
* the target stays framed. The look point is biased between player and target
|
|
79
106
|
* by `framingBias`.
|
|
80
|
-
|
|
107
|
+
* @internal
|
|
108
|
+
*/
|
|
81
109
|
export declare function lockOnPose(player: Vec3, target: Vec3, config: LockOnCameraConfig | undefined, fov: number): {
|
|
82
110
|
pose: CameraPose;
|
|
83
111
|
yaw: number;
|
|
@@ -93,11 +121,22 @@ export interface ResolvedChase {
|
|
|
93
121
|
bankPerYawRate: number;
|
|
94
122
|
bankMax: number;
|
|
95
123
|
bankDamping: number;
|
|
124
|
+
/** Max fraction of the slip angle the anchor yaw follows toward velocity; 0 = drift-lag off (#1051). */
|
|
125
|
+
velocityYawBlend: number;
|
|
126
|
+
/** Below this planar speed the drift-lag feature is fully off. */
|
|
127
|
+
velocityYawMinSpeed: number;
|
|
128
|
+
/** Exponential smoothing rate toward the blended anchor yaw (1-exp(-response*dt)). */
|
|
129
|
+
velocityYawResponse: number;
|
|
96
130
|
}
|
|
131
|
+
/** @internal */
|
|
97
132
|
export declare function resolveChase(config: ChaseCameraConfig | undefined): ResolvedChase;
|
|
98
|
-
/** Velocity-lead the follow point (#286.9): aim `leadTime` seconds along the target's frame velocity, clamped to `leadMax`.
|
|
133
|
+
/** Velocity-lead the follow point (#286.9): aim `leadTime` seconds along the target's frame velocity, clamped to `leadMax`.
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
99
136
|
export declare function leadFollowPoint(follow: Vec3, previous: Vec3, dt: number, resolved: ResolvedChase): Vec3;
|
|
100
|
-
/** One smoothing step of the chase rig's turn-bank roll (#286.10): roll opposes yaw rate, clamped and exponentially damped.
|
|
137
|
+
/** One smoothing step of the chase rig's turn-bank roll (#286.10): roll opposes yaw rate, clamped and exponentially damped.
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
101
140
|
export declare function bankRollStep(currentRoll: number, yaw: number, previousYaw: number, dt: number, resolved: ResolvedChase): number;
|
|
102
141
|
export interface ResolvedObserver {
|
|
103
142
|
distance: number;
|
|
@@ -105,14 +144,37 @@ export interface ResolvedObserver {
|
|
|
105
144
|
lookHeight: number;
|
|
106
145
|
orbitSpeed: number;
|
|
107
146
|
}
|
|
147
|
+
/** @internal */
|
|
108
148
|
export declare function resolveObserver(config: ObserverCameraConfig | undefined): ResolvedObserver;
|
|
109
|
-
/** Detached spectator pose: orbits `subject` at a fixed distance/height, never reading player input.
|
|
149
|
+
/** Detached spectator pose: orbits `subject` at a fixed distance/height, never reading player input.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
110
152
|
export declare function observerPose(subject: Vec3, angle: number, resolved: ResolvedObserver, fov: number): CameraPose;
|
|
111
|
-
/** Desired chase-camera position behind a vehicle facing `yaw` (before spring smoothing).
|
|
153
|
+
/** Desired chase-camera position behind a vehicle facing `yaw` (before spring smoothing).
|
|
154
|
+
* @internal
|
|
155
|
+
*/
|
|
112
156
|
export declare function chaseDesiredPosition(follow: Vec3, yaw: number, resolved: ResolvedChase): Vec3;
|
|
113
|
-
/** Look point ahead of / above a chased vehicle.
|
|
157
|
+
/** Look point ahead of / above a chased vehicle.
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
114
160
|
export declare function chaseLookAt(follow: Vec3, yaw: number, resolved: ResolvedChase): Vec3;
|
|
115
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* Drift-lag (#1051): the yaw the chase anchor should aim before temporal
|
|
163
|
+
* smoothing. When the target slides, its planar `velocity` diverges from its
|
|
164
|
+
* `heading`; this rotates the anchor from heading partway toward the velocity
|
|
165
|
+
* direction so the camera reveals the car's side while travel stays framed. The
|
|
166
|
+
* traversed fraction is `velocityYawBlend` scaled by how far the slip angle has
|
|
167
|
+
* opened (0 at straight-line travel, full at a 90° slide), so ordinary driving
|
|
168
|
+
* is untouched. Returns the raw heading — no swing — when the feature is off,
|
|
169
|
+
* when planar speed is below `velocityYawMinSpeed`, or when the target is
|
|
170
|
+
* reversing (velocity opposed to heading forward). Blending is shortest-arc, so
|
|
171
|
+
* it stays correct across the ±PI wrap seam.
|
|
172
|
+
* @internal
|
|
173
|
+
*/
|
|
174
|
+
export declare function velocityYawTarget(heading: number, velocity: Vec3, resolved: ResolvedChase): number;
|
|
175
|
+
/** World-space seat pose (cockpit/hood/rear) rigidly attached to the vehicle.
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
116
178
|
export declare function seatPose(follow: Vec3, yaw: number, local: {
|
|
117
179
|
x?: number;
|
|
118
180
|
y?: number;
|
|
@@ -122,10 +184,15 @@ export interface TraumaState {
|
|
|
122
184
|
trauma: number;
|
|
123
185
|
time: number;
|
|
124
186
|
}
|
|
187
|
+
/** @internal */
|
|
125
188
|
export declare function createTrauma(): TraumaState;
|
|
126
|
-
/** Add trauma (0..1) and clamp; larger hits raise the ceiling toward 1.
|
|
189
|
+
/** Add trauma (0..1) and clamp; larger hits raise the ceiling toward 1.
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
127
192
|
export declare function addTrauma(state: TraumaState, amount: number): void;
|
|
128
|
-
/** Decay trauma linearly toward 0 and advance the shake clock.
|
|
193
|
+
/** Decay trauma linearly toward 0 and advance the shake clock.
|
|
194
|
+
* @internal
|
|
195
|
+
*/
|
|
129
196
|
export declare function stepTrauma(state: TraumaState, decayPerSecond: number, dt: number): void;
|
|
130
197
|
export interface ShakeOffset {
|
|
131
198
|
x: number;
|
|
@@ -136,14 +203,35 @@ export interface ShakeOffset {
|
|
|
136
203
|
* Positional + roll shake for the current trauma. Shake magnitude is
|
|
137
204
|
* `trauma^exponent` so small hits stay subtle; the oscillation is deterministic
|
|
138
205
|
* per-seed pseudo-noise (no per-frame RNG, so identical inputs reproduce).
|
|
139
|
-
|
|
206
|
+
* @internal
|
|
207
|
+
*/
|
|
140
208
|
export declare function shakeOffset(state: TraumaState, config: {
|
|
141
209
|
maxOffset?: number;
|
|
142
210
|
maxRoll?: number;
|
|
143
211
|
exponent?: number;
|
|
144
212
|
frequency?: number;
|
|
145
213
|
} | undefined): ShakeOffset;
|
|
146
|
-
/**
|
|
214
|
+
/** Calibrated trauma decay for `traumaShake`/`impactPresets` (trauma/second). */
|
|
215
|
+
export declare const CALIBRATED_TRAUMA_SHAKE_DECAY_PER_SECOND = 1.4;
|
|
216
|
+
/** Calibrated positional shake amplitude at full trauma (world units) for `traumaShake`. */
|
|
217
|
+
export declare const CALIBRATED_TRAUMA_SHAKE_MAX_OFFSET = 0.55;
|
|
218
|
+
/** Calibrated rotational shake amplitude at full trauma (radians) for `traumaShake`. */
|
|
219
|
+
export declare const CALIBRATED_TRAUMA_SHAKE_MAX_ROLL = 0.1;
|
|
220
|
+
/** Calibrated shake noise frequency (Hz) for `traumaShake`. */
|
|
221
|
+
export declare const CALIBRATED_TRAUMA_SHAKE_FREQUENCY = 32;
|
|
222
|
+
/**
|
|
223
|
+
* Calibrated trauma² camera shake: offset scales with `trauma` squared using
|
|
224
|
+
* game-feel defaults (decay 1.4/s, max offset 0.55, max roll 0.1 rad, noise
|
|
225
|
+
* freq 32×t) — the curve `impactPresets` trauma values are calibrated
|
|
226
|
+
* against, so an impact "just looks right" without hand-tuning `shakeOffset`.
|
|
227
|
+
*
|
|
228
|
+
* @capability camera-shake calibrated trauma² camera kick with zero tuning
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
export declare function traumaShake(trauma: number, time?: number): ShakeOffset;
|
|
232
|
+
/** Linear cross-fade between two full camera poses (position, lookAt, fov).
|
|
233
|
+
* @internal
|
|
234
|
+
*/
|
|
147
235
|
export declare function crossfadePose(from: CameraPose, to: CameraPose, t: number): CameraPose;
|
|
148
236
|
export interface DirectorCameraValues {
|
|
149
237
|
/** `undefined` = no runtime override (fall back to static); `null` = explicitly follow nothing. */
|
|
@@ -164,7 +252,8 @@ export interface ResolvedDirectedCamera {
|
|
|
164
252
|
* (#196.2). `director` omitted, or its fields `undefined`/`null`, is a pure
|
|
165
253
|
* passthrough to `staticConfig` so mounting a director with no active override
|
|
166
254
|
* changes nothing.
|
|
167
|
-
|
|
255
|
+
* @internal
|
|
256
|
+
*/
|
|
168
257
|
export declare function resolveDirectedCamera(director: DirectorCameraValues | undefined, staticConfig: StaticCameraValues): ResolvedDirectedCamera;
|
|
169
258
|
export interface CinematicSample {
|
|
170
259
|
pose: CameraPose;
|
|
@@ -175,5 +264,6 @@ export interface CinematicSample {
|
|
|
175
264
|
* travel time from the previous keyframe into it; segment easing is per the
|
|
176
265
|
* destination keyframe. Reports `done` once past the final keyframe (unless
|
|
177
266
|
* looping, which wraps `elapsed` into the total path duration).
|
|
178
|
-
|
|
267
|
+
* @internal
|
|
268
|
+
*/
|
|
179
269
|
export declare function cinematicSample(keyframes: readonly CameraKeyframe[], elapsed: number, loop: boolean, fallbackFov: number): CinematicSample;
|
package/dist/camera/rigMath.js
CHANGED
|
@@ -6,28 +6,36 @@ function actionCodeList(codes) {
|
|
|
6
6
|
const modes = codes;
|
|
7
7
|
return [...(modes.hold ?? []), ...(modes.toggle ?? [])];
|
|
8
8
|
}
|
|
9
|
+
/** @internal */
|
|
9
10
|
export function rtsPanKeysConflict(input) {
|
|
10
11
|
if (input === undefined)
|
|
11
12
|
return false;
|
|
12
13
|
return Object.values(input).some((codes) => actionCodeList(codes).some((code) => RTS_WASD_CODES.includes(code)));
|
|
13
14
|
}
|
|
15
|
+
/** @internal */
|
|
14
16
|
export function clamp(value, min, max) {
|
|
15
17
|
return value < min ? min : value > max ? max : value;
|
|
16
18
|
}
|
|
19
|
+
/** @internal */
|
|
17
20
|
export function lerp(from, to, blend) {
|
|
18
21
|
return from + (to - from) * blend;
|
|
19
22
|
}
|
|
23
|
+
/** @internal */
|
|
20
24
|
export function smoothstep(t) {
|
|
21
25
|
const clamped = clamp(t, 0, 1);
|
|
22
26
|
return clamped * clamped * (3 - 2 * clamped);
|
|
23
27
|
}
|
|
28
|
+
/** @internal */
|
|
24
29
|
export function forwardVector(yaw) {
|
|
25
30
|
return { x: Math.sin(yaw), y: 0, z: Math.cos(yaw) };
|
|
26
31
|
}
|
|
27
|
-
/** Screen-right of a yaw: `forward × up` with up = +Y.
|
|
32
|
+
/** Screen-right of a yaw: `forward × up` with up = +Y.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
28
35
|
export function rightVector(yaw) {
|
|
29
36
|
return { x: -Math.cos(yaw), y: 0, z: Math.sin(yaw) };
|
|
30
37
|
}
|
|
38
|
+
/** @internal */
|
|
31
39
|
export function resolveTopDown(config) {
|
|
32
40
|
return {
|
|
33
41
|
height: config?.height ?? 18,
|
|
@@ -46,7 +54,8 @@ export function resolveTopDown(config) {
|
|
|
46
54
|
* the camera→target ray above the ground (PI/2 = straight down); `yaw` is the
|
|
47
55
|
* fixed azimuth (PI/4 reads isometric). Height sets zoom; horizontal boom is
|
|
48
56
|
* derived so the look angle stays constant as height changes.
|
|
49
|
-
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
50
59
|
export function topDownPose(follow, resolved, fov) {
|
|
51
60
|
const lookAt = {
|
|
52
61
|
x: follow.x + resolved.offset.x,
|
|
@@ -65,10 +74,13 @@ export function topDownPose(follow, resolved, fov) {
|
|
|
65
74
|
fov,
|
|
66
75
|
};
|
|
67
76
|
}
|
|
68
|
-
/** Exponential spring-arm approach toward a desired point (frame-rate independent).
|
|
77
|
+
/** Exponential spring-arm approach toward a desired point (frame-rate independent).
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
69
80
|
export function springArmStep(current, desired, damping, dt) {
|
|
70
81
|
return lerpVec3(current, desired, smoothBlend(dt, damping));
|
|
71
82
|
}
|
|
83
|
+
/** @internal */
|
|
72
84
|
export function resolveSideScroll(config) {
|
|
73
85
|
return {
|
|
74
86
|
axis: config?.axis ?? "x",
|
|
@@ -78,7 +90,9 @@ export function resolveSideScroll(config) {
|
|
|
78
90
|
followSmoothing: config?.followSmoothing ?? 8,
|
|
79
91
|
};
|
|
80
92
|
}
|
|
81
|
-
/** Frame-rate independent follow blend for the side-scroll rig; `followSmoothing <= 0` hard-locks (blend 1) instead of freezing.
|
|
93
|
+
/** Frame-rate independent follow blend for the side-scroll rig; `followSmoothing <= 0` hard-locks (blend 1) instead of freezing.
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
82
96
|
export function sideScrollFollowBlend(followSmoothing, dt) {
|
|
83
97
|
return followSmoothing <= 0 ? 1 : smoothBlend(dt, followSmoothing);
|
|
84
98
|
}
|
|
@@ -86,7 +100,8 @@ export function sideScrollFollowBlend(followSmoothing, dt) {
|
|
|
86
100
|
* Fixed lateral 2.5D follow pose: the camera sits perpendicular to the travel
|
|
87
101
|
* axis at `distance`, above the entity by `height`, and looks at the entity
|
|
88
102
|
* raised by `lookHeight`. Axis "x" watches from +z; axis "z" watches from +x.
|
|
89
|
-
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
90
105
|
export function resolveSideScrollPose(entityPos, resolved, fov) {
|
|
91
106
|
const perpendicular = resolved.axis === "x" ? { x: 0, y: 0, z: 1 } : { x: 1, y: 0, z: 0 };
|
|
92
107
|
return {
|
|
@@ -99,7 +114,9 @@ export function resolveSideScrollPose(entityPos, resolved, fov) {
|
|
|
99
114
|
fov,
|
|
100
115
|
};
|
|
101
116
|
}
|
|
102
|
-
/** Speed→FOV curve: FOV climbs from base to max as speed rises to `speedForMax`.
|
|
117
|
+
/** Speed→FOV curve: FOV climbs from base to max as speed rises to `speedForMax`.
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
103
120
|
export function speedToFov(speed, curve) {
|
|
104
121
|
const base = curve?.base ?? 55;
|
|
105
122
|
const max = curve?.max ?? 78;
|
|
@@ -108,11 +125,15 @@ export function speedToFov(speed, curve) {
|
|
|
108
125
|
return base;
|
|
109
126
|
return lerp(base, max, clamp(speed / speedForMax, 0, 1));
|
|
110
127
|
}
|
|
111
|
-
/** Yaw that points from `from` toward `to` on the XZ plane (matches shell forward = (sin, cos)).
|
|
128
|
+
/** Yaw that points from `from` toward `to` on the XZ plane (matches shell forward = (sin, cos)).
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
112
131
|
export function yawTo(from, to) {
|
|
113
132
|
return Math.atan2(to.x - from.x, to.z - from.z);
|
|
114
133
|
}
|
|
115
|
-
/** Shortest signed angular delta from `a` to `b`, wrapped to (-PI, PI].
|
|
134
|
+
/** Shortest signed angular delta from `a` to `b`, wrapped to (-PI, PI].
|
|
135
|
+
* @internal
|
|
136
|
+
*/
|
|
116
137
|
export function angleDelta(a, b) {
|
|
117
138
|
let delta = (b - a) % (Math.PI * 2);
|
|
118
139
|
if (delta > Math.PI)
|
|
@@ -121,10 +142,13 @@ export function angleDelta(a, b) {
|
|
|
121
142
|
delta += Math.PI * 2;
|
|
122
143
|
return delta;
|
|
123
144
|
}
|
|
124
|
-
/** Exponential yaw smoothing that respects wrap-around.
|
|
145
|
+
/** Exponential yaw smoothing that respects wrap-around.
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
125
148
|
export function smoothYaw(current, desired, speed, dt) {
|
|
126
149
|
return current + angleDelta(current, desired) * smoothBlend(dt, speed);
|
|
127
150
|
}
|
|
151
|
+
/** @internal */
|
|
128
152
|
export function resolveShoulder(config, aiming) {
|
|
129
153
|
const hip = {
|
|
130
154
|
shoulderOffset: config?.shoulderOffset ?? 0.6,
|
|
@@ -142,7 +166,9 @@ export function resolveShoulder(config, aiming) {
|
|
|
142
166
|
fov: ads?.fov ?? hip.fov * 0.75,
|
|
143
167
|
};
|
|
144
168
|
}
|
|
145
|
-
/** Blend two shoulder framings by an ADS factor in [0,1].
|
|
169
|
+
/** Blend two shoulder framings by an ADS factor in [0,1].
|
|
170
|
+
* @internal
|
|
171
|
+
*/
|
|
146
172
|
export function blendShoulder(hip, ads, t) {
|
|
147
173
|
return {
|
|
148
174
|
shoulderOffset: lerp(hip.shoulderOffset, ads.shoulderOffset, t),
|
|
@@ -155,7 +181,8 @@ export function blendShoulder(hip, ads, t) {
|
|
|
155
181
|
* Over-the-shoulder camera pose. The boom sits behind the follow point along
|
|
156
182
|
* `yaw`, raised by `heightOffset`, and pushed sideways by `shoulderOffset`
|
|
157
183
|
* (sign set by `sideSign`: +1 right, -1 left). `pitch` tilts the look point.
|
|
158
|
-
|
|
184
|
+
* @internal
|
|
185
|
+
*/
|
|
159
186
|
export function shoulderPose(follow, yaw, pitch, sideSign, shoulder) {
|
|
160
187
|
const forward = forwardVector(yaw);
|
|
161
188
|
const right = rightVector(yaw);
|
|
@@ -181,7 +208,8 @@ export function shoulderPose(follow, yaw, pitch, sideSign, shoulder) {
|
|
|
181
208
|
* Lock-on pose: camera sits behind the player along the player→target vector so
|
|
182
209
|
* the target stays framed. The look point is biased between player and target
|
|
183
210
|
* by `framingBias`.
|
|
184
|
-
|
|
211
|
+
* @internal
|
|
212
|
+
*/
|
|
185
213
|
export function lockOnPose(player, target, config, fov) {
|
|
186
214
|
const distance = config?.distance ?? 5;
|
|
187
215
|
const height = config?.height ?? 2.4;
|
|
@@ -201,6 +229,7 @@ export function lockOnPose(player, target, config, fov) {
|
|
|
201
229
|
};
|
|
202
230
|
return { pose: { position, lookAt: focus, fov }, yaw };
|
|
203
231
|
}
|
|
232
|
+
/** @internal */
|
|
204
233
|
export function resolveChase(config) {
|
|
205
234
|
return {
|
|
206
235
|
distance: config?.distance ?? 6,
|
|
@@ -213,9 +242,14 @@ export function resolveChase(config) {
|
|
|
213
242
|
bankPerYawRate: config?.bank?.perYawRate ?? 0,
|
|
214
243
|
bankMax: config?.bank?.max ?? 0.35,
|
|
215
244
|
bankDamping: config?.bank?.damping ?? 8,
|
|
245
|
+
velocityYawBlend: config?.velocityYaw === undefined ? 0 : config.velocityYaw.blend ?? 0.65,
|
|
246
|
+
velocityYawMinSpeed: config?.velocityYaw?.minSpeed ?? 4,
|
|
247
|
+
velocityYawResponse: config?.velocityYaw?.response ?? 6,
|
|
216
248
|
};
|
|
217
249
|
}
|
|
218
|
-
/** Velocity-lead the follow point (#286.9): aim `leadTime` seconds along the target's frame velocity, clamped to `leadMax`.
|
|
250
|
+
/** Velocity-lead the follow point (#286.9): aim `leadTime` seconds along the target's frame velocity, clamped to `leadMax`.
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
219
253
|
export function leadFollowPoint(follow, previous, dt, resolved) {
|
|
220
254
|
if (resolved.leadTime <= 0 || dt <= 0)
|
|
221
255
|
return follow;
|
|
@@ -236,7 +270,9 @@ function wrapAngle(angle) {
|
|
|
236
270
|
wrapped += Math.PI * 2;
|
|
237
271
|
return wrapped;
|
|
238
272
|
}
|
|
239
|
-
/** One smoothing step of the chase rig's turn-bank roll (#286.10): roll opposes yaw rate, clamped and exponentially damped.
|
|
273
|
+
/** One smoothing step of the chase rig's turn-bank roll (#286.10): roll opposes yaw rate, clamped and exponentially damped.
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
240
276
|
export function bankRollStep(currentRoll, yaw, previousYaw, dt, resolved) {
|
|
241
277
|
if (resolved.bankPerYawRate <= 0)
|
|
242
278
|
return 0;
|
|
@@ -244,6 +280,7 @@ export function bankRollStep(currentRoll, yaw, previousYaw, dt, resolved) {
|
|
|
244
280
|
const target = clamp(-yawRate * resolved.bankPerYawRate, -resolved.bankMax, resolved.bankMax);
|
|
245
281
|
return currentRoll + (target - currentRoll) * Math.min(1, resolved.bankDamping * dt);
|
|
246
282
|
}
|
|
283
|
+
/** @internal */
|
|
247
284
|
export function resolveObserver(config) {
|
|
248
285
|
return {
|
|
249
286
|
distance: config?.distance ?? 8,
|
|
@@ -252,7 +289,9 @@ export function resolveObserver(config) {
|
|
|
252
289
|
orbitSpeed: config?.orbitSpeed ?? 0.2,
|
|
253
290
|
};
|
|
254
291
|
}
|
|
255
|
-
/** Detached spectator pose: orbits `subject` at a fixed distance/height, never reading player input.
|
|
292
|
+
/** Detached spectator pose: orbits `subject` at a fixed distance/height, never reading player input.
|
|
293
|
+
* @internal
|
|
294
|
+
*/
|
|
256
295
|
export function observerPose(subject, angle, resolved, fov) {
|
|
257
296
|
const position = {
|
|
258
297
|
x: subject.x + Math.sin(angle) * resolved.distance,
|
|
@@ -265,7 +304,9 @@ export function observerPose(subject, angle, resolved, fov) {
|
|
|
265
304
|
fov,
|
|
266
305
|
};
|
|
267
306
|
}
|
|
268
|
-
/** Desired chase-camera position behind a vehicle facing `yaw` (before spring smoothing).
|
|
307
|
+
/** Desired chase-camera position behind a vehicle facing `yaw` (before spring smoothing).
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
269
310
|
export function chaseDesiredPosition(follow, yaw, resolved) {
|
|
270
311
|
const back = forwardVector(yaw);
|
|
271
312
|
return {
|
|
@@ -274,7 +315,9 @@ export function chaseDesiredPosition(follow, yaw, resolved) {
|
|
|
274
315
|
z: follow.z - back.z * resolved.distance,
|
|
275
316
|
};
|
|
276
317
|
}
|
|
277
|
-
/** Look point ahead of / above a chased vehicle.
|
|
318
|
+
/** Look point ahead of / above a chased vehicle.
|
|
319
|
+
* @internal
|
|
320
|
+
*/
|
|
278
321
|
export function chaseLookAt(follow, yaw, resolved) {
|
|
279
322
|
const forward = forwardVector(yaw);
|
|
280
323
|
return {
|
|
@@ -283,7 +326,35 @@ export function chaseLookAt(follow, yaw, resolved) {
|
|
|
283
326
|
z: follow.z + forward.z * 2,
|
|
284
327
|
};
|
|
285
328
|
}
|
|
286
|
-
/**
|
|
329
|
+
/**
|
|
330
|
+
* Drift-lag (#1051): the yaw the chase anchor should aim before temporal
|
|
331
|
+
* smoothing. When the target slides, its planar `velocity` diverges from its
|
|
332
|
+
* `heading`; this rotates the anchor from heading partway toward the velocity
|
|
333
|
+
* direction so the camera reveals the car's side while travel stays framed. The
|
|
334
|
+
* traversed fraction is `velocityYawBlend` scaled by how far the slip angle has
|
|
335
|
+
* opened (0 at straight-line travel, full at a 90° slide), so ordinary driving
|
|
336
|
+
* is untouched. Returns the raw heading — no swing — when the feature is off,
|
|
337
|
+
* when planar speed is below `velocityYawMinSpeed`, or when the target is
|
|
338
|
+
* reversing (velocity opposed to heading forward). Blending is shortest-arc, so
|
|
339
|
+
* it stays correct across the ±PI wrap seam.
|
|
340
|
+
* @internal
|
|
341
|
+
*/
|
|
342
|
+
export function velocityYawTarget(heading, velocity, resolved) {
|
|
343
|
+
if (resolved.velocityYawBlend <= 0)
|
|
344
|
+
return heading;
|
|
345
|
+
const speed = Math.hypot(velocity.x, velocity.z);
|
|
346
|
+
if (speed < resolved.velocityYawMinSpeed)
|
|
347
|
+
return heading;
|
|
348
|
+
const forward = forwardVector(heading);
|
|
349
|
+
if (forward.x * velocity.x + forward.z * velocity.z < 0)
|
|
350
|
+
return heading;
|
|
351
|
+
const slip = angleDelta(heading, Math.atan2(velocity.x, velocity.z));
|
|
352
|
+
const slipFraction = clamp(Math.abs(slip) / (Math.PI / 2), 0, 1);
|
|
353
|
+
return heading + slip * resolved.velocityYawBlend * slipFraction;
|
|
354
|
+
}
|
|
355
|
+
/** World-space seat pose (cockpit/hood/rear) rigidly attached to the vehicle.
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
287
358
|
export function seatPose(follow, yaw, local, fov) {
|
|
288
359
|
const forward = forwardVector(yaw);
|
|
289
360
|
const right = rightVector(yaw);
|
|
@@ -301,14 +372,19 @@ export function seatPose(follow, yaw, local, fov) {
|
|
|
301
372
|
fov,
|
|
302
373
|
};
|
|
303
374
|
}
|
|
375
|
+
/** @internal */
|
|
304
376
|
export function createTrauma() {
|
|
305
377
|
return { trauma: 0, time: 0 };
|
|
306
378
|
}
|
|
307
|
-
/** Add trauma (0..1) and clamp; larger hits raise the ceiling toward 1.
|
|
379
|
+
/** Add trauma (0..1) and clamp; larger hits raise the ceiling toward 1.
|
|
380
|
+
* @internal
|
|
381
|
+
*/
|
|
308
382
|
export function addTrauma(state, amount) {
|
|
309
383
|
state.trauma = clamp(state.trauma + amount, 0, 1);
|
|
310
384
|
}
|
|
311
|
-
/** Decay trauma linearly toward 0 and advance the shake clock.
|
|
385
|
+
/** Decay trauma linearly toward 0 and advance the shake clock.
|
|
386
|
+
* @internal
|
|
387
|
+
*/
|
|
312
388
|
export function stepTrauma(state, decayPerSecond, dt) {
|
|
313
389
|
state.time += dt;
|
|
314
390
|
state.trauma = clamp(state.trauma - decayPerSecond * dt, 0, 1);
|
|
@@ -321,7 +397,8 @@ function hashNoise(seed, time, frequency) {
|
|
|
321
397
|
* Positional + roll shake for the current trauma. Shake magnitude is
|
|
322
398
|
* `trauma^exponent` so small hits stay subtle; the oscillation is deterministic
|
|
323
399
|
* per-seed pseudo-noise (no per-frame RNG, so identical inputs reproduce).
|
|
324
|
-
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
325
402
|
export function shakeOffset(state, config) {
|
|
326
403
|
const maxOffset = config?.maxOffset ?? 0.5;
|
|
327
404
|
const maxRoll = config?.maxRoll ?? 0.06;
|
|
@@ -336,7 +413,33 @@ export function shakeOffset(state, config) {
|
|
|
336
413
|
roll: maxRoll * shake * hashNoise(3, state.time, frequency),
|
|
337
414
|
};
|
|
338
415
|
}
|
|
339
|
-
/**
|
|
416
|
+
/** Calibrated trauma decay for `traumaShake`/`impactPresets` (trauma/second). */
|
|
417
|
+
export const CALIBRATED_TRAUMA_SHAKE_DECAY_PER_SECOND = 1.4;
|
|
418
|
+
/** Calibrated positional shake amplitude at full trauma (world units) for `traumaShake`. */
|
|
419
|
+
export const CALIBRATED_TRAUMA_SHAKE_MAX_OFFSET = 0.55;
|
|
420
|
+
/** Calibrated rotational shake amplitude at full trauma (radians) for `traumaShake`. */
|
|
421
|
+
export const CALIBRATED_TRAUMA_SHAKE_MAX_ROLL = 0.1;
|
|
422
|
+
/** Calibrated shake noise frequency (Hz) for `traumaShake`. */
|
|
423
|
+
export const CALIBRATED_TRAUMA_SHAKE_FREQUENCY = 32;
|
|
424
|
+
/**
|
|
425
|
+
* Calibrated trauma² camera shake: offset scales with `trauma` squared using
|
|
426
|
+
* game-feel defaults (decay 1.4/s, max offset 0.55, max roll 0.1 rad, noise
|
|
427
|
+
* freq 32×t) — the curve `impactPresets` trauma values are calibrated
|
|
428
|
+
* against, so an impact "just looks right" without hand-tuning `shakeOffset`.
|
|
429
|
+
*
|
|
430
|
+
* @capability camera-shake calibrated trauma² camera kick with zero tuning
|
|
431
|
+
* @internal
|
|
432
|
+
*/
|
|
433
|
+
export function traumaShake(trauma, time = 0) {
|
|
434
|
+
return shakeOffset({ trauma: clamp(trauma, 0, 1), time }, {
|
|
435
|
+
maxOffset: CALIBRATED_TRAUMA_SHAKE_MAX_OFFSET,
|
|
436
|
+
maxRoll: CALIBRATED_TRAUMA_SHAKE_MAX_ROLL,
|
|
437
|
+
frequency: CALIBRATED_TRAUMA_SHAKE_FREQUENCY,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/** Linear cross-fade between two full camera poses (position, lookAt, fov).
|
|
441
|
+
* @internal
|
|
442
|
+
*/
|
|
340
443
|
export function crossfadePose(from, to, t) {
|
|
341
444
|
const blend = clamp(t, 0, 1);
|
|
342
445
|
return {
|
|
@@ -350,7 +453,8 @@ export function crossfadePose(from, to, t) {
|
|
|
350
453
|
* (#196.2). `director` omitted, or its fields `undefined`/`null`, is a pure
|
|
351
454
|
* passthrough to `staticConfig` so mounting a director with no active override
|
|
352
455
|
* changes nothing.
|
|
353
|
-
|
|
456
|
+
* @internal
|
|
457
|
+
*/
|
|
354
458
|
export function resolveDirectedCamera(director, staticConfig) {
|
|
355
459
|
const directedFollow = director?.followEntityId;
|
|
356
460
|
const followEntityId = directedFollow === undefined ? staticConfig.followEntityId : directedFollow;
|
|
@@ -363,7 +467,8 @@ export function resolveDirectedCamera(director, staticConfig) {
|
|
|
363
467
|
* travel time from the previous keyframe into it; segment easing is per the
|
|
364
468
|
* destination keyframe. Reports `done` once past the final keyframe (unless
|
|
365
469
|
* looping, which wraps `elapsed` into the total path duration).
|
|
366
|
-
|
|
470
|
+
* @internal
|
|
471
|
+
*/
|
|
367
472
|
export function cinematicSample(keyframes, elapsed, loop, fallbackFov) {
|
|
368
473
|
if (keyframes.length === 0) {
|
|
369
474
|
const zero = { x: 0, y: 0, z: 0 };
|
|
@@ -6,10 +6,12 @@ import type { CameraRigKind, GameCameraConfig } from "@jgengine/core/game/playab
|
|
|
6
6
|
* of a rig's own config block selects that rig, checked in the fixed order below
|
|
7
7
|
* (#207.8) so a config carrying more than one block resolves deterministically
|
|
8
8
|
* instead of depending on object key order. Set `rig` explicitly to break a tie.
|
|
9
|
-
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
10
11
|
export declare function resolveRigKind(config: GameCameraConfig | undefined): CameraRigKind;
|
|
11
12
|
/**
|
|
12
13
|
* The turntable rig is a flat facade over the observer's point-orbit mode: map
|
|
13
14
|
* its `target`/`distance`/… onto an observer block so ObserverRig runs unchanged.
|
|
14
|
-
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
15
17
|
export declare function turntableAsObserver(config: GameCameraConfig | undefined): GameCameraConfig;
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* of a rig's own config block selects that rig, checked in the fixed order below
|
|
6
6
|
* (#207.8) so a config carrying more than one block resolves deterministically
|
|
7
7
|
* instead of depending on object key order. Set `rig` explicitly to break a tie.
|
|
8
|
-
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
9
10
|
export function resolveRigKind(config) {
|
|
10
11
|
if (config?.rig !== undefined)
|
|
11
12
|
return config.rig;
|
|
@@ -34,7 +35,8 @@ export function resolveRigKind(config) {
|
|
|
34
35
|
/**
|
|
35
36
|
* The turntable rig is a flat facade over the observer's point-orbit mode: map
|
|
36
37
|
* its `target`/`distance`/… onto an observer block so ObserverRig runs unchanged.
|
|
37
|
-
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
38
40
|
export function turntableAsObserver(config) {
|
|
39
41
|
const t = config?.turntable;
|
|
40
42
|
if (t === undefined)
|
|
@@ -8,8 +8,12 @@ export { createCameraShakeChannel } from "./shakeChannelMath.js";
|
|
|
8
8
|
* default channel directly.
|
|
9
9
|
*/
|
|
10
10
|
export declare const defaultCameraShakeChannel: CameraShakeChannel;
|
|
11
|
-
/** Feed the default camera-shake channel from anywhere (see G7 hitstop cross-cut).
|
|
11
|
+
/** Feed the default camera-shake channel from anywhere (see G7 hitstop cross-cut).
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
12
14
|
export declare function cameraShake(amplitude: number, decayPerSecond?: number): void;
|
|
13
15
|
export declare const CameraShakeContext: import("react").Context<CameraShakeChannel>;
|
|
14
|
-
/** The active rig's shake channel — call `.shake(...)` to add trauma from React UI.
|
|
16
|
+
/** The active rig's shake channel — call `.shake(...)` to add trauma from React UI.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
15
19
|
export declare function useCameraShake(): CameraShakeChannel;
|
|
@@ -8,12 +8,16 @@ export { createCameraShakeChannel } from "./shakeChannelMath.js";
|
|
|
8
8
|
* default channel directly.
|
|
9
9
|
*/
|
|
10
10
|
export const defaultCameraShakeChannel = createCameraShakeChannel();
|
|
11
|
-
/** Feed the default camera-shake channel from anywhere (see G7 hitstop cross-cut).
|
|
11
|
+
/** Feed the default camera-shake channel from anywhere (see G7 hitstop cross-cut).
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
12
14
|
export function cameraShake(amplitude, decayPerSecond) {
|
|
13
15
|
defaultCameraShakeChannel.shake(amplitude, decayPerSecond);
|
|
14
16
|
}
|
|
15
17
|
export const CameraShakeContext = createContext(defaultCameraShakeChannel);
|
|
16
|
-
/** The active rig's shake channel — call `.shake(...)` to add trauma from React UI.
|
|
18
|
+
/** The active rig's shake channel — call `.shake(...)` to add trauma from React UI.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
17
21
|
export function useCameraShake() {
|
|
18
22
|
return useContext(CameraShakeContext);
|
|
19
23
|
}
|