@threenative/core 0.1.0 → 0.3.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/LICENSE +21 -0
- package/README.md +45 -0
- package/capabilities.json +3542 -0
- package/dist/audio-Dp2mXpD3.d.ts +94 -0
- package/dist/canvas-layer-CtrZHgIh.d.ts +608 -0
- package/dist/game-CYIaKhgl.d.ts +980 -0
- package/dist/hot.d.ts +33 -5
- package/dist/hot.js +10 -2
- package/dist/index.d.ts +1480 -41
- package/dist/index.js +12604 -1104
- package/dist/playtest.d.ts +36 -5
- package/dist/playtest.js +160 -468
- package/dist/react.d.ts +175 -0
- package/dist/react.js +635 -0
- package/dist/ui-layer.d.ts +306 -0
- package/dist/ui-layer.js +425 -0
- package/dist/world.d.ts +55 -0
- package/dist/world.js +175 -0
- package/mcp/assets.mjs +5 -0
- package/mcp/engine-server.mjs +253 -0
- package/mcp/engine.mjs +23 -0
- package/mcp/install.mjs +77 -0
- package/mcp/launch.mjs +72 -0
- package/mcp/sculpt.mjs +5 -0
- package/mcp/servers.mjs +59 -0
- package/package.json +49 -6
- package/patches/three@0.185.1.patch +287 -0
- package/scripts/apply-three-patch.mjs +297 -0
- package/scripts/bundle-engine-mcp.mjs +15 -0
- package/scripts/ensure-mcp.mjs +36 -0
- package/scripts/generate-version.mjs +13 -0
- package/scripts/postinstall.mjs +6 -0
- package/dist/audio-3vkjtiuo.d.ts +0 -35
- package/dist/game-DRPs3M7r.d.ts +0 -269
package/dist/index.d.ts
CHANGED
|
@@ -1,85 +1,1524 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
1
|
+
import * as three from 'three';
|
|
2
|
+
import { AnimationMixer, AnimationClip, Object3D, Camera, Vector3, Group, Matrix4, Mesh, BufferGeometry, Material, InstancedMesh, Box3, Scene, Sprite, DataTexture, CatmullRomCurve3, Texture } from 'three';
|
|
3
|
+
import { a as IGamePluginRuntime, b as IGamePluginHooks } from './game-CYIaKhgl.js';
|
|
4
|
+
export { C as ContextMenuPolicy, c as IAssetLoader, d as IAssetLoaderOptions, e as ICtx, I as IGame, f as IGameObservationContribution, g as IGameObservationSampleRequest, h as IGamePlatformSource, i as IInputAction, j as IInputGamepad, k as IPointerDragHandle, l as IPointerEvent3D, m as IPointerEvents3D, n as IPointerEvents3DOptions, o as IPointerEvents3DPicker, p as IPointerState, q as IRandom, r as IRawInputPointer, s as IRawInputPointerEdge, t as IRawInputState, u as IRaycastOptions, v as IScenePickerOptions, w as IThreeNativeBootSplash, x as IThreeNativeConfig, y as IThreeNativeIconVariants, z as IThreeNativeTexturesConfig, A as ITweenOptions, B as IWarmUpOptions, D as IWarmUpProgress, E as IWarmUpRenderer, F as IWarmUpReport, G as InputBindings, H as InputPlatformSource, P as PointerEvent3DListener, J as PointerEvent3DType, K as PointerEvents3D, S as Scene, L as SceneFrame, M as ScenePicker, N as ScheduleHandle, O as Scheduler, T as ThreeNativeBackgroundMode, Q as ThreeNativeOrientation, R as ThreeNativeUiRenderer, U as createAssetLoader, V as createRandom, W as defineGame, X as warmUpScene } from './game-CYIaKhgl.js';
|
|
5
|
+
export { A as AudioBus, I as IAudioBusOptions, b as IAudioPlayOptions } from './audio-Dp2mXpD3.js';
|
|
6
|
+
import { I as IRendererLike } from './canvas-layer-CtrZHgIh.js';
|
|
7
|
+
export { C as CanvasLayer, F as FRAME_BUDGET_MARKER, a as FRAME_BUDGET_PHASES, b as FRAME_HITCH_MARKER, c as FrameBudget, d as FrameBudgetPhase, e as IFrameBudgetOptions, f as IFrameBudgetSummary, g as IFrameBudgetWindow, h as IFramePhaseSample, i as IRenderChainApplied, j as IRenderChainBudgetWindow, k as IRenderChainDroppedStage, l as IRenderChainOptions, m as IRenderChainRenderer, n as IRenderChainRequest, o as IRenderChainStage, p as IRenderChainStageContext, q as IRenderChainVelocityMeasurement, r as IRenderChainVelocityReport, s as IRenderChainVelocityRequest, t as IRenderChainVelocityResult, u as IVelocityRenderPass, R as RENDER_CHAIN_MARKER, v as RENDER_CHAIN_STAGE_ORDER, w as RENDER_CHAIN_TIERS, x as RenderChain, y as RenderChainSource, z as RenderChainStageName, A as RenderChainTier, B as RenderChainTierRequest, D as RenderChainVelocitySource, V as VELOCITY_OUTPUT_NAME, E as VELOCITY_PREVIOUS_BONE_MATRICES, G as VELOCITY_PREVIOUS_INSTANCE_MATRICES, H as VELOCITY_PREVIOUS_WORLD_MATRIX, J as VelocityTracker, K as ensureVelocityOutput, L as prewarm, M as readRenderChainObservation, N as readRenderChainReport, O as readVelocityPreviousBoneMatrices, P as readVelocityPreviousMatrices, Q as readVelocityPreviousWorldMatrix, S as velocityTexture, T as withVelocityContext } from './canvas-layer-CtrZHgIh.js';
|
|
8
|
+
import * as three_webgpu from 'three/webgpu';
|
|
9
|
+
import { StorageTexture, ComputeNode, UniformNode, Node, TextureNode, NodeMaterial, StorageBufferNode, StructTypeNode, Data3DTexture, SpriteNodeMaterial, StorageTextureNode } from 'three/webgpu';
|
|
10
|
+
import { Node as Node$1 } from 'three/src/nodes/Nodes.js';
|
|
7
11
|
import 'zustand/vanilla';
|
|
8
12
|
|
|
9
|
-
interface
|
|
13
|
+
interface IAnimationPlayerOptions {
|
|
10
14
|
readonly clips: readonly AnimationClip[];
|
|
11
15
|
readonly root: Object3D;
|
|
16
|
+
/**
|
|
17
|
+
* Match a travelling clip's playback rate to the ground the body actually covers.
|
|
18
|
+
*
|
|
19
|
+
* On by default, because a model whose feet do not agree with its motion is the single most
|
|
20
|
+
* common thing wrong with a character in a game built here, and every game solves it the same
|
|
21
|
+
* way. Set `false` to keep the authored rate; the measurement below stays live either way and
|
|
22
|
+
* says that it was overridden. The convention re-times locomotion only: a `"once"` clip — a
|
|
23
|
+
* death, a flinch — always plays at its authored rate.
|
|
24
|
+
*/
|
|
25
|
+
readonly strideSync?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The object whose travel counts as ground covered. Defaults to `root`.
|
|
28
|
+
*
|
|
29
|
+
* Name the body a game moves when the rig is a child of it, which is the usual shape: the clip
|
|
30
|
+
* writes the model's own root track, so measuring the same object the mixer writes would read
|
|
31
|
+
* the clip's motion back as if it were the body's.
|
|
32
|
+
*/
|
|
33
|
+
readonly strideRoot?: Object3D;
|
|
12
34
|
}
|
|
13
|
-
|
|
35
|
+
/**
|
|
36
|
+
* What the feet are doing against what the body is doing.
|
|
37
|
+
*
|
|
38
|
+
* Reported whether or not the convention is applied: turning a convention off must not turn its
|
|
39
|
+
* measurement off, or a game that opted out has no way to know what it cost.
|
|
40
|
+
*/
|
|
41
|
+
interface IStrideReport {
|
|
42
|
+
/** Metres of ground the current clip carries per clip-second, at rate 1. Zero if it travels none. */
|
|
43
|
+
readonly clipGroundSpeed: number;
|
|
44
|
+
/** Metres per second the root has actually covered, smoothed over the last update. */
|
|
45
|
+
readonly groundSpeed: number;
|
|
46
|
+
/** The playback rate those two imply, clamped to `limits`. */
|
|
47
|
+
readonly rate: number;
|
|
48
|
+
/** True when that rate is being applied to the action. */
|
|
49
|
+
readonly synced: boolean;
|
|
50
|
+
/** True when a rate was measured and deliberately not applied. */
|
|
51
|
+
readonly overridden: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface IAnimationPlayOptions {
|
|
14
54
|
readonly fade?: number;
|
|
55
|
+
/**
|
|
56
|
+
* `"loop"` (default) repeats; `"once"` plays through and holds the last frame. A `"once"` clip
|
|
57
|
+
* also keeps its authored rate — stride sync re-times locomotion, not events.
|
|
58
|
+
*/
|
|
59
|
+
readonly mode?: "loop" | "once";
|
|
15
60
|
}
|
|
16
61
|
declare class AnimationPlayer {
|
|
17
62
|
#private;
|
|
18
63
|
readonly mixer: AnimationMixer;
|
|
19
|
-
constructor(options:
|
|
64
|
+
constructor(options: IAnimationPlayerOptions);
|
|
20
65
|
get current(): string | undefined;
|
|
21
66
|
get advancedFrames(): number;
|
|
22
|
-
|
|
67
|
+
/** True when a `"once"` clip has reached its end and is holding. */
|
|
68
|
+
get finished(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* What the feet are doing against what the body is doing, as of the last `update`.
|
|
71
|
+
*
|
|
72
|
+
* Live whether or not the convention is applied. A game that set `strideSync: false` reads
|
|
73
|
+
* `overridden: true` here next to the rate it declined, which is the only way an override can
|
|
74
|
+
* be honest about what it turned off.
|
|
75
|
+
*/
|
|
76
|
+
get stride(): IStrideReport;
|
|
77
|
+
/** The clip behind a name, for a game that wants the action or the raw `AnimationClip`. */
|
|
78
|
+
clip(name: string): AnimationClip;
|
|
79
|
+
play(name: string, options?: IAnimationPlayOptions): void;
|
|
23
80
|
update(dt: number): void;
|
|
24
81
|
stop(): void;
|
|
25
82
|
dispose(): void;
|
|
26
83
|
}
|
|
27
84
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
readonly
|
|
32
|
-
|
|
33
|
-
readonly
|
|
34
|
-
readonly cancelFrame?: (handle: number) => void;
|
|
85
|
+
type BillboardLockAxis = "x" | "y" | "z";
|
|
86
|
+
interface IBillboard3DOptions {
|
|
87
|
+
/** Camera whose view direction or position the object follows. */
|
|
88
|
+
readonly camera: Camera;
|
|
89
|
+
/** Restrict the facing rotation to one world axis, for example `"y"` for a tree. */
|
|
90
|
+
readonly lockAxis?: BillboardLockAxis;
|
|
35
91
|
}
|
|
36
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Orient one game-owned object toward a camera without owning its geometry or surface.
|
|
94
|
+
*
|
|
95
|
+
* The object is updated only when its owner calls {@link update}; there is no scene-wide registry
|
|
96
|
+
* or per-frame traversal. Perspective cameras face from the object's world position toward the
|
|
97
|
+
* camera, while orthographic cameras use their parallel view direction. The final world rotation
|
|
98
|
+
* is converted back into the object's local space, so a rotated parent remains correct.
|
|
99
|
+
*/
|
|
100
|
+
declare class Billboard3D {
|
|
37
101
|
#private;
|
|
38
|
-
readonly
|
|
39
|
-
readonly
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
102
|
+
readonly object: Object3D;
|
|
103
|
+
readonly camera: Camera;
|
|
104
|
+
readonly lockAxis: BillboardLockAxis | undefined;
|
|
105
|
+
constructor(object: Object3D, options: IBillboard3DOptions);
|
|
106
|
+
/** Apply the current camera pose to the object and return this helper for fluent setup. */
|
|
107
|
+
update(camera?: Camera): this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
type CameraShakeCurve = (phase: number) => number;
|
|
111
|
+
interface ICameraShakeOptions {
|
|
112
|
+
/** Position amplitude in world metres, supplied by the game. */
|
|
113
|
+
readonly amplitude: Vector3;
|
|
114
|
+
/** Rotation amplitude in radians around x/y/z, supplied by the game. */
|
|
115
|
+
readonly rotationAmplitude: Vector3;
|
|
116
|
+
/** Curve cycles per second, supplied by the game. */
|
|
117
|
+
readonly frequency: number;
|
|
118
|
+
/** Exponential envelope decay per second, supplied by the game. */
|
|
119
|
+
readonly decay: number;
|
|
120
|
+
/** Game-authored waveform sampled at `elapsed * frequency * 2π`. */
|
|
121
|
+
readonly curve: CameraShakeCurve;
|
|
122
|
+
}
|
|
123
|
+
interface ICameraShakeOffset {
|
|
124
|
+
readonly position: Vector3;
|
|
125
|
+
readonly rotation: Vector3;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Produce a transient camera offset without owning or mutating a camera.
|
|
129
|
+
*
|
|
130
|
+
* The caller supplies the amplitudes, frequency, decay and waveform. {@link update} evaluates the
|
|
131
|
+
* waveform against the caller's fixed-step delta and returns a reusable position/rotation offset;
|
|
132
|
+
* a template can compose it after its own camera rig and damping.
|
|
133
|
+
*/
|
|
134
|
+
declare class CameraShake {
|
|
135
|
+
#private;
|
|
136
|
+
readonly amplitude: Vector3;
|
|
137
|
+
readonly rotationAmplitude: Vector3;
|
|
138
|
+
readonly frequency: number;
|
|
139
|
+
readonly decay: number;
|
|
140
|
+
readonly curve: CameraShakeCurve;
|
|
141
|
+
readonly offset: ICameraShakeOffset;
|
|
142
|
+
constructor(options: ICameraShakeOptions);
|
|
143
|
+
get active(): boolean;
|
|
144
|
+
get elapsed(): number;
|
|
145
|
+
/** Start or restart the authored shake waveform. */
|
|
146
|
+
trigger(): this;
|
|
147
|
+
/** Stop the effect and clear the offset. */
|
|
148
|
+
stop(): this;
|
|
149
|
+
/** Evaluate the current offset and advance by one caller-supplied fixed-step delta. */
|
|
150
|
+
update(dt: number): ICameraShakeOffset;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type AtmosphereRgb = readonly [number, number, number];
|
|
154
|
+
type AtmosphereVector = AtmosphereRgb | Readonly<{
|
|
155
|
+
x: number;
|
|
156
|
+
y: number;
|
|
157
|
+
z: number;
|
|
158
|
+
}> | Vector3;
|
|
159
|
+
/** Physical inputs for the atmosphere model. Coefficients use 1/km and radii use km. */
|
|
160
|
+
interface IAtmosphereParameters {
|
|
161
|
+
readonly rayleigh: AtmosphereVector;
|
|
162
|
+
readonly mie: AtmosphereVector;
|
|
163
|
+
readonly ozone: AtmosphereVector;
|
|
164
|
+
readonly planetRadius: number;
|
|
165
|
+
readonly atmosphereRadius: number;
|
|
166
|
+
}
|
|
167
|
+
interface IResolvedAtmosphereParameters {
|
|
168
|
+
readonly rayleigh: Vector3;
|
|
169
|
+
readonly mie: Vector3;
|
|
170
|
+
readonly ozone: Vector3;
|
|
171
|
+
readonly planetRadius: number;
|
|
172
|
+
readonly atmosphereRadius: number;
|
|
173
|
+
}
|
|
174
|
+
type IAtmosphereParameterPatch = Partial<IAtmosphereParameters>;
|
|
175
|
+
interface ISolarPositionInput {
|
|
176
|
+
readonly date?: Date | string;
|
|
177
|
+
readonly dayOfYear?: number;
|
|
178
|
+
readonly timeOfDay?: number;
|
|
179
|
+
readonly latitude: number;
|
|
180
|
+
readonly longitude: number;
|
|
181
|
+
/** UTC offset in hours. Use zero when `date` is already UTC. */
|
|
182
|
+
readonly utcOffset?: number;
|
|
183
|
+
}
|
|
184
|
+
interface ISolarPosition {
|
|
185
|
+
elevation: number;
|
|
186
|
+
azimuth: number;
|
|
187
|
+
}
|
|
188
|
+
/** Validate and clone game-owned coefficients. There is intentionally no Earth fallback.
|
|
189
|
+
* @situation validate atmosphere coefficients before a game creates its sky
|
|
190
|
+
* @constraint provide all three coefficient vectors and both radii; omitted fields are errors
|
|
191
|
+
* @example const parameters = resolveAtmosphereParameters({ rayleigh, mie, ozone, planetRadius, atmosphereRadius });
|
|
192
|
+
*/
|
|
193
|
+
declare function resolveAtmosphereParameters(options: IAtmosphereParameters): IResolvedAtmosphereParameters;
|
|
194
|
+
/** Apply a partial game-owned atmosphere change while preserving validation.
|
|
195
|
+
* @situation change scattering coefficients and rebake an atmosphere
|
|
196
|
+
* @constraint patches cannot introduce omitted, negative, or non-finite physical values
|
|
197
|
+
* @example atmosphere.setAtmosphere({ rayleigh: [0.008, 0.016, 0.04] });
|
|
198
|
+
*/
|
|
199
|
+
declare function updateAtmosphereParameters(current: IResolvedAtmosphereParameters, patch: IAtmosphereParameterPatch): IResolvedAtmosphereParameters;
|
|
200
|
+
/**
|
|
201
|
+
* Return the direct vertical transmittance of the supplied atmosphere.
|
|
202
|
+
*
|
|
203
|
+
* The coefficient fixture follows the Hillaire/Bruneton Earth model: an exponential Rayleigh
|
|
204
|
+
* column, an exponential aerosol column, and the triangular ozone column. This CPU value is a
|
|
205
|
+
* small validation oracle for the same coefficients that the GPU LUT kernels consume.
|
|
206
|
+
* @situation check a supplied atmosphere's direct vertical transmittance
|
|
207
|
+
* @constraint use the returned value as a validation oracle; the rendered path samples the LUT
|
|
208
|
+
* @example const zenith = zenithTransmittance({ rayleigh, mie, ozone, planetRadius, atmosphereRadius });
|
|
209
|
+
*/
|
|
210
|
+
declare function zenithTransmittance(parameters: IAtmosphereParameters | IResolvedAtmosphereParameters): AtmosphereRgb;
|
|
211
|
+
/** Approximate direct transmittance for a ray leaving the ground in a supplied direction.
|
|
212
|
+
* @situation colour a game-owned sun from atmosphere extinction
|
|
213
|
+
* @constraint pass a non-zero direction; coefficients and radii come from the game
|
|
214
|
+
* @example const transmittance = directionalTransmittance(parameters, sunDirection);
|
|
215
|
+
*/
|
|
216
|
+
declare function directionalTransmittance(parameters: IAtmosphereParameters | IResolvedAtmosphereParameters, direction: Vector3): Vector3;
|
|
217
|
+
/** Calculate solar elevation and azimuth from time, latitude, and longitude.
|
|
218
|
+
* @situation move a sun across a real day at a game's latitude and longitude
|
|
219
|
+
* @constraint dates are interpreted as UTC unless utcOffset is supplied; no fixed sun direction is assumed
|
|
220
|
+
* @constraint pass a mutable { azimuth, elevation } target to reuse the result object in a steady frame loop
|
|
221
|
+
* @example const sun = solarPosition({ date, latitude: 49.28, longitude: -123.12, utcOffset: -8 });
|
|
222
|
+
*/
|
|
223
|
+
declare function solarPosition(input: ISolarPositionInput, target?: ISolarPosition): ISolarPosition;
|
|
224
|
+
declare function solarPosition(date: Date | string, latitude: number, longitude: number, target?: ISolarPosition): ISolarPosition;
|
|
225
|
+
/** Convert solar elevation and azimuth degrees into a normalized Three.js direction.
|
|
226
|
+
* @situation aim a template's sun from solarPosition output
|
|
227
|
+
* @constraint elevation and azimuth must be finite degrees
|
|
228
|
+
* @example const direction = directionFromSolarPosition(sun.elevation, sun.azimuth);
|
|
229
|
+
*/
|
|
230
|
+
declare function directionFromSolarPosition(elevation: number, azimuth: number): Vector3;
|
|
231
|
+
|
|
232
|
+
interface IAtmosphereLutResolution {
|
|
233
|
+
readonly width: number;
|
|
234
|
+
readonly height: number;
|
|
235
|
+
}
|
|
236
|
+
interface IAtmosphereLutResolutions {
|
|
237
|
+
readonly transmittance: IAtmosphereLutResolution;
|
|
238
|
+
readonly multiScattering: IAtmosphereLutResolution;
|
|
239
|
+
readonly skyView: IAtmosphereLutResolution;
|
|
240
|
+
}
|
|
241
|
+
/** The reference dimensions; games may provide smaller dimensions when their startup budget says so. */
|
|
242
|
+
declare const ATMOSPHERE_LUT_RESOLUTIONS: IAtmosphereLutResolutions;
|
|
243
|
+
declare const LUT_RESOLUTIONS: IAtmosphereLutResolutions;
|
|
244
|
+
interface IParameterUniforms {
|
|
245
|
+
readonly atmosphereRadius: UniformNode<"float", number>;
|
|
246
|
+
readonly mie: UniformNode<"vec3", three.Vector3>;
|
|
247
|
+
readonly ozone: UniformNode<"vec3", three.Vector3>;
|
|
248
|
+
readonly planetRadius: UniformNode<"float", number>;
|
|
249
|
+
readonly rayleigh: UniformNode<"vec3", three.Vector3>;
|
|
250
|
+
}
|
|
251
|
+
/** Resolve the three LUT dimensions, allowing a game to trade startup cost for resolution.
|
|
252
|
+
* @situation choose atmosphere LUT dimensions for a measured startup budget
|
|
253
|
+
* @constraint every width and height must be a positive integer; the dimensions are not a named fidelity tier
|
|
254
|
+
* @example const resolutions = resolveAtmosphereLutResolutions({ skyView: { width: 128, height: 72 } });
|
|
255
|
+
*/
|
|
256
|
+
declare function resolveAtmosphereLutResolutions(resolutions: Partial<IAtmosphereLutResolutions> | undefined): IAtmosphereLutResolutions;
|
|
257
|
+
/** Own the transmittance, multi-scattering, and sky-view compute lookup textures.
|
|
258
|
+
* @situation bake the three atmosphere LUTs once before a game shows its world
|
|
259
|
+
* @constraint supply all physical parameters; this class creates no scene appearance
|
|
260
|
+
* @example const luts = new AtmosphereLuts({ rayleigh, mie, ozone, planetRadius, atmosphereRadius });
|
|
261
|
+
*/
|
|
262
|
+
declare class AtmosphereLuts {
|
|
263
|
+
#private;
|
|
264
|
+
readonly resolutions: IAtmosphereLutResolutions;
|
|
265
|
+
readonly transmittance: StorageTexture;
|
|
266
|
+
readonly multiScattering: StorageTexture;
|
|
267
|
+
readonly skyView: StorageTexture;
|
|
268
|
+
readonly warmupNodes: readonly ComputeNode[];
|
|
269
|
+
readonly uniforms: IParameterUniforms;
|
|
270
|
+
constructor(parameters: IAtmosphereParametersLike, resolutions?: Partial<IAtmosphereLutResolutions>);
|
|
271
|
+
get hash(): string;
|
|
272
|
+
update(parameters: IAtmosphereParametersLike): void;
|
|
273
|
+
sampleTransmittance(uv: Node<"vec2">): TextureNode;
|
|
274
|
+
sampleSkyView(uv: Node<"vec2">): TextureNode;
|
|
275
|
+
dispose(): void;
|
|
276
|
+
}
|
|
277
|
+
type IAtmosphereParametersLike = IResolvedAtmosphereParameters | IAtmosphereParameters;
|
|
278
|
+
|
|
279
|
+
/** The structural contract consumed by the compute registry from PRD-242. */
|
|
280
|
+
interface IComputeDriven$1 {
|
|
281
|
+
readonly warmupNodes: readonly unknown[];
|
|
282
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
283
|
+
readonly processCadence?: "fixed" | "render";
|
|
284
|
+
process(renderer: IRendererLike): void;
|
|
285
|
+
detach(): void;
|
|
286
|
+
readonly released: boolean;
|
|
287
|
+
}
|
|
288
|
+
interface IAtmosphereOptions extends IAtmosphereParameters {
|
|
289
|
+
readonly resolutions?: Partial<IAtmosphereLutResolutions>;
|
|
290
|
+
}
|
|
291
|
+
interface IAtmosphereScenePass {
|
|
292
|
+
getTextureNode(name?: string): Node<"vec4">;
|
|
293
|
+
}
|
|
294
|
+
type AtmosphereDirection = Vector3 | readonly [number, number, number] | Node<"vec3">;
|
|
295
|
+
/**
|
|
296
|
+
* Own the compute lifetime and expose only parameter-driven atmosphere nodes.
|
|
297
|
+
*
|
|
298
|
+
* The class deliberately creates no mesh, material, or scene light. A template chooses all of
|
|
299
|
+
* those, and the same object remains useful when a game supplies a completely different look.
|
|
300
|
+
* @situation render a sunrise that changes as time and place change
|
|
301
|
+
* @situation add distance haze from the depth of a scene pass
|
|
302
|
+
* @constraint supply rayleigh, mie, ozone, planetRadius, and atmosphereRadius; there is no Earth fallback
|
|
303
|
+
* @constraint the game creates the sky object, surface, and sun from the returned nodes
|
|
304
|
+
* @example const atmosphere = new Atmosphere({ rayleigh, mie, ozone, planetRadius, atmosphereRadius });
|
|
305
|
+
* ctx.add(atmosphere);
|
|
306
|
+
*/
|
|
307
|
+
declare class Atmosphere extends Group implements IComputeDriven$1 {
|
|
308
|
+
#private;
|
|
309
|
+
readonly luts: AtmosphereLuts;
|
|
310
|
+
constructor(options: IAtmosphereOptions);
|
|
311
|
+
get parameters(): IResolvedAtmosphereParameters;
|
|
312
|
+
get warmupNodes(): readonly ComputeNode[];
|
|
313
|
+
get released(): boolean;
|
|
314
|
+
get hash(): string;
|
|
315
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
316
|
+
process(renderer?: IRendererLike | undefined): void;
|
|
317
|
+
detach(): void;
|
|
318
|
+
setAtmosphere(patch: IAtmosphereParameterPatch): this;
|
|
319
|
+
setCoefficients(patch: IAtmosphereParameterPatch): this;
|
|
320
|
+
setSunDirection(elevation: number, azimuth: number): this;
|
|
321
|
+
setSunDirection(direction: Vector3): this;
|
|
322
|
+
setSunDirection(position: Pick<ISolarPosition, "elevation" | "azimuth">): this;
|
|
323
|
+
getSunDirection(target?: Vector3): Vector3;
|
|
324
|
+
/** Return the game-owned sky radiance lookup as a TSL vec3 or a CPU validation sample. */
|
|
325
|
+
radiance(direction: AtmosphereDirection): Node<"vec3"> | Vector3;
|
|
326
|
+
/** Return direct solar transmittance as a TSL vec3 or a CPU validation sample. */
|
|
327
|
+
sunTransmittance(direction: AtmosphereDirection): Node<"vec3"> | Vector3;
|
|
328
|
+
/**
|
|
329
|
+
* Composite scene colour against game-supplied in-scattered radiance using scene-pass depth.
|
|
330
|
+
*
|
|
331
|
+
* The optional radiance node lets the game apply its own exposure or artistic tint. Leaving it
|
|
332
|
+
* out uses the raw unit-illumination LUT value and does not introduce a framework look.
|
|
333
|
+
*/
|
|
334
|
+
aerialPerspective(scenePass: IAtmosphereScenePass, depth: Node<"float"> | number, inScatteredRadiance?: Node<"vec3"> | Vector3): Node<"vec4">;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Calculate solar elevation and azimuth for one UTC date.
|
|
338
|
+
* @situation calculate a sun direction from a timestamp and a game location
|
|
339
|
+
* @constraint dates are interpreted as UTC; use solarPosition for an explicit local offset
|
|
340
|
+
* @example const sun = solarPositionAt(new Date(), 49.28, -123.12);
|
|
341
|
+
*/
|
|
342
|
+
declare function solarPositionAt(date: Date | string, latitude: number, longitude: number): ISolarPosition;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The lifecycle contract for a game-owned GPU simulation.
|
|
346
|
+
*
|
|
347
|
+
* A compute-driven object owns its kernels, buffers, and appearance. The framework only attaches
|
|
348
|
+
* the active renderer, warms the kernels before the world is shown, dispatches process calls at
|
|
349
|
+
* the object's declared cadence, and releases the object when its scene ends.
|
|
350
|
+
*/
|
|
351
|
+
interface IComputeDriven {
|
|
352
|
+
/** Kernels to compile before the world is shown. Read once, at attach. */
|
|
353
|
+
readonly warmupNodes: readonly unknown[];
|
|
354
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
355
|
+
/**
|
|
356
|
+
* The loop phase that dispatches `process`. Defaults to fixed-step; render cadence preserves the
|
|
357
|
+
* existing behavior of consumers whose simulation is intentionally tied to presentation.
|
|
358
|
+
*/
|
|
359
|
+
readonly processCadence?: "fixed" | "render";
|
|
360
|
+
/** Dispatched once per fixed step, in scene-add order unless render cadence is declared. */
|
|
361
|
+
process(renderer: IRendererLike): void;
|
|
362
|
+
detach(): void;
|
|
363
|
+
readonly released: boolean;
|
|
364
|
+
}
|
|
365
|
+
/** The ordered registry used by the game loop for all compute-driven scene objects. */
|
|
366
|
+
declare class ComputeDrivenRegistry {
|
|
367
|
+
#private;
|
|
368
|
+
get size(): number;
|
|
369
|
+
/** Attach and remember one object. Re-adding the same object is idempotent. */
|
|
370
|
+
add(object: Object3D & IComputeDriven, renderer: IRendererLike): void;
|
|
371
|
+
/** Release one object without disturbing the order of the remaining objects. */
|
|
372
|
+
remove(driven: IComputeDriven): void;
|
|
373
|
+
/** Kernels in the same order as their objects were added to the scene. */
|
|
374
|
+
get warmupNodes(): readonly unknown[];
|
|
375
|
+
/** Dispatch fixed-step objects once; detached scene children are released before dispatch. */
|
|
376
|
+
process(renderer: IRendererLike): void;
|
|
377
|
+
/** Dispatch render-cadence objects once; detached scene children are released before dispatch. */
|
|
378
|
+
processRender(renderer: IRendererLike): void;
|
|
379
|
+
/** Release every registered object, continuing after a failure so no resource is stranded. */
|
|
380
|
+
clear(): void;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
interface IGPUReadbackOptions {
|
|
384
|
+
/** The GPU storage attribute to copy — a TSL storage node's `.value`. */
|
|
385
|
+
readonly attribute: unknown;
|
|
386
|
+
/**
|
|
387
|
+
* Frames between readback requests. `1` asks every frame; larger values throttle.
|
|
388
|
+
*
|
|
389
|
+
* A copy off the GPU costs a queue submission and a mapped buffer, so a game reading a field it
|
|
390
|
+
* only consults for physics asks for it every few frames and pays the staleness instead.
|
|
391
|
+
*/
|
|
392
|
+
readonly everyFrames: number;
|
|
393
|
+
}
|
|
394
|
+
/** A landed copy of GPU memory, with the age of the frame that produced it. */
|
|
395
|
+
interface IGPUReadbackSample {
|
|
396
|
+
readonly data: Float32Array;
|
|
397
|
+
/**
|
|
398
|
+
* Frames between the frame whose GPU state these bytes hold and the frame reading them.
|
|
399
|
+
*
|
|
400
|
+
* This is the number a caller must not be allowed to ignore. A buoyancy solver that treats a
|
|
401
|
+
* 4-frame-old surface as this frame's surface floats a hull through the water it is drawn on,
|
|
402
|
+
* and nothing in the frame says so.
|
|
403
|
+
*/
|
|
404
|
+
readonly staleFrames: number;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* A throttled, fire-and-forget copy of a GPU buffer into CPU memory.
|
|
408
|
+
*
|
|
409
|
+
* Every sample it hands back carries its own age. That is the whole point: the copy is
|
|
410
|
+
* asynchronous, so the bytes are always some frames behind the GPU, and a class that hid that
|
|
411
|
+
* would let a caller mistake stale data for live data with no way to find out.
|
|
412
|
+
*
|
|
413
|
+
* `request()` never awaits and never blocks the frame. One copy is in flight at a time; requests
|
|
414
|
+
* made while one is pending are dropped rather than queued, because a backlog of copies of a field
|
|
415
|
+
* that has already moved on is latency with no information in it.
|
|
416
|
+
*/
|
|
417
|
+
declare class GPUReadback {
|
|
418
|
+
#private;
|
|
419
|
+
readonly everyFrames: number;
|
|
420
|
+
constructor(options: IGPUReadbackOptions);
|
|
421
|
+
get released(): boolean;
|
|
422
|
+
/** True while a copy is in flight. A game that wants to pace its own work can read it. */
|
|
423
|
+
get pending(): boolean;
|
|
424
|
+
/** The newest landed bytes, or `undefined` before the first copy lands. */
|
|
425
|
+
get data(): Float32Array | undefined;
|
|
426
|
+
/**
|
|
427
|
+
* How many frames old the landed bytes are.
|
|
428
|
+
*
|
|
429
|
+
* Before anything has landed this is the number of frames since construction, which grows
|
|
430
|
+
* without bound on purpose: "no data yet" and "data from frame zero" must not read the same.
|
|
431
|
+
*/
|
|
432
|
+
get staleFrames(): number;
|
|
433
|
+
/** The newest bytes with their age attached, or `undefined` before the first copy lands. */
|
|
434
|
+
get sample(): IGPUReadbackSample | undefined;
|
|
435
|
+
/** Requests, landings and failures, for a report that has to say why a sample is old. */
|
|
436
|
+
get stats(): {
|
|
437
|
+
readonly requests: number;
|
|
438
|
+
readonly lands: number;
|
|
439
|
+
readonly failures: number;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Advances the frame clock and starts a copy when the throttle allows one.
|
|
443
|
+
*
|
|
444
|
+
* Safe to call every frame. It returns before the GPU has answered — awaiting it is the one
|
|
445
|
+
* thing that would turn this class into the stall it exists to avoid.
|
|
446
|
+
*/
|
|
447
|
+
request(renderer: IRendererLike): void;
|
|
448
|
+
/** Drops the attribute reference and the landed bytes. Further requests throw. */
|
|
449
|
+
dispose(): void;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
interface IClothTopologyOptions {
|
|
453
|
+
/** Original geometry vertex indices that never move. Duplicate positions pin together. */
|
|
454
|
+
readonly pinned: readonly number[];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** Packed collision input supplied by an optional dependency such as `@threenative/physics`. */
|
|
458
|
+
interface ISoftBodyCollision {
|
|
459
|
+
readonly capacity: number;
|
|
460
|
+
writeBoxes(target: Float32Array, worldToLocal: Matrix4): number;
|
|
461
|
+
}
|
|
462
|
+
interface ISoftBody3DOptions extends IClothTopologyOptions {
|
|
463
|
+
/** Spring acceleration per metre of stretch. Required; the game owns the cloth response. */
|
|
464
|
+
readonly stiffness: number;
|
|
465
|
+
/** Exponential velocity decay per second. Required; zero disables damping. */
|
|
466
|
+
readonly damping: number;
|
|
467
|
+
/** Local-space acceleration in metres per second squared. */
|
|
468
|
+
readonly gravity: readonly [number, number, number];
|
|
469
|
+
/** Local-space wind acceleration in metres per second squared. */
|
|
470
|
+
readonly wind: readonly [number, number, number];
|
|
471
|
+
/** Existing physics bodies translated by the physics package; omitted when cloth has no world collision. */
|
|
472
|
+
readonly collision?: ISoftBodyCollision;
|
|
473
|
+
/** Framework fixed-step duration. Defaults to the engine convention of 1/60 second. */
|
|
474
|
+
readonly timeStep?: number;
|
|
475
|
+
/** Throttled GPU position readback for gameplay/proof; zero disables it. */
|
|
476
|
+
readonly readbackEveryFrames?: number;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Simulate an ordinary game-authored triangle mesh as cloth on the existing fixed-step GPU lane.
|
|
480
|
+
*
|
|
481
|
+
* The mesh supplies every visible choice. This class welds exporter duplicates, owns spring and
|
|
482
|
+
* position buffers, and replaces only the cloned material's position node. It adds no material,
|
|
483
|
+
* colour, texture, wind, stiffness, damping, or pinning default.
|
|
484
|
+
*
|
|
485
|
+
* @situation make a flag, cape, or curtain move as cloth
|
|
486
|
+
* @situation simulate a deforming surface while keeping one edge pinned
|
|
487
|
+
* @situation simulate cloth sails blowing in the wind
|
|
488
|
+
* @situation make cloth sails billow in wind on a ship
|
|
489
|
+
* @constraint the mesh must use one Three.js node material and contain complete triangles
|
|
490
|
+
* @constraint pinned, stiffness, damping, gravity, and wind are required game-owned inputs
|
|
491
|
+
* @constraint Pixel 8 steady upper bound for the shipped 45-vertex pennant with readback every two frames: whole-starter update p95 4.66 ms, render p95 3.56 ms, and GPU timer 0.05 ms across three 300-frame final-rung windows at 552x248 with 4x MSAA; these whole-scene numbers are not isolated solver cost
|
|
492
|
+
* @override timeStep follows the engine 1/60-second convention unless the game overrides it
|
|
493
|
+
* @override readbackEveryFrames enables an explicitly stale CPU position sample; zero disables it
|
|
494
|
+
* @example const flag = new SoftBody3D(flagMesh, { pinned: topEdge, stiffness: 35, damping: 1.8, gravity: [0, -9.81, 0], wind: [1.5, 0, 0.4] });
|
|
495
|
+
*/
|
|
496
|
+
declare class SoftBody3D extends Mesh<BufferGeometry, NodeMaterial> implements IComputeDriven {
|
|
497
|
+
#private;
|
|
498
|
+
readonly processCadence: "fixed";
|
|
499
|
+
readonly warmupNodes: readonly ComputeNode[];
|
|
500
|
+
readonly stiffness: number;
|
|
501
|
+
readonly damping: number;
|
|
502
|
+
readonly timeStep: number;
|
|
503
|
+
readonly gravity: Vector3;
|
|
504
|
+
readonly wind: Vector3;
|
|
505
|
+
readonly uniqueVertexCount: number;
|
|
506
|
+
readonly springCount: number;
|
|
507
|
+
constructor(mesh: Mesh, options: ISoftBody3DOptions);
|
|
508
|
+
get released(): boolean;
|
|
509
|
+
get steps(): number;
|
|
510
|
+
/** Latest asynchronous GPU positions and their age, when readback was requested. */
|
|
511
|
+
get sample(): IGPUReadbackSample | undefined;
|
|
512
|
+
debug(): Record<string, unknown>;
|
|
513
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
514
|
+
process(renderer?: IRendererLike | undefined): void;
|
|
515
|
+
detach(): void;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** Selects the meshes that become part of a GPU trace set. */
|
|
519
|
+
interface IGPUSceneBVHOptions {
|
|
520
|
+
readonly include?: (object: Mesh) => boolean;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* A material range expressed in packed index elements, after the BVH leaf reorder.
|
|
524
|
+
*
|
|
525
|
+
* `start` and `count` address the uploaded index buffer, not the source geometry, so a single
|
|
526
|
+
* source material can appear as more than one range once the SAH sort interleaves its triangles.
|
|
527
|
+
*/
|
|
528
|
+
interface IGPUSceneBVHMaterialGroup {
|
|
529
|
+
readonly count: number;
|
|
530
|
+
readonly materialIndex: number;
|
|
531
|
+
readonly start: number;
|
|
532
|
+
}
|
|
533
|
+
/** The upstream TSL ray query, exposed without renaming or wrapping it. */
|
|
534
|
+
type GPUSceneBVHTraceFunction = (...args: readonly unknown[]) => unknown;
|
|
535
|
+
declare const bvhIntersectFirstHit: GPUSceneBVHTraceFunction;
|
|
536
|
+
declare const rayStruct: StructTypeNode;
|
|
537
|
+
/**
|
|
538
|
+
* Snapshot selected scene meshes into world-space storage buffers for an upstream TSL BVH query.
|
|
539
|
+
*
|
|
540
|
+
* This class owns packing, residency, and release. It deliberately does not own the ray query or
|
|
541
|
+
* a rendered effect: a game imports the exact upstream `bvhIntersectFirstHit` and `rayStruct`
|
|
542
|
+
* exports through the core entry point and uses these four named nodes in its own `src/render/`
|
|
543
|
+
* kernel. The snapshot is static until `rebuild()`.
|
|
544
|
+
*/
|
|
545
|
+
declare class GPUSceneBVH extends Group implements IComputeDriven {
|
|
546
|
+
#private;
|
|
547
|
+
readonly indices: StorageBufferNode<"uvec3">;
|
|
548
|
+
readonly nodes: StorageBufferNode<"struct">;
|
|
549
|
+
readonly normals: StorageBufferNode<"vec3">;
|
|
550
|
+
readonly positions: StorageBufferNode<"vec3">;
|
|
551
|
+
readonly warmupNodes: readonly unknown[];
|
|
552
|
+
constructor(scene: Object3D, options?: IGPUSceneBVHOptions);
|
|
553
|
+
get buildMs(): number;
|
|
554
|
+
get materialGroups(): readonly IGPUSceneBVHMaterialGroup[];
|
|
555
|
+
get objectCount(): number;
|
|
556
|
+
get released(): boolean;
|
|
557
|
+
get triangleCount(): number;
|
|
558
|
+
get vertexCount(): number;
|
|
559
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
560
|
+
process(_renderer: IRendererLike): void;
|
|
561
|
+
/** Repack the selected scene objects and replace the GPU buffers behind the stable node handles. */
|
|
562
|
+
rebuild(): void;
|
|
563
|
+
/** Dispose every storage attribute owned by this snapshot. Safe to call more than once. */
|
|
564
|
+
detach(): void;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** One instance's transform, in the units the geometry was authored in. */
|
|
568
|
+
interface IInstancedPlacement {
|
|
569
|
+
/** World position of the instance's origin, as `[x, y, z]`. */
|
|
570
|
+
readonly position: readonly [number, number, number];
|
|
571
|
+
/** Euler rotation in radians, as `[x, y, z]`. Default none. */
|
|
572
|
+
readonly rotation?: readonly [number, number, number];
|
|
573
|
+
/** Per-axis scale as `[x, y, z]`, or one number for all three. Default 1. */
|
|
574
|
+
readonly scale?: readonly [number, number, number] | number;
|
|
575
|
+
}
|
|
576
|
+
interface IInstancedBatchOptions {
|
|
577
|
+
/** The shape every instance draws, supplied by the game. */
|
|
578
|
+
readonly geometry: BufferGeometry;
|
|
579
|
+
/**
|
|
580
|
+
* The surface every instance draws with, supplied by the game and used by reference — recolour
|
|
581
|
+
* it and the whole batch recolours. Required: collapsing the draws is the engine's job, what
|
|
582
|
+
* the props look like never is.
|
|
583
|
+
*/
|
|
584
|
+
readonly material: Material;
|
|
585
|
+
}
|
|
586
|
+
interface IInstancedBatchBuildOptions {
|
|
587
|
+
/** Passed straight to the built mesh. Default `false`, as in Three.js. */
|
|
588
|
+
readonly castShadow?: boolean;
|
|
589
|
+
/** Name on the built mesh, so a capture or a scene dump can tell one batch from another. */
|
|
590
|
+
readonly name?: string;
|
|
591
|
+
/** Added to this object when the batch builds. Omit to take the mesh and place it yourself. */
|
|
592
|
+
readonly parent?: Object3D;
|
|
593
|
+
/** Passed straight to the built mesh. Default `false`, as in Three.js. */
|
|
594
|
+
readonly receiveShadow?: boolean;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Collapses many copies of one shape into a single draw, without knowing the count up front.
|
|
598
|
+
*
|
|
599
|
+
* `new InstancedMesh(geometry, material, count)` needs `count` before anything has been placed, so
|
|
600
|
+
* a procedural builder either walks its own layout twice, over-allocates and fixes `.count`
|
|
601
|
+
* afterwards, or gathers transforms into an array first. This is that array, with the
|
|
602
|
+
* `Object3D`-scratch-and-`updateMatrix` dance and the post-fill bookkeeping — `instanceMatrix`
|
|
603
|
+
* invalidation and a bounding sphere the culler can use — done once instead of at every site.
|
|
604
|
+
*
|
|
605
|
+
* It decides nothing about how the result looks: the shape, the surface and every transform are
|
|
606
|
+
* the game's, and the built mesh is handed back so the game can keep animating instances by index.
|
|
607
|
+
*/
|
|
608
|
+
declare class InstancedBatch {
|
|
609
|
+
#private;
|
|
610
|
+
readonly geometry: BufferGeometry;
|
|
611
|
+
readonly material: Material;
|
|
612
|
+
constructor(options: IInstancedBatchOptions);
|
|
613
|
+
/** How many instances have been placed so far. */
|
|
614
|
+
get count(): number;
|
|
615
|
+
/** The built mesh, or `undefined` before {@link build} — never a guess. */
|
|
616
|
+
get mesh(): InstancedMesh | undefined;
|
|
617
|
+
/**
|
|
618
|
+
* Records one instance from a matrix the game composed itself, and returns its instance index.
|
|
619
|
+
*
|
|
620
|
+
* The matrix is copied, so the caller may reuse a single scratch `Matrix4` across every call.
|
|
621
|
+
*/
|
|
622
|
+
add(matrix: Matrix4): number;
|
|
623
|
+
/** Records one instance from position, scale and Euler rotation, and returns its instance index. */
|
|
624
|
+
place(placement: IInstancedPlacement): number;
|
|
625
|
+
/**
|
|
626
|
+
* Records one instance stretched between two points, and returns its instance index.
|
|
627
|
+
*
|
|
628
|
+
* Chains, tie rods, railing bars, struts and cables are all "from A to B" rather than "at P with
|
|
629
|
+
* rotation R". Deriving the orientation here is what keeps every caller from hand-computing an
|
|
630
|
+
* Euler angle that goes wrong the moment one endpoint moves.
|
|
631
|
+
*/
|
|
632
|
+
span(from: readonly [number, number, number], to: readonly [number, number, number], radius: number): number;
|
|
633
|
+
/**
|
|
634
|
+
* Turns everything placed so far into one `InstancedMesh`, and returns it.
|
|
635
|
+
*
|
|
636
|
+
* Returns `undefined` when nothing was placed. That is deliberate: `new InstancedMesh(g, m, 0)`
|
|
637
|
+
* satisfies every type check and draws nothing, so a builder whose layout produced no instances
|
|
638
|
+
* would look identical to one that worked. `undefined` puts that case in the caller's types.
|
|
639
|
+
*/
|
|
640
|
+
build(options?: IInstancedBatchBuildOptions): InstancedMesh | undefined;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* A mesh that draws only the clusters this camera can resolve.
|
|
645
|
+
*
|
|
646
|
+
* The asset pipeline bakes a cluster DAG into the `.glb` (`TN_virtual_geometry`); the loader returns
|
|
647
|
+
* one of these when it finds one, and an ordinary `Mesh` when it does not. Nothing about how the
|
|
648
|
+
* mesh looks lives here: `geometry`, `material` and every appearance parameter are the game's, and
|
|
649
|
+
* swapping the surface at any time swaps what draws.
|
|
650
|
+
*
|
|
651
|
+
* The rule, and it asks a cluster nothing about its neighbours: **draw a cluster when its own error
|
|
652
|
+
* projects to fewer screen pixels than the threshold, and its parent group's does not.** Each side
|
|
653
|
+
* is projected through the sphere of the group it belongs to, which is what keeps the cut watertight
|
|
654
|
+
* as the camera moves — a group's sphere encloses every child's, so the parent's projected error can
|
|
655
|
+
* never fall below a child's.
|
|
656
|
+
*/
|
|
657
|
+
/** The baked payload, exactly as `TN_virtual_geometry` stores it. */
|
|
658
|
+
interface IClusterTable {
|
|
659
|
+
/** Per cluster, `[centreX, centreY, centreZ, radius]`. Culling reads it; PRD-283 will. */
|
|
660
|
+
readonly bounds: Float32Array;
|
|
661
|
+
/** Per cluster, `[axisX, axisY, axisZ, cutoff]`. */
|
|
662
|
+
readonly cones: Float32Array;
|
|
663
|
+
/** Per cluster, `[ownError, parentError]`, in the mesh's own units. */
|
|
664
|
+
readonly errors: Float32Array;
|
|
665
|
+
/** Cluster-ordered triangles for every level, indexing the geometry's vertex buffer. */
|
|
666
|
+
readonly indices: Uint32Array;
|
|
667
|
+
/** Per cluster, the sphere `parentError` is projected through. */
|
|
668
|
+
readonly parentSpheres: Float32Array;
|
|
669
|
+
/** Per cluster, `[start, count]` into {@link IClusterTable.indices}. */
|
|
670
|
+
readonly ranges: Uint32Array;
|
|
671
|
+
/** Per cluster, the sphere its own error is projected through. */
|
|
672
|
+
readonly sourceSpheres: Float32Array;
|
|
673
|
+
}
|
|
674
|
+
interface IClusteredMeshOptions {
|
|
675
|
+
/**
|
|
676
|
+
* Screen-space error a cluster may show, in pixels, before its children are drawn instead.
|
|
677
|
+
*
|
|
678
|
+
* One pixel is the honest default: the point of the technique is that what the camera cannot
|
|
679
|
+
* resolve is never submitted. Raising it trades fidelity for triangles, and the number is the
|
|
680
|
+
* game's to choose.
|
|
681
|
+
*/
|
|
682
|
+
readonly errorPixels?: number;
|
|
683
|
+
/**
|
|
684
|
+
* How far the camera must move, in the mesh's own units, before the cut is taken again.
|
|
685
|
+
*
|
|
686
|
+
* Popping is a defect, not a tuning parameter: a camera standing still and breathing must not
|
|
687
|
+
* flip a cluster back and forth. Below this the previous cut is kept, which is always a cut some
|
|
688
|
+
* camera would have chosen and therefore always watertight. Default is a thousandth of the mesh's
|
|
689
|
+
* radius.
|
|
690
|
+
*/
|
|
691
|
+
readonly recutDistance?: number;
|
|
692
|
+
}
|
|
693
|
+
declare class ClusteredMesh extends Mesh {
|
|
694
|
+
#private;
|
|
695
|
+
/** The baked payload. Read-only at run time; the bake is the only thing that writes it. */
|
|
696
|
+
readonly table: IClusterTable;
|
|
697
|
+
/** Screen-space error budget in pixels. Writable — it is the game's call. */
|
|
698
|
+
errorPixels: number;
|
|
699
|
+
/** Camera movement, in the mesh's own units, below which the previous cut is kept. */
|
|
700
|
+
recutDistance: number;
|
|
701
|
+
constructor(geometry: BufferGeometry, surface: Material | Material[], table: IClusterTable, options?: IClusteredMeshOptions);
|
|
702
|
+
/** Clusters in the current cut. */
|
|
703
|
+
get drawnClusters(): number;
|
|
704
|
+
/** Triangles the current cut submits. */
|
|
705
|
+
get drawnTriangles(): number;
|
|
706
|
+
/**
|
|
707
|
+
* Chooses this frame's cut and compacts it into one index range.
|
|
708
|
+
*
|
|
709
|
+
* Called by the game before it renders, not from `onBeforeRender`: an empty cut has to skip the
|
|
710
|
+
* draw rather than submit a zero-count one, and by the time three calls `onBeforeRender` the draw
|
|
711
|
+
* is already on the list. A mesh this leaves invisible is made visible again by the next call,
|
|
712
|
+
* which is why the call belongs in the frame loop rather than in the renderer.
|
|
713
|
+
*
|
|
714
|
+
* @returns triangles the mesh will draw.
|
|
715
|
+
*/
|
|
716
|
+
update(camera: Camera, viewportHeight: number): number;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Takes every clustered mesh and every clustered batch under `root` through this frame's cut.
|
|
720
|
+
*
|
|
721
|
+
* The engine calls this itself, once a frame, before the render — virtual geometry ships on and a
|
|
722
|
+
* game that has to remember to call something has not been given it. A scene holding neither costs
|
|
723
|
+
* one traversal that finds nothing.
|
|
724
|
+
*
|
|
725
|
+
* @returns triangles the clustered meshes and batches will submit.
|
|
726
|
+
*/
|
|
727
|
+
declare function updateClusteredMeshes(root: {
|
|
728
|
+
traverse(callback: (object: object) => void): void;
|
|
729
|
+
}, camera: Camera, viewportHeight: number): number;
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Many copies of one clustered body, each drawn at the detail its own distance earns.
|
|
733
|
+
*
|
|
734
|
+
* `InstancedBatch` collapses repeated props into one draw; this does the same for a body that
|
|
735
|
+
* carries a cluster DAG, and adds the part `InstancedBatch` cannot do — a copy twelve metres away
|
|
736
|
+
* and a copy two hundred metres away do not draw the same triangles.
|
|
737
|
+
*
|
|
738
|
+
* **Why instances are bucketed rather than cut one by one.** One indexed draw has one index range,
|
|
739
|
+
* and multi-draw indirect is not portably available on this stack, so *n* different cuts would mean
|
|
740
|
+
* *n* draws and *n* index buffers — on four hundred boulders that is a gigabyte of index data to
|
|
741
|
+
* save vertex work. Instead the copies are grouped by distance, one cut is taken per occupied
|
|
742
|
+
* group, and each group draws as one instanced draw. Every group's cut is a real cut of the DAG and
|
|
743
|
+
* therefore watertight; the group is cut at the distance of its *nearest* member, so no copy is ever
|
|
744
|
+
* drawn coarser than its own distance allows — only finer, by at most the width of one group.
|
|
745
|
+
*
|
|
746
|
+
* Geometry, surface and every transform are the game's, exactly as with `InstancedBatch`.
|
|
747
|
+
*/
|
|
748
|
+
/** One copy's transform, in the units the geometry was authored in. */
|
|
749
|
+
interface IClusteredPlacement {
|
|
750
|
+
readonly position: readonly [number, number, number];
|
|
751
|
+
readonly rotation?: readonly [number, number, number];
|
|
752
|
+
readonly scale?: readonly [number, number, number] | number;
|
|
753
|
+
}
|
|
754
|
+
interface IClusteredBatchOptions {
|
|
755
|
+
/**
|
|
756
|
+
* How far the camera must move, as a fraction of its distance to the nearest copy, before the
|
|
757
|
+
* cut is taken again. Default 1%.
|
|
758
|
+
*
|
|
759
|
+
* The engine cuts every batch every frame, so this is the difference between a walk that costs a
|
|
760
|
+
* few hundred cluster tests and one that costs half a million. A camera that has not moved
|
|
761
|
+
* meaningfully keeps the previous cut, which is always a cut some camera would have chosen and
|
|
762
|
+
* therefore always watertight.
|
|
763
|
+
*/
|
|
764
|
+
readonly recutFraction?: number;
|
|
765
|
+
/**
|
|
766
|
+
* Ratio between one distance group and the next, above 1.
|
|
767
|
+
*
|
|
768
|
+
* Narrower groups follow each copy's own distance more closely and cost one more draw each.
|
|
769
|
+
* 1.25 is the default: about a dozen groups across a scene that spans a few hundred metres.
|
|
770
|
+
*/
|
|
771
|
+
readonly distanceRatio?: number;
|
|
772
|
+
/** Screen-space error budget in pixels, as {@link ClusteredMesh}. Default 1. */
|
|
773
|
+
readonly errorPixels?: number;
|
|
774
|
+
/** The shape every copy draws, carrying the baked cluster table. */
|
|
775
|
+
readonly geometry: BufferGeometry;
|
|
776
|
+
/** The surface every copy draws with, supplied by the game and used by reference. */
|
|
777
|
+
readonly material: Material;
|
|
778
|
+
/** The bake, exactly as `TN_virtual_geometry` stores it. */
|
|
779
|
+
readonly table: IClusterTable;
|
|
780
|
+
}
|
|
781
|
+
interface IClusteredBatchBuildOptions {
|
|
782
|
+
readonly castShadow?: boolean;
|
|
783
|
+
readonly name?: string;
|
|
784
|
+
readonly parent: Object3D;
|
|
785
|
+
readonly receiveShadow?: boolean;
|
|
786
|
+
}
|
|
787
|
+
declare class ClusteredBatch {
|
|
788
|
+
#private;
|
|
789
|
+
constructor(options: IClusteredBatchOptions);
|
|
790
|
+
/** How many copies are placed. */
|
|
791
|
+
get count(): number;
|
|
792
|
+
/** Draws this batch will submit — one per occupied distance group. */
|
|
793
|
+
get drawCalls(): number;
|
|
794
|
+
/** Triangles the current cut submits, over every copy. */
|
|
795
|
+
get drawnTriangles(): number;
|
|
796
|
+
/** Adds one copy. Returns its index, so the game can move it later. */
|
|
797
|
+
place(placement: IClusteredPlacement): number;
|
|
798
|
+
/** Attaches the batch to the scene. Nothing draws until {@link ClusteredBatch.update} runs. */
|
|
799
|
+
build(options: IClusteredBatchBuildOptions): Object3D;
|
|
800
|
+
/**
|
|
801
|
+
* Chooses this frame's cut for every distance group.
|
|
802
|
+
*
|
|
803
|
+
* @returns triangles the batch will submit.
|
|
804
|
+
*/
|
|
805
|
+
update(camera: Camera, viewportHeight: number): number;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/** The atlas has one copied edge texel on either side of each packed SH sub-volume. */
|
|
809
|
+
declare const ATLAS_PADDING = 1;
|
|
810
|
+
/** The machine-readable marker emitted whenever the probe state changes. */
|
|
811
|
+
declare const PROBE_VOLUME_MARKER = "TN_PROBE_VOLUME";
|
|
812
|
+
type IVector3Like = {
|
|
813
|
+
readonly x: number;
|
|
814
|
+
readonly y: number;
|
|
815
|
+
readonly z: number;
|
|
816
|
+
};
|
|
817
|
+
type IProbePosition = Vector3 | Node<"vec3">;
|
|
818
|
+
/** One RGB L2 spherical-harmonic coefficient, in the upstream probe ordering. */
|
|
819
|
+
interface IProbeVolumeCoefficient {
|
|
820
|
+
readonly r: number;
|
|
821
|
+
readonly g: number;
|
|
822
|
+
readonly b: number;
|
|
823
|
+
}
|
|
824
|
+
/** Probe density in probes per world unit, either isotropic or per-axis. */
|
|
825
|
+
type ProbeVolumeDensity = number | readonly [number, number, number] | IVector3Like;
|
|
826
|
+
interface IProbeVolumeOptions {
|
|
827
|
+
/** World-space bounds; the volume does not move with the object after construction. */
|
|
828
|
+
readonly bounds: Box3 | {
|
|
829
|
+
readonly min: IVector3Like;
|
|
830
|
+
readonly max: IVector3Like;
|
|
831
|
+
};
|
|
832
|
+
/** Probe spacing expressed as probes per world unit. */
|
|
833
|
+
readonly density: ProbeVolumeDensity;
|
|
834
|
+
/** Optional device limit supplied by a host that knows it before construction. */
|
|
835
|
+
readonly maxTextureDimension3D?: number;
|
|
836
|
+
/** Alias for integrations that expose the WebGPU limit under a device-oriented name. */
|
|
837
|
+
readonly deviceTextureLimit?: number;
|
|
838
|
+
/** Maximum wall-clock work per render phase, in milliseconds. */
|
|
839
|
+
readonly bakeBudgetMs?: number;
|
|
840
|
+
/** Additional guard that keeps a clock-less host from processing an unbounded queue. */
|
|
841
|
+
readonly maxWorkItemsPerFrame?: number;
|
|
842
|
+
/** Resolution of each captured cube face. The static-lighting default is intentionally small. */
|
|
843
|
+
readonly cubemapSize?: number;
|
|
844
|
+
readonly near?: number;
|
|
845
|
+
readonly far?: number;
|
|
846
|
+
/** Additional indirect passes after the direct-light pass. */
|
|
847
|
+
readonly bounces?: number;
|
|
848
|
+
/** Injectable clock for deterministic scheduling tests. */
|
|
849
|
+
readonly now?: () => number;
|
|
850
|
+
readonly report?: (line: string) => void;
|
|
851
|
+
}
|
|
852
|
+
interface IProbeVolumeBakeProgress {
|
|
853
|
+
readonly completed: number;
|
|
854
|
+
readonly total: number;
|
|
855
|
+
readonly fraction: number;
|
|
856
|
+
readonly probesCompleted: number;
|
|
857
|
+
readonly probesTotal: number;
|
|
858
|
+
readonly pass: number;
|
|
859
|
+
readonly passes: number;
|
|
860
|
+
}
|
|
861
|
+
interface IProbeVolumeObservation {
|
|
862
|
+
readonly marker: typeof PROBE_VOLUME_MARKER;
|
|
863
|
+
readonly status: "unbaked" | "baking" | "ready";
|
|
864
|
+
readonly stale: boolean;
|
|
865
|
+
readonly unbaked: boolean;
|
|
866
|
+
/** `null` means no completed bake has established an age yet. */
|
|
867
|
+
readonly stalenessFrames: number | null;
|
|
868
|
+
readonly probeCount: number;
|
|
869
|
+
readonly atlasBytes: number;
|
|
870
|
+
readonly atlas: {
|
|
871
|
+
readonly width: number;
|
|
872
|
+
readonly height: number;
|
|
873
|
+
readonly depth: number;
|
|
874
|
+
};
|
|
875
|
+
readonly bakeProgress: IProbeVolumeBakeProgress;
|
|
876
|
+
/** Wall-clock time spent by the most recent incremental render-phase slice. */
|
|
877
|
+
readonly bakeCostMs: number;
|
|
878
|
+
readonly bakeBudgetMs: number;
|
|
879
|
+
/** True while pass zero samples a black texture instead of a previous bake. */
|
|
880
|
+
readonly samplingIsolated: boolean;
|
|
881
|
+
}
|
|
882
|
+
/** Read a complete marker-shaped observation without accepting malformed data. */
|
|
883
|
+
declare function readProbeVolumeObservation(value: unknown): IProbeVolumeObservation | undefined;
|
|
884
|
+
/**
|
|
885
|
+
* A WebGPU irradiance probe volume.
|
|
886
|
+
*
|
|
887
|
+
* The volume owns placement, GPU bake scheduling and a single padded atlas. It owns no light,
|
|
888
|
+
* material or colour: every coefficient comes from the scene rendered by its cube cameras. Add it
|
|
889
|
+
* through `ctx.add()` so `process()` runs in the render phase measured by `FrameBudget`.
|
|
890
|
+
*
|
|
891
|
+
* Bakes are static-lighting-first and explicit. Call `requestBake(scene)` after lights and static
|
|
892
|
+
* geometry are authored; a completed bake is reused until the game requests another one.
|
|
893
|
+
*/
|
|
894
|
+
declare class ProbeVolume extends Object3D implements IComputeDriven {
|
|
895
|
+
#private;
|
|
896
|
+
readonly isProbeVolume = true;
|
|
897
|
+
readonly processCadence: "render";
|
|
898
|
+
readonly warmupNodes: readonly unknown[];
|
|
899
|
+
readonly boundingBox: Box3;
|
|
900
|
+
readonly resolution: Vector3;
|
|
901
|
+
constructor(options: IProbeVolumeOptions);
|
|
902
|
+
get texture(): Data3DTexture;
|
|
903
|
+
get atlasDepth(): number;
|
|
904
|
+
get atlasBytes(): number;
|
|
905
|
+
get probeCount(): number;
|
|
906
|
+
get released(): boolean;
|
|
907
|
+
get atlasData(): Float32Array;
|
|
908
|
+
get observation(): IProbeVolumeObservation;
|
|
909
|
+
/** Attach the active renderer; only WebGPU has the 3D render-target contract this class needs. */
|
|
910
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
911
|
+
/** Start or coalesce an incremental static bake for a scene. */
|
|
912
|
+
requestBake(scene: Scene, options?: {
|
|
913
|
+
readonly bounces?: number;
|
|
914
|
+
}): Promise<void>;
|
|
915
|
+
/** Alias that reads naturally at call sites that want an awaitable bake request. */
|
|
916
|
+
bake(scene: Scene, options?: {
|
|
917
|
+
readonly bounces?: number;
|
|
918
|
+
}): Promise<void>;
|
|
919
|
+
bake(renderer: IRendererLike, scene: Scene, options?: {
|
|
920
|
+
readonly bounces?: number;
|
|
921
|
+
}): Promise<void>;
|
|
922
|
+
/**
|
|
923
|
+
* Return the L2 irradiance node for a world position and world normal.
|
|
924
|
+
*
|
|
925
|
+
* `sample()` with no arguments is the material-friendly form and reads `positionWorld` and
|
|
926
|
+
* `normalWorld`. Passing numeric vectors is reserved for diagnostics and deterministic tests;
|
|
927
|
+
* it evaluates the same SH coefficients held by the atlas packer.
|
|
928
|
+
*/
|
|
929
|
+
sample(): Node<"vec3">;
|
|
930
|
+
sample(position: Vector3, normal: Vector3): Vector3;
|
|
931
|
+
sample(position: IProbePosition, normal?: Vector3 | Node<"vec3">): Vector3 | Node<"vec3">;
|
|
932
|
+
sampleIrradiance(position: Vector3, normal: Vector3): Vector3;
|
|
933
|
+
/** The GPU graph used by `sample`; exposed so generated materials can compose it explicitly. */
|
|
934
|
+
sampleNode(position?: Node<"vec3">, normal?: Node<"vec3">): Node<"vec3">;
|
|
935
|
+
/** Internal data seam used by unit/conformance fixtures to seed a known SH atlas without a GPU. */
|
|
936
|
+
setProbeCoefficients(ix: number, iy: number, iz: number, coefficients: readonly IProbeVolumeCoefficient[]): void;
|
|
937
|
+
/** One bounded render-phase slice. The game loop calls this through IComputeDriven. */
|
|
938
|
+
process(renderer: IRendererLike): void;
|
|
939
|
+
detach(): void;
|
|
940
|
+
get paddedSlices(): number;
|
|
941
|
+
get maximumDimension(): number;
|
|
942
|
+
get totalWork(): number;
|
|
943
|
+
probePosition(ix: number, iy: number, iz: number, target?: Vector3): Vector3;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/** One band of the spectrum, drawn on its own patch. */
|
|
947
|
+
interface ISpectralOceanCascade {
|
|
948
|
+
/** The world-space edge length, in metres, this cascade's grid tiles across. */
|
|
949
|
+
readonly patchSize: number;
|
|
950
|
+
}
|
|
951
|
+
interface ISpectralOceanOptions {
|
|
952
|
+
/** Grid resolution per cascade. A power of two; the transform has no other shape. */
|
|
953
|
+
readonly resolution: number;
|
|
954
|
+
/**
|
|
955
|
+
* The cascades, largest patch first.
|
|
956
|
+
*
|
|
957
|
+
* Each one carries only the wavelengths the next-smaller patch cannot resolve, so the bands do
|
|
958
|
+
* not overlap and a wave is never counted twice. One cascade is a toy: the join between bands is
|
|
959
|
+
* where a spectral ocean visibly fails, so there is nothing to look at until there are two.
|
|
960
|
+
*/
|
|
961
|
+
readonly cascades: readonly ISpectralOceanCascade[];
|
|
962
|
+
readonly windSpeed: number;
|
|
963
|
+
/** Wind heading in radians. */
|
|
964
|
+
readonly windDirection: number;
|
|
965
|
+
readonly gravity: number;
|
|
966
|
+
/** Overall spectrum scale. A wave height decision, so the game owns the number. */
|
|
967
|
+
readonly amplitude: number;
|
|
968
|
+
/**
|
|
969
|
+
* How sharply waves align with the wind, as the exponent on the directional spread.
|
|
970
|
+
*
|
|
971
|
+
* A spectrum-tuning number with no defensible default, so there is none.
|
|
972
|
+
*/
|
|
973
|
+
readonly directionality: number;
|
|
974
|
+
/** Horizontal displacement scale. Zero is a pure heightfield; higher values sharpen crests. */
|
|
975
|
+
readonly choppiness: number;
|
|
976
|
+
/** Waves shorter than this are cut off, in metres. */
|
|
977
|
+
readonly smallWaveCutoff: number;
|
|
978
|
+
readonly seed: number;
|
|
979
|
+
/**
|
|
980
|
+
* Which clock advances the simulation. Defaults to the game's fixed step.
|
|
981
|
+
*
|
|
982
|
+
* Fixed, because this sea is something the game reads: `sampleHeight` reports its age in frames,
|
|
983
|
+
* and a field advanced by the display makes that age mean a different amount of time on every
|
|
984
|
+
* machine. A game whose ocean is only ever looked at can pass `"render"` and pay for exactly the
|
|
985
|
+
* frames it draws.
|
|
986
|
+
*/
|
|
987
|
+
readonly cadence?: "fixed" | "render";
|
|
988
|
+
/**
|
|
989
|
+
* The grid the CPU height query is sampled on, per side. Zero disables the query entirely.
|
|
990
|
+
*
|
|
991
|
+
* This is not the simulation resolution. It is the size of the only thing copied back off the
|
|
992
|
+
* GPU, so it is the whole cost of being able to float something: `readbackResolution` squared
|
|
993
|
+
* floats, every `readbackEveryFrames` frames.
|
|
994
|
+
*/
|
|
995
|
+
readonly readbackResolution: number;
|
|
996
|
+
/** Frames between height copies. Ignored when `readbackResolution` is zero. */
|
|
997
|
+
readonly readbackEveryFrames: number;
|
|
998
|
+
}
|
|
999
|
+
/** A height read from the CPU copy, with the age of the frame that produced it. */
|
|
1000
|
+
interface ISpectralOceanHeight {
|
|
1001
|
+
readonly height: number;
|
|
1002
|
+
/**
|
|
1003
|
+
* Frames between the GPU state this height came from and now.
|
|
1004
|
+
*
|
|
1005
|
+
* A spectral ocean cannot offer an exact CPU height — there is no closed form, only texels the
|
|
1006
|
+
* GPU made — so this number is the contract. A caller that ignores it floats a hull on water
|
|
1007
|
+
* that is not the water being drawn, and nothing in the frame says so.
|
|
1008
|
+
*/
|
|
1009
|
+
readonly staleFrames: number;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* A spectral ocean: cascaded wave spectra, inverse-transformed on the GPU every frame.
|
|
1013
|
+
*
|
|
1014
|
+
* It draws nothing. The game builds its own mesh and its own material and reads
|
|
1015
|
+
* `cascadeDisplacement(index)`; every colour, every foam threshold, every sky this water reflects
|
|
1016
|
+
* is the game's, and none of it can be changed from here.
|
|
1017
|
+
*
|
|
1018
|
+
* What it offers that an analytic wave function cannot is the look. What it cannot offer is an
|
|
1019
|
+
* exact CPU height: there is no closed form, only the texels the GPU produced, so `sampleHeight`
|
|
1020
|
+
* is a throttled copy that is always some frames behind and always says how many. A game that
|
|
1021
|
+
* needs the height to be exact wants an analytic field instead — that is a different contract, and
|
|
1022
|
+
* the reason this class has a different name rather than a flag.
|
|
1023
|
+
*/
|
|
1024
|
+
declare class SpectralOcean extends Object3D implements IComputeDriven {
|
|
1025
|
+
#private;
|
|
1026
|
+
readonly resolution: number;
|
|
1027
|
+
readonly cascades: readonly ISpectralOceanCascade[];
|
|
1028
|
+
readonly processCadence: "fixed" | "render";
|
|
1029
|
+
readonly warmupNodes: readonly ComputeNode[];
|
|
1030
|
+
/** Floats copied off the GPU per readback, so a report can state the cost rather than imply it. */
|
|
1031
|
+
readonly readbackFloats: number;
|
|
1032
|
+
constructor(options: ISpectralOceanOptions);
|
|
1033
|
+
get released(): boolean;
|
|
1034
|
+
/** Simulation steps dispatched. A report that cannot count its own dispatches proves nothing. */
|
|
1035
|
+
get steps(): number;
|
|
1036
|
+
/** How old the CPU height copy is, or `undefined` when the height query is switched off. */
|
|
1037
|
+
get staleFrames(): number | undefined;
|
|
1038
|
+
/** The `(displaceX, height, displaceZ, fold)` buffer the game's material reads. */
|
|
1039
|
+
cascadeDisplacement(index: number): StorageBufferNode<"vec4">;
|
|
1040
|
+
/** The world-space tile size of one cascade, which the game's material needs to place it. */
|
|
1041
|
+
cascadePatchSize(index: number): number;
|
|
1042
|
+
/** Seconds of wave time. The game advances it, so a paused game has a paused sea. */
|
|
1043
|
+
advance(seconds: number): void;
|
|
1044
|
+
/**
|
|
1045
|
+
* The surface height at a world position, and how many frames behind it is.
|
|
1046
|
+
*
|
|
1047
|
+
* `undefined` until the first copy lands, and `undefined` forever when the height query was
|
|
1048
|
+
* switched off — never zero, because a hull floating at zero is indistinguishable from a hull
|
|
1049
|
+
* floating at sea level and that is exactly the mistake this must not allow.
|
|
1050
|
+
*/
|
|
1051
|
+
sampleHeight(x: number, z: number): ISpectralOceanHeight | undefined;
|
|
1052
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
1053
|
+
process(renderer?: IRendererLike | undefined): void;
|
|
1054
|
+
detach(): void;
|
|
47
1055
|
}
|
|
48
1056
|
|
|
49
|
-
interface
|
|
1057
|
+
interface IGPUParticles3DBuffers {
|
|
50
1058
|
readonly positions: StorageBufferNode<"vec3">;
|
|
51
1059
|
readonly velocities: StorageBufferNode<"vec3">;
|
|
52
1060
|
}
|
|
53
|
-
interface
|
|
1061
|
+
interface IGPUParticles3DOptions {
|
|
54
1062
|
readonly amount: number;
|
|
55
1063
|
readonly material: SpriteNodeMaterial;
|
|
56
|
-
readonly start: (buffers:
|
|
57
|
-
readonly process: (buffers:
|
|
1064
|
+
readonly start: (buffers: IGPUParticles3DBuffers) => ComputeNode;
|
|
1065
|
+
readonly process: (buffers: IGPUParticles3DBuffers) => ComputeNode;
|
|
58
1066
|
}
|
|
59
|
-
declare class GPUParticles3D extends Sprite {
|
|
1067
|
+
declare class GPUParticles3D extends Sprite implements IComputeDriven {
|
|
60
1068
|
#private;
|
|
61
1069
|
readonly amount: number;
|
|
62
|
-
readonly buffers:
|
|
1070
|
+
readonly buffers: IGPUParticles3DBuffers;
|
|
1071
|
+
readonly processCadence: "render";
|
|
1072
|
+
readonly warmupNodes: readonly ComputeNode[];
|
|
63
1073
|
emitting: boolean;
|
|
64
|
-
constructor(options:
|
|
1074
|
+
constructor(options: IGPUParticles3DOptions);
|
|
65
1075
|
get released(): boolean;
|
|
66
|
-
attachRenderer(renderer:
|
|
67
|
-
process(renderer?:
|
|
1076
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
1077
|
+
process(renderer?: IRendererLike | undefined): void;
|
|
68
1078
|
restart(): void;
|
|
69
1079
|
detach(): void;
|
|
70
1080
|
}
|
|
71
1081
|
|
|
1082
|
+
interface IFluidFieldVector2 {
|
|
1083
|
+
readonly x: number;
|
|
1084
|
+
readonly y: number;
|
|
1085
|
+
}
|
|
1086
|
+
interface IFluidFieldOptions {
|
|
1087
|
+
readonly resolution: number;
|
|
1088
|
+
readonly viscosity?: number;
|
|
1089
|
+
readonly pressureIterations?: number;
|
|
1090
|
+
readonly maxSplats?: number;
|
|
1091
|
+
readonly timeStep?: number;
|
|
1092
|
+
readonly vorticity?: number;
|
|
1093
|
+
readonly splatRadius?: number;
|
|
1094
|
+
}
|
|
1095
|
+
interface IFluidFieldSampler {
|
|
1096
|
+
sample(uv: Node$1<"vec2">): StorageTextureNode;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Run a deterministic GPU fluid field whose data stays available to the game's render graph.
|
|
1100
|
+
*
|
|
1101
|
+
* The class is deliberately a scene object with the compute-driven lifecycle contract: adding it
|
|
1102
|
+
* to a game scene attaches the renderer, warm-up sees every pass, fixed steps dispatch the passes,
|
|
1103
|
+
* and removing it releases every GPU allocation. `dye` and `velocity` are read-only numeric
|
|
1104
|
+
* samplers; the game decides what those values become when drawn.
|
|
1105
|
+
* @situation simulate smoke, fire, fog, wind, or fluid response on a grid
|
|
1106
|
+
* @situation inject a touch, pointer, or gameplay impulse into a fluid field
|
|
1107
|
+
* @situation sample fluid dye or velocity in a game-owned render node
|
|
1108
|
+
* @constraint add the field through `ctx.add` so renderer attachment, fixed-step dispatch, and release are automatic
|
|
1109
|
+
* @constraint `dye` and `velocity` are numeric samplers; appearance stays in the game's `src/render/` code
|
|
1110
|
+
* @override pressureIterations, viscosity, vorticity, and splatRadius tune the solver without changing its pass order
|
|
1111
|
+
* @example const field = new FluidField2D({ resolution: 256, viscosity: 0, pressureIterations: 20 });
|
|
1112
|
+
* ctx.add(field);
|
|
1113
|
+
* field.splat({ x: 0.5, y: 0.5 }, { x: 0.2, y: 0 }, 1);
|
|
1114
|
+
*/
|
|
1115
|
+
declare class FluidField2D extends Group {
|
|
1116
|
+
#private;
|
|
1117
|
+
readonly resolution: number;
|
|
1118
|
+
readonly viscosity: number;
|
|
1119
|
+
readonly pressureIterations: number;
|
|
1120
|
+
readonly maxSplats: number;
|
|
1121
|
+
readonly timeStep: number;
|
|
1122
|
+
readonly vorticity: number;
|
|
1123
|
+
readonly splatRadius: number;
|
|
1124
|
+
readonly processCadence: "fixed";
|
|
1125
|
+
readonly warmupNodes: readonly ComputeNode[];
|
|
1126
|
+
readonly velocity: IFluidFieldSampler;
|
|
1127
|
+
readonly dye: IFluidFieldSampler;
|
|
1128
|
+
constructor(options: IFluidFieldOptions);
|
|
1129
|
+
get released(): boolean;
|
|
1130
|
+
get queuedSplats(): number;
|
|
1131
|
+
get steps(): number;
|
|
1132
|
+
get splatsApplied(): number;
|
|
1133
|
+
attachRenderer(renderer: IRendererLike): void;
|
|
1134
|
+
splat(uv: IFluidFieldVector2, velocity: IFluidFieldVector2, amount: number): void;
|
|
1135
|
+
process(renderer?: IRendererLike | undefined): void;
|
|
1136
|
+
detach(): void;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
type WaveDirection = readonly [number, number] | {
|
|
1140
|
+
readonly x: number;
|
|
1141
|
+
readonly z?: number;
|
|
1142
|
+
readonly y?: number;
|
|
1143
|
+
};
|
|
1144
|
+
interface IWaveFieldWave {
|
|
1145
|
+
readonly amplitude?: number;
|
|
1146
|
+
readonly direction: WaveDirection;
|
|
1147
|
+
readonly wavelength: number;
|
|
1148
|
+
readonly speed: number;
|
|
1149
|
+
readonly phase?: number;
|
|
1150
|
+
readonly steepness?: number;
|
|
1151
|
+
}
|
|
1152
|
+
interface IWaveFieldDomainWarp {
|
|
1153
|
+
readonly direction?: WaveDirection;
|
|
1154
|
+
readonly waveVector?: WaveDirection;
|
|
1155
|
+
readonly displacement?: WaveDirection;
|
|
1156
|
+
readonly amplitude?: number;
|
|
1157
|
+
readonly wavelength?: number;
|
|
1158
|
+
readonly speed: number;
|
|
1159
|
+
readonly phase?: number;
|
|
1160
|
+
}
|
|
1161
|
+
interface IWaveFieldOptions {
|
|
1162
|
+
readonly waves: readonly IWaveFieldWave[];
|
|
1163
|
+
readonly domainWarp?: readonly IWaveFieldDomainWarp[];
|
|
1164
|
+
}
|
|
1165
|
+
interface IWaveFieldSample {
|
|
1166
|
+
readonly height: number;
|
|
1167
|
+
readonly normal: Vector3;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* An analytic wave field with one packed parameter source for CPU sampling and TSL displacement.
|
|
1171
|
+
* It owns no geometry or appearance; a game chooses how the returned displacement is drawn.
|
|
1172
|
+
*/
|
|
1173
|
+
declare class WaveField {
|
|
1174
|
+
#private;
|
|
1175
|
+
readonly waves: readonly IWaveFieldWave[];
|
|
1176
|
+
readonly domainWarp: readonly IWaveFieldDomainWarp[];
|
|
1177
|
+
readonly parameters: Readonly<Float32Array>;
|
|
1178
|
+
readonly time: three_webgpu.UniformNode<"float", number>;
|
|
1179
|
+
constructor(options: IWaveFieldOptions);
|
|
1180
|
+
/** Update the default graph clock. Explicit sample times remain available for fixed-step code. */
|
|
1181
|
+
setTime(value: number): void;
|
|
1182
|
+
sample(x: number, z: number, time: number): IWaveFieldSample;
|
|
1183
|
+
/** Return a TSL node that displaces local vertices using the same packed values as `sample`. */
|
|
1184
|
+
displacementNode(timeNode?: three_webgpu.UniformNode<"float", number>): three_webgpu.Node<"vec3">;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* A soft round sprite, built as pixel data rather than by painting a canvas.
|
|
1189
|
+
*
|
|
1190
|
+
* Canvas-drawn images sample black under `WebGPURenderer` — a documented trap that cost a shipped
|
|
1191
|
+
* game real debugging time — so sprite images are written straight into pixel data. A radial alpha
|
|
1192
|
+
* falloff is also the whole difference between a puff and a rectangle: a flat quad reads as a grey
|
|
1193
|
+
* box, the same quad with this alpha reads as smoke.
|
|
1194
|
+
*
|
|
1195
|
+
* @param size edge length in pixels
|
|
1196
|
+
* @param hardness 0 fades from the very centre, 1 keeps a solid core out to the rim
|
|
1197
|
+
*/
|
|
1198
|
+
declare function softCircleDataTexture(size?: number, hardness?: number): DataTexture;
|
|
1199
|
+
|
|
1200
|
+
interface ITracerPool3DOptions {
|
|
1201
|
+
/** Slots in the pool. Shots over the count recycle the oldest streak. Default 12. */
|
|
1202
|
+
readonly count?: number;
|
|
1203
|
+
/**
|
|
1204
|
+
* The streak's shape, supplied by the game. The default is a neutral unit-length cylinder
|
|
1205
|
+
* along +Y with its base at the origin — the pool stretches it along y, so any geometry laid
|
|
1206
|
+
* out the same way works. Override it to change the streak's cross-section or silhouette.
|
|
1207
|
+
*/
|
|
1208
|
+
readonly geometry?: BufferGeometry;
|
|
1209
|
+
/**
|
|
1210
|
+
* The streak's surface, supplied by the game and cloned per slot so each can fade
|
|
1211
|
+
* independently. Required: pooling, travel and fading are the engine's; what the streak
|
|
1212
|
+
* looks like never is. Set `opacity` to the peak brightness and pass a transparent,
|
|
1213
|
+
* additive surface for the usual bright-fade look.
|
|
1214
|
+
*/
|
|
1215
|
+
readonly material: Material;
|
|
1216
|
+
/** Longest streak in metres; shorter shots get a shorter streak. Default 3.2. */
|
|
1217
|
+
readonly segmentLength?: number;
|
|
1218
|
+
/** Travel speed in metres per second. Default 360. */
|
|
1219
|
+
readonly speed?: number;
|
|
1220
|
+
/** Seconds a streak lives before fading out fully. Default 0.11. */
|
|
1221
|
+
readonly lifetime?: number;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Per-shot overrides a game passes to {@link TracerPool3D.spawn} — shot-to-shot variation
|
|
1225
|
+
* keeps two rounds from reading as one drawn line. The values are the game's (usually from
|
|
1226
|
+
* its seeded random so replays stay identical); the pool only applies them.
|
|
1227
|
+
*/
|
|
1228
|
+
interface ITracerSpawnOptions {
|
|
1229
|
+
/** Longest streak for this shot, in metres. Defaults to the pool's `segmentLength`. */
|
|
1230
|
+
readonly segmentLength?: number;
|
|
1231
|
+
/** Seconds this streak lives before fading out fully. Defaults to the pool's `lifetime`. */
|
|
1232
|
+
readonly lifetime?: number;
|
|
1233
|
+
/** Multiplier on this streak's cross-section (x/z scale). Default 1. */
|
|
1234
|
+
readonly widthScale?: number;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Pooled travelling bullet streaks for hitscan shots.
|
|
1238
|
+
*
|
|
1239
|
+
* A hitscan round leaves nothing to see, so a shot is only a sound and a number — you cannot tell
|
|
1240
|
+
* where it went or who is firing. The pool draws a short bright segment that travels from the
|
|
1241
|
+
* muzzle toward the point reached and fades out. Segments are stretched cylinders rather than
|
|
1242
|
+
* `Line`s, because line width is not portable across backends and a one-pixel line is invisible at
|
|
1243
|
+
* thirty metres.
|
|
1244
|
+
*
|
|
1245
|
+
* Every member starts visible at zero opacity, so the whole pool doubles as a pipeline prewarm
|
|
1246
|
+
* surface (`prewarm(tracers)`); nothing is created while firing. Call {@link update} once per
|
|
1247
|
+
* frame and {@link dispose} with the owning scene.
|
|
1248
|
+
*/
|
|
1249
|
+
declare class TracerPool3D {
|
|
1250
|
+
#private;
|
|
1251
|
+
constructor(parent: Object3D, options: ITracerPool3DOptions);
|
|
1252
|
+
/**
|
|
1253
|
+
* Stop submitting the slots that are not carrying a shot.
|
|
1254
|
+
*
|
|
1255
|
+
* The pool is resident from construction at zero opacity so its pipeline compiles during
|
|
1256
|
+
* loading; the cost is a draw per dead streak every frame forever, and on a phone the draw call
|
|
1257
|
+
* is the expensive part, not the triangles. Once compiled the pipeline is cached, so `spawn`
|
|
1258
|
+
* re-showing a slot is free. Call this a second or two into the scene, after `prewarm` — not on
|
|
1259
|
+
* the first frame, or the compile this exists to force will not have happened yet.
|
|
1260
|
+
*/
|
|
1261
|
+
settle(): void;
|
|
1262
|
+
/**
|
|
1263
|
+
* Draw one round travelling from `from` along `direction` for `distance` metres.
|
|
1264
|
+
* `options` carries the game's per-shot variation; omit it for the pool defaults.
|
|
1265
|
+
*/
|
|
1266
|
+
spawn(from: Vector3, direction: Vector3, distance: number, options?: ITracerSpawnOptions): void;
|
|
1267
|
+
/** Advance every live streak; call once per frame with the frame's delta seconds. */
|
|
1268
|
+
update(dt: number): void;
|
|
1269
|
+
/** Remove every mesh from the parent and release pooled surfaces. Game-owned geometry survives. */
|
|
1270
|
+
dispose(): void;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
interface IPathFollow3DOptions {
|
|
1274
|
+
readonly loop?: boolean;
|
|
1275
|
+
readonly points: readonly Vector3[];
|
|
1276
|
+
readonly speed?: number;
|
|
1277
|
+
}
|
|
1278
|
+
interface IPathFollow3DSample {
|
|
1279
|
+
point: Vector3;
|
|
1280
|
+
progress: number;
|
|
1281
|
+
tangent: Vector3;
|
|
1282
|
+
}
|
|
1283
|
+
interface IPathFollow3DProjection {
|
|
1284
|
+
distanceFromStart: number;
|
|
1285
|
+
lateralDistance: number;
|
|
1286
|
+
tangent: Vector3;
|
|
1287
|
+
point: Vector3;
|
|
1288
|
+
segment: number;
|
|
1289
|
+
}
|
|
1290
|
+
/** A portable, distance-based follower for an authored Three.js route. */
|
|
1291
|
+
declare class PathFollow3D {
|
|
1292
|
+
#private;
|
|
1293
|
+
readonly curve: CatmullRomCurve3;
|
|
1294
|
+
readonly loop: boolean;
|
|
1295
|
+
readonly totalLength: number;
|
|
1296
|
+
constructor(options: IPathFollow3DOptions);
|
|
1297
|
+
get completed(): boolean;
|
|
1298
|
+
get progress(): number;
|
|
1299
|
+
get speed(): number;
|
|
1300
|
+
set speed(value: number);
|
|
1301
|
+
advance(dt: number, target?: IPathFollow3DSample): IPathFollow3DSample;
|
|
1302
|
+
progressTo(distance: number): this;
|
|
1303
|
+
sample(distance?: number, target?: IPathFollow3DSample): IPathFollow3DSample;
|
|
1304
|
+
pointAt(distance: number, target?: IPathFollow3DSample): IPathFollow3DSample;
|
|
1305
|
+
project(position: Vector3, target?: IPathFollow3DProjection): IPathFollow3DProjection;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
interface IGroundSnapOptions {
|
|
1309
|
+
/** Whether to apply the correction. Measurement and `clearance` continue when this is false. */
|
|
1310
|
+
readonly enabled?: boolean;
|
|
1311
|
+
/** Maximum correction speed in metres per second. Unset follows the authored pose exactly. */
|
|
1312
|
+
readonly maxRate?: number;
|
|
1313
|
+
/** Visual meshes to measure. Defaults to every mesh below `model`. */
|
|
1314
|
+
readonly meshes?: readonly Object3D[];
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Keeps the lowest posed point of a rendered model on a surface.
|
|
1318
|
+
*
|
|
1319
|
+
* This is render grounding, not collider snap-to-ground. It uses a cached skin envelope so a
|
|
1320
|
+
* frame loop never runs the precise per-vertex bounds path. `enabled` is deliberately a range:
|
|
1321
|
+
* turning correction off still leaves `clearance` and `audit()` truthful.
|
|
1322
|
+
*/
|
|
1323
|
+
declare class GroundSnap {
|
|
1324
|
+
readonly model: Object3D;
|
|
1325
|
+
readonly meshes: readonly Object3D[] | undefined;
|
|
1326
|
+
enabled: boolean;
|
|
1327
|
+
maxRate: number | undefined;
|
|
1328
|
+
clearance: number | null;
|
|
1329
|
+
private readonly parentInverse;
|
|
1330
|
+
private readonly parentOrigin;
|
|
1331
|
+
private readonly parentTarget;
|
|
1332
|
+
constructor(model: Object3D, options?: IGroundSnapOptions);
|
|
1333
|
+
/** Move `group` so its lowest posed point meets `surfaceY`, then report the real clearance. */
|
|
1334
|
+
apply(group: Object3D, surfaceY: number, dt: number): void;
|
|
1335
|
+
private applyWorldCorrection;
|
|
1336
|
+
/**
|
|
1337
|
+
* Compare the cheap envelope's lower bound with a precise vertex measurement.
|
|
1338
|
+
*
|
|
1339
|
+
* This is intentionally opt-in: calling it in `apply()` would restore the frame-time defect
|
|
1340
|
+
* this class exists to remove. A negative result means the envelope is below the precise skin.
|
|
1341
|
+
*/
|
|
1342
|
+
audit(): number | null;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
type ThreePoseVector = readonly [number, number, number];
|
|
1346
|
+
type ThreePoseQuaternion = readonly [number, number, number, number];
|
|
1347
|
+
interface IThreePoseBounds {
|
|
1348
|
+
readonly min: ThreePoseVector;
|
|
1349
|
+
readonly max: ThreePoseVector;
|
|
1350
|
+
readonly size: ThreePoseVector;
|
|
1351
|
+
}
|
|
1352
|
+
/** JSON-safe world-space measurements for one Three.js object and its visual bounds. */
|
|
1353
|
+
interface IThreePoseMeasurement {
|
|
1354
|
+
readonly name: string;
|
|
1355
|
+
readonly type: string;
|
|
1356
|
+
readonly position: ThreePoseVector;
|
|
1357
|
+
readonly quaternion: ThreePoseQuaternion;
|
|
1358
|
+
readonly scale: ThreePoseVector;
|
|
1359
|
+
readonly axes: {
|
|
1360
|
+
readonly x: ThreePoseVector;
|
|
1361
|
+
readonly y: ThreePoseVector;
|
|
1362
|
+
readonly z: ThreePoseVector;
|
|
1363
|
+
};
|
|
1364
|
+
readonly bounds: IThreePoseBounds | null;
|
|
1365
|
+
}
|
|
1366
|
+
interface IMeasureThreePoseOptions {
|
|
1367
|
+
/**
|
|
1368
|
+
* Objects whose geometry forms the reported bounds. Defaults to `object`.
|
|
1369
|
+
* This path is precise; it walks every vertex. Do not call it in a frame loop — see
|
|
1370
|
+
* `posedBounds`.
|
|
1371
|
+
*/
|
|
1372
|
+
readonly bounds?: readonly Object3D[] | false;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Measure an Object3D in world space for attachment and animation diagnostics.
|
|
1376
|
+
*
|
|
1377
|
+
* Passing explicit `bounds` lets a probe measure a body without an attached weapon or
|
|
1378
|
+
* invisible hitbox. The result is JSON-safe so it can cross the browser playtest bridge.
|
|
1379
|
+
*/
|
|
1380
|
+
declare function measureThreePose(object: Object3D, options?: IMeasureThreePoseOptions): IThreePoseMeasurement;
|
|
1381
|
+
/**
|
|
1382
|
+
* Cheap world-space bounds for a posed model.
|
|
1383
|
+
*
|
|
1384
|
+
* The first call pays the precise vertex walk to build a conservative skin envelope. Later calls
|
|
1385
|
+
* read one world-matrix translation per contributing bone and allocate nothing. The returned
|
|
1386
|
+
* object is cached for the root and updated in place; copy it if it must outlive the next call.
|
|
1387
|
+
*/
|
|
1388
|
+
declare function posedBounds(root: Object3D, meshes?: readonly Object3D[]): IThreePoseBounds;
|
|
1389
|
+
|
|
1390
|
+
type PlatformRuntime = "web" | "native";
|
|
1391
|
+
type PlatformOS = "android" | "ios" | "linux" | "macos" | "windows" | "unknown";
|
|
1392
|
+
type PlatformFormFactor = "mobile" | "desktop" | "unknown";
|
|
1393
|
+
interface IPlatformInfo {
|
|
1394
|
+
readonly runtime: PlatformRuntime;
|
|
1395
|
+
readonly os: PlatformOS;
|
|
1396
|
+
readonly formFactor: PlatformFormFactor;
|
|
1397
|
+
readonly maxTouchPoints: number;
|
|
1398
|
+
}
|
|
1399
|
+
declare function getPlatform(): Readonly<IPlatformInfo>;
|
|
1400
|
+
declare function isWeb(): boolean;
|
|
1401
|
+
declare function isNative(): boolean;
|
|
1402
|
+
declare function isMobile(): boolean;
|
|
1403
|
+
declare function isTouchscreenAvailable(): boolean;
|
|
1404
|
+
|
|
1405
|
+
type ReplayPointer = readonly [number, number, number, number, number];
|
|
1406
|
+
interface IReplayRecordingSample {
|
|
1407
|
+
readonly keys: readonly string[];
|
|
1408
|
+
readonly pointer?: ReplayPointer;
|
|
1409
|
+
readonly tick: number;
|
|
1410
|
+
}
|
|
1411
|
+
interface IReplayRecording {
|
|
1412
|
+
readonly input: readonly IReplayRecordingSample[];
|
|
1413
|
+
readonly randomState: number;
|
|
1414
|
+
readonly runtime: {
|
|
1415
|
+
agent: string;
|
|
1416
|
+
core: string;
|
|
1417
|
+
portable?: boolean;
|
|
1418
|
+
rapier: string | null;
|
|
1419
|
+
step: number;
|
|
1420
|
+
};
|
|
1421
|
+
readonly seed: number;
|
|
1422
|
+
readonly ticks: number;
|
|
1423
|
+
readonly version: 1;
|
|
1424
|
+
}
|
|
1425
|
+
declare function parseReplayRecording(value: unknown): IReplayRecording;
|
|
1426
|
+
|
|
72
1427
|
type Recording = IReplayRecording;
|
|
1428
|
+
interface IReplayOptions {
|
|
1429
|
+
/** Allow a recording to be replayed by a different host with the same simulation contract. */
|
|
1430
|
+
readonly portable?: boolean;
|
|
1431
|
+
}
|
|
73
1432
|
type ReplayPublic = {
|
|
74
1433
|
readonly recording: Recording | undefined;
|
|
75
1434
|
readonly runId: symbol;
|
|
76
1435
|
};
|
|
77
|
-
declare function replay<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined>():
|
|
78
|
-
declare function createReplayDriver(recording: Recording, target: EventTarget, pointerTarget?: EventTarget): ((runtime:
|
|
79
|
-
prepare: (runtime:
|
|
1436
|
+
declare function replay<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined>(options?: IReplayOptions): IGamePluginHooks<TState, TPhysics> & ReplayPublic;
|
|
1437
|
+
declare function createReplayDriver(recording: Recording, target: EventTarget, pointerTarget?: EventTarget): ((runtime: IGamePluginRuntime) => number) & {
|
|
1438
|
+
prepare: (runtime: IGamePluginRuntime) => void;
|
|
80
1439
|
runId: symbol;
|
|
81
1440
|
};
|
|
82
1441
|
|
|
83
|
-
|
|
1442
|
+
type SpritePlaybackMode = "loop" | "pingPong" | "once";
|
|
1443
|
+
interface ISpriteFrame3D {
|
|
1444
|
+
/** Pixels from the left edge of the atlas. */
|
|
1445
|
+
readonly x: number;
|
|
1446
|
+
/** Pixels from the top edge when `origin` is `"top-left"`. */
|
|
1447
|
+
readonly y: number;
|
|
1448
|
+
readonly width: number;
|
|
1449
|
+
readonly height: number;
|
|
1450
|
+
/** Seconds this frame is held; every frame must provide its own authored timing. */
|
|
1451
|
+
readonly duration: number;
|
|
1452
|
+
}
|
|
1453
|
+
interface ISpriteAnimator3DOptions {
|
|
1454
|
+
/** The game-owned atlas texture. Its filters, wrapping and surface remain untouched. */
|
|
1455
|
+
readonly texture: Texture;
|
|
1456
|
+
/** Pixel-space atlas rectangles with per-frame durations. */
|
|
1457
|
+
readonly frames: readonly ISpriteFrame3D[];
|
|
1458
|
+
readonly mode?: SpritePlaybackMode;
|
|
1459
|
+
/** Atlas coordinate origin; top-left is conventional for exported sprite sheets. */
|
|
1460
|
+
readonly origin?: "top-left" | "bottom-left";
|
|
1461
|
+
/** Start advancing immediately unless the game explicitly opts out. */
|
|
1462
|
+
readonly autoPlay?: boolean;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Advance a game-owned atlas texture on the fixed step supplied by its owner.
|
|
1466
|
+
*
|
|
1467
|
+
* This helper changes only `texture.offset` and `texture.repeat`. The game still chooses the
|
|
1468
|
+
* texture, filters, wrapping, surface, geometry and every frame's duration. Call {@link update}
|
|
1469
|
+
* from the scene's fixed-step update; no wall clock or global animation loop is consulted.
|
|
1470
|
+
*/
|
|
1471
|
+
declare class SpriteAnimator3D {
|
|
1472
|
+
#private;
|
|
1473
|
+
readonly texture: Texture;
|
|
1474
|
+
readonly frames: readonly ISpriteFrame3D[];
|
|
1475
|
+
readonly mode: SpritePlaybackMode;
|
|
1476
|
+
readonly origin: "top-left" | "bottom-left";
|
|
1477
|
+
constructor(options: ISpriteAnimator3DOptions);
|
|
1478
|
+
get frameIndex(): number;
|
|
1479
|
+
get elapsed(): number;
|
|
1480
|
+
get finished(): boolean;
|
|
1481
|
+
get playing(): boolean;
|
|
1482
|
+
/** Advance by one caller-supplied fixed-step delta. */
|
|
1483
|
+
update(dt: number): this;
|
|
1484
|
+
/** Pause fixed-step advancement while leaving the selected frame visible. */
|
|
1485
|
+
pause(): this;
|
|
1486
|
+
/** Resume, restarting a completed one-shot from frame zero. */
|
|
1487
|
+
play(): this;
|
|
1488
|
+
/** Stop and reset to the first authored frame. */
|
|
1489
|
+
stop(): this;
|
|
1490
|
+
/** Select a frame without advancing time. */
|
|
1491
|
+
setFrame(index: number): this;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
type NormaliseAxis = "height" | "longest";
|
|
1495
|
+
interface INormaliseToMetresOptions {
|
|
1496
|
+
readonly metres: number;
|
|
1497
|
+
readonly axis: NormaliseAxis;
|
|
1498
|
+
/** Crown bone to use for a skinned height measurement. Defaults to a named/highest bone. */
|
|
1499
|
+
readonly top?: Object3D | string;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Scale an asset to a real-world size and return the factor applied.
|
|
1503
|
+
*
|
|
1504
|
+
* Height for a skinned asset comes from its crown bone, not its bind-pose Box3. Longest-axis
|
|
1505
|
+
* normalization remains a geometry measurement because it is used for rigid props and weapons.
|
|
1506
|
+
*/
|
|
1507
|
+
declare function normaliseToMetres(object: Object3D, options: INormaliseToMetresOptions): number;
|
|
1508
|
+
|
|
1509
|
+
/** Names of every bone in traversal order. Empty for an unskinned model. */
|
|
1510
|
+
declare function skeletonBones(root: Object3D): readonly string[];
|
|
1511
|
+
/** Parent a child to a named bone while preserving the child's authored world scale. */
|
|
1512
|
+
declare function attachToBone(root: Object3D, boneName: string, child: Object3D): Object3D;
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* The version this library reports.
|
|
1516
|
+
*
|
|
1517
|
+
* It read `0.1.0` while the package published `0.2.0`, and `__tests__/build.spec.ts` asserted the
|
|
1518
|
+
* stale literal, so the test held the bug in place rather than catching it. A literal is
|
|
1519
|
+
* unavoidable here — core is bundled for browsers and cannot read `package.json` at runtime — so
|
|
1520
|
+
* the spec now asserts this equals the manifest instead of asserting a number somebody typed.
|
|
1521
|
+
*/
|
|
1522
|
+
declare const version = "0.3.0";
|
|
84
1523
|
|
|
85
|
-
export {
|
|
1524
|
+
export { ATLAS_PADDING, ATMOSPHERE_LUT_RESOLUTIONS, AnimationPlayer, Atmosphere, type AtmosphereDirection, AtmosphereLuts, type AtmosphereRgb, Billboard3D, type BillboardLockAxis, CameraShake, type CameraShakeCurve, ClusteredBatch, ClusteredMesh, ComputeDrivenRegistry, FluidField2D, GPUParticles3D, GPUReadback, GPUSceneBVH, type GPUSceneBVHTraceFunction, GroundSnap, type IAnimationPlayOptions, type IAnimationPlayerOptions, type IAtmosphereLutResolution, type IAtmosphereLutResolutions, type IAtmosphereOptions, type IAtmosphereParameterPatch, type IAtmosphereParameters, type IAtmosphereScenePass, type IBillboard3DOptions, type ICameraShakeOffset, type ICameraShakeOptions, type IClusterTable, type IClusteredBatchBuildOptions, type IClusteredBatchOptions, type IClusteredMeshOptions, type IClusteredPlacement, type IComputeDriven, type IFluidFieldOptions, type IFluidFieldSampler, type IFluidFieldVector2, type IGPUReadbackOptions, type IGPUReadbackSample, type IGPUSceneBVHMaterialGroup, type IGPUSceneBVHOptions, IGamePluginHooks, IGamePluginRuntime, type IGroundSnapOptions, type IInstancedBatchBuildOptions, type IInstancedBatchOptions, type IInstancedPlacement, type IMeasureThreePoseOptions, 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 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 IWaveFieldDomainWarp, 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, SoftBody3D, SpectralOcean, SpriteAnimator3D, type SpritePlaybackMode, type ThreePoseQuaternion, type ThreePoseVector, TracerPool3D, type WaveDirection, WaveField, attachToBone, bvhIntersectFirstHit, createReplayDriver, directionFromSolarPosition, directionalTransmittance, getPlatform, isMobile, isNative, isTouchscreenAvailable, isWeb, measureThreePose, normaliseToMetres, parseReplayRecording, posedBounds, rayStruct, readProbeVolumeObservation, replay, resolveAtmosphereLutResolutions, resolveAtmosphereParameters, skeletonBones, softCircleDataTexture, solarPosition, solarPositionAt, updateAtmosphereParameters, updateClusteredMeshes, version, zenithTransmittance };
|