@threenative/core 0.3.1 → 0.3.3
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/capabilities.json +1344 -88
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CYKk2WTu.d.ts} +7 -0
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-C1SnMoJ-.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-D_6r-k4Y.d.ts} +365 -7
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CMklJs6r.d.ts} +1 -1
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1040 -19
- package/dist/index.js +5111 -470
- package/dist/playtest.d.ts +4 -4
- package/dist/playtest.js +44 -9
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-Cy4qeBOA.d.ts} +247 -13
- package/dist/ui-layer.d.ts +13 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +3 -3
- package/mcp/blender-server.mjs +1 -1
- package/mcp/engine-server.mjs +33 -5
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +179 -19
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,221 @@
|
|
|
1
1
|
import * as three from 'three';
|
|
2
|
-
import {
|
|
3
|
-
export { I as IAssetLoader, a as IAssetLoaderOptions, c as createAssetLoader, r as reconcileMirroredClips } from './assets-
|
|
4
|
-
export { A as AudioBus, I as IAudioBusOptions, b as IAudioPlayOptions } from './audio-
|
|
5
|
-
export { C as CanvasLayer } from './canvas-layer-
|
|
6
|
-
import { a as IGamePluginRuntime, b as IGamePluginHooks } from './game-
|
|
7
|
-
export { A as AfterPhysicsCallback, C as ContextMenuPolicy, c as ICtx, I as IGame,
|
|
2
|
+
import { Object3D, AnimationClip, AnimationMixer, Camera, Vector3, Group, Matrix4, Mesh, BufferGeometry, Material, InstancedMesh, ColorRepresentation, Box3, Scene, DirectionalLight, Sprite, DataTexture, CatmullRomCurve3, Texture } from 'three';
|
|
3
|
+
export { I as IAssetLoader, a as IAssetLoaderOptions, c as createAssetLoader, r as reconcileMirroredClips } from './assets-CYKk2WTu.js';
|
|
4
|
+
export { A as AudioBus, I as IAudioBusOptions, b as IAudioPlayOptions, r as resetAudioCueLedger } from './audio-7i3Xl0l3.js';
|
|
5
|
+
export { C as CanvasLayer } from './canvas-layer-C1SnMoJ-.js';
|
|
6
|
+
import { a as IGamePluginRuntime, b as IGamePluginHooks } from './game-D_6r-k4Y.js';
|
|
7
|
+
export { A as AfterPhysicsCallback, C as ContextMenuPolicy, G as GEOMETRY_ASSET_KEY, c as GEOMETRY_CAPTURE_DEFAULT_LIMIT, d as GEOMETRY_CAPTURE_MAX_LIMIT, e as GEOMETRY_CAPTURE_SORTS, f as GEOMETRY_CAPTURE_TIMEOUT_MS, g as GEOMETRY_CAPTURE_WALK_CAP, h as GeometryCaptureSort, i as ICtx, I as IGame, j as IGameObservationContribution, k as IGameObservationSampleRequest, l as IGamePlatformSource, m as IGeometryCaptureAsset, n as IGeometryCaptureMesh, o as IGeometryCapturePass, p as IGeometryCaptureReport, q as IGeometryCaptureRequest, r as IGeometryCaptureRow, s as IInputAction, t as IInputGamepad, u as IPointerDragHandle, v as IPointerEvent3D, w as IPointerEvents3D, x as IPointerEvents3DOptions, y as IPointerEvents3DPicker, z as IPointerState, B as IRandom, D as IRawInputPointer, E as IRawInputPointerEdge, F as IRawInputState, H as IRaycastOptions, J as IScenePickerOptions, K as IThreeNativeAudioConfig, L as IThreeNativeAudioLoop, M as IThreeNativeAudioOverride, N as IThreeNativeAudioSpectrum, O as IThreeNativeBootSplash, P as IThreeNativeConfig, Q as IThreeNativeIconVariants, R as IThreeNativeLodConfig, S as IThreeNativeLodGenerationConfig, T as IThreeNativeLodOverride, U as IThreeNativeLodRuntimeConfig, V as IThreeNativeTexturesConfig, W as ITweenOptions, X as IWarmUpCacheOptions, Y as IWarmUpObservation, Z as IWarmUpOptions, _ as IWarmUpProgress, $ as IWarmUpRenderer, a0 as IWarmUpReport, a1 as InputBindings, a2 as InputPlatformSource, a3 as PointerEvent3DListener, a4 as PointerEvent3DType, a5 as PointerEvents3D, a6 as Scene, a7 as SceneFrame, a8 as ScenePicker, a9 as ScheduleHandle, aa as Scheduler, ab as ThreeNativeBackgroundMode, ac as ThreeNativeLodMinTrianglesScope, ad as ThreeNativeLodPreset, ae as ThreeNativeOrientation, af as ThreeNativeUiRenderer, ag as WarmUpCacheStatus, ah as WarmUpObservationStatus, ai as afterPhysics, aj as createRandom, ak as defineGame, al as warmUpScene } from './game-D_6r-k4Y.js';
|
|
8
8
|
import * as three_webgpu from 'three/webgpu';
|
|
9
9
|
import { StorageTexture, ComputeNode, UniformNode, Node, TextureNode, NodeMaterial, StorageBufferNode, StructTypeNode, Data3DTexture, ShadowBaseNode, NodeBuilder, NodeFrame, SpriteNodeMaterial, StorageTextureNode } from 'three/webgpu';
|
|
10
|
-
import { I as IRendererLike } from './renderer-
|
|
11
|
-
export { D as DEFAULT_PIPELINE_CENSUS_LIMIT,
|
|
12
|
-
import { I as IComputeDriven$1, a as IGPUReadbackSample } from './gpu-readback-
|
|
13
|
-
export { C as ComputeDrivenRegistry, G as GPUReadback, b as IGPUReadbackOptions } from './gpu-readback-
|
|
10
|
+
import { I as IRendererLike, F as FramePassKind, a as IFrameBudgetWindow } from './renderer-Cy4qeBOA.js';
|
|
11
|
+
export { D as DEFAULT_PIPELINE_CENSUS_LIMIT, b as FRAME_BUDGET_MARKER, c as FRAME_BUDGET_PHASES, d as FRAME_HITCH_MARKER, e as FrameBudget, f as FrameBudgetPhase, g as FrameCounters, h as ICounterDevice, i as IFrameBudgetOptions, j as IFrameBudgetPassSummary, k as IFrameBudgetSummary, l as IFrameCounters, m as IFramePhaseSample, n as IPipelineCensus, o as IPipelineCensusCounts, p as IPipelineCensusEvent, q as IPipelineCensusOptions, r as IPipelineProvenance, s as IPipelineShaderObservation, t as IRenderChainApplied, u as IRenderChainBudgetWindow, v as IRenderChainDroppedStage, w as IRenderChainOptions, x as IRenderChainRenderer, y as IRenderChainRequest, z as IRenderChainStage, A as IRenderChainStageContext, B as IRenderChainVelocityMeasurement, C as IRenderChainVelocityReport, E as IRenderChainVelocityRequest, G as IRenderChainVelocityResult, H as IRenderPassSample, J as IVelocityRenderPass, P as PIPELINE_CENSUS_CAPABILITY, K as PIPELINE_CENSUS_VERSION, L as PipelineCensus, M as PipelineCensusMode, N as PipelineCensusStatus, R as RENDER_CHAIN_MARKER, O as RENDER_CHAIN_STAGE_ORDER, Q as RENDER_CHAIN_TIERS, S as RenderChain, T as RenderChainSource, U as RenderChainStageId, V as RenderChainStageName, W as RenderChainTier, X as RenderChainTierRequest, Y as RenderChainVelocitySource, Z as VELOCITY_OUTPUT_NAME, _ as VELOCITY_PREVIOUS_BONE_MATRICES, $ as VELOCITY_PREVIOUS_INSTANCE_MATRICES, a0 as VELOCITY_PREVIOUS_WORLD_MATRIX, a1 as VelocityTracker, a2 as counterDeviceOf, a3 as createPipelineCensus, a4 as ensureVelocityOutput, a5 as prewarm, a6 as readRenderChainObservation, a7 as readRenderChainReport, a8 as readVelocityPreviousBoneMatrices, a9 as readVelocityPreviousMatrices, aa as readVelocityPreviousWorldMatrix, ab as velocityTexture, ac as withVelocityContext } from './renderer-Cy4qeBOA.js';
|
|
12
|
+
import { I as IComputeDriven$1, a as IGPUReadbackSample } from './gpu-readback-CMklJs6r.js';
|
|
13
|
+
export { C as ComputeDrivenRegistry, G as GPUReadback, b as IGPUReadbackOptions } from './gpu-readback-CMklJs6r.js';
|
|
14
14
|
import { Node as Node$1 } from 'three/src/nodes/Nodes.js';
|
|
15
15
|
import 'zustand/vanilla';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Nested cost spans across the render phase, default-off.
|
|
19
|
+
*
|
|
20
|
+
* The frame budget names six phases and one of them — `render` — is 16.1 ms of a 20.2 ms frame
|
|
21
|
+
* while every engine-owned thing inside it sums to under 1.5 ms. A phase that big with nothing
|
|
22
|
+
* inside it is not a measurement, it is a question, and four ranked optimisation options were
|
|
23
|
+
* priced against four different answers to it. This is the instrument that answers it: a span tree
|
|
24
|
+
* whose leaves are the parts of the render phase, with the leftover computed rather than assumed.
|
|
25
|
+
*
|
|
26
|
+
* Three properties make it trustworthy enough to price work against:
|
|
27
|
+
*
|
|
28
|
+
* - **The residual is arithmetic, not a category.** Every non-leaf reports its own duration minus
|
|
29
|
+
* the durations of the spans it contains, so an unmeasured part shows up as a number instead of
|
|
30
|
+
* being quietly filed under "other". A child that outlives its parent reports a negative
|
|
31
|
+
* residual rather than being clamped to zero, because a negative residual means the nesting is
|
|
32
|
+
* wrong and a clamped one means nothing at all.
|
|
33
|
+
* - **It is default-off and costs one guarded return when off.** `TN_FRAME_SPANS=1` installs it;
|
|
34
|
+
* unset, every call site is `if (recorder === undefined) return;` with no clock read.
|
|
35
|
+
* - **Nothing here decides anything.** It measures the path that exists. It does not select a
|
|
36
|
+
* renderer, a traversal strategy or a pass order, and no number it produces is evidence for
|
|
37
|
+
* owning the renderer.
|
|
38
|
+
*
|
|
39
|
+
* The ids are integers on the hot path — a span call must not allocate a string to be cheap enough
|
|
40
|
+
* to put around a per-draw call — and the names exist only in the report.
|
|
41
|
+
*/
|
|
42
|
+
/** Marker printed once per report window when spans are installed. */
|
|
43
|
+
declare const SPANS_MARKER = "TN_FRAME_SPANS";
|
|
44
|
+
/**
|
|
45
|
+
* The launch flag that installs the spans. Off by default: this is a diagnostic that adds work to
|
|
46
|
+
* the frame it measures, so a game must ask for it.
|
|
47
|
+
*/
|
|
48
|
+
declare const SPANS_FLAG = "TN_FRAME_SPANS";
|
|
49
|
+
/**
|
|
50
|
+
* Every span, in the order the report prints them. Integers, because `beginSpan` runs per draw.
|
|
51
|
+
*
|
|
52
|
+
* `pass` and its nested kinds are one id per render call three makes, not one per camera: three
|
|
53
|
+
* renders the main camera first and the shadow and reflection cameras from inside that call, so
|
|
54
|
+
* the nesting is the renderer's own and a flat list of passes would have to invent a relationship
|
|
55
|
+
* the measurement already has.
|
|
56
|
+
*/
|
|
57
|
+
declare const SPANS: {
|
|
58
|
+
/** Compute-driven render work dispatched by the engine before the world render. */
|
|
59
|
+
readonly compute: 0;
|
|
60
|
+
/** The engine's own `beforeRender` seam, where a game prepares the scene it is about to draw. */
|
|
61
|
+
readonly beforeRender: 1;
|
|
62
|
+
/** The scene-graph matrix walk. */
|
|
63
|
+
readonly sceneUpdate: 2;
|
|
64
|
+
/** The projection's reconcile, when one is installed. */
|
|
65
|
+
readonly reconcile: 3;
|
|
66
|
+
/** Virtual-geometry clustered mesh update. */
|
|
67
|
+
readonly clustered: 4;
|
|
68
|
+
/** Automatic discrete LOD selection. */
|
|
69
|
+
readonly lod: 5;
|
|
70
|
+
/** The projected-size cull, apply and restore together. */
|
|
71
|
+
readonly cull: 6;
|
|
72
|
+
/** The main camera's render call. */
|
|
73
|
+
readonly mainPass: 7;
|
|
74
|
+
/** A nested render call three names as a shadow map. */
|
|
75
|
+
readonly shadowPass: 8;
|
|
76
|
+
/** A nested render call three names as a reflector. */
|
|
77
|
+
readonly reflectionPass: 9;
|
|
78
|
+
/** Any other nested render call. */
|
|
79
|
+
readonly nestedPass: 10;
|
|
80
|
+
/** Render-list construction inside a render call. */
|
|
81
|
+
readonly projectObject: 11;
|
|
82
|
+
/** Render-list sort inside a render call. */
|
|
83
|
+
readonly sort: 12;
|
|
84
|
+
/** Per-draw submission inside a render call, accumulated across the draws of one pass. */
|
|
85
|
+
readonly draw: 13;
|
|
86
|
+
};
|
|
87
|
+
type SpanId = (typeof SPANS)[keyof typeof SPANS];
|
|
88
|
+
/** Names, indexed by id. The only place a span name is a string. */
|
|
89
|
+
declare const SPAN_NAMES: readonly string[];
|
|
90
|
+
declare const SPAN_COUNT: number;
|
|
91
|
+
/** One span's cost across a reported window. */
|
|
92
|
+
interface ISpanSummary {
|
|
93
|
+
/** Frames in the window that entered this span at least once. */
|
|
94
|
+
readonly frames: number;
|
|
95
|
+
/** Entries per frame, mean over the frames that entered it. */
|
|
96
|
+
readonly perFrame: number;
|
|
97
|
+
readonly mean: number;
|
|
98
|
+
readonly p50: number;
|
|
99
|
+
readonly p95: number;
|
|
100
|
+
readonly max: number;
|
|
101
|
+
/**
|
|
102
|
+
* This span's own time minus the time of the spans inside it, at p50.
|
|
103
|
+
*
|
|
104
|
+
* Negative when a child outlived its parent, which means the nesting is wrong. Never clamped: a
|
|
105
|
+
* clamped residual reads as a complete attribution, and this is the number that says whether the
|
|
106
|
+
* attribution is complete.
|
|
107
|
+
*/
|
|
108
|
+
readonly residualP50: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* One reported window of the span tree.
|
|
112
|
+
*
|
|
113
|
+
* `residual` is the render phase's own cost with every top-level span subtracted — the number this
|
|
114
|
+
* instrument exists to shrink. `coverage` is its complement as a fraction of the phase, and a
|
|
115
|
+
* reader that sees 0.68 is being told that a third of the phase is still unmeasured.
|
|
116
|
+
*/
|
|
117
|
+
interface ISpanWindow {
|
|
118
|
+
readonly window: number;
|
|
119
|
+
readonly frames: number;
|
|
120
|
+
/** The render phase as the frame budget charged it, at p50. */
|
|
121
|
+
readonly renderMs: number;
|
|
122
|
+
readonly residualMs: number;
|
|
123
|
+
readonly coverage: number;
|
|
124
|
+
/** Frames whose span nesting exceeded `MAX_DEPTH`, whose residual is therefore incomplete. */
|
|
125
|
+
readonly overflowed: number;
|
|
126
|
+
readonly spans: Readonly<Record<string, ISpanSummary>>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Accumulates a nested span tree, one frame at a time, and reports it windowed.
|
|
130
|
+
*
|
|
131
|
+
* The owner brackets a frame with `beginFrame` and `endFrame`; the render phase's own duration is
|
|
132
|
+
* handed in at the close rather than measured here, so the root residual is arithmetic against the
|
|
133
|
+
* number the frame budget already reports and cannot drift from it.
|
|
134
|
+
*
|
|
135
|
+
* Nesting is strict: `end(id)` must name the span `begin(id)` opened. A mismatch throws, because a
|
|
136
|
+
* tree whose parentage is wrong produces a residual that is confidently wrong, and that is worse
|
|
137
|
+
* than a loud failure in a mode a game opted into.
|
|
138
|
+
*/
|
|
139
|
+
declare class SpanRecorder {
|
|
140
|
+
#private;
|
|
141
|
+
readonly capacity: number;
|
|
142
|
+
constructor(capacity?: number);
|
|
143
|
+
/** Opens a span. Returns false when the tree is already at `MAX_DEPTH` and the span is dropped. */
|
|
144
|
+
begin(id: SpanId, now: number): boolean;
|
|
145
|
+
/** Closes the span `begin` opened. Throws when it names a different span. */
|
|
146
|
+
end(id: SpanId, now: number): void;
|
|
147
|
+
/**
|
|
148
|
+
* Adds `ms` to a span that is entered many times per frame — a per-draw call, or a call site that
|
|
149
|
+
* cannot be bracketed. Counts as one entry and attributes to the enclosing span exactly as a
|
|
150
|
+
* bracketed one would.
|
|
151
|
+
*/
|
|
152
|
+
add(id: SpanId, ms: number): void;
|
|
153
|
+
/** How many spans are currently open. Zero outside a frame's own work. */
|
|
154
|
+
get depth(): number;
|
|
155
|
+
/**
|
|
156
|
+
* Throws a half-open frame away without recording it.
|
|
157
|
+
*
|
|
158
|
+
* A frame that failed mid-render has a span stack that cannot be closed honestly, and recording
|
|
159
|
+
* it would put a fabricated zero in the window. The loop abandons it instead, so the window only
|
|
160
|
+
* ever reports frames whose spans all closed.
|
|
161
|
+
*/
|
|
162
|
+
abandonFrame(): void;
|
|
163
|
+
/** Records the frame's totals against the render phase the frame budget charged. */
|
|
164
|
+
endFrame(renderPhaseMs: number): void;
|
|
165
|
+
/** The window just closed, or `undefined` when no frame was recorded in it. */
|
|
166
|
+
window(): ISpanWindow | undefined;
|
|
167
|
+
}
|
|
168
|
+
/** Installs or removes the recorder every `beginSpan`/`endSpan` routes to. */
|
|
169
|
+
declare function setSpanRecorder(next: SpanRecorder | undefined): void;
|
|
170
|
+
declare function spanRecorder(): SpanRecorder | undefined;
|
|
171
|
+
/** The one clock the spans and the frame budget share, so their numbers are comparable. */
|
|
172
|
+
declare function spanNow(): number;
|
|
173
|
+
/**
|
|
174
|
+
* Opens a span. One guarded return when spans are off — no clock read, no allocation, no branch on
|
|
175
|
+
* anything but the recorder's own presence.
|
|
176
|
+
*/
|
|
177
|
+
declare function beginSpan(id: SpanId): void;
|
|
178
|
+
declare function endSpan(id: SpanId): void;
|
|
179
|
+
/** Adds an already-measured duration to a span entered many times per frame. */
|
|
180
|
+
declare function addSpan(id: SpanId, ms: number): void;
|
|
181
|
+
/**
|
|
182
|
+
* Whether `TN_FRAME_SPANS` asks for spans on this launch.
|
|
183
|
+
*
|
|
184
|
+
* Three ways in, because the three launches have three different seams and none of them is
|
|
185
|
+
* `import.meta.env` (Vite replaces that at build time, and a measurement flag must be settable
|
|
186
|
+
* without rebuilding the game): a native host forwards `TN_FRAME_SPANS` through `process.env`, a
|
|
187
|
+
* browser page carries `?tnFrameSpans=1` in its URL, and any harness can set
|
|
188
|
+
* `globalThis.__tnFrameSpans` before the game boots.
|
|
189
|
+
*/
|
|
190
|
+
declare function spansRequested(): boolean;
|
|
191
|
+
/** The window as the marker line prints it. */
|
|
192
|
+
declare function formatSpansWindow(window: ISpanWindow): string;
|
|
193
|
+
|
|
194
|
+
/** A stride measured at unit world scale, so uniformly scaled clones can share it. */
|
|
195
|
+
interface ISharedStride {
|
|
196
|
+
/** Metres of ground per clip-second at rate 1, per unit of uniform world scale. */
|
|
197
|
+
readonly groundSpeed: number;
|
|
198
|
+
readonly inPlace: boolean;
|
|
199
|
+
}
|
|
200
|
+
declare class RigPreparation {
|
|
201
|
+
#private;
|
|
202
|
+
/** The cached binding count for an equivalent preparation, or `undefined` on a miss. */
|
|
203
|
+
boundCount(source: Object3D, clip: AnimationClip): number | undefined;
|
|
204
|
+
rememberBound(source: Object3D, clip: AnimationClip, bound: number): void;
|
|
205
|
+
/** The cached unit-scale stride for an equivalent, fully covered clip, or `undefined`. */
|
|
206
|
+
stride(source: Object3D, clip: AnimationClip): ISharedStride | undefined;
|
|
207
|
+
rememberStride(source: Object3D, clip: AnimationClip, stride: ISharedStride): void;
|
|
208
|
+
}
|
|
209
|
+
|
|
17
210
|
interface IAnimationPlayerOptions {
|
|
18
211
|
readonly clips: readonly AnimationClip[];
|
|
19
212
|
readonly root: Object3D;
|
|
20
213
|
readonly requiredClips?: readonly string[] | Readonly<Record<string, string>>;
|
|
214
|
+
/**
|
|
215
|
+
* Shared preparation for clones of one source rig. Set by `SkeletalMesh3D`; a standalone
|
|
216
|
+
* player leaves it undefined and keeps the per-instance binding audit and stride sample.
|
|
217
|
+
*/
|
|
218
|
+
readonly preparation?: RigPreparation;
|
|
21
219
|
/**
|
|
22
220
|
* Match a travelling clip's playback rate to the ground the body actually covers.
|
|
23
221
|
*
|
|
@@ -192,6 +390,37 @@ declare class CameraShake {
|
|
|
192
390
|
update(dt: number): ICameraShakeOffset;
|
|
193
391
|
}
|
|
194
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Why a launch stopped, said where the player can read it.
|
|
395
|
+
*
|
|
396
|
+
* A launch has exactly two ways to go wrong quietly, and both were measured on a real game:
|
|
397
|
+
* progress stops moving (a decode that never returns, an asset that never settles) and the GPU
|
|
398
|
+
* device is lost (another process had taken 94% of VRAM). In both the loop keeps iterating, the
|
|
399
|
+
* loading layer keeps painting, and the only account of what happened is a line on stdout — which
|
|
400
|
+
* a player does not have, and which a bug report therefore never carries.
|
|
401
|
+
*
|
|
402
|
+
* So the failure is reported as text, once, through `onLaunchFailure` — and core stops there. Core
|
|
403
|
+
* cannot draw it: on the native host `document` is a Three.js compatibility stub whose
|
|
404
|
+
* `appendChild` is a no-op, so an overlay written here would be invisible on exactly the target
|
|
405
|
+
* that needs it. The page owns the presentation (and the copy-to-clipboard button players are
|
|
406
|
+
* asked for when they report a launch that hung); this owns the noticing and the wording.
|
|
407
|
+
*/
|
|
408
|
+
/** What failed. `stalled` is "no progress for a while"; `device-lost` is the GPU going away. */
|
|
409
|
+
type LaunchFailureKind = "stalled" | "device-lost";
|
|
410
|
+
interface ILaunchFailure {
|
|
411
|
+
readonly kind: LaunchFailureKind;
|
|
412
|
+
/** One human sentence, already naming the numbers — this is what the Copy button copies. */
|
|
413
|
+
readonly message: string;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Called for every launch failure the engine notices, with the message to show the player.
|
|
417
|
+
*
|
|
418
|
+
* @situation show the player why the game stopped loading instead of leaving the loading screen up
|
|
419
|
+
* @situation report a stalled launch or a lost GPU device in the game's own UI
|
|
420
|
+
* @example const off = onLaunchFailure((failure) => shell.loading({ failure: failure.message }));
|
|
421
|
+
*/
|
|
422
|
+
declare function onLaunchFailure(listener: (failure: ILaunchFailure) => void): () => void;
|
|
423
|
+
|
|
195
424
|
type AtmosphereRgb = readonly [number, number, number];
|
|
196
425
|
type AtmosphereVector = AtmosphereRgb | Readonly<{
|
|
197
426
|
x: number;
|
|
@@ -593,9 +822,20 @@ interface IMergePart {
|
|
|
593
822
|
interface IMergePartsOptions {
|
|
594
823
|
/** Named in the error when the merge is refused. Say what was being built. */
|
|
595
824
|
readonly label: string;
|
|
825
|
+
/**
|
|
826
|
+
* Channels to keep from each part besides `position`. Absent or empty keeps today's
|
|
827
|
+
* position-only merge, whose normals are recomputed from the merged result.
|
|
828
|
+
*
|
|
829
|
+
* `"normal"` keeps each part's authored normals, transformed by the part's placement matrix
|
|
830
|
+
* (the inverse-transpose normal matrix) and **never** recomputed. `"uv"` keeps each part's
|
|
831
|
+
* texture coordinates verbatim — the placement matrix moves position and normal, so UV values
|
|
832
|
+
* are retained unchanged. A part that does not carry a listed channel refuses the merge.
|
|
833
|
+
*/
|
|
834
|
+
readonly preserve?: readonly ("uv" | "normal")[];
|
|
596
835
|
}
|
|
597
836
|
/**
|
|
598
|
-
* Merge game-authored pieces into one buffer, keeping each piece's own colour
|
|
837
|
+
* Merge game-authored pieces into one buffer, keeping each piece's own colour and, when asked,
|
|
838
|
+
* its uv and authored normals.
|
|
599
839
|
*
|
|
600
840
|
* Two things go wrong every time an agent bakes a building, a ship or a character out of
|
|
601
841
|
* primitives, and neither is about how any of it looks. `mergeGeometries` returns `null` on
|
|
@@ -603,7 +843,9 @@ interface IMergePartsOptions {
|
|
|
603
843
|
* a hundred indexed primitives — is invisible until the whole scene is missing; and a merged
|
|
604
844
|
* buffer draws with one surface, so per-piece colour is gone unless every piece carries a flat
|
|
605
845
|
* `color` attribute written before the merge. Writing that attribute is mechanical. The colours
|
|
606
|
-
* are entirely the game's, one per part, and changing them changes nothing here.
|
|
846
|
+
* are entirely the game's, one per part, and changing them changes nothing here. By default the
|
|
847
|
+
* merged normals are recomputed from the merged buffer; `preserve` keeps the authored normals and
|
|
848
|
+
* texture coordinates instead so an imported model's shading survives the bake.
|
|
607
849
|
*/
|
|
608
850
|
declare function mergeParts(parts: Iterable<IMergePart>, options: IMergePartsOptions): BufferGeometry;
|
|
609
851
|
|
|
@@ -686,8 +928,9 @@ declare class ClusteredMesh extends Mesh {
|
|
|
686
928
|
* Takes every clustered mesh and every clustered batch under `root` through this frame's cut.
|
|
687
929
|
*
|
|
688
930
|
* The engine calls this itself, once a frame, before the render — virtual geometry ships on and a
|
|
689
|
-
* game that has to remember to call something has not been given it.
|
|
690
|
-
*
|
|
931
|
+
* game that has to remember to call something has not been given it. The first call censuses the
|
|
932
|
+
* root once and subscribes to its graph events; later calls walk only the clustered meshes and
|
|
933
|
+
* batch roots themselves, so a scene holding neither costs a map lookup.
|
|
691
934
|
*
|
|
692
935
|
* @returns triangles the clustered meshes and batches will submit.
|
|
693
936
|
*/
|
|
@@ -772,6 +1015,420 @@ declare class ClusteredBatch {
|
|
|
772
1015
|
update(camera: Camera, viewportHeight: number): number;
|
|
773
1016
|
}
|
|
774
1017
|
|
|
1018
|
+
/** The LOD0 geometry of a mesh under a discrete chain, or the mesh's own geometry otherwise. */
|
|
1019
|
+
declare function baseGeometryOf(mesh: Mesh): BufferGeometry;
|
|
1020
|
+
/**
|
|
1021
|
+
* Takes every discrete-LOD mesh under `root` through this frame's selection.
|
|
1022
|
+
*
|
|
1023
|
+
* The engine calls this itself, once a frame, after matrices are synced and before the render.
|
|
1024
|
+
* Multi-view callers that need the finest level across passes use {@link selectLodLevel} directly;
|
|
1025
|
+
* this entry point selects for the one camera it is handed — which is the main view, and is at
|
|
1026
|
+
* least as fine as a shadow pass of lower resolution needs.
|
|
1027
|
+
*
|
|
1028
|
+
* @returns triangles the managed meshes will submit this frame.
|
|
1029
|
+
*/
|
|
1030
|
+
declare function updateModelLods(root: {
|
|
1031
|
+
traverse(callback: (object: object) => void): void;
|
|
1032
|
+
}, camera: Camera, viewportHeight: number): number;
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* What the gate did on the last frame, in the shape a frame-budget window reports.
|
|
1036
|
+
*
|
|
1037
|
+
* `enabled: false` means the game declined the gate, and the counts are still measured — turning
|
|
1038
|
+
* the convention off must not turn its measurement off. Every exemption is named separately so an
|
|
1039
|
+
* override is visible rather than silent.
|
|
1040
|
+
*/
|
|
1041
|
+
interface IRenderCameraCullReport {
|
|
1042
|
+
readonly schemaVersion: 1;
|
|
1043
|
+
readonly enabled: boolean;
|
|
1044
|
+
/**
|
|
1045
|
+
* False when the camera has no perspective distance term (orthographic) or no viewport to
|
|
1046
|
+
* project into; the gate then leaves the whole scene drawn rather than guessing.
|
|
1047
|
+
*/
|
|
1048
|
+
readonly cameraResolved: boolean;
|
|
1049
|
+
readonly thresholdPixels: number;
|
|
1050
|
+
/** Renderables walked this frame. */
|
|
1051
|
+
readonly considered: number;
|
|
1052
|
+
/** Renderables hidden by this gate this frame. */
|
|
1053
|
+
readonly culled: number;
|
|
1054
|
+
readonly exemptCameraAttached: number;
|
|
1055
|
+
readonly exemptMarked: number;
|
|
1056
|
+
readonly exemptShadowCasters: number;
|
|
1057
|
+
readonly exemptWithoutBounds: number;
|
|
1058
|
+
/**
|
|
1059
|
+
* Objects whose position buffer is rewritten every frame, so the gate cannot trust a bound it
|
|
1060
|
+
* cannot afford to rescan. Kept drawn, like `frustumCulled = false`.
|
|
1061
|
+
*/
|
|
1062
|
+
readonly exemptDynamicBounds: number;
|
|
1063
|
+
/** Objects that already set `frustumCulled = false`, and so never had trustworthy bounds. */
|
|
1064
|
+
readonly exemptFrustumCulled: number;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Keep an object drawn regardless of how small the render camera resolves it.
|
|
1068
|
+
*
|
|
1069
|
+
* The named per-object override for the projected-size gate, which is on by default. Mark the
|
|
1070
|
+
* player's own cockpit, a nameplate, a quest marker, or anything a game never wants to pop out.
|
|
1071
|
+
* `alwaysRender(object, false)` removes the marker. The count of marked objects is reported beside
|
|
1072
|
+
* the cull, so an override is stated rather than hidden.
|
|
1073
|
+
*
|
|
1074
|
+
* @situation keep a small object drawn when the engine would skip it as too far to resolve
|
|
1075
|
+
* @situation stop my cockpit, marker or player model popping out at distance
|
|
1076
|
+
* @situation a tiny object disappeared at range and I need it always visible
|
|
1077
|
+
* @constraint the marker is per object and survives scene rebuilds only as long as the object does
|
|
1078
|
+
* @constraint disabling the gate (`renderer.minimumProjectedPixels: false`) keeps its measurement on
|
|
1079
|
+
* @example alwaysRender(ctx.camera.children[0]); // a camera-attached cockpit stays drawn
|
|
1080
|
+
*/
|
|
1081
|
+
declare function alwaysRender(object: Object3D, enabled?: boolean): void;
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* The per-frame world-matrix walk, with a hidden subtree left where it stands.
|
|
1085
|
+
*
|
|
1086
|
+
* `three`'s `Object3D.updateMatrixWorld` recurses into every child of every node whatever its
|
|
1087
|
+
* `visible` flag, multiplying a world matrix for each. That is the correct default for a library
|
|
1088
|
+
* that cannot know what a game is doing, and it is the wrong one for a renderer that is about to
|
|
1089
|
+
* skip exactly those subtrees: a full-detail body while its merged stand-in draws, a hidden LOD
|
|
1090
|
+
* level, a parked or hangared model each pay the walk and none of them can draw.
|
|
1091
|
+
*
|
|
1092
|
+
* Native flight in `sandbox/midway-open-pacific` profiled `updateMatrixWorld` plus
|
|
1093
|
+
* `multiplyMatrices` at **29 % of all JavaScript ticks**, and the same scene's hand-rolled
|
|
1094
|
+
* visible-only pass walked **779 nodes per frame instead of 9,903**, 3.1 ms down to 0.3 ms. That
|
|
1095
|
+
* pass was the game's; this is the engine's, so the next game does not write it again.
|
|
1096
|
+
*
|
|
1097
|
+
* The pass mirrors three exactly for a visible node: `matrixAutoUpdate` -> `updateMatrix()`, then
|
|
1098
|
+
* the `matrixWorldNeedsUpdate || force` recompute honouring `matrixWorldAutoUpdate` and a null
|
|
1099
|
+
* parent, then force the children. A node with `visible === false` still composes its **own**
|
|
1100
|
+
* matrix — that is what makes the next part cheap — and is not recursed into; when it was forced
|
|
1101
|
+
* (or carried a dirty flag) it is remembered, so the first frame it is visible again its whole
|
|
1102
|
+
* subtree is recomputed with `force = true`.
|
|
1103
|
+
*
|
|
1104
|
+
* Two things are never dropped by the pruning, because both are read while nothing above them is
|
|
1105
|
+
* visible:
|
|
1106
|
+
*
|
|
1107
|
+
* - a class that overrides `updateMatrixWorld` runs its own. `SkinnedMesh` refreshes
|
|
1108
|
+
* `bindMatrixInverse`, `Camera` its `matrixWorldInverse`; re-implementing only the base walk
|
|
1109
|
+
* left every deck-crew sailor that had moved since load drawing with a stale bind matrix and
|
|
1110
|
+
* vanishing from the frame. Their subtrees are small, so walking them whole costs nothing;
|
|
1111
|
+
* - a hidden node that holds a `Bone` is walked, because a visible `SkinnedMesh` draws with its
|
|
1112
|
+
* skeleton's matrices wherever the armature happens to sit. Whether bones sit below a node is
|
|
1113
|
+
* decided once and remembered — a rig is built whole and does not grow.
|
|
1114
|
+
*
|
|
1115
|
+
* A game that reads a **hidden** object's `matrixWorld` directly must not rely on this pass
|
|
1116
|
+
* having reached it: use `getWorldPosition`/`getWorldQuaternion`/`getWorldScale` (which update the
|
|
1117
|
+
* chain they need) or call `object.updateWorldMatrix(true, false)` first. The convention's named
|
|
1118
|
+
* override is `renderer.matrixWorld: "all"`, which visits every node exactly as three's own walk
|
|
1119
|
+
* does; `"visible"` is the default.
|
|
1120
|
+
*/
|
|
1121
|
+
/** How much of the scene graph the engine walks for world matrices each frame. */
|
|
1122
|
+
type MatrixWorldMode = "visible" | "all";
|
|
1123
|
+
/** What the pass did on the last frame, for the frame telemetry a window reports. */
|
|
1124
|
+
interface IMatrixWorldReport {
|
|
1125
|
+
readonly schemaVersion: 1;
|
|
1126
|
+
readonly mode: MatrixWorldMode;
|
|
1127
|
+
/**
|
|
1128
|
+
* Nodes the engine walked this frame, summed over every application (authored scene and a
|
|
1129
|
+
* projection mirror when one is in use). One number both ways, so `"all"` can be read as the
|
|
1130
|
+
* cost of the walk the convention just removed.
|
|
1131
|
+
*/
|
|
1132
|
+
readonly visited: number;
|
|
1133
|
+
}
|
|
1134
|
+
interface IMatrixWorldOptions {
|
|
1135
|
+
/** `"visible"` (default) skips a hidden subtree; `"all"` reproduces three's own walk. */
|
|
1136
|
+
readonly mode?: MatrixWorldMode;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* One game's world-matrix walk, holding the state the pruning needs between frames.
|
|
1140
|
+
*
|
|
1141
|
+
* The stale set and the bone cache live on the instance rather than in module scope: two games,
|
|
1142
|
+
* two playtest scenarios or two roots in one process must not share "this subtree was skipped
|
|
1143
|
+
* while hidden", or one game's hidden node would be force-refreshed by the other's frame.
|
|
1144
|
+
*/
|
|
1145
|
+
declare class MatrixWorldPass {
|
|
1146
|
+
#private;
|
|
1147
|
+
constructor(options?: IMatrixWorldOptions);
|
|
1148
|
+
get mode(): MatrixWorldMode;
|
|
1149
|
+
/** What the current frame has walked so far, across every {@link apply} call. */
|
|
1150
|
+
get report(): IMatrixWorldReport;
|
|
1151
|
+
/** Starts a frame's count. The pass's state (stale set, bone cache) is untouched. */
|
|
1152
|
+
beginFrame(): void;
|
|
1153
|
+
/**
|
|
1154
|
+
* Walks `root`, mirroring three for every node the mode visits.
|
|
1155
|
+
*
|
|
1156
|
+
* Returns the nodes this call visited, so a caller can report one application's cost without
|
|
1157
|
+
* also reporting the frame's total.
|
|
1158
|
+
*/
|
|
1159
|
+
apply(root: Object3D, force?: boolean): number;
|
|
1160
|
+
/** Forgets what was skipped. A whole-scene swap has no hidden ancestors left to refresh. */
|
|
1161
|
+
dispose(): void;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Where the spans attach to three's own render path.
|
|
1166
|
+
*
|
|
1167
|
+
* Kept apart from the recorder because they are two different kinds of risk. The recorder is
|
|
1168
|
+
* arithmetic over a stack and is proven by unit tests; this file reaches into another library's
|
|
1169
|
+
* private methods, and every line of it is a bet about how three 0.185 is shaped. Reading them
|
|
1170
|
+
* separately is how the bet stays visible.
|
|
1171
|
+
*
|
|
1172
|
+
* Every wrapper is an own property on the instance, so it is the object the frame actually uses
|
|
1173
|
+
* and uninstalling restores the prototype's method. Three's private methods are read defensively:
|
|
1174
|
+
* a renderer whose internals have moved loses that span rather than throwing, and the span simply
|
|
1175
|
+
* does not appear in the report — an absent measurement, never a fabricated zero.
|
|
1176
|
+
*/
|
|
1177
|
+
|
|
1178
|
+
/** The slice of the raw three renderer the probes wrap. Structural, so a test can stand in a fake. */
|
|
1179
|
+
interface ISpanProbeTarget {
|
|
1180
|
+
render(scene: unknown, camera: unknown): unknown;
|
|
1181
|
+
_projectObject?(...args: unknown[]): void;
|
|
1182
|
+
_renderObjectDirect?(...args: unknown[]): void;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Wraps the renderer's own render path so each part of a frame lands in its own span.
|
|
1186
|
+
*
|
|
1187
|
+
* Every wrapper is an own property on the instance, so it is the object the frame actually uses and
|
|
1188
|
+
* uninstalling restores the prototype's method. Three's private methods are read defensively: a
|
|
1189
|
+
* renderer whose internals have moved loses that span rather than throwing, and the span simply
|
|
1190
|
+
* does not appear in the report — an absent measurement, never a fabricated zero.
|
|
1191
|
+
*
|
|
1192
|
+
* The recursion guards matter: `_projectObject` calls itself once per child, so only the outermost
|
|
1193
|
+
* call opens a span, and `render` is deliberately not guarded because a nested render *is* the
|
|
1194
|
+
* shadow and reflection passes.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function installSpanProbes(target: ISpanProbeTarget, root: Object3D): () => void;
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* The engine telling the agent that built the scene what a human would otherwise find by playing.
|
|
1200
|
+
*
|
|
1201
|
+
* A scene reached 1,815 triangles per draw with its GPU ten times under budget and nobody knew,
|
|
1202
|
+
* because the census and the phase split are measured every frame and nothing reads them. The
|
|
1203
|
+
* frame already carries the shape: how many objects the cull considered, how many draws each pass
|
|
1204
|
+
* submitted, how many casters are exempt, and what share of the frame the GPU actually used. This
|
|
1205
|
+
* turns those numbers into a verdict, once per reported window.
|
|
1206
|
+
*
|
|
1207
|
+
* **The rule is derived, never a constant an author is told to revisit.** It fires when
|
|
1208
|
+
*
|
|
1209
|
+
* - the GPU used less than a third of the frame — the device is not the constraint, and
|
|
1210
|
+
* - the JS render phase alone is longer than the display's own period — so the scene cannot make
|
|
1211
|
+
* the display's rate even if everything else in the frame were free.
|
|
1212
|
+
*
|
|
1213
|
+
* Both numbers come from the frame meter. The display's period comes from the host's own
|
|
1214
|
+
* presentation cap where there is one, and otherwise from the frame rate the game itself declared;
|
|
1215
|
+
* no third source is invented, and a launch that can name neither gets no verdict rather than a
|
|
1216
|
+
* guessed one.
|
|
1217
|
+
*
|
|
1218
|
+
* It is silent on an honestly GPU-bound scene with the same draw count, which is the property that
|
|
1219
|
+
* makes it worth reading: a warning that fires on every heavy scene is a warning nobody reads.
|
|
1220
|
+
*/
|
|
1221
|
+
|
|
1222
|
+
/** Marker printed at most once per reported window. */
|
|
1223
|
+
declare const SCENE_WARNING_MARKER = "TN_SCENE_WARNING";
|
|
1224
|
+
/**
|
|
1225
|
+
* What the frame already knows about the scene's shape.
|
|
1226
|
+
*
|
|
1227
|
+
* Every field is a count the engine measures anyway — the projection's cull census and the render
|
|
1228
|
+
* pass budget — so the warning adds a reading, never a measurement.
|
|
1229
|
+
*/
|
|
1230
|
+
interface ISceneShape {
|
|
1231
|
+
/** Objects the projected-size cull looked at this window. */
|
|
1232
|
+
readonly objectsConsidered: number;
|
|
1233
|
+
/** Objects it hid. */
|
|
1234
|
+
readonly culled: number;
|
|
1235
|
+
/** Objects exempt from the gate because they cast shadows. */
|
|
1236
|
+
readonly shadowExemptCasters: number;
|
|
1237
|
+
/** Draws submitted per pass kind, from the render pass budget. */
|
|
1238
|
+
readonly draws: Readonly<Partial<Record<FramePassKind, number>>>;
|
|
1239
|
+
/** Triangles per draw across every pass; the number a merge or an atlas moves. */
|
|
1240
|
+
readonly trianglesPerDraw: number;
|
|
1241
|
+
}
|
|
1242
|
+
/** The verdict, and the shape behind it. */
|
|
1243
|
+
interface ISceneWarning {
|
|
1244
|
+
readonly window: number;
|
|
1245
|
+
/** The GPU's share of the frame, as a fraction. */
|
|
1246
|
+
readonly gpuShare: number;
|
|
1247
|
+
/** The JS render phase's mean, in milliseconds. */
|
|
1248
|
+
readonly renderMs: number;
|
|
1249
|
+
/** The period the display works at, in milliseconds, and where that number came from. */
|
|
1250
|
+
readonly displayPeriodMs: number;
|
|
1251
|
+
readonly displaySource: "host-cap" | "declared-target";
|
|
1252
|
+
/** The largest thing the CPU describes every frame, and its share of the terms compared. */
|
|
1253
|
+
readonly dominantTerm: string;
|
|
1254
|
+
readonly dominantShare: number;
|
|
1255
|
+
readonly shape: ISceneShape;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* The display's own period, in milliseconds, or `undefined` when nothing can say.
|
|
1259
|
+
*
|
|
1260
|
+
* The host's presentation cap first, because on a native launch that is literally the rate frames
|
|
1261
|
+
* reach the display at. A browser has no refresh-rate API, so the rate the game declared is the
|
|
1262
|
+
* only honest second source — and it is the same number the resolution scaler already judges
|
|
1263
|
+
* against, not a new one.
|
|
1264
|
+
*/
|
|
1265
|
+
declare function displayPeriodMs(declaredTargetFps: number | undefined): {
|
|
1266
|
+
ms: number;
|
|
1267
|
+
source: ISceneWarning["displaySource"];
|
|
1268
|
+
} | undefined;
|
|
1269
|
+
/**
|
|
1270
|
+
* The verdict for one reported window, or `undefined` when the frame does not earn one.
|
|
1271
|
+
*
|
|
1272
|
+
* Fails closed in both directions: a window whose GPU was never measured gets no verdict, because
|
|
1273
|
+
* "the GPU is idle" is a claim and an absent reading is not evidence for it; and a window with no
|
|
1274
|
+
* shape to report gets none either, because a warning that cannot say what to change is noise.
|
|
1275
|
+
*/
|
|
1276
|
+
declare function sceneWarning(window: IFrameBudgetWindow, shape: ISceneShape | undefined, declaredTargetFps: number | undefined): ISceneWarning | undefined;
|
|
1277
|
+
/**
|
|
1278
|
+
* The scene's shape for one window, from the counts the frame already reported.
|
|
1279
|
+
*
|
|
1280
|
+
* `undefined` when the window carries no pass split: without draws there is nothing to name, and
|
|
1281
|
+
* a verdict built on an absent census would be a sentence about a scene nobody measured.
|
|
1282
|
+
*/
|
|
1283
|
+
declare function describeSceneShape(window: IFrameBudgetWindow, cull: IRenderCameraCullReport | undefined): ISceneShape | undefined;
|
|
1284
|
+
/** The one-line summary a reader sees without opening a log viewer. */
|
|
1285
|
+
declare function describeSceneWarning(warning: ISceneWarning): string;
|
|
1286
|
+
/** The marker line. */
|
|
1287
|
+
declare function formatSceneWarning(warning: ISceneWarning): string;
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* Authored static subtrees: stop recomposing transforms that nobody moves.
|
|
1291
|
+
*
|
|
1292
|
+
* The matrix walk is 2.22 ms of the reference game's 16.1 ms render phase, and almost none of what
|
|
1293
|
+
* it recomputes changed since the previous frame — island geometry, deck fittings, static props and
|
|
1294
|
+
* terrain are composed from the same position, quaternion and scale every frame, then multiplied
|
|
1295
|
+
* into the same world matrix, for as long as the game runs.
|
|
1296
|
+
*
|
|
1297
|
+
* **What this deletes, exactly, so the claim can be checked.** Three's `updateMatrixWorld` does
|
|
1298
|
+
* three things per object: `updateMatrix()` composes the local matrix when `matrixAutoUpdate`,
|
|
1299
|
+
* the world matrix is multiplied out when `matrixWorldNeedsUpdate || force`, and the walk recurses
|
|
1300
|
+
* into every child — the recursion is unconditional in three 0.185, so a frozen subtree still gets
|
|
1301
|
+
* visited. Freezing removes the two composes and keeps the visit. That is the honest bound: this
|
|
1302
|
+
* deletes arithmetic, not traversal, and the traversal is a separate lever with a separate PRD.
|
|
1303
|
+
*
|
|
1304
|
+
* **Staticness is authored, never guessed.** A heuristic that decides an object has "not moved in
|
|
1305
|
+
* 60 frames" is a correctness bug with no reproduction, so nothing here watches gameplay. What the
|
|
1306
|
+
* engine does do is check the promise where it is cheap to check it: the root's own local transform
|
|
1307
|
+
* is compared against what was frozen, once per frame, and a root the author moved thaws and
|
|
1308
|
+
* refreezes itself. That is O(static roots), not O(objects), and it turns the most common way to
|
|
1309
|
+
* get this wrong into a non-event. Writes deeper inside a frozen subtree are the author's to
|
|
1310
|
+
* announce with `invalidateStatic`, and `TN_RENDERLIST_VALIDATE=1` is what proves they did.
|
|
1311
|
+
*/
|
|
1312
|
+
|
|
1313
|
+
/** Marker printed with the static census on each reported window. */
|
|
1314
|
+
declare const STATIC_TRANSFORM_MARKER = "TN_STATIC_TRANSFORMS";
|
|
1315
|
+
/**
|
|
1316
|
+
* Marks a subtree static: its transforms are composed once here and never again until something
|
|
1317
|
+
* changes them.
|
|
1318
|
+
*
|
|
1319
|
+
* Idempotent, and it answers the version the subtree is now at, so a caller can assert on the
|
|
1320
|
+
* contract rather than on the flag. Marking a root twice re-arms it, which is the same thing
|
|
1321
|
+
* `invalidateStatic` does and the reason a generator can emit the call unconditionally.
|
|
1322
|
+
*/
|
|
1323
|
+
declare function markStatic(root: Object3D): number;
|
|
1324
|
+
/**
|
|
1325
|
+
* Thaws a subtree: every object composes again from the next walk.
|
|
1326
|
+
*
|
|
1327
|
+
* The flags are restored to three's defaults rather than to whatever they were before, because a
|
|
1328
|
+
* game that had already turned `matrixAutoUpdate` off for its own reasons and then marked the
|
|
1329
|
+
* subtree static is asking for the same behaviour either way.
|
|
1330
|
+
*/
|
|
1331
|
+
declare function unmarkStatic(root: Object3D): void;
|
|
1332
|
+
/**
|
|
1333
|
+
* Announces that something inside a frozen subtree moved. The subtree recomposes once and refreezes
|
|
1334
|
+
* at its new transform.
|
|
1335
|
+
*
|
|
1336
|
+
* Takes the object that moved, or the root; either way the root that owns it is what re-arms, since
|
|
1337
|
+
* a world matrix deeper in the subtree is a product of everything above it.
|
|
1338
|
+
*/
|
|
1339
|
+
declare function invalidateStatic(object: Object3D): number | undefined;
|
|
1340
|
+
/** Whether this object is a frozen root. */
|
|
1341
|
+
declare function isStatic(root: Object3D): boolean;
|
|
1342
|
+
/** One window's census of what is frozen and what had to thaw. */
|
|
1343
|
+
interface IStaticTransformCensus {
|
|
1344
|
+
/** Subtrees currently frozen. */
|
|
1345
|
+
readonly roots: number;
|
|
1346
|
+
/** Objects inside them, which is the count that stopped recomposing. */
|
|
1347
|
+
readonly objects: number;
|
|
1348
|
+
/** Roots the per-frame check found moved, and re-armed, since the previous census. */
|
|
1349
|
+
readonly rearmed: number;
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Re-arms any frozen root whose authored transform has changed since it was frozen.
|
|
1353
|
+
*
|
|
1354
|
+
* Called once per render phase, before the walk. It reads 26 numbers per frozen root and writes
|
|
1355
|
+
* nothing when nothing moved, so a scene that stays still pays a comparison per subtree and no
|
|
1356
|
+
* allocation at all. It cannot see a write deeper inside the subtree — that is `invalidateStatic`'s
|
|
1357
|
+
* job and `TN_RENDERLIST_VALIDATE=1`'s proof — and it does not pretend to.
|
|
1358
|
+
*/
|
|
1359
|
+
declare function refreshStaticTransforms(): void;
|
|
1360
|
+
/** The census, and the re-arm counter it resets. */
|
|
1361
|
+
declare function staticTransformCensus(): IStaticTransformCensus;
|
|
1362
|
+
/** Drops every registration. A game that tore down its scene must not keep its roots alive. */
|
|
1363
|
+
declare function resetStaticTransforms(): void;
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* The parity oracle for anything that stops recomputing a transform.
|
|
1367
|
+
*
|
|
1368
|
+
* A cache that is right on the scene you tested and wrong on the one you did not is worse than no
|
|
1369
|
+
* cache, because it ships as a visual bug nobody can reproduce. This is the instrument that makes
|
|
1370
|
+
* the difference falsifiable: with `TN_RENDERLIST_VALIDATE=1`, every frame recomputes every world
|
|
1371
|
+
* matrix from the authored transforms, the long way, and compares it elementwise against the one
|
|
1372
|
+
* the frame is about to draw with. The first disagreement throws, naming the object and the
|
|
1373
|
+
* element, because a validation mode that logs and continues is a validation mode nobody reads.
|
|
1374
|
+
*
|
|
1375
|
+
* It is a validation mode, not a proof: it proves the frames it ran on. Run it on the scenes you
|
|
1376
|
+
* care about, in CI, for as many frames as you can afford.
|
|
1377
|
+
*
|
|
1378
|
+
* Off by default and expensive by construction — it does the work it is checking, twice.
|
|
1379
|
+
*/
|
|
1380
|
+
|
|
1381
|
+
/** Marker printed when the validator is installed, so a log says which mode produced it. */
|
|
1382
|
+
declare const RENDERLIST_VALIDATE_MARKER = "TN_RENDERLIST_VALIDATE";
|
|
1383
|
+
/** The launch flag. */
|
|
1384
|
+
declare const RENDERLIST_VALIDATE_FLAG = "TN_RENDERLIST_VALIDATE";
|
|
1385
|
+
/** Whether `TN_RENDERLIST_VALIDATE` asks for validation on this launch. */
|
|
1386
|
+
declare function renderListValidationRequested(): boolean;
|
|
1387
|
+
/** What one frame's check found. */
|
|
1388
|
+
interface IValidationReport {
|
|
1389
|
+
/** Objects whose world matrix was recomputed and compared. */
|
|
1390
|
+
readonly checked: number;
|
|
1391
|
+
/** Objects skipped because they own their own world matrix; reported, never counted as checked. */
|
|
1392
|
+
readonly gameOwned: number;
|
|
1393
|
+
/** Frames validated so far. */
|
|
1394
|
+
readonly frames: number;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Recomputes every world matrix under `root` and throws on the first that disagrees.
|
|
1398
|
+
*
|
|
1399
|
+
* The recomputation is deliberately independent of the flags a freeze sets: it composes the local
|
|
1400
|
+
* matrix from `position`/`quaternion`/`scale` when the object composes its own, uses the authored
|
|
1401
|
+
* `matrix` when it does not, and multiplies by the parent's *recomputed* world matrix rather than
|
|
1402
|
+
* the cached one — so an error at the top of a subtree cannot be hidden by a matching error
|
|
1403
|
+
* underneath it.
|
|
1404
|
+
*/
|
|
1405
|
+
declare function validateWorldMatrices(root: Object3D): {
|
|
1406
|
+
checked: number;
|
|
1407
|
+
gameOwned: number;
|
|
1408
|
+
};
|
|
1409
|
+
/**
|
|
1410
|
+
* The per-frame validator, installed when the flag asks for it.
|
|
1411
|
+
*
|
|
1412
|
+
* Holds a frame counter so the marker can say how much was proven, which is the difference between
|
|
1413
|
+
* "validation passed" and "validation passed on 1,800 frames of the reference game".
|
|
1414
|
+
*/
|
|
1415
|
+
declare class RenderListValidator {
|
|
1416
|
+
#private;
|
|
1417
|
+
/**
|
|
1418
|
+
* Validates one frame. Throws on the first divergence.
|
|
1419
|
+
*
|
|
1420
|
+
* `roots` is the thing being drawn plus every frozen subtree. Those are not the same object
|
|
1421
|
+
* when the engine's projection is collapsing the scene: the draw root is then the mirror, and
|
|
1422
|
+
* validating only it reported three objects checked and zero divergences on a scene with two
|
|
1423
|
+
* hundred frozen meshes, one of which really was stale. A freeze is authored on the authored
|
|
1424
|
+
* scene, so the authored roots are checked whether or not they are what reaches the GPU.
|
|
1425
|
+
*/
|
|
1426
|
+
frame(...roots: readonly Object3D[]): void;
|
|
1427
|
+
report(): IValidationReport;
|
|
1428
|
+
}
|
|
1429
|
+
/** The marker line for one reported window. */
|
|
1430
|
+
declare function formatValidationReport(report: IValidationReport): string;
|
|
1431
|
+
|
|
775
1432
|
/** The atlas has one copied edge texel on either side of each packed SH sub-volume. */
|
|
776
1433
|
declare const ATLAS_PADDING = 1;
|
|
777
1434
|
/** The machine-readable marker emitted whenever the probe state changes. */
|
|
@@ -1531,6 +2188,100 @@ declare class FluidField2D extends Group {
|
|
|
1531
2188
|
detach(): void;
|
|
1532
2189
|
}
|
|
1533
2190
|
|
|
2191
|
+
/**
|
|
2192
|
+
* A disturbance that propagates. `WaveField` in this same package evaluates a fixed analytic
|
|
2193
|
+
* swell: it is the sea a game always has, and nothing a game does changes it. This is the other
|
|
2194
|
+
* half — a square patch of surface that is flat until something hits it, carries the rings
|
|
2195
|
+
* outward at a real celerity, and forgets them again.
|
|
2196
|
+
*
|
|
2197
|
+
* The solve is the 2-D wave equation on a regular grid, plus an advected foam density. It owns no
|
|
2198
|
+
* Three.js object, no material and no colour: it reports height, foam and horizontal flow, and the
|
|
2199
|
+
* game decides what those look like. Add its height to an analytic swell; do not replace one.
|
|
2200
|
+
*
|
|
2201
|
+
* There is deliberately no obstacle or hull mask. A mask is only correct for a body that does not
|
|
2202
|
+
* move, and a game whose hulls move is better served drawing the displacement those hulls make
|
|
2203
|
+
* than re-rasterising a mask every time one of them advances a metre.
|
|
2204
|
+
*/
|
|
2205
|
+
interface IRippleFieldFlow {
|
|
2206
|
+
x: number;
|
|
2207
|
+
z: number;
|
|
2208
|
+
}
|
|
2209
|
+
interface IRippleFieldOptions {
|
|
2210
|
+
/** Cells per side. Cost is quadratic in this; 128 over a 400 m patch is ~3 m per cell. */
|
|
2211
|
+
readonly resolution: number;
|
|
2212
|
+
/** Width of the patch in metres. */
|
|
2213
|
+
readonly size: number;
|
|
2214
|
+
/** Wave celerity in metres per second. Sets both ring speed and the stable step. */
|
|
2215
|
+
readonly speed?: number;
|
|
2216
|
+
/** Bulk damping. Higher forgets a disturbance sooner. */
|
|
2217
|
+
readonly damping?: number;
|
|
2218
|
+
/** Seconds for undisturbed foam to halve. */
|
|
2219
|
+
readonly foamHalfLife?: number;
|
|
2220
|
+
/** Steady surface drift in metres per second, added to the solved flow. */
|
|
2221
|
+
readonly current?: IRippleFieldFlow;
|
|
2222
|
+
/**
|
|
2223
|
+
* Integration step in seconds. Defaults to 1/60 s, or the CFL limit for this cell size and
|
|
2224
|
+
* celerity where that is smaller.
|
|
2225
|
+
*/
|
|
2226
|
+
readonly step?: number;
|
|
2227
|
+
/** Substeps one `advance` will run before it drops the rest of the frame's time. */
|
|
2228
|
+
readonly maxSteps?: number;
|
|
2229
|
+
}
|
|
2230
|
+
declare class RippleField {
|
|
2231
|
+
#private;
|
|
2232
|
+
readonly resolution: number;
|
|
2233
|
+
readonly size: number;
|
|
2234
|
+
readonly dx: number;
|
|
2235
|
+
readonly speed: number;
|
|
2236
|
+
readonly damping: number;
|
|
2237
|
+
readonly step: number;
|
|
2238
|
+
readonly maxSteps: number;
|
|
2239
|
+
foamHalfLife: number;
|
|
2240
|
+
current: IRippleFieldFlow;
|
|
2241
|
+
/** Surface height per cell, row-major from the patch's -x/-z corner. Read-only to the game. */
|
|
2242
|
+
height: Float32Array;
|
|
2243
|
+
/** Foam density per cell in 0..1. */
|
|
2244
|
+
foam: Float32Array;
|
|
2245
|
+
/** Horizontal flow per cell, metres per second, excluding `current`. */
|
|
2246
|
+
readonly flowX: Float32Array;
|
|
2247
|
+
readonly flowZ: Float32Array;
|
|
2248
|
+
/** Patch centre in world metres, always snapped to a whole cell. */
|
|
2249
|
+
centerX: number;
|
|
2250
|
+
centerZ: number;
|
|
2251
|
+
/** Simulated seconds elapsed. */
|
|
2252
|
+
time: number;
|
|
2253
|
+
/** Bumped whenever a cell changes, so a texture upload can skip an unchanged frame. */
|
|
2254
|
+
version: number;
|
|
2255
|
+
constructor(options: IRippleFieldOptions);
|
|
2256
|
+
/** True while the point is inside the patch, `margin` metres in from its rim. */
|
|
2257
|
+
contains(x: number, z: number, margin?: number): boolean;
|
|
2258
|
+
/**
|
|
2259
|
+
* Move the patch so it covers the action. The grid shifts by whole cells and the newly exposed
|
|
2260
|
+
* band arrives flat, which is what the sponge rim had already damped it to.
|
|
2261
|
+
*/
|
|
2262
|
+
recenter(x: number, z: number): void;
|
|
2263
|
+
/**
|
|
2264
|
+
* Push the surface at a point: a Gaussian of the given radius, with the kernel corrected so the
|
|
2265
|
+
* disturbance injects no net volume. Without that correction every splash slowly raises the sea.
|
|
2266
|
+
* Returns false when the point is outside the patch.
|
|
2267
|
+
*/
|
|
2268
|
+
impulse(x: number, z: number, radius: number, amplitude: number, foam?: number): boolean;
|
|
2269
|
+
/** Lay down entrained air without claiming a pressure impulse happened. */
|
|
2270
|
+
depositFoam(x: number, z: number, radius: number, amount: number): boolean;
|
|
2271
|
+
/** Run whole fixed steps to consume `dt`, and report how many ran. */
|
|
2272
|
+
advance(dt: number): number;
|
|
2273
|
+
/** Disturbance height in metres at a world point, zero outside the patch. */
|
|
2274
|
+
heightAt(x: number, z: number): number;
|
|
2275
|
+
/** Foam density in 0..1 at a world point. */
|
|
2276
|
+
foamAt(x: number, z: number): number;
|
|
2277
|
+
/** Surface flow in metres per second at a world point, including `current`. */
|
|
2278
|
+
flowAt(x: number, z: number, out?: IRippleFieldFlow): IRippleFieldFlow;
|
|
2279
|
+
/** Total disturbance energy. Zero on an undisturbed patch; useful as a test oracle. */
|
|
2280
|
+
energy(): number;
|
|
2281
|
+
/** Flatten the patch and forget every disturbance. Position, options and time survive. */
|
|
2282
|
+
reset(): void;
|
|
2283
|
+
}
|
|
2284
|
+
|
|
1534
2285
|
type WaveDirection = readonly [number, number] | {
|
|
1535
2286
|
readonly x: number;
|
|
1536
2287
|
readonly z?: number;
|
|
@@ -1597,7 +2348,21 @@ declare class WaveField {
|
|
|
1597
2348
|
constructor(options: IWaveFieldOptions);
|
|
1598
2349
|
/** Update the default graph clock. Explicit sample times remain available for fixed-step code. */
|
|
1599
2350
|
setTime(value: number): void;
|
|
2351
|
+
/**
|
|
2352
|
+
* Surface height and normal at a point. Allocates the result and its vector; a caller that wants
|
|
2353
|
+
* only the number should call `heightAt`, which is the same evaluation without either.
|
|
2354
|
+
*/
|
|
1600
2355
|
sample(x: number, z: number, time: number): IWaveFieldSample;
|
|
2356
|
+
/**
|
|
2357
|
+
* Surface height at a point, as a number.
|
|
2358
|
+
*
|
|
2359
|
+
* The scalar half of `sample`, and the same arithmetic in the same order, so the two agree
|
|
2360
|
+
* exactly. What it skips is everything only a normal needs: the domain warp's jacobian, the
|
|
2361
|
+
* cosine and slope of every wave, the normalisation, the result object and the `Vector3`. A
|
|
2362
|
+
* floating hull, a splash query or a whitewater height test asks this question thousands of times
|
|
2363
|
+
* a frame and throws the normal away every time.
|
|
2364
|
+
*/
|
|
2365
|
+
heightAt(x: number, z: number, time: number): number;
|
|
1601
2366
|
/** Surface height at a point, as a graph. The scalar half of what `sample` returns. */
|
|
1602
2367
|
heightNode(options?: IWaveFieldGraphOptions): Node<"float">;
|
|
1603
2368
|
/**
|
|
@@ -1617,17 +2382,53 @@ declare class WaveField {
|
|
|
1617
2382
|
displacementNode(timeNode?: three_webgpu.UniformNode<"float", number>): Node<"vec3">;
|
|
1618
2383
|
}
|
|
1619
2384
|
|
|
1620
|
-
/**
|
|
2385
|
+
/** What the mirrored pass costs: how big it is, how many of them, and how much of the world. */
|
|
1621
2386
|
interface IWaterReflectionOptions {
|
|
1622
2387
|
/**
|
|
1623
2388
|
* The mirrored pass's render target, as a fraction of the drawing buffer.
|
|
1624
2389
|
*
|
|
1625
|
-
*
|
|
1626
|
-
* whether a
|
|
2390
|
+
* How many *pixels* the second pass costs. Half is the usual answer. This is not on its own the
|
|
2391
|
+
* number that decides whether a surface is affordable, and a game that reads it that way will
|
|
2392
|
+
* measure no improvement and conclude its water is free: a scene with many objects is bound by
|
|
2393
|
+
* the draw calls the mirrored pass submits, not by its pixels, and those do not shrink with the
|
|
2394
|
+
* target. Measured on `sandbox/midway-open-pacific` at 1920x1080 on an nvidia/turing adapter with
|
|
2395
|
+
* 1,965 draws in the frame, halving this again — 0.5 to 0.25 — moved GPU p95 from 17.80 ms to
|
|
2396
|
+
* 17.58 ms. Removing the pass entirely moved it to 7.67 ms. `layers` is the number that mattered.
|
|
1627
2397
|
*/
|
|
1628
2398
|
readonly resolutionScale: number;
|
|
1629
2399
|
/** Whether this surface may appear in other reflectors' passes. Off is one pass; on is n². */
|
|
1630
2400
|
readonly bounces?: boolean;
|
|
2401
|
+
/**
|
|
2402
|
+
* Which layers the mirrored pass draws, as a three `Layers` mask. Omit to draw everything the
|
|
2403
|
+
* scene camera draws, which is the default and what a reflection means when nothing says
|
|
2404
|
+
* otherwise.
|
|
2405
|
+
*
|
|
2406
|
+
* This is how much *world* the second pass costs, and on a crowded scene it is the whole bill.
|
|
2407
|
+
* The mirrored pass is a second draw of everything, so a frame with sixty-eight aircraft in it
|
|
2408
|
+
* pays for sixty-eight aircraft twice — once where the player can see them and once in the water,
|
|
2409
|
+
* where they are a few pixels and half of them are behind the camera anyway. Put the big
|
|
2410
|
+
* silhouettes a player actually reads in the water on their own layer and name it here.
|
|
2411
|
+
*
|
|
2412
|
+
* The mask decides what appears in the mirror, so the game owns it: this only carries the number
|
|
2413
|
+
* through to the pass, and a game that omits it gets the whole world reflected as before.
|
|
2414
|
+
*/
|
|
2415
|
+
readonly layers?: number;
|
|
2416
|
+
/**
|
|
2417
|
+
* How often the mirrored pass redraws, in presented frames.
|
|
2418
|
+
*
|
|
2419
|
+
* The reflection is a second render of the world and on a crowded sea it is the largest single
|
|
2420
|
+
* item in the frame. It is also low-frequency: a swell, a hull and a wake read the same whether
|
|
2421
|
+
* the mirror was taken this frame or two frames ago, and at speed the eye cannot hold a reflected
|
|
2422
|
+
* silhouette still enough to notice it lag. `2` redraws every second frame and samples the
|
|
2423
|
+
* previous frame's target in between, halving the pass's cost; a higher number halves it again.
|
|
2424
|
+
*
|
|
2425
|
+
* Omit it — or pass `1` — and the pass redraws every frame, which is the shipping behaviour and
|
|
2426
|
+
* the default. The trade is temporal: while the camera moves, the reflection is up to
|
|
2427
|
+
* `refreshInterval - 1` frames behind the scene it mirrors. Fast camera motion can make a
|
|
2428
|
+
* reflected edge shimmer, and a moving object's reflection trails it. A still camera sees none of
|
|
2429
|
+
* that, so name it only where the measured pass dominates the frame.
|
|
2430
|
+
*/
|
|
2431
|
+
readonly refreshInterval?: number;
|
|
1631
2432
|
}
|
|
1632
2433
|
interface IWaterSurfaceOptions {
|
|
1633
2434
|
/** World-space height of the surface, in metres. The mirror plane, and where thickness is 0. */
|
|
@@ -1681,6 +2482,19 @@ declare class WaterSurface3D {
|
|
|
1681
2482
|
*/
|
|
1682
2483
|
readonly target: Object3D | undefined;
|
|
1683
2484
|
constructor(options: IWaterSurfaceOptions);
|
|
2485
|
+
/**
|
|
2486
|
+
* The camera the mirrored pass draws this surface with, for one scene camera.
|
|
2487
|
+
*
|
|
2488
|
+
* The pass mints one of these per scene camera, lazily, and reflects the camera through the
|
|
2489
|
+
* mirror plane each frame. It is exposed because `layers` is not the only thing a game may need
|
|
2490
|
+
* to say about the second draw — a near/far pair is the other — and because a reflection you
|
|
2491
|
+
* cannot inspect is a reflection you cannot cost. Undefined when this surface has no reflection.
|
|
2492
|
+
*/
|
|
2493
|
+
reflectionCameraFor(camera: Camera): Camera | undefined;
|
|
2494
|
+
/** What the mirrored pass draws, as the mask the game supplied. Undefined means everything. */
|
|
2495
|
+
readonly reflectionLayers: number | undefined;
|
|
2496
|
+
/** How often the mirrored pass redraws, in presented frames. `1` is every frame, the default. */
|
|
2497
|
+
readonly reflectionRefreshInterval: number;
|
|
1684
2498
|
/** The world-space height of the surface, in metres. */
|
|
1685
2499
|
get level(): number;
|
|
1686
2500
|
/** Move the surface — a tide, a sluice, a flooding room. The mirror plane follows. */
|
|
@@ -1872,6 +2686,213 @@ declare class GroundSnap {
|
|
|
1872
2686
|
audit(): number | null;
|
|
1873
2687
|
}
|
|
1874
2688
|
|
|
2689
|
+
/**
|
|
2690
|
+
* Force-integrated fixed-wing flight dynamics for a game-owned aircraft.
|
|
2691
|
+
*
|
|
2692
|
+
* The model integrates lift, drag, thrust and aerodynamic moments in SI units, `+Y` up, with the
|
|
2693
|
+
* nose down local `-Z`. Attitude is a quaternion, so there are no Euler clamps and no commanded
|
|
2694
|
+
* velocity vectors: the game writes controls, the model writes forces, and the aircraft keeps its
|
|
2695
|
+
* own momentum. Every number that decides how the aircraft looks or performs — mass, wing area,
|
|
2696
|
+
* engine power, inertia, control authority, wind, damage multipliers and payload — arrives from
|
|
2697
|
+
* the game. This module owns only the mechanism that turns those inputs into motion.
|
|
2698
|
+
*/
|
|
2699
|
+
interface IFlightVector3 {
|
|
2700
|
+
x: number;
|
|
2701
|
+
y: number;
|
|
2702
|
+
z: number;
|
|
2703
|
+
}
|
|
2704
|
+
interface IFlightQuaternion {
|
|
2705
|
+
x: number;
|
|
2706
|
+
y: number;
|
|
2707
|
+
z: number;
|
|
2708
|
+
w: number;
|
|
2709
|
+
}
|
|
2710
|
+
interface IFlightAxes {
|
|
2711
|
+
readonly r: IFlightVector3;
|
|
2712
|
+
readonly u: IFlightVector3;
|
|
2713
|
+
readonly f: IFlightVector3;
|
|
2714
|
+
}
|
|
2715
|
+
/** Per-aircraft constants. The game supplies these; the model ships no airframe of its own. */
|
|
2716
|
+
interface IAircraftAirframe {
|
|
2717
|
+
/** Empty mass, kg. */
|
|
2718
|
+
readonly dryMass: number;
|
|
2719
|
+
/** Mass of a full fuel load, kg. */
|
|
2720
|
+
readonly fuelMass: number;
|
|
2721
|
+
/** Reference wing area, m². */
|
|
2722
|
+
readonly wingArea: number;
|
|
2723
|
+
/** Wingspan, m. */
|
|
2724
|
+
readonly span: number;
|
|
2725
|
+
/** Mean aerodynamic chord, m. */
|
|
2726
|
+
readonly chord: number;
|
|
2727
|
+
/** Shaft power at full throttle, W. */
|
|
2728
|
+
readonly power: number;
|
|
2729
|
+
/** Propeller efficiency, 0–1. */
|
|
2730
|
+
readonly propEfficiency: number;
|
|
2731
|
+
/** Maximum static thrust, N. */
|
|
2732
|
+
readonly staticThrust: number;
|
|
2733
|
+
/** Pitch moment of inertia, kg·m². */
|
|
2734
|
+
readonly pitchInertia: number;
|
|
2735
|
+
/** Roll moment of inertia, kg·m². */
|
|
2736
|
+
readonly rollInertia: number;
|
|
2737
|
+
/** Yaw moment of inertia, kg·m². */
|
|
2738
|
+
readonly yawInertia: number;
|
|
2739
|
+
}
|
|
2740
|
+
/** Multipliers a game applies for damage, load or upgrades. 1 / 0 is the undamaged case. */
|
|
2741
|
+
interface IFlightModifiers {
|
|
2742
|
+
readonly power: number;
|
|
2743
|
+
readonly lift: number;
|
|
2744
|
+
readonly drag: number;
|
|
2745
|
+
readonly roll: number;
|
|
2746
|
+
readonly controls: number;
|
|
2747
|
+
}
|
|
2748
|
+
declare const NEUTRAL_FLIGHT_MODIFIERS: IFlightModifiers;
|
|
2749
|
+
/** One fixed step of pilot input. Every field is a dimensionless command in `[-1, 1]`. */
|
|
2750
|
+
interface IFlightControls {
|
|
2751
|
+
readonly turn?: number;
|
|
2752
|
+
readonly pitch?: number;
|
|
2753
|
+
readonly rudder?: number;
|
|
2754
|
+
readonly wheelBrake?: boolean;
|
|
2755
|
+
/** When true, stability assist is bypassed and the game commands the attitude directly. */
|
|
2756
|
+
readonly autopilot?: boolean;
|
|
2757
|
+
}
|
|
2758
|
+
/** The moving deck an aircraft launches from. */
|
|
2759
|
+
interface IFlightDeck {
|
|
2760
|
+
readonly x: number;
|
|
2761
|
+
readonly y?: number;
|
|
2762
|
+
readonly z: number;
|
|
2763
|
+
readonly heading: number;
|
|
2764
|
+
readonly speed: number;
|
|
2765
|
+
readonly length: number;
|
|
2766
|
+
readonly width: number;
|
|
2767
|
+
}
|
|
2768
|
+
interface IFlightState {
|
|
2769
|
+
x: number;
|
|
2770
|
+
y: number;
|
|
2771
|
+
z: number;
|
|
2772
|
+
vx: number;
|
|
2773
|
+
vy: number;
|
|
2774
|
+
vz: number;
|
|
2775
|
+
attitude?: IFlightQuaternion;
|
|
2776
|
+
heading: number;
|
|
2777
|
+
pitch: number;
|
|
2778
|
+
roll: number;
|
|
2779
|
+
rollRate: number;
|
|
2780
|
+
pitchRate: number;
|
|
2781
|
+
yawRate: number;
|
|
2782
|
+
speed: number;
|
|
2783
|
+
ias: number;
|
|
2784
|
+
groundSpeed: number;
|
|
2785
|
+
throttle: number;
|
|
2786
|
+
rpm: number;
|
|
2787
|
+
fuel: number;
|
|
2788
|
+
hp: number;
|
|
2789
|
+
engineCut: boolean;
|
|
2790
|
+
gear: boolean;
|
|
2791
|
+
brakes: boolean;
|
|
2792
|
+
gearPos: number;
|
|
2793
|
+
brakePos: number;
|
|
2794
|
+
flapPos: number;
|
|
2795
|
+
flaps: number;
|
|
2796
|
+
aileron: number;
|
|
2797
|
+
elevator: number;
|
|
2798
|
+
rudder: number;
|
|
2799
|
+
controlAileron: number;
|
|
2800
|
+
assist: boolean;
|
|
2801
|
+
trim: number;
|
|
2802
|
+
gforce: number;
|
|
2803
|
+
aoa: number;
|
|
2804
|
+
beta: number;
|
|
2805
|
+
stall: number;
|
|
2806
|
+
/** Mass of everything under the wings, kg. The game writes it as stores are released. */
|
|
2807
|
+
payloadMass: number;
|
|
2808
|
+
/** Extra flat-plate drag coefficient from external stores. */
|
|
2809
|
+
payloadDrag: number;
|
|
2810
|
+
flightTime: number;
|
|
2811
|
+
lift: number;
|
|
2812
|
+
drag: number;
|
|
2813
|
+
thrust: number;
|
|
2814
|
+
mass: number;
|
|
2815
|
+
deckSpeed?: number;
|
|
2816
|
+
deckLateral?: number;
|
|
2817
|
+
deckOffset?: number;
|
|
2818
|
+
chocks?: boolean;
|
|
2819
|
+
}
|
|
2820
|
+
interface IFlightEnvironment {
|
|
2821
|
+
readonly airframe: IAircraftAirframe;
|
|
2822
|
+
readonly wind?: IFlightVector3;
|
|
2823
|
+
readonly modifiers?: IFlightModifiers;
|
|
2824
|
+
readonly gravity?: number;
|
|
2825
|
+
/** Height of the carrier deck surface above the water, m. */
|
|
2826
|
+
readonly deckHeight?: number;
|
|
2827
|
+
}
|
|
2828
|
+
interface IFlightForces {
|
|
2829
|
+
readonly x: number;
|
|
2830
|
+
readonly y: number;
|
|
2831
|
+
readonly z: number;
|
|
2832
|
+
readonly normalLoad: number;
|
|
2833
|
+
readonly lift: number;
|
|
2834
|
+
readonly drag: number;
|
|
2835
|
+
readonly thrust: number;
|
|
2836
|
+
readonly alpha: number;
|
|
2837
|
+
readonly beta: number;
|
|
2838
|
+
readonly airspeed: number;
|
|
2839
|
+
readonly density: number;
|
|
2840
|
+
readonly mass: number;
|
|
2841
|
+
readonly qs: number;
|
|
2842
|
+
readonly axes: IFlightAxes;
|
|
2843
|
+
readonly cl: number;
|
|
2844
|
+
readonly cd: number;
|
|
2845
|
+
readonly stall: number;
|
|
2846
|
+
readonly critical: number;
|
|
2847
|
+
}
|
|
2848
|
+
/** ISA air density at altitude `y` metres, kg/m³. */
|
|
2849
|
+
declare function airDensity(y: number): number;
|
|
2850
|
+
/** Current mass of the aircraft from its empty mass, fuel load and game-written payload. */
|
|
2851
|
+
declare function aircraftMass(state: IFlightState, airframe: IAircraftAirframe): number;
|
|
2852
|
+
/** Lift and drag coefficients for an angle of attack and the deployed high-lift devices. */
|
|
2853
|
+
declare function aerodynamicCoefficients(alpha: number, flaps?: number, gear?: number, brakes?: number): {
|
|
2854
|
+
cl: number;
|
|
2855
|
+
cd: number;
|
|
2856
|
+
stall: number;
|
|
2857
|
+
critical: number;
|
|
2858
|
+
};
|
|
2859
|
+
/** Write a heading/pitch/roll pose into the body quaternion. Nose down local `-Z`. */
|
|
2860
|
+
declare function setAttitude(state: IFlightState, heading?: number, pitch?: number, roll?: number): IFlightQuaternion;
|
|
2861
|
+
/** Right, up and forward unit vectors of the body frame. */
|
|
2862
|
+
declare function attitudeAxes(state: IFlightState): IFlightAxes;
|
|
2863
|
+
/** Height of the gear contact point below the body origin at the current pitch. */
|
|
2864
|
+
declare function gearClearance(state: IFlightState): number;
|
|
2865
|
+
interface IFlightModelOptions<TState extends IFlightState = IFlightState> extends IFlightEnvironment {
|
|
2866
|
+
/** The game's own aircraft object, extended with whatever else the game needs on it. */
|
|
2867
|
+
readonly state: TState;
|
|
2868
|
+
}
|
|
2869
|
+
/**
|
|
2870
|
+
* One aircraft's dynamics: a thin owner of a game-authored state object plus its environment.
|
|
2871
|
+
*
|
|
2872
|
+
* The step record copies the options' own fields once, at construction — so `airframe`, `wind`,
|
|
2873
|
+
* `gravity` and `deckHeight` are read from the objects those fields reference, which stay live,
|
|
2874
|
+
* but replacing a field on the options object afterwards is not observed. Change a model's airframe
|
|
2875
|
+
* or deck by building it again, as a game that binds aircraft to carriers already does.
|
|
2876
|
+
*
|
|
2877
|
+
* @example
|
|
2878
|
+
* const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });
|
|
2879
|
+
* model.setAttitude(0, 0.2, 0);
|
|
2880
|
+
* model.step(1 / 60, { turn: -1, pitch: 0.4 });
|
|
2881
|
+
*/
|
|
2882
|
+
declare class FlightModel<TState extends IFlightState = IFlightState> {
|
|
2883
|
+
#private;
|
|
2884
|
+
readonly state: TState;
|
|
2885
|
+
readonly environment: IFlightEnvironment;
|
|
2886
|
+
constructor(options: IFlightModelOptions<TState>);
|
|
2887
|
+
setAttitude(heading?: number, pitch?: number, roll?: number): void;
|
|
2888
|
+
reset(): void;
|
|
2889
|
+
axes(): IFlightAxes;
|
|
2890
|
+
gearClearance(): number;
|
|
2891
|
+
forces(modifiers?: IFlightModifiers): IFlightForces;
|
|
2892
|
+
step(dt: number, controls: IFlightControls, modifiers?: IFlightModifiers): void;
|
|
2893
|
+
stepDeck(deck: IFlightDeck, dt: number, controls: IFlightControls, modifiers?: IFlightModifiers): "liftoff" | "overrun" | null;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
1875
2896
|
type ThreePoseVector = readonly [number, number, number];
|
|
1876
2897
|
type ThreePoseQuaternion = readonly [number, number, number, number];
|
|
1877
2898
|
interface IThreePoseBounds {
|
|
@@ -2189,6 +3210,6 @@ declare function boneLengthDeviations(root: Object3D, bind: IBoneLengthSnapshot,
|
|
|
2189
3210
|
* unavoidable here — core is bundled for browsers and cannot read `package.json` at runtime — so
|
|
2190
3211
|
* the spec now asserts this equals the manifest instead of asserting a number somebody typed.
|
|
2191
3212
|
*/
|
|
2192
|
-
declare const version = "0.3.
|
|
3213
|
+
declare const version = "0.3.3";
|
|
2193
3214
|
|
|
2194
|
-
export { ATLAS_PADDING, ATMOSPHERE_LUT_RESOLUTIONS, AnimationPlayer, Atmosphere, type AtmosphereDirection, AtmosphereLuts, type AtmosphereRgb, Billboard3D, type BillboardLockAxis, CameraShake, type CameraShakeCurve, ClusteredBatch, ClusteredMesh, FluidField2D, GPUParticles3D, GPUSceneBVH, type GPUSceneBVHTraceFunction, GroundSnap, type IAddInSlicesOptions, type IAddInSlicesProgress, type IAddInSlicesReport, type IAnimationPlayOptions, type IAnimationPlayerOptions, type IAtmosphereLutResolution, type IAtmosphereLutResolutions, type IAtmosphereOptions, type IAtmosphereParameterPatch, type IAtmosphereParameters, type IAtmosphereScenePass, type IBillboard3DOptions, type IBoneContactReport, type IBoneLengthDeviation, type IBoneLengthDeviationReport, type IBoneLengthDeviationsOptions, type IBoneLengthSnapshot, type IBonePoseError, type ICameraShakeOffset, type ICameraShakeOptions, type IClipBindingReport, type IClipCoverageReport, type IClipPoseErrorOptions, type IClipPoseErrorReport, type IClipPoseSubject, type IClipTrackBinding, type IClusterTable, type IClusteredBatchBuildOptions, type IClusteredBatchOptions, type IClusteredMeshOptions, type IClusteredPlacement, IComputeDriven$1 as IComputeDriven, type IFluidFieldOptions, type IFluidFieldSampler, type IFluidFieldVector2, IGPUReadbackSample, type IGPUSceneBVHMaterialGroup, type IGPUSceneBVHOptions, IGamePluginHooks, IGamePluginRuntime, type IGroundSnapOptions, type IInstancedBatchBuildOptions, type IInstancedBatchOptions, type IInstancedPlacement, type ILoadAllOptions, type ILoadAllProgress, type IMeasureThreePoseOptions, type IMergePart, type IMergePartsOptions, type INormaliseToMetresOptions, type IPathFollow3DOptions, type IPathFollow3DProjection, type IPathFollow3DSample, type IPlatformInfo, type IProbeVolumeBakeProgress, type IProbeVolumeCoefficient, type IProbeVolumeObservation, type IProbeVolumeOptions, type IReplayOptions, type IReplayRecording, type IReplayRecordingSample, type IResolvedAtmosphereParameters, type ISkeletalMesh3DOptions, type ISoftBody3DOptions, type ISoftBodyCollision, type ISolarPosition, type ISolarPositionInput, type ISpectralOceanCascade, type ISpectralOceanHeight, type ISpectralOceanOptions, type ISpriteAnimator3DOptions, type ISpriteFrame3D, type IStrideReport, type IThreePoseBounds, type IThreePoseMeasurement, type ITracerPool3DOptions, type ITracerSpawnOptions, type IVirtualShadowOptions, type IVirtualShadowStats, type IWaterReflectionOptions, type IWaterSurfaceOptions, type IWaveFieldDomainWarp, type IWaveFieldGraphOptions, type IWaveFieldOptions, type IWaveFieldSample, type IWaveFieldWave, InstancedBatch, LUT_RESOLUTIONS, type NormaliseAxis, PROBE_VOLUME_MARKER, PathFollow3D, type PlatformFormFactor, type PlatformOS, type PlatformRuntime, ProbeVolume, type ProbeVolumeDensity, type Recording, type ReplayPointer, SkeletalMesh3D, SoftBody3D, SpectralOcean, SpriteAnimator3D, type SpritePlaybackMode, type ThreePoseQuaternion, type ThreePoseVector, TracerPool3D, VIRTUAL_SHADOW_MARKER, VIRTUAL_SHADOW_MOVER_LAYER, VirtualShadowNode, WaterSurface3D, type WaveDirection, WaveField, addInSlices, attachToBone, boneContact, boneLengthDeviations, boneLengths, bvhIntersectFirstHit, clipBoneCoverage, clipPoseError, clipTrackBindings, createReplayDriver, directionFromSolarPosition, directionalTransmittance, getPlatform, isMobile, isNative, isTouchscreenAvailable, isWeb, loadAll, measureThreePose, mergeParts, normaliseToMetres, parseReplayRecording, posedBounds, rayStruct, readProbeVolumeObservation, readVirtualShadowMarker, replay, resolveAtmosphereLutResolutions, resolveAtmosphereParameters, skeletonBones, softCircleDataTexture, solarPosition, solarPositionAt, updateAtmosphereParameters, updateClusteredMeshes, version, zenithTransmittance };
|
|
3215
|
+
export { ATLAS_PADDING, ATMOSPHERE_LUT_RESOLUTIONS, AnimationPlayer, Atmosphere, type AtmosphereDirection, AtmosphereLuts, type AtmosphereRgb, Billboard3D, type BillboardLockAxis, CameraShake, type CameraShakeCurve, ClusteredBatch, ClusteredMesh, FlightModel, FluidField2D, FramePassKind, GPUParticles3D, GPUSceneBVH, type GPUSceneBVHTraceFunction, GroundSnap, type IAddInSlicesOptions, type IAddInSlicesProgress, type IAddInSlicesReport, type IAircraftAirframe, type IAnimationPlayOptions, type IAnimationPlayerOptions, type IAtmosphereLutResolution, type IAtmosphereLutResolutions, type IAtmosphereOptions, type IAtmosphereParameterPatch, type IAtmosphereParameters, type IAtmosphereScenePass, type IBillboard3DOptions, type IBoneContactReport, type IBoneLengthDeviation, type IBoneLengthDeviationReport, type IBoneLengthDeviationsOptions, type IBoneLengthSnapshot, type IBonePoseError, type ICameraShakeOffset, type ICameraShakeOptions, type IClipBindingReport, type IClipCoverageReport, type IClipPoseErrorOptions, type IClipPoseErrorReport, type IClipPoseSubject, type IClipTrackBinding, type IClusterTable, type IClusteredBatchBuildOptions, type IClusteredBatchOptions, type IClusteredMeshOptions, type IClusteredPlacement, IComputeDriven$1 as IComputeDriven, type IFlightAxes, type IFlightControls, type IFlightDeck, type IFlightEnvironment, type IFlightForces, type IFlightModelOptions, type IFlightModifiers, type IFlightQuaternion, type IFlightState, type IFlightVector3, type IFluidFieldOptions, type IFluidFieldSampler, type IFluidFieldVector2, IFrameBudgetWindow, IGPUReadbackSample, type IGPUSceneBVHMaterialGroup, type IGPUSceneBVHOptions, IGamePluginHooks, IGamePluginRuntime, type IGroundSnapOptions, type IInstancedBatchBuildOptions, type IInstancedBatchOptions, type IInstancedPlacement, type ILaunchFailure, type ILoadAllOptions, type ILoadAllProgress, type IMatrixWorldReport, type IMeasureThreePoseOptions, type IMergePart, type IMergePartsOptions, type INormaliseToMetresOptions, type IPathFollow3DOptions, type IPathFollow3DProjection, type IPathFollow3DSample, type IPlatformInfo, type IProbeVolumeBakeProgress, type IProbeVolumeCoefficient, type IProbeVolumeObservation, type IProbeVolumeOptions, type IReplayOptions, type IReplayRecording, type IReplayRecordingSample, type IResolvedAtmosphereParameters, type IRippleFieldFlow, type IRippleFieldOptions, type ISceneShape, type ISceneWarning, type ISkeletalMesh3DOptions, type ISoftBody3DOptions, type ISoftBodyCollision, type ISolarPosition, type ISolarPositionInput, type ISpanProbeTarget, type ISpanSummary, type ISpanWindow, type ISpectralOceanCascade, type ISpectralOceanHeight, type ISpectralOceanOptions, type ISpriteAnimator3DOptions, type ISpriteFrame3D, type IStaticTransformCensus, type IStrideReport, type IThreePoseBounds, type IThreePoseMeasurement, type ITracerPool3DOptions, type ITracerSpawnOptions, type IValidationReport, type IVirtualShadowOptions, type IVirtualShadowStats, type IWaterReflectionOptions, type IWaterSurfaceOptions, type IWaveFieldDomainWarp, type IWaveFieldGraphOptions, type IWaveFieldOptions, type IWaveFieldSample, type IWaveFieldWave, InstancedBatch, LUT_RESOLUTIONS, type LaunchFailureKind, type MatrixWorldMode, MatrixWorldPass, NEUTRAL_FLIGHT_MODIFIERS, type NormaliseAxis, PROBE_VOLUME_MARKER, PathFollow3D, type PlatformFormFactor, type PlatformOS, type PlatformRuntime, ProbeVolume, type ProbeVolumeDensity, RENDERLIST_VALIDATE_FLAG, RENDERLIST_VALIDATE_MARKER, type Recording, RenderListValidator, type ReplayPointer, RippleField, SCENE_WARNING_MARKER, SPANS, SPANS_FLAG, SPANS_MARKER, SPAN_COUNT, SPAN_NAMES, STATIC_TRANSFORM_MARKER, SkeletalMesh3D, SoftBody3D, type SpanId, SpanRecorder, SpectralOcean, SpriteAnimator3D, type SpritePlaybackMode, type ThreePoseQuaternion, type ThreePoseVector, TracerPool3D, VIRTUAL_SHADOW_MARKER, VIRTUAL_SHADOW_MOVER_LAYER, VirtualShadowNode, WaterSurface3D, type WaveDirection, WaveField, addInSlices, addSpan, aerodynamicCoefficients, airDensity, aircraftMass, alwaysRender, attachToBone, attitudeAxes, baseGeometryOf, beginSpan, boneContact, boneLengthDeviations, boneLengths, bvhIntersectFirstHit, clipBoneCoverage, clipPoseError, clipTrackBindings, createReplayDriver, describeSceneShape, describeSceneWarning, directionFromSolarPosition, directionalTransmittance, displayPeriodMs, endSpan, formatSceneWarning, formatSpansWindow, formatValidationReport, gearClearance, getPlatform, installSpanProbes, invalidateStatic, isMobile, isNative, isStatic, isTouchscreenAvailable, isWeb, loadAll, markStatic, measureThreePose, mergeParts, normaliseToMetres, onLaunchFailure, parseReplayRecording, posedBounds, rayStruct, readProbeVolumeObservation, readVirtualShadowMarker, refreshStaticTransforms, renderListValidationRequested, replay, resetStaticTransforms, resolveAtmosphereLutResolutions, resolveAtmosphereParameters, sceneWarning, setAttitude, setSpanRecorder, skeletonBones, softCircleDataTexture, solarPosition, solarPositionAt, spanNow, spanRecorder, spansRequested, staticTransformCensus, unmarkStatic, updateAtmosphereParameters, updateClusteredMeshes, updateModelLods, validateWorldMatrices, version, zenithTransmittance };
|