@threenative/core 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/capabilities.json +1891 -101
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CqvE429w.d.ts} +52 -3
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-CljaDv4D.d.ts} +503 -11
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1496 -43
- package/dist/index.js +8345 -753
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +96 -11
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-CfsS2hxi.d.ts} +436 -14
- package/dist/ui-layer.d.ts +15 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +900 -14
- package/dist/world.js +10876 -126
- package/gpl/fixtures/make_world_fixture.py +184 -0
- package/gpl/recipes/_common.py +11 -0
- package/gpl/recipes/decimate.py +11 -5
- package/gpl/recipes/export_world.py +682 -0
- package/mcp/blender-server.mjs +55 -1
- package/mcp/engine-server.mjs +84 -22
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2228 -105
- package/scripts/apply-three-patch.mjs +61 -37
package/capabilities.json
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"entries": [
|
|
3
|
+
{
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"constraints": [
|
|
6
|
+
"deterministic by construction: the packing order is derived from the sources, never from directory order, so a rebuild places every source at the same pixel",
|
|
7
|
+
"a source the scene samples outside [0, 1] is excluded and reported, never clamped onto a shared page",
|
|
8
|
+
"page bounds hold regardless of input order; padding keeps a mip tap from reaching the next source"
|
|
9
|
+
],
|
|
10
|
+
"example": "const { pages, transforms, excluded } = packAtlas(sources, { pageSize: 4096, padding: 4 });",
|
|
11
|
+
"importPath": "@threenative/assets",
|
|
12
|
+
"kind": "function",
|
|
13
|
+
"overrides": [],
|
|
14
|
+
"package": "@threenative/assets",
|
|
15
|
+
"signature": "export function atlasManifest(result: IAtlasResult): string { … }",
|
|
16
|
+
"situations": [
|
|
17
|
+
"a merge found nothing to collapse because each imported part owns its own texture",
|
|
18
|
+
"cut the material count of an imported model pack at build time"
|
|
19
|
+
],
|
|
20
|
+
"summary": "Pack texture sources into deterministic atlas pages and answer each source's UV transform, so a build can stop giving every material a private texture.",
|
|
21
|
+
"supersedes": [],
|
|
22
|
+
"symbol": "atlasManifest"
|
|
23
|
+
},
|
|
3
24
|
{
|
|
4
25
|
"aliases": [],
|
|
5
26
|
"constraints": [
|
|
@@ -22,6 +43,27 @@
|
|
|
22
43
|
"supersedes": [],
|
|
23
44
|
"symbol": "audioPass"
|
|
24
45
|
},
|
|
46
|
+
{
|
|
47
|
+
"aliases": [],
|
|
48
|
+
"constraints": [
|
|
49
|
+
"reads geometry and materials only: no GPU, no runtime, no game",
|
|
50
|
+
"a texture whose size the document does not state is reported excluded, never assumed square",
|
|
51
|
+
"a model the reader cannot open is named, never skipped silently"
|
|
52
|
+
],
|
|
53
|
+
"example": "pnpm census:content public/assets",
|
|
54
|
+
"importPath": "@threenative/assets",
|
|
55
|
+
"kind": "function",
|
|
56
|
+
"overrides": [],
|
|
57
|
+
"package": "@threenative/assets",
|
|
58
|
+
"signature": "export function censusDocument( model: string, document: Document, pageSize = 4_096, ): IContentCensus { … }",
|
|
59
|
+
"situations": [
|
|
60
|
+
"find out whether \"fewer objects\" is available in this content before promising it",
|
|
61
|
+
"explain why a per-material merge collapsed nothing"
|
|
62
|
+
],
|
|
63
|
+
"summary": "Census one glTF document, or a directory of them: the merge buckets a scene has now, and the buckets it would have once its atlasable textures shared pages.",
|
|
64
|
+
"supersedes": [],
|
|
65
|
+
"symbol": "censusDocument"
|
|
66
|
+
},
|
|
25
67
|
{
|
|
26
68
|
"aliases": [],
|
|
27
69
|
"constraints": [
|
|
@@ -42,6 +84,26 @@
|
|
|
42
84
|
"supersedes": [],
|
|
43
85
|
"symbol": "compileAssets"
|
|
44
86
|
},
|
|
87
|
+
{
|
|
88
|
+
"aliases": [],
|
|
89
|
+
"constraints": [
|
|
90
|
+
"the signature ignores the material name, which is what made every imported part a singleton, and keeps materials apart on any field it does not understand",
|
|
91
|
+
"the census reads geometry and materials only: no GPU, no runtime, no game"
|
|
92
|
+
],
|
|
93
|
+
"example": "pnpm census:content public/assets",
|
|
94
|
+
"importPath": "@threenative/assets",
|
|
95
|
+
"kind": "function",
|
|
96
|
+
"overrides": [],
|
|
97
|
+
"package": "@threenative/assets",
|
|
98
|
+
"signature": "export function dedupeMaterials(materials: readonly IMaterialState[]): { … }",
|
|
99
|
+
"situations": [
|
|
100
|
+
"decide whether fewer objects is actually available in this content",
|
|
101
|
+
"report why a per-material merge collapsed nothing"
|
|
102
|
+
],
|
|
103
|
+
"summary": "Collapse materials that became identical once their textures shared an atlas page, and count the buckets a merge would find — before and after — so the promise can be checked rather than made.",
|
|
104
|
+
"supersedes": [],
|
|
105
|
+
"symbol": "dedupeMaterials"
|
|
106
|
+
},
|
|
45
107
|
{
|
|
46
108
|
"aliases": [],
|
|
47
109
|
"constraints": ["an empty row list produces no report lines"],
|
|
@@ -148,6 +210,47 @@
|
|
|
148
210
|
"supersedes": [],
|
|
149
211
|
"symbol": "lightmapPass"
|
|
150
212
|
},
|
|
213
|
+
{
|
|
214
|
+
"aliases": [],
|
|
215
|
+
"constraints": [
|
|
216
|
+
"the signature ignores the material name, which is what made every imported part a singleton, and keeps materials apart on any field it does not understand",
|
|
217
|
+
"the census reads geometry and materials only: no GPU, no runtime, no game"
|
|
218
|
+
],
|
|
219
|
+
"example": "pnpm census:content public/assets",
|
|
220
|
+
"importPath": "@threenative/assets",
|
|
221
|
+
"kind": "function",
|
|
222
|
+
"overrides": [],
|
|
223
|
+
"package": "@threenative/assets",
|
|
224
|
+
"signature": "export function materialSignature(material: IMaterialState): string { … }",
|
|
225
|
+
"situations": [
|
|
226
|
+
"decide whether fewer objects is actually available in this content",
|
|
227
|
+
"report why a per-material merge collapsed nothing"
|
|
228
|
+
],
|
|
229
|
+
"summary": "Collapse materials that became identical once their textures shared an atlas page, and count the buckets a merge would find — before and after — so the promise can be checked rather than made.",
|
|
230
|
+
"supersedes": [],
|
|
231
|
+
"symbol": "materialSignature"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"aliases": [],
|
|
235
|
+
"constraints": [
|
|
236
|
+
"reads geometry and materials only: no GPU, no runtime, no game",
|
|
237
|
+
"a texture whose size the document does not state is reported excluded, never assumed square",
|
|
238
|
+
"a model the reader cannot open is named, never skipped silently"
|
|
239
|
+
],
|
|
240
|
+
"example": "pnpm census:content public/assets",
|
|
241
|
+
"importPath": "@threenative/assets",
|
|
242
|
+
"kind": "function",
|
|
243
|
+
"overrides": [],
|
|
244
|
+
"package": "@threenative/assets",
|
|
245
|
+
"signature": "export function materialStateOf(material: Material): IMaterialState { … }",
|
|
246
|
+
"situations": [
|
|
247
|
+
"find out whether \"fewer objects\" is available in this content before promising it",
|
|
248
|
+
"explain why a per-material merge collapsed nothing"
|
|
249
|
+
],
|
|
250
|
+
"summary": "Census one glTF document, or a directory of them: the merge buckets a scene has now, and the buckets it would have once its atlasable textures shared pages.",
|
|
251
|
+
"supersedes": [],
|
|
252
|
+
"symbol": "materialStateOf"
|
|
253
|
+
},
|
|
151
254
|
{
|
|
152
255
|
"aliases": [],
|
|
153
256
|
"constraints": [
|
|
@@ -167,6 +270,27 @@
|
|
|
167
270
|
"supersedes": [],
|
|
168
271
|
"symbol": "modelPass"
|
|
169
272
|
},
|
|
273
|
+
{
|
|
274
|
+
"aliases": [],
|
|
275
|
+
"constraints": [
|
|
276
|
+
"deterministic by construction: the packing order is derived from the sources, never from directory order, so a rebuild places every source at the same pixel",
|
|
277
|
+
"a source the scene samples outside [0, 1] is excluded and reported, never clamped onto a shared page",
|
|
278
|
+
"page bounds hold regardless of input order; padding keeps a mip tap from reaching the next source"
|
|
279
|
+
],
|
|
280
|
+
"example": "const { pages, transforms, excluded } = packAtlas(sources, { pageSize: 4096, padding: 4 });",
|
|
281
|
+
"importPath": "@threenative/assets",
|
|
282
|
+
"kind": "function",
|
|
283
|
+
"overrides": [],
|
|
284
|
+
"package": "@threenative/assets",
|
|
285
|
+
"signature": "export function packAtlas( sources: readonly IAtlasSource[], options: IAtlasOptions = { … }",
|
|
286
|
+
"situations": [
|
|
287
|
+
"a merge found nothing to collapse because each imported part owns its own texture",
|
|
288
|
+
"cut the material count of an imported model pack at build time"
|
|
289
|
+
],
|
|
290
|
+
"summary": "Pack texture sources into deterministic atlas pages and answer each source's UV transform, so a build can stop giving every material a private texture.",
|
|
291
|
+
"supersedes": [],
|
|
292
|
+
"symbol": "packAtlas"
|
|
293
|
+
},
|
|
170
294
|
{
|
|
171
295
|
"aliases": [],
|
|
172
296
|
"constraints": [
|
|
@@ -225,6 +349,48 @@
|
|
|
225
349
|
"supersedes": [],
|
|
226
350
|
"symbol": "resolveBasisTranscoder"
|
|
227
351
|
},
|
|
352
|
+
{
|
|
353
|
+
"aliases": [],
|
|
354
|
+
"constraints": [
|
|
355
|
+
"a surface is tiling when its own UVs leave [0, 1]; glTF's default wrap is REPEAT, so the sampler alone excludes almost everything and is the wrong test",
|
|
356
|
+
"the rewrite is in place, and a buffer that does not hold pairs throws rather than rewriting half a coordinate",
|
|
357
|
+
"`resolveSourceTexel` is the inverse, so a build can round-trip a texel instead of asserting the arithmetic against itself"
|
|
358
|
+
],
|
|
359
|
+
"example": "if (!uvsTile(uv)) rewriteUvs(uv, transforms.get(source)!);",
|
|
360
|
+
"importPath": "@threenative/assets",
|
|
361
|
+
"kind": "function",
|
|
362
|
+
"overrides": [],
|
|
363
|
+
"package": "@threenative/assets",
|
|
364
|
+
"signature": "export function resolveSourceTexel( atlasUv: readonly [number, number], transform: IAtlasTransform, source: { … }",
|
|
365
|
+
"situations": [
|
|
366
|
+
"rewrite a model's texture coordinates after packing its images into an atlas",
|
|
367
|
+
"tell a surface that tiles from one that merely has a repeating sampler"
|
|
368
|
+
],
|
|
369
|
+
"summary": "Move a mesh's UVs onto its atlas page, and decide from the geometry which meshes may not go.",
|
|
370
|
+
"supersedes": [],
|
|
371
|
+
"symbol": "resolveSourceTexel"
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"aliases": [],
|
|
375
|
+
"constraints": [
|
|
376
|
+
"a surface is tiling when its own UVs leave [0, 1]; glTF's default wrap is REPEAT, so the sampler alone excludes almost everything and is the wrong test",
|
|
377
|
+
"the rewrite is in place, and a buffer that does not hold pairs throws rather than rewriting half a coordinate",
|
|
378
|
+
"`resolveSourceTexel` is the inverse, so a build can round-trip a texel instead of asserting the arithmetic against itself"
|
|
379
|
+
],
|
|
380
|
+
"example": "if (!uvsTile(uv)) rewriteUvs(uv, transforms.get(source)!);",
|
|
381
|
+
"importPath": "@threenative/assets",
|
|
382
|
+
"kind": "function",
|
|
383
|
+
"overrides": [],
|
|
384
|
+
"package": "@threenative/assets",
|
|
385
|
+
"signature": "export function rewriteUvs(uv: Float32Array, transform: IAtlasTransform): Float32Array { … }",
|
|
386
|
+
"situations": [
|
|
387
|
+
"rewrite a model's texture coordinates after packing its images into an atlas",
|
|
388
|
+
"tell a surface that tiles from one that merely has a repeating sampler"
|
|
389
|
+
],
|
|
390
|
+
"summary": "Move a mesh's UVs onto its atlas page, and decide from the geometry which meshes may not go.",
|
|
391
|
+
"supersedes": [],
|
|
392
|
+
"symbol": "rewriteUvs"
|
|
393
|
+
},
|
|
228
394
|
{
|
|
229
395
|
"aliases": [],
|
|
230
396
|
"constraints": [
|
|
@@ -265,6 +431,48 @@
|
|
|
265
431
|
"supersedes": [],
|
|
266
432
|
"symbol": "texturePass"
|
|
267
433
|
},
|
|
434
|
+
{
|
|
435
|
+
"aliases": [],
|
|
436
|
+
"constraints": [
|
|
437
|
+
"reads geometry and materials only: no GPU, no runtime, no game",
|
|
438
|
+
"a texture whose size the document does not state is reported excluded, never assumed square",
|
|
439
|
+
"a model the reader cannot open is named, never skipped silently"
|
|
440
|
+
],
|
|
441
|
+
"example": "pnpm census:content public/assets",
|
|
442
|
+
"importPath": "@threenative/assets",
|
|
443
|
+
"kind": "function",
|
|
444
|
+
"overrides": [],
|
|
445
|
+
"package": "@threenative/assets",
|
|
446
|
+
"signature": "export function totalCensus(entries: readonly IContentCensus[]): Omit<IContentCensus, \"model\"> { … }",
|
|
447
|
+
"situations": [
|
|
448
|
+
"find out whether \"fewer objects\" is available in this content before promising it",
|
|
449
|
+
"explain why a per-material merge collapsed nothing"
|
|
450
|
+
],
|
|
451
|
+
"summary": "Census one glTF document, or a directory of them: the merge buckets a scene has now, and the buckets it would have once its atlasable textures shared pages.",
|
|
452
|
+
"supersedes": [],
|
|
453
|
+
"symbol": "totalCensus"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"aliases": [],
|
|
457
|
+
"constraints": [
|
|
458
|
+
"a surface is tiling when its own UVs leave [0, 1]; glTF's default wrap is REPEAT, so the sampler alone excludes almost everything and is the wrong test",
|
|
459
|
+
"the rewrite is in place, and a buffer that does not hold pairs throws rather than rewriting half a coordinate",
|
|
460
|
+
"`resolveSourceTexel` is the inverse, so a build can round-trip a texel instead of asserting the arithmetic against itself"
|
|
461
|
+
],
|
|
462
|
+
"example": "if (!uvsTile(uv)) rewriteUvs(uv, transforms.get(source)!);",
|
|
463
|
+
"importPath": "@threenative/assets",
|
|
464
|
+
"kind": "function",
|
|
465
|
+
"overrides": [],
|
|
466
|
+
"package": "@threenative/assets",
|
|
467
|
+
"signature": "export function uvsTile(uv: ArrayLike<number>): boolean { … }",
|
|
468
|
+
"situations": [
|
|
469
|
+
"rewrite a model's texture coordinates after packing its images into an atlas",
|
|
470
|
+
"tell a surface that tiles from one that merely has a repeating sampler"
|
|
471
|
+
],
|
|
472
|
+
"summary": "Move a mesh's UVs onto its atlas page, and decide from the geometry which meshes may not go.",
|
|
473
|
+
"supersedes": [],
|
|
474
|
+
"symbol": "uvsTile"
|
|
475
|
+
},
|
|
268
476
|
{
|
|
269
477
|
"aliases": [],
|
|
270
478
|
"constraints": [
|
|
@@ -284,6 +492,47 @@
|
|
|
284
492
|
"supersedes": [],
|
|
285
493
|
"symbol": "watchAssets"
|
|
286
494
|
},
|
|
495
|
+
{
|
|
496
|
+
"aliases": [],
|
|
497
|
+
"constraints": [
|
|
498
|
+
"the signature ignores the material name, which is what made every imported part a singleton, and keeps materials apart on any field it does not understand",
|
|
499
|
+
"the census reads geometry and materials only: no GPU, no runtime, no game"
|
|
500
|
+
],
|
|
501
|
+
"example": "pnpm census:content public/assets",
|
|
502
|
+
"importPath": "@threenative/assets",
|
|
503
|
+
"kind": "function",
|
|
504
|
+
"overrides": [],
|
|
505
|
+
"package": "@threenative/assets",
|
|
506
|
+
"signature": "export function withAtlasTextures( materials: readonly IMaterialState[], pageOf: (texture: string) => string | undefined, ): IMaterialState[] { … }",
|
|
507
|
+
"situations": [
|
|
508
|
+
"decide whether fewer objects is actually available in this content",
|
|
509
|
+
"report why a per-material merge collapsed nothing"
|
|
510
|
+
],
|
|
511
|
+
"summary": "Collapse materials that became identical once their textures shared an atlas page, and count the buckets a merge would find — before and after — so the promise can be checked rather than made.",
|
|
512
|
+
"supersedes": [],
|
|
513
|
+
"symbol": "withAtlasTextures"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
"aliases": [],
|
|
517
|
+
"constraints": [
|
|
518
|
+
"a surface is tiling when its own UVs leave [0, 1]; glTF's default wrap is REPEAT, so the sampler alone excludes almost everything and is the wrong test",
|
|
519
|
+
"the rewrite is in place, and a buffer that does not hold pairs throws rather than rewriting half a coordinate",
|
|
520
|
+
"`resolveSourceTexel` is the inverse, so a build can round-trip a texel instead of asserting the arithmetic against itself"
|
|
521
|
+
],
|
|
522
|
+
"example": "if (!uvsTile(uv)) rewriteUvs(uv, transforms.get(source)!);",
|
|
523
|
+
"importPath": "@threenative/assets",
|
|
524
|
+
"kind": "function",
|
|
525
|
+
"overrides": [],
|
|
526
|
+
"package": "@threenative/assets",
|
|
527
|
+
"signature": "export function wrapTiles(wrapS: number | undefined, wrapT: number | undefined): boolean { … }",
|
|
528
|
+
"situations": [
|
|
529
|
+
"rewrite a model's texture coordinates after packing its images into an atlas",
|
|
530
|
+
"tell a surface that tiles from one that merely has a repeating sampler"
|
|
531
|
+
],
|
|
532
|
+
"summary": "Move a mesh's UVs onto its atlas page, and decide from the geometry which meshes may not go.",
|
|
533
|
+
"supersedes": [],
|
|
534
|
+
"symbol": "wrapTiles"
|
|
535
|
+
},
|
|
287
536
|
{
|
|
288
537
|
"aliases": [],
|
|
289
538
|
"constraints": [
|
|
@@ -307,6 +556,49 @@
|
|
|
307
556
|
"supersedes": [],
|
|
308
557
|
"symbol": "addInSlices"
|
|
309
558
|
},
|
|
559
|
+
{
|
|
560
|
+
"aliases": [],
|
|
561
|
+
"constraints": [
|
|
562
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
563
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
564
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
565
|
+
],
|
|
566
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
567
|
+
"importPath": "@threenative/core",
|
|
568
|
+
"kind": "function",
|
|
569
|
+
"overrides": [],
|
|
570
|
+
"package": "@threenative/core",
|
|
571
|
+
"signature": "export function addSpan(id: SpanId, ms: number): void { … }",
|
|
572
|
+
"situations": [
|
|
573
|
+
"find out what inside the render phase is actually costing the frame",
|
|
574
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
575
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
576
|
+
],
|
|
577
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
578
|
+
"supersedes": [],
|
|
579
|
+
"symbol": "addSpan"
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"aliases": [],
|
|
583
|
+
"constraints": [
|
|
584
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
585
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
586
|
+
],
|
|
587
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
588
|
+
"importPath": "@threenative/core",
|
|
589
|
+
"kind": "function",
|
|
590
|
+
"overrides": [],
|
|
591
|
+
"package": "@threenative/core",
|
|
592
|
+
"signature": "export function aerodynamicCoefficients( alpha: number, flaps = 0, gear = 0, brakes = 0, ): { … }",
|
|
593
|
+
"situations": [
|
|
594
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
595
|
+
"launch an aircraft off a moving carrier deck",
|
|
596
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
597
|
+
],
|
|
598
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
599
|
+
"supersedes": [],
|
|
600
|
+
"symbol": "aerodynamicCoefficients"
|
|
601
|
+
},
|
|
310
602
|
{
|
|
311
603
|
"aliases": [],
|
|
312
604
|
"constraints": ["register from a scene context; callbacks are cleared when that scene exits"],
|
|
@@ -324,6 +616,74 @@
|
|
|
324
616
|
"supersedes": [],
|
|
325
617
|
"symbol": "afterPhysics"
|
|
326
618
|
},
|
|
619
|
+
{
|
|
620
|
+
"aliases": [],
|
|
621
|
+
"constraints": [
|
|
622
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
623
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
624
|
+
],
|
|
625
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
626
|
+
"importPath": "@threenative/core",
|
|
627
|
+
"kind": "function",
|
|
628
|
+
"overrides": [],
|
|
629
|
+
"package": "@threenative/core",
|
|
630
|
+
"signature": "export function aircraftMass(state: IFlightState, airframe: IAircraftAirframe): number { … }",
|
|
631
|
+
"situations": [
|
|
632
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
633
|
+
"launch an aircraft off a moving carrier deck",
|
|
634
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
635
|
+
],
|
|
636
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
637
|
+
"supersedes": [],
|
|
638
|
+
"symbol": "aircraftMass"
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
"aliases": [],
|
|
642
|
+
"constraints": [
|
|
643
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
644
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
645
|
+
],
|
|
646
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
647
|
+
"importPath": "@threenative/core",
|
|
648
|
+
"kind": "function",
|
|
649
|
+
"overrides": [],
|
|
650
|
+
"package": "@threenative/core",
|
|
651
|
+
"signature": "export function airDensity(y: number): number { … }",
|
|
652
|
+
"situations": [
|
|
653
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
654
|
+
"launch an aircraft off a moving carrier deck",
|
|
655
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
656
|
+
],
|
|
657
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
658
|
+
"supersedes": [],
|
|
659
|
+
"symbol": "airDensity"
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
"aliases": [],
|
|
663
|
+
"constraints": [
|
|
664
|
+
"the marker is per object and is reported as `exemptMarked` in the projection window",
|
|
665
|
+
"`renderer.minimumProjectedPixels: false` leaves the scene drawn and keeps the measurement on",
|
|
666
|
+
"the marker is per object and survives scene rebuilds only as long as the object does",
|
|
667
|
+
"disabling the gate (`renderer.minimumProjectedPixels: false`) keeps its measurement on"
|
|
668
|
+
],
|
|
669
|
+
"example": "import { alwaysRender } from \"@threenative/core\";\nalwaysRender(ctx.camera.children[0]); // a camera-attached cockpit stays drawn",
|
|
670
|
+
"importPath": "@threenative/core",
|
|
671
|
+
"kind": "function",
|
|
672
|
+
"overrides": [
|
|
673
|
+
"renderer.minimumProjectedPixels sets the projected-pixel threshold, default 0.5"
|
|
674
|
+
],
|
|
675
|
+
"package": "@threenative/core",
|
|
676
|
+
"signature": "export function alwaysRender(object: Object3D, enabled = true): void { … }",
|
|
677
|
+
"situations": [
|
|
678
|
+
"keep a small object drawn when the engine would skip it as too far to resolve",
|
|
679
|
+
"stop my cockpit, marker or player model popping out at distance",
|
|
680
|
+
"a tiny object disappeared at range and I need it always visible",
|
|
681
|
+
"widen or narrow the projected-size cull with a named threshold"
|
|
682
|
+
],
|
|
683
|
+
"summary": "Keep an object drawn even when the render camera cannot resolve it. The engine's projected-size gate is on by default: an object whose world bounding sphere projects to fewer than 0.5 raster pixels in the camera about to render it is not submitted, per camera. Mark the player's own cockpit, a nameplate, a quest marker, or anything a game never wants to pop out of the frame. `alwaysRender(object, false)` removes the marker. Camera-attached objects and shadow casters are already kept, and the number of marked objects is reported beside the cull in the `TN_PROJECTION` window rather than hidden. The threshold itself is `renderer.minimumProjectedPixels` — a larger number cuts more aggressively, `false` leaves every object drawn while still measuring.",
|
|
684
|
+
"supersedes": [],
|
|
685
|
+
"symbol": "alwaysRender"
|
|
686
|
+
},
|
|
327
687
|
{
|
|
328
688
|
"aliases": [],
|
|
329
689
|
"constraints": [
|
|
@@ -409,6 +769,27 @@
|
|
|
409
769
|
"supersedes": [],
|
|
410
770
|
"symbol": "attachToBone"
|
|
411
771
|
},
|
|
772
|
+
{
|
|
773
|
+
"aliases": [],
|
|
774
|
+
"constraints": [
|
|
775
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
776
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
777
|
+
],
|
|
778
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
779
|
+
"importPath": "@threenative/core",
|
|
780
|
+
"kind": "function",
|
|
781
|
+
"overrides": [],
|
|
782
|
+
"package": "@threenative/core",
|
|
783
|
+
"signature": "export function attitudeAxes(state: IFlightState): IFlightAxes { … }",
|
|
784
|
+
"situations": [
|
|
785
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
786
|
+
"launch an aircraft off a moving carrier deck",
|
|
787
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
788
|
+
],
|
|
789
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
790
|
+
"supersedes": [],
|
|
791
|
+
"symbol": "attitudeAxes"
|
|
792
|
+
},
|
|
412
793
|
{
|
|
413
794
|
"aliases": [],
|
|
414
795
|
"constraints": [
|
|
@@ -431,6 +812,44 @@
|
|
|
431
812
|
"supersedes": ["new Audio("],
|
|
432
813
|
"symbol": "AudioBus"
|
|
433
814
|
},
|
|
815
|
+
{
|
|
816
|
+
"aliases": [],
|
|
817
|
+
"constraints": [],
|
|
818
|
+
"example": "const geometry = baseGeometryOf(mesh);",
|
|
819
|
+
"importPath": "@threenative/core",
|
|
820
|
+
"kind": "function",
|
|
821
|
+
"overrides": [],
|
|
822
|
+
"package": "@threenative/core",
|
|
823
|
+
"signature": "export function baseGeometryOf(mesh: Mesh): BufferGeometry { … }",
|
|
824
|
+
"situations": [
|
|
825
|
+
"collide or ray-test the authored geometry of a mesh whose render detail changes with distance"
|
|
826
|
+
],
|
|
827
|
+
"summary": "The full-detail geometry of a mesh the loader gave an automatic LOD chain. Selection swaps `mesh.geometry`, so a ray test or a collision body built from the current geometry would change with the camera. Framework picking and gameplay collide against this instead: the authored LOD0, which never changes as the camera moves.",
|
|
828
|
+
"supersedes": [],
|
|
829
|
+
"symbol": "baseGeometryOf"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
"aliases": [],
|
|
833
|
+
"constraints": [
|
|
834
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
835
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
836
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
837
|
+
],
|
|
838
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
839
|
+
"importPath": "@threenative/core",
|
|
840
|
+
"kind": "function",
|
|
841
|
+
"overrides": [],
|
|
842
|
+
"package": "@threenative/core",
|
|
843
|
+
"signature": "export function beginSpan(id: SpanId): void { … }",
|
|
844
|
+
"situations": [
|
|
845
|
+
"find out what inside the render phase is actually costing the frame",
|
|
846
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
847
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
848
|
+
],
|
|
849
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
850
|
+
"supersedes": [],
|
|
851
|
+
"symbol": "beginSpan"
|
|
852
|
+
},
|
|
434
853
|
{
|
|
435
854
|
"aliases": [],
|
|
436
855
|
"constraints": [
|
|
@@ -511,6 +930,26 @@
|
|
|
511
930
|
"supersedes": [],
|
|
512
931
|
"symbol": "boneLengths"
|
|
513
932
|
},
|
|
933
|
+
{
|
|
934
|
+
"aliases": [],
|
|
935
|
+
"constraints": [
|
|
936
|
+
"call rebuild() after a scene transform or geometry change; the snapshot is static by default",
|
|
937
|
+
"rebuild() is an explicit CPU SAH build proportional to selected triangles; process() is a no-op, and the game pays upstream traversal per shader ray"
|
|
938
|
+
],
|
|
939
|
+
"example": "const bvh = ctx.add(new GPUSceneBVH(ctx.scene, { include: (object) => object.userData.traceable === true }));",
|
|
940
|
+
"importPath": "@threenative/core",
|
|
941
|
+
"kind": "function",
|
|
942
|
+
"overrides": [],
|
|
943
|
+
"package": "@threenative/core",
|
|
944
|
+
"signature": "bvhIntersectFirstHit = upstream.bvhIntersectFirstHit",
|
|
945
|
+
"situations": [
|
|
946
|
+
"trace thousands of scene rays inside a TSL kernel",
|
|
947
|
+
"build a contact-occlusion or visibility query over loaded meshes"
|
|
948
|
+
],
|
|
949
|
+
"summary": "Pack a selected static scene into TSL storage nodes for an upstream BVH ray query.",
|
|
950
|
+
"supersedes": [],
|
|
951
|
+
"symbol": "bvhIntersectFirstHit"
|
|
952
|
+
},
|
|
514
953
|
{
|
|
515
954
|
"aliases": [],
|
|
516
955
|
"constraints": [
|
|
@@ -544,9 +983,29 @@
|
|
|
544
983
|
"place a HUD layer above the Three.js scene",
|
|
545
984
|
"attach a canvas layer to a camera"
|
|
546
985
|
],
|
|
547
|
-
"summary": "Manage a camera or screen-facing canvas layer.",
|
|
986
|
+
"summary": "Manage a camera or screen-facing canvas layer.",
|
|
987
|
+
"supersedes": [],
|
|
988
|
+
"symbol": "CanvasLayer"
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
"aliases": [],
|
|
992
|
+
"constraints": [
|
|
993
|
+
"the browser grants capture only from a user gesture and a refusal is reported, never swallowed",
|
|
994
|
+
"a relative binding requests capture on canvas click unless `captureOnClick: false` opts out"
|
|
995
|
+
],
|
|
996
|
+
"example": "canvas.addEventListener(\"click\", () => captureMouse(canvas));",
|
|
997
|
+
"importPath": "@threenative/core",
|
|
998
|
+
"kind": "function",
|
|
999
|
+
"overrides": [],
|
|
1000
|
+
"package": "@threenative/core",
|
|
1001
|
+
"signature": "export function captureMouse(target: EventTarget): Promise<void> | undefined { … }",
|
|
1002
|
+
"situations": [
|
|
1003
|
+
"lock the mouse pointer so first-person mouse look keeps the cursor out of the way",
|
|
1004
|
+
"stop the cursor leaving the window in the middle of a turn"
|
|
1005
|
+
],
|
|
1006
|
+
"summary": "Lock the pointer to the game's surface: the capture every first-person mouse look needs. A browser grants capture only from a user gesture, so call it from a click or a pointerdown handler. A relative binding such as `look: { pointerRelative: true }` already requests capture on the first canvas click; this is the same request for a game that starts capture from another named gesture, and `ctx.input.captureMouse()` is the map's own way to ask.",
|
|
548
1007
|
"supersedes": [],
|
|
549
|
-
"symbol": "
|
|
1008
|
+
"symbol": "captureMouse"
|
|
550
1009
|
},
|
|
551
1010
|
{
|
|
552
1011
|
"aliases": [],
|
|
@@ -676,6 +1135,27 @@
|
|
|
676
1135
|
"supersedes": [],
|
|
677
1136
|
"symbol": "ComputeDrivenRegistry"
|
|
678
1137
|
},
|
|
1138
|
+
{
|
|
1139
|
+
"aliases": [],
|
|
1140
|
+
"constraints": [
|
|
1141
|
+
"counts command-encoder and queue methods only; `mapAsync` and the presentation path are named, not folded in",
|
|
1142
|
+
"`gpuBytes` is `queue.writeBuffer` exactly, so it reconciles against a driver; texture uploads are not included",
|
|
1143
|
+
"`jsAllocBytes` needs `performance.memory` and stays absent where the platform lacks it"
|
|
1144
|
+
],
|
|
1145
|
+
"example": "const counters = FrameCounters.install(counterDeviceOf(renderer.raw));",
|
|
1146
|
+
"importPath": "@threenative/core",
|
|
1147
|
+
"kind": "function",
|
|
1148
|
+
"overrides": [],
|
|
1149
|
+
"package": "@threenative/core",
|
|
1150
|
+
"signature": "export function counterDeviceOf(raw: unknown): unknown { … }",
|
|
1151
|
+
"situations": [
|
|
1152
|
+
"decide whether a CPU-bound frame is paying for the V8-to-host boundary",
|
|
1153
|
+
"measure how many bytes a frame writes into GPU buffers, and how many commands it issues"
|
|
1154
|
+
],
|
|
1155
|
+
"summary": "Count the frame's host-boundary crossings and the bytes it writes into GPU buffers, off unless `TN_FRAME_SPANS` asks for them. On the frame budget's own window as `counters`, so a crossing count and a millisecond split describe the same frames.",
|
|
1156
|
+
"supersedes": [],
|
|
1157
|
+
"symbol": "counterDeviceOf"
|
|
1158
|
+
},
|
|
679
1159
|
{
|
|
680
1160
|
"aliases": ["different props in each area", "first playable screen external assets"],
|
|
681
1161
|
"constraints": [
|
|
@@ -724,6 +1204,7 @@
|
|
|
724
1204
|
"package": "@threenative/core",
|
|
725
1205
|
"signature": "export function createRandom(seed?: number): IRandom { … }",
|
|
726
1206
|
"situations": [
|
|
1207
|
+
"get a seeded deterministic random number generator — the same mulberry32 a game would hand-roll",
|
|
727
1208
|
"seed enemy patrol choices",
|
|
728
1209
|
"reproduce the same procedural level in a playtest"
|
|
729
1210
|
],
|
|
@@ -750,6 +1231,47 @@
|
|
|
750
1231
|
"supersedes": [],
|
|
751
1232
|
"symbol": "createReplayDriver"
|
|
752
1233
|
},
|
|
1234
|
+
{
|
|
1235
|
+
"aliases": [],
|
|
1236
|
+
"constraints": [
|
|
1237
|
+
"every value is required; there is no default sun, sky, haze or exposure",
|
|
1238
|
+
"`skySize` must keep the sky box's corners inside the camera's far plane",
|
|
1239
|
+
"shadowExtents follow `VirtualShadowNode`: half-widths, finest first, strictly increasing"
|
|
1240
|
+
],
|
|
1241
|
+
"example": "const daylight = new Daylight({ follow: ctx.camera, sunDirection, sunColor, sunIntensity: 4, shadowExtents: [24, 96, 320], sky: { turbidity: 3, rayleigh: 1.4, mieCoefficient: 0.004, mieDirectionalG: 0.8 }, fill: { sky, ground, intensity: 1.1 }, haze: { color: horizon, density: 0.0011 }, exposure: 2 ** -0.6, skySize: 1600 });\nctx.add(daylight);",
|
|
1242
|
+
"importPath": "@threenative/core",
|
|
1243
|
+
"kind": "class",
|
|
1244
|
+
"overrides": [
|
|
1245
|
+
"sky uniforms stay live on `daylight.sky`; the light and fill are `daylight.sun` and `daylight.fill`"
|
|
1246
|
+
],
|
|
1247
|
+
"package": "@threenative/core",
|
|
1248
|
+
"signature": "export class Daylight extends Group implements IComputeDriven { … }",
|
|
1249
|
+
"situations": [
|
|
1250
|
+
"daytime sky, sun and shadows for a large outdoor map",
|
|
1251
|
+
"distant terrain should fade into the sky instead of a coloured wall",
|
|
1252
|
+
"match a Blender look-dev scene's sun, sky and exposure in the game"
|
|
1253
|
+
],
|
|
1254
|
+
"summary": "An outdoor daylight rig: physical sky, one sun with open-world shadows that follow the eye, hemisphere fill, sky-coloured haze and the AgX tone curve. Every value is the game's.",
|
|
1255
|
+
"supersedes": [],
|
|
1256
|
+
"symbol": "Daylight"
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
"aliases": [],
|
|
1260
|
+
"constraints": [
|
|
1261
|
+
"a name in camelCase becomes UPPER_SNAKE: `debugFlag(\"freeCam\")` reads `?freeCam` or `TN_DEBUG_FREE_CAM`",
|
|
1262
|
+
"`0` and `false` are off, so a saved URL cannot turn a switch back on"
|
|
1263
|
+
],
|
|
1264
|
+
"example": "import { debugFlag } from \"@threenative/core\";\nif (debugFlag(\"freeCam\")) camera.flyMode = true;",
|
|
1265
|
+
"importPath": "@threenative/core",
|
|
1266
|
+
"kind": "function",
|
|
1267
|
+
"overrides": [],
|
|
1268
|
+
"package": "@threenative/core",
|
|
1269
|
+
"signature": "export function debugFlag(name: string): boolean { … }",
|
|
1270
|
+
"situations": ["read a debug toggle from the URL or an environment variable"],
|
|
1271
|
+
"summary": "Read a debug switch from the URL, or from `TN_DEBUG_*` in the environment on a native launch.",
|
|
1272
|
+
"supersedes": [],
|
|
1273
|
+
"symbol": "debugFlag"
|
|
1274
|
+
},
|
|
753
1275
|
{
|
|
754
1276
|
"aliases": [
|
|
755
1277
|
"firing line nearest target crosshair",
|
|
@@ -778,6 +1300,48 @@
|
|
|
778
1300
|
"supersedes": [],
|
|
779
1301
|
"symbol": "defineGame"
|
|
780
1302
|
},
|
|
1303
|
+
{
|
|
1304
|
+
"aliases": [],
|
|
1305
|
+
"constraints": [
|
|
1306
|
+
"the rule is derived from the frame's own numbers; the display period comes from the host's presentation cap or the game's declared target, never a constant",
|
|
1307
|
+
"no verdict without a measured GPU reading and a pass census — an absent measurement is not evidence",
|
|
1308
|
+
"the record carries objects considered, draws per pass, triangles per draw and shadow-exempt casters, so the reader is not dependent on the ranking"
|
|
1309
|
+
],
|
|
1310
|
+
"example": "defineGame({ display: { maxFps: 60 }, scenes: { Play } });",
|
|
1311
|
+
"importPath": "@threenative/core",
|
|
1312
|
+
"kind": "function",
|
|
1313
|
+
"overrides": [],
|
|
1314
|
+
"package": "@threenative/core",
|
|
1315
|
+
"signature": "export function describeSceneShape( window: IFrameBudgetWindow, cull: IRenderCameraCullReport | undefined, ): ISceneShape | undefined { … }",
|
|
1316
|
+
"situations": [
|
|
1317
|
+
"find out whether a slow frame is the scene's shape or the device",
|
|
1318
|
+
"tell an authoring agent what to reduce before it promises a merge"
|
|
1319
|
+
],
|
|
1320
|
+
"summary": "Warn the agent that built the scene before a human plays it: a frame whose GPU is idle while its JS render phase is longer than the display's own period is a scene-shape problem, and the engine already has the shape. On by default, printed at most once per reported window as `TN_SCENE_WARNING`, and silent on a scene that is honestly GPU-bound.",
|
|
1321
|
+
"supersedes": [],
|
|
1322
|
+
"symbol": "describeSceneShape"
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
"aliases": [],
|
|
1326
|
+
"constraints": [
|
|
1327
|
+
"the rule is derived from the frame's own numbers; the display period comes from the host's presentation cap or the game's declared target, never a constant",
|
|
1328
|
+
"no verdict without a measured GPU reading and a pass census — an absent measurement is not evidence",
|
|
1329
|
+
"the record carries objects considered, draws per pass, triangles per draw and shadow-exempt casters, so the reader is not dependent on the ranking"
|
|
1330
|
+
],
|
|
1331
|
+
"example": "defineGame({ display: { maxFps: 60 }, scenes: { Play } });",
|
|
1332
|
+
"importPath": "@threenative/core",
|
|
1333
|
+
"kind": "function",
|
|
1334
|
+
"overrides": [],
|
|
1335
|
+
"package": "@threenative/core",
|
|
1336
|
+
"signature": "export function describeSceneWarning(warning: ISceneWarning): string { … }",
|
|
1337
|
+
"situations": [
|
|
1338
|
+
"find out whether a slow frame is the scene's shape or the device",
|
|
1339
|
+
"tell an authoring agent what to reduce before it promises a merge"
|
|
1340
|
+
],
|
|
1341
|
+
"summary": "Warn the agent that built the scene before a human plays it: a frame whose GPU is idle while its JS render phase is longer than the display's own period is a scene-shape problem, and the engine already has the shape. On by default, printed at most once per reported window as `TN_SCENE_WARNING`, and silent on a scene that is honestly GPU-bound.",
|
|
1342
|
+
"supersedes": [],
|
|
1343
|
+
"symbol": "describeSceneWarning"
|
|
1344
|
+
},
|
|
781
1345
|
{
|
|
782
1346
|
"aliases": [],
|
|
783
1347
|
"constraints": ["pass a non-zero direction; coefficients and radii come from the game"],
|
|
@@ -806,6 +1370,49 @@
|
|
|
806
1370
|
"supersedes": [],
|
|
807
1371
|
"symbol": "directionFromSolarPosition"
|
|
808
1372
|
},
|
|
1373
|
+
{
|
|
1374
|
+
"aliases": [],
|
|
1375
|
+
"constraints": [
|
|
1376
|
+
"the rule is derived from the frame's own numbers; the display period comes from the host's presentation cap or the game's declared target, never a constant",
|
|
1377
|
+
"no verdict without a measured GPU reading and a pass census — an absent measurement is not evidence",
|
|
1378
|
+
"the record carries objects considered, draws per pass, triangles per draw and shadow-exempt casters, so the reader is not dependent on the ranking"
|
|
1379
|
+
],
|
|
1380
|
+
"example": "defineGame({ display: { maxFps: 60 }, scenes: { Play } });",
|
|
1381
|
+
"importPath": "@threenative/core",
|
|
1382
|
+
"kind": "function",
|
|
1383
|
+
"overrides": [],
|
|
1384
|
+
"package": "@threenative/core",
|
|
1385
|
+
"signature": "export function displayPeriodMs( declaredTargetFps: number | undefined, ): { … }",
|
|
1386
|
+
"situations": [
|
|
1387
|
+
"find out whether a slow frame is the scene's shape or the device",
|
|
1388
|
+
"tell an authoring agent what to reduce before it promises a merge"
|
|
1389
|
+
],
|
|
1390
|
+
"summary": "Warn the agent that built the scene before a human plays it: a frame whose GPU is idle while its JS render phase is longer than the display's own period is a scene-shape problem, and the engine already has the shape. On by default, printed at most once per reported window as `TN_SCENE_WARNING`, and silent on a scene that is honestly GPU-bound.",
|
|
1391
|
+
"supersedes": [],
|
|
1392
|
+
"symbol": "displayPeriodMs"
|
|
1393
|
+
},
|
|
1394
|
+
{
|
|
1395
|
+
"aliases": [],
|
|
1396
|
+
"constraints": [
|
|
1397
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
1398
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
1399
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
1400
|
+
],
|
|
1401
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
1402
|
+
"importPath": "@threenative/core",
|
|
1403
|
+
"kind": "function",
|
|
1404
|
+
"overrides": [],
|
|
1405
|
+
"package": "@threenative/core",
|
|
1406
|
+
"signature": "export function endSpan(id: SpanId): void { … }",
|
|
1407
|
+
"situations": [
|
|
1408
|
+
"find out what inside the render phase is actually costing the frame",
|
|
1409
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
1410
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
1411
|
+
],
|
|
1412
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
1413
|
+
"supersedes": [],
|
|
1414
|
+
"symbol": "endSpan"
|
|
1415
|
+
},
|
|
809
1416
|
{
|
|
810
1417
|
"aliases": [],
|
|
811
1418
|
"constraints": [
|
|
@@ -828,6 +1435,41 @@
|
|
|
828
1435
|
"supersedes": [],
|
|
829
1436
|
"symbol": "ensureVelocityOutput"
|
|
830
1437
|
},
|
|
1438
|
+
{
|
|
1439
|
+
"aliases": [],
|
|
1440
|
+
"constraints": ["development builds only; a production build publishes nothing"],
|
|
1441
|
+
"example": "import { exposeDebug } from \"@threenative/core\";\nexposeDebug(\"player\", player);\n// then from the console: __THREENATIVE__.debug.player",
|
|
1442
|
+
"importPath": "@threenative/core",
|
|
1443
|
+
"kind": "function",
|
|
1444
|
+
"overrides": [],
|
|
1445
|
+
"package": "@threenative/core",
|
|
1446
|
+
"signature": "export function exposeDebug(name: string, value: unknown): void { … }",
|
|
1447
|
+
"situations": ["expose a game object to a capture script or the console in dev builds"],
|
|
1448
|
+
"summary": "Publish one game object under `__THREENATIVE__.debug` for a capture script or the console.",
|
|
1449
|
+
"supersedes": [],
|
|
1450
|
+
"symbol": "exposeDebug"
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
"aliases": [],
|
|
1454
|
+
"constraints": [
|
|
1455
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
1456
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
1457
|
+
],
|
|
1458
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
1459
|
+
"importPath": "@threenative/core",
|
|
1460
|
+
"kind": "class",
|
|
1461
|
+
"overrides": [],
|
|
1462
|
+
"package": "@threenative/core",
|
|
1463
|
+
"signature": "export class FlightModel<TState extends IFlightState = IFlightState> { … }",
|
|
1464
|
+
"situations": [
|
|
1465
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
1466
|
+
"launch an aircraft off a moving carrier deck",
|
|
1467
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
1468
|
+
],
|
|
1469
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
1470
|
+
"supersedes": [],
|
|
1471
|
+
"symbol": "FlightModel"
|
|
1472
|
+
},
|
|
831
1473
|
{
|
|
832
1474
|
"aliases": [],
|
|
833
1475
|
"constraints": [
|
|
@@ -857,7 +1499,73 @@
|
|
|
857
1499
|
{
|
|
858
1500
|
"aliases": [],
|
|
859
1501
|
"constraints": [
|
|
860
|
-
"
|
|
1502
|
+
"the rule is derived from the frame's own numbers; the display period comes from the host's presentation cap or the game's declared target, never a constant",
|
|
1503
|
+
"no verdict without a measured GPU reading and a pass census — an absent measurement is not evidence",
|
|
1504
|
+
"the record carries objects considered, draws per pass, triangles per draw and shadow-exempt casters, so the reader is not dependent on the ranking"
|
|
1505
|
+
],
|
|
1506
|
+
"example": "defineGame({ display: { maxFps: 60 }, scenes: { Play } });",
|
|
1507
|
+
"importPath": "@threenative/core",
|
|
1508
|
+
"kind": "function",
|
|
1509
|
+
"overrides": [],
|
|
1510
|
+
"package": "@threenative/core",
|
|
1511
|
+
"signature": "export function formatSceneWarning(warning: ISceneWarning): string { … }",
|
|
1512
|
+
"situations": [
|
|
1513
|
+
"find out whether a slow frame is the scene's shape or the device",
|
|
1514
|
+
"tell an authoring agent what to reduce before it promises a merge"
|
|
1515
|
+
],
|
|
1516
|
+
"summary": "Warn the agent that built the scene before a human plays it: a frame whose GPU is idle while its JS render phase is longer than the display's own period is a scene-shape problem, and the engine already has the shape. On by default, printed at most once per reported window as `TN_SCENE_WARNING`, and silent on a scene that is honestly GPU-bound.",
|
|
1517
|
+
"supersedes": [],
|
|
1518
|
+
"symbol": "formatSceneWarning"
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
"aliases": [],
|
|
1522
|
+
"constraints": [
|
|
1523
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
1524
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
1525
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
1526
|
+
],
|
|
1527
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
1528
|
+
"importPath": "@threenative/core",
|
|
1529
|
+
"kind": "function",
|
|
1530
|
+
"overrides": [],
|
|
1531
|
+
"package": "@threenative/core",
|
|
1532
|
+
"signature": "export function formatSpansWindow(window: ISpanWindow): string { … }",
|
|
1533
|
+
"situations": [
|
|
1534
|
+
"find out what inside the render phase is actually costing the frame",
|
|
1535
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
1536
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
1537
|
+
],
|
|
1538
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
1539
|
+
"supersedes": [],
|
|
1540
|
+
"symbol": "formatSpansWindow"
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
"aliases": [],
|
|
1544
|
+
"constraints": [
|
|
1545
|
+
"off by default and expensive by construction — it does the work it is checking, twice",
|
|
1546
|
+
"it throws on the first divergence rather than logging; a validation mode that continues is one nobody reads",
|
|
1547
|
+
"it proves the frames it ran on and nothing else"
|
|
1548
|
+
],
|
|
1549
|
+
"example": "if (renderListValidationRequested()) console.log(formatValidationReport(report));",
|
|
1550
|
+
"importPath": "@threenative/core",
|
|
1551
|
+
"kind": "function",
|
|
1552
|
+
"overrides": [],
|
|
1553
|
+
"package": "@threenative/core",
|
|
1554
|
+
"signature": "export function formatValidationReport(report: IValidationReport): string { … }",
|
|
1555
|
+
"situations": [
|
|
1556
|
+
"prove a static freeze did not leave a stale transform on screen",
|
|
1557
|
+
"gate a scene in CI against silent transform divergence"
|
|
1558
|
+
],
|
|
1559
|
+
"summary": "Prove that nothing a cache skipped changed the picture: `TN_RENDERLIST_VALIDATE=1` recomputes every world matrix the long way, every frame, and throws on the first element that disagrees with what the frame is about to draw.",
|
|
1560
|
+
"supersedes": [],
|
|
1561
|
+
"symbol": "formatValidationReport"
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
"aliases": [],
|
|
1565
|
+
"constraints": [
|
|
1566
|
+
"on by default and printed as TN_FRAME_BUDGET; defineGame({ frameBudget: false }) silences the marker, not the measurement",
|
|
1567
|
+
"per-pass numbers are attributed to the innermost active render call, so nested shadow and reflection passes do not read as main",
|
|
1568
|
+
"GPU is a mean/p50/p95/max series over resolved frames (`gpu`) with `gpuStale` counting frames that had no fresh reading; absent means no timestamps, never zero"
|
|
861
1569
|
],
|
|
862
1570
|
"example": "defineGame({ frameBudget: { reportEvery: 120 }, scenes: { Play } });",
|
|
863
1571
|
"importPath": "@threenative/core",
|
|
@@ -866,13 +1574,59 @@
|
|
|
866
1574
|
"package": "@threenative/core",
|
|
867
1575
|
"signature": "export class FrameBudget { … }",
|
|
868
1576
|
"situations": [
|
|
1577
|
+
"show an on-screen frame time meter with p50, p95 and p99 percentiles",
|
|
869
1578
|
"find out why a game runs slowly on a phone",
|
|
870
|
-
"attribute a frame to present wait, simulation, three.js render, or overlay"
|
|
1579
|
+
"attribute a frame to present wait, simulation, three.js render, or overlay",
|
|
1580
|
+
"tell whether the GPU is the frame's constraint from a per-frame series, not one lagged timestamp",
|
|
1581
|
+
"split a frame's draw calls and triangles per render pass (main, shadow, reflection)",
|
|
1582
|
+
"tell a shadow or reflection pass's cost from the main colour pass"
|
|
871
1583
|
],
|
|
872
|
-
"summary": "Read where the frame's milliseconds went, per presented frame, on any platform.",
|
|
1584
|
+
"summary": "Read where the frame's milliseconds went, per presented frame, on any platform; each `TN_FRAME_BUDGET` window also carries the GPU time per resolved frame and the draw calls and triangles each render pass submitted.",
|
|
873
1585
|
"supersedes": [],
|
|
874
1586
|
"symbol": "FrameBudget"
|
|
875
1587
|
},
|
|
1588
|
+
{
|
|
1589
|
+
"aliases": [],
|
|
1590
|
+
"constraints": [
|
|
1591
|
+
"counts command-encoder and queue methods only; `mapAsync` and the presentation path are named, not folded in",
|
|
1592
|
+
"`gpuBytes` is `queue.writeBuffer` exactly, so it reconciles against a driver; texture uploads are not included",
|
|
1593
|
+
"`jsAllocBytes` needs `performance.memory` and stays absent where the platform lacks it"
|
|
1594
|
+
],
|
|
1595
|
+
"example": "const counters = FrameCounters.install(counterDeviceOf(renderer.raw));",
|
|
1596
|
+
"importPath": "@threenative/core",
|
|
1597
|
+
"kind": "class",
|
|
1598
|
+
"overrides": [],
|
|
1599
|
+
"package": "@threenative/core",
|
|
1600
|
+
"signature": "export class FrameCounters { … }",
|
|
1601
|
+
"situations": [
|
|
1602
|
+
"decide whether a CPU-bound frame is paying for the V8-to-host boundary",
|
|
1603
|
+
"measure how many bytes a frame writes into GPU buffers, and how many commands it issues"
|
|
1604
|
+
],
|
|
1605
|
+
"summary": "Count the frame's host-boundary crossings and the bytes it writes into GPU buffers, off unless `TN_FRAME_SPANS` asks for them. On the frame budget's own window as `counters`, so a crossing count and a millisecond split describe the same frames.",
|
|
1606
|
+
"supersedes": [],
|
|
1607
|
+
"symbol": "FrameCounters"
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
"aliases": [],
|
|
1611
|
+
"constraints": [
|
|
1612
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
1613
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
1614
|
+
],
|
|
1615
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
1616
|
+
"importPath": "@threenative/core",
|
|
1617
|
+
"kind": "function",
|
|
1618
|
+
"overrides": [],
|
|
1619
|
+
"package": "@threenative/core",
|
|
1620
|
+
"signature": "export function gearClearance(state: IFlightState): number { … }",
|
|
1621
|
+
"situations": [
|
|
1622
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
1623
|
+
"launch an aircraft off a moving carrier deck",
|
|
1624
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
1625
|
+
],
|
|
1626
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
1627
|
+
"supersedes": [],
|
|
1628
|
+
"symbol": "gearClearance"
|
|
1629
|
+
},
|
|
876
1630
|
{
|
|
877
1631
|
"aliases": [],
|
|
878
1632
|
"constraints": [
|
|
@@ -978,6 +1732,48 @@
|
|
|
978
1732
|
"supersedes": [],
|
|
979
1733
|
"symbol": "GroundSnap"
|
|
980
1734
|
},
|
|
1735
|
+
{
|
|
1736
|
+
"aliases": [],
|
|
1737
|
+
"constraints": [
|
|
1738
|
+
"`buttons` is the gamepad and `mouseButtons` the mouse; `up`/`down`/`left`/`right` are the directions of `vector(name)`, not the keys that press it",
|
|
1739
|
+
"scroll, pinch and pointer-relative sources are declared on the binding and read through `axis(name)`; a game adds no window listener of its own"
|
|
1740
|
+
],
|
|
1741
|
+
"example": "const game = defineGame({ input: { move: { up: [\"KeyW\"], down: [\"KeyS\"], left: [\"KeyA\"], right: [\"KeyD\"] } }, scenes: { Play } });\n// inside the scene, per frame: ctx.input.axis(\"move\") is 0 at rest and 1 at full tilt",
|
|
1742
|
+
"importPath": "@threenative/core",
|
|
1743
|
+
"kind": "class",
|
|
1744
|
+
"overrides": [],
|
|
1745
|
+
"package": "@threenative/core",
|
|
1746
|
+
"signature": "export class InputMap { … }",
|
|
1747
|
+
"situations": [
|
|
1748
|
+
"map WASD keys to a movement axis instead of reading the held key set in the update loop",
|
|
1749
|
+
"read a jump, a fire or a reload as one named action bound to key, gamepad button and mouse button together",
|
|
1750
|
+
"read mouse look, wheel zoom or a two-finger pinch as an axis the frame loop already ticks"
|
|
1751
|
+
],
|
|
1752
|
+
"summary": "The map a game reads input through: named actions, 2D vectors and scalar axes resolved from keyboard, gamepad, mouse, wheel, pinch and touch. `defineGame({ input })` builds one and hands it to the running game as `ctx.input`, so the usual route is a binding in the config and `ctx.input.axis(\"move\")` in the update. Construct one directly to drive a menu, a replay or a test outside a running game.",
|
|
1753
|
+
"supersedes": [],
|
|
1754
|
+
"symbol": "InputMap"
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
"aliases": [],
|
|
1758
|
+
"constraints": [
|
|
1759
|
+
"returns an uninstall that restores every wrapper, asserted by test",
|
|
1760
|
+
"a renderer whose internals have moved loses that span rather than throwing, and it is absent from the report rather than zero",
|
|
1761
|
+
"only the outermost `_projectObject` opens a span, because three's recurses per child"
|
|
1762
|
+
],
|
|
1763
|
+
"example": "const uninstall = installSpanProbes(renderer.raw, scene);",
|
|
1764
|
+
"importPath": "@threenative/core",
|
|
1765
|
+
"kind": "function",
|
|
1766
|
+
"overrides": [],
|
|
1767
|
+
"package": "@threenative/core",
|
|
1768
|
+
"signature": "export function installSpanProbes(target: ISpanProbeTarget, root: Object3D): () => void { … }",
|
|
1769
|
+
"situations": [
|
|
1770
|
+
"measure which part of three's render path costs the frame",
|
|
1771
|
+
"attach the span tree to a renderer a test or a tool constructed itself"
|
|
1772
|
+
],
|
|
1773
|
+
"summary": "Attach the span probes to a renderer: three's `render`, `_projectObject`, the render list's `sort`, the per-draw submission, and the scene-graph walk. Installed for you when `TN_FRAME_SPANS` asks; exported so a harness can wrap a renderer it owns.",
|
|
1774
|
+
"supersedes": [],
|
|
1775
|
+
"symbol": "installSpanProbes"
|
|
1776
|
+
},
|
|
981
1777
|
{
|
|
982
1778
|
"aliases": ["landmarks points of interest", "obstacles collectibles increasing pace"],
|
|
983
1779
|
"constraints": [
|
|
@@ -1003,6 +1799,27 @@
|
|
|
1003
1799
|
"supersedes": [],
|
|
1004
1800
|
"symbol": "InstancedBatch"
|
|
1005
1801
|
},
|
|
1802
|
+
{
|
|
1803
|
+
"aliases": [],
|
|
1804
|
+
"constraints": [
|
|
1805
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
1806
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
1807
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
1808
|
+
],
|
|
1809
|
+
"example": "invalidateStatic(drawbridge);",
|
|
1810
|
+
"importPath": "@threenative/core",
|
|
1811
|
+
"kind": "function",
|
|
1812
|
+
"overrides": [],
|
|
1813
|
+
"package": "@threenative/core",
|
|
1814
|
+
"signature": "export function invalidateStatic(object: Object3D): number | undefined { … }",
|
|
1815
|
+
"situations": [
|
|
1816
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
1817
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
1818
|
+
],
|
|
1819
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
1820
|
+
"supersedes": [],
|
|
1821
|
+
"symbol": "invalidateStatic"
|
|
1822
|
+
},
|
|
1006
1823
|
{
|
|
1007
1824
|
"aliases": [],
|
|
1008
1825
|
"constraints": [
|
|
@@ -1045,6 +1862,27 @@
|
|
|
1045
1862
|
"supersedes": [],
|
|
1046
1863
|
"symbol": "isNative"
|
|
1047
1864
|
},
|
|
1865
|
+
{
|
|
1866
|
+
"aliases": [],
|
|
1867
|
+
"constraints": [
|
|
1868
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
1869
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
1870
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
1871
|
+
],
|
|
1872
|
+
"example": "invalidateStatic(drawbridge);",
|
|
1873
|
+
"importPath": "@threenative/core",
|
|
1874
|
+
"kind": "function",
|
|
1875
|
+
"overrides": [],
|
|
1876
|
+
"package": "@threenative/core",
|
|
1877
|
+
"signature": "export function isStatic(root: Object3D): boolean { … }",
|
|
1878
|
+
"situations": [
|
|
1879
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
1880
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
1881
|
+
],
|
|
1882
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
1883
|
+
"supersedes": [],
|
|
1884
|
+
"symbol": "isStatic"
|
|
1885
|
+
},
|
|
1048
1886
|
{
|
|
1049
1887
|
"aliases": [],
|
|
1050
1888
|
"constraints": [
|
|
@@ -1097,17 +1935,81 @@
|
|
|
1097
1935
|
"importPath": "@threenative/core",
|
|
1098
1936
|
"kind": "function",
|
|
1099
1937
|
"overrides": [
|
|
1100
|
-
"concurrency defaults to 6; `marker: false` silences the TN_LOAD_ALL line, not onProgress"
|
|
1938
|
+
"concurrency defaults to 6; `marker: false` silences the TN_LOAD_ALL line, not onProgress"
|
|
1939
|
+
],
|
|
1940
|
+
"package": "@threenative/core",
|
|
1941
|
+
"signature": "export async function loadAll<TIn, TOut>( items: readonly TIn[], load: (item: TIn, index: number) => Promise<TOut>, options: ILoadAllOptions = { … }",
|
|
1942
|
+
"situations": [
|
|
1943
|
+
"load many models or textures in parallel instead of one at a time",
|
|
1944
|
+
"keep a loading screen moving while a list of assets downloads"
|
|
1945
|
+
],
|
|
1946
|
+
"summary": "Load a list with bounded concurrency, returning results in the input's order.",
|
|
1947
|
+
"supersedes": [],
|
|
1948
|
+
"symbol": "loadAll"
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
"aliases": [],
|
|
1952
|
+
"constraints": [
|
|
1953
|
+
"a non-positive viewport height, a non-positive frustum height or an unprojectable camera throws",
|
|
1954
|
+
"a non-positive depth has no projected scale and returns Infinity"
|
|
1955
|
+
],
|
|
1956
|
+
"example": "const pixels = lodPixelScale(camera, canvas.clientHeight, mesh.position.distanceTo(camera.position));",
|
|
1957
|
+
"importPath": "@threenative/core",
|
|
1958
|
+
"kind": "function",
|
|
1959
|
+
"overrides": [],
|
|
1960
|
+
"package": "@threenative/core",
|
|
1961
|
+
"signature": "export function lodPixelScale(camera: Camera, viewportHeight: number, depth: number): number { … }",
|
|
1962
|
+
"situations": [
|
|
1963
|
+
"pick a level of detail from an object's projected size in pixels on screen",
|
|
1964
|
+
"know how many screen pixels a world-space error covers at a given distance"
|
|
1965
|
+
],
|
|
1966
|
+
"summary": "The screen pixels one world unit covers at `depth` for this camera and viewport. Perspective divides the projected scale by the depth; orthographic has no depth term and uses the frustum height instead. This is the number a level of detail is chosen against: multiply a level's world-space error by it and you have the on-screen error a player can see, which is the comparison `updateModelLods` makes from the baked chain.",
|
|
1967
|
+
"supersedes": [],
|
|
1968
|
+
"symbol": "lodPixelScale"
|
|
1969
|
+
},
|
|
1970
|
+
{
|
|
1971
|
+
"aliases": [],
|
|
1972
|
+
"constraints": [
|
|
1973
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
1974
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
1975
|
+
],
|
|
1976
|
+
"example": "markStatic(island); invalidateStatic(drawbridge);",
|
|
1977
|
+
"importPath": "@threenative/core",
|
|
1978
|
+
"kind": "function",
|
|
1979
|
+
"overrides": [],
|
|
1980
|
+
"package": "@threenative/core",
|
|
1981
|
+
"signature": "export function markStatic(root: Object3D): number { … }",
|
|
1982
|
+
"situations": [
|
|
1983
|
+
"freeze a static mesh or subtree so its matrices are not recomputed every frame",
|
|
1984
|
+
"stop the engine recomposing the transforms of props, terrain and buildings each frame"
|
|
1985
|
+
],
|
|
1986
|
+
"summary": "Freeze a subtree nobody moves: `markStatic(root)` composes its transforms once and stops the per-frame recompose. It is the call a game makes on scenery, terrain, buildings and props. The engine re-arms a root whose own transform the game changes; a write deeper inside a frozen subtree is announced with `invalidateStatic(object)`.",
|
|
1987
|
+
"supersedes": [],
|
|
1988
|
+
"symbol": "markStatic"
|
|
1989
|
+
},
|
|
1990
|
+
{
|
|
1991
|
+
"aliases": [],
|
|
1992
|
+
"constraints": [
|
|
1993
|
+
"a game that reads a hidden object's matrixWorld directly must use getWorldPosition or updateWorldMatrix(true, false) first",
|
|
1994
|
+
"`renderer.matrixWorld: \"all\"` visits every node; `TN_PROJECTION` reports the visited count either way"
|
|
1995
|
+
],
|
|
1996
|
+
"example": "import { MatrixWorldPass } from \"@threenative/core\";\nconst pass = new MatrixWorldPass(); // renderer.matrixWorld defaults to \"visible\"",
|
|
1997
|
+
"importPath": "@threenative/core",
|
|
1998
|
+
"kind": "class",
|
|
1999
|
+
"overrides": [
|
|
2000
|
+
"renderer.matrixWorld: \"all\" runs three's full walk instead of the visible-only default"
|
|
1101
2001
|
],
|
|
1102
2002
|
"package": "@threenative/core",
|
|
1103
|
-
"signature": "export
|
|
2003
|
+
"signature": "export class MatrixWorldPass { … }",
|
|
1104
2004
|
"situations": [
|
|
1105
|
-
"
|
|
1106
|
-
"
|
|
2005
|
+
"update the world matrices of the visible objects each frame instead of the whole scene",
|
|
2006
|
+
"the per-frame world matrix walk is hot in a profile",
|
|
2007
|
+
"stop multiplying matrices for hidden models, LOD levels and merged stand-ins",
|
|
2008
|
+
"a game needs every node walked, exactly as three's own updateMatrixWorld does"
|
|
1107
2009
|
],
|
|
1108
|
-
"summary": "
|
|
2010
|
+
"summary": "Walk the scene graph's world matrices each frame without recursing into a hidden subtree. On by default as `renderer.matrixWorld: \"visible\"`. three's `updateMatrixWorld` recurses into every child whatever its `visible` flag, so a hidden LOD body, a merged stand-in and a parked model cost a world-matrix multiply each while nothing under them can draw. This pass mirrors three exactly for every visible node and defers a hidden node's subtree until the frame it shows again. A class that overrides `updateMatrixWorld` (`SkinnedMesh`, `Camera`) runs its own, and a hidden node that holds bones is walked, so no skeleton or view matrix goes stale.",
|
|
1109
2011
|
"supersedes": [],
|
|
1110
|
-
"symbol": "
|
|
2012
|
+
"symbol": "MatrixWorldPass"
|
|
1111
2013
|
},
|
|
1112
2014
|
{
|
|
1113
2015
|
"aliases": [],
|
|
@@ -1126,24 +2028,52 @@
|
|
|
1126
2028
|
{
|
|
1127
2029
|
"aliases": [],
|
|
1128
2030
|
"constraints": [
|
|
1129
|
-
"
|
|
2031
|
+
"the material is the game's own instance and the split follows the materials the game",
|
|
2032
|
+
"the meshes come back in root's local space and unparented, with the originals still in the",
|
|
2033
|
+
"a skinned or instanced mesh, and a mesh with several materials, is left out — its",
|
|
2034
|
+
"a group where only some meshes carry uv throws naming the label rather than losing the"
|
|
2035
|
+
],
|
|
2036
|
+
"example": "const [hull, deck] = mergeByMaterial(ship, { label: \"ship\" });\n// a piece that must keep moving at run time:\nconst [steady] = mergeByMaterial(ship, { label: \"ship\", skip: (mesh) => mesh.name === \"radar\" });",
|
|
2037
|
+
"importPath": "@threenative/core",
|
|
2038
|
+
"kind": "function",
|
|
2039
|
+
"overrides": ["skip leaves one mesh out of its group and out of the result"],
|
|
2040
|
+
"package": "@threenative/core",
|
|
2041
|
+
"signature": "export function mergeByMaterial(root: Object3D, options: IMergeByMaterialOptions): Mesh[] { … }",
|
|
2042
|
+
"situations": [
|
|
2043
|
+
"collapse a building or ship of dozens of boxes into one draw call per material",
|
|
2044
|
+
"consolidate the static parts of a group before adding it to the scene"
|
|
2045
|
+
],
|
|
2046
|
+
"summary": "Bake a hierarchy's static meshes into one mesh per material, with their transforms baked in. already made; nothing here decides appearance tree: add them to root and remove the sources yourself, or both draw vertices are not its own to bake texture mapping; a missing normal is recomputed",
|
|
2047
|
+
"supersedes": [],
|
|
2048
|
+
"symbol": "mergeByMaterial"
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
"aliases": [],
|
|
2052
|
+
"constraints": [
|
|
2053
|
+
"every part is de-indexed; without preserve it is stripped to position and normals are recomputed from the merged buffer",
|
|
2054
|
+
"preserve keeps the listed channels, transforming position and normal by the part's placement matrix while UV values are retained unchanged",
|
|
2055
|
+
"a part that does not carry a listed preserve channel throws naming the label, the part and the channel",
|
|
1130
2056
|
"either every part names a color or none does, and a mix throws",
|
|
1131
2057
|
"an empty part list throws, and a merge three.js refuses throws naming the label"
|
|
1132
2058
|
],
|
|
1133
|
-
"example": "const wall = new Mesh(mergeParts(pieces, { label: \"gatehouse\" }), stone);\n// pieces are meshes, or { geometry, matrix, color } when the colour is per piece:\nconst banner = mergeParts([{ color: 0x8b2f1a, geometry: cloth, matrix: placement }], { label: \"banner\" });",
|
|
2059
|
+
"example": "const wall = new Mesh(mergeParts(pieces, { label: \"gatehouse\" }), stone);\n// pieces are meshes, or { geometry, matrix, color } when the colour is per piece:\nconst banner = mergeParts([{ color: 0x8b2f1a, geometry: cloth, matrix: placement }], { label: \"banner\" });\n// keep a model's texture UVs and authored normals while baking its transforms:\nconst hull = mergeParts(hullParts, { label: \"hull\", preserve: [\"uv\", \"normal\"] });",
|
|
1134
2060
|
"importPath": "@threenative/core",
|
|
1135
2061
|
"kind": "function",
|
|
1136
2062
|
"overrides": [
|
|
1137
|
-
"color is per part and optional; without it no colour attribute is written and the surface alone decides"
|
|
2063
|
+
"color is per part and optional; without it no colour attribute is written and the surface alone decides",
|
|
2064
|
+
"preserve is optional and empty by default: position-only merge with recomputed normals, exactly as before"
|
|
1138
2065
|
],
|
|
1139
2066
|
"package": "@threenative/core",
|
|
1140
2067
|
"signature": "export function mergeParts( parts: Iterable<IMergePart>, options: IMergePartsOptions, ): BufferGeometry { … }",
|
|
1141
2068
|
"situations": [
|
|
1142
2069
|
"bake a building, ship or character authored out of primitives into one draw call",
|
|
2070
|
+
"merge multiple static Three.js meshes into one mesh per material",
|
|
2071
|
+
"consolidate the static parts of an imported glTF model into one buffer",
|
|
2072
|
+
"preserve texture UV coordinates and authored normals while baking object transforms",
|
|
1143
2073
|
"merge many small geometries and keep each piece's own colour",
|
|
1144
2074
|
"stop mergeGeometries from silently returning null on an extruded shape"
|
|
1145
2075
|
],
|
|
1146
|
-
"summary": "Merge pieces a game authored out of primitives into one buffer, keeping each piece's own
|
|
2076
|
+
"summary": "Merge pieces a game authored out of primitives into one buffer, keeping each piece's own look. `InstancedBatch` collapses many copies of one shape; this collapses many *different* shapes that never move relative to each other — a building, a ship, a character built from boxes, or the static parts of an imported glTF model. Two things go wrong every time and neither is about how any of it looks. `mergeGeometries` hands back `null` on mismatched inputs instead of throwing, and the usual mismatch is invisible: one `ExtrudeGeometry` is non-indexed while every other primitive is indexed, so the merge fails at the first piece and the scene never loads. And a merged buffer draws with one surface, so per-piece colour is gone unless every piece carries a flat `color` attribute written before the merge. Both are mechanical. By default every part is stripped to position and the normals are recomputed from the merged buffer; pass `preserve` to keep authored normals and texture UVs while baking each part's object transform. Geometry, placement, colour and the surface it draws with all stay the game's.",
|
|
1147
2077
|
"supersedes": [],
|
|
1148
2078
|
"symbol": "mergeParts"
|
|
1149
2079
|
},
|
|
@@ -1166,6 +2096,23 @@
|
|
|
1166
2096
|
"supersedes": ["new Box3().setFromObject("],
|
|
1167
2097
|
"symbol": "normaliseToMetres"
|
|
1168
2098
|
},
|
|
2099
|
+
{
|
|
2100
|
+
"aliases": [],
|
|
2101
|
+
"constraints": [],
|
|
2102
|
+
"example": "const off = onLaunchFailure((failure) => shell.loading({ failure: failure.message }));",
|
|
2103
|
+
"importPath": "@threenative/core",
|
|
2104
|
+
"kind": "function",
|
|
2105
|
+
"overrides": [],
|
|
2106
|
+
"package": "@threenative/core",
|
|
2107
|
+
"signature": "export function onLaunchFailure(listener: (failure: ILaunchFailure) => void): () => void { … }",
|
|
2108
|
+
"situations": [
|
|
2109
|
+
"show the player why the game stopped loading instead of leaving the loading screen up",
|
|
2110
|
+
"report a stalled launch or a lost GPU device in the game's own UI"
|
|
2111
|
+
],
|
|
2112
|
+
"summary": "Called for every launch failure the engine notices, with the message to show the player.",
|
|
2113
|
+
"supersedes": [],
|
|
2114
|
+
"symbol": "onLaunchFailure"
|
|
2115
|
+
},
|
|
1169
2116
|
{
|
|
1170
2117
|
"aliases": [],
|
|
1171
2118
|
"constraints": ["recordings are version 1; the parser fails closed with TN_REPLAY_* codes"],
|
|
@@ -1453,6 +2400,27 @@
|
|
|
1453
2400
|
"supersedes": [],
|
|
1454
2401
|
"symbol": "reconcileMirroredClips"
|
|
1455
2402
|
},
|
|
2403
|
+
{
|
|
2404
|
+
"aliases": [],
|
|
2405
|
+
"constraints": [
|
|
2406
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
2407
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
2408
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
2409
|
+
],
|
|
2410
|
+
"example": "invalidateStatic(drawbridge);",
|
|
2411
|
+
"importPath": "@threenative/core",
|
|
2412
|
+
"kind": "function",
|
|
2413
|
+
"overrides": [],
|
|
2414
|
+
"package": "@threenative/core",
|
|
2415
|
+
"signature": "export function refreshStaticTransforms(): void { … }",
|
|
2416
|
+
"situations": [
|
|
2417
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
2418
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
2419
|
+
],
|
|
2420
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
2421
|
+
"supersedes": [],
|
|
2422
|
+
"symbol": "refreshStaticTransforms"
|
|
2423
|
+
},
|
|
1456
2424
|
{
|
|
1457
2425
|
"aliases": [],
|
|
1458
2426
|
"constraints": [
|
|
@@ -1474,6 +2442,48 @@
|
|
|
1474
2442
|
"supersedes": [],
|
|
1475
2443
|
"symbol": "RenderChain"
|
|
1476
2444
|
},
|
|
2445
|
+
{
|
|
2446
|
+
"aliases": [],
|
|
2447
|
+
"constraints": [
|
|
2448
|
+
"off by default and expensive by construction — it does the work it is checking, twice",
|
|
2449
|
+
"it throws on the first divergence rather than logging; a validation mode that continues is one nobody reads",
|
|
2450
|
+
"it proves the frames it ran on and nothing else"
|
|
2451
|
+
],
|
|
2452
|
+
"example": "if (renderListValidationRequested()) console.log(formatValidationReport(report));",
|
|
2453
|
+
"importPath": "@threenative/core",
|
|
2454
|
+
"kind": "function",
|
|
2455
|
+
"overrides": [],
|
|
2456
|
+
"package": "@threenative/core",
|
|
2457
|
+
"signature": "export function renderListValidationRequested(): boolean { … }",
|
|
2458
|
+
"situations": [
|
|
2459
|
+
"prove a static freeze did not leave a stale transform on screen",
|
|
2460
|
+
"gate a scene in CI against silent transform divergence"
|
|
2461
|
+
],
|
|
2462
|
+
"summary": "Prove that nothing a cache skipped changed the picture: `TN_RENDERLIST_VALIDATE=1` recomputes every world matrix the long way, every frame, and throws on the first element that disagrees with what the frame is about to draw.",
|
|
2463
|
+
"supersedes": [],
|
|
2464
|
+
"symbol": "renderListValidationRequested"
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
"aliases": [],
|
|
2468
|
+
"constraints": [
|
|
2469
|
+
"off by default and expensive by construction — it does the work it is checking, twice",
|
|
2470
|
+
"it throws on the first divergence rather than logging; a validation mode that continues is one nobody reads",
|
|
2471
|
+
"it proves the frames it ran on and nothing else"
|
|
2472
|
+
],
|
|
2473
|
+
"example": "if (renderListValidationRequested()) console.log(formatValidationReport(report));",
|
|
2474
|
+
"importPath": "@threenative/core",
|
|
2475
|
+
"kind": "class",
|
|
2476
|
+
"overrides": [],
|
|
2477
|
+
"package": "@threenative/core",
|
|
2478
|
+
"signature": "export class RenderListValidator { … }",
|
|
2479
|
+
"situations": [
|
|
2480
|
+
"prove a static freeze did not leave a stale transform on screen",
|
|
2481
|
+
"gate a scene in CI against silent transform divergence"
|
|
2482
|
+
],
|
|
2483
|
+
"summary": "Prove that nothing a cache skipped changed the picture: `TN_RENDERLIST_VALIDATE=1` recomputes every world matrix the long way, every frame, and throws on the first element that disagrees with what the frame is about to draw.",
|
|
2484
|
+
"supersedes": [],
|
|
2485
|
+
"symbol": "RenderListValidator"
|
|
2486
|
+
},
|
|
1477
2487
|
{
|
|
1478
2488
|
"aliases": ["fixed seed fixed-step simulation"],
|
|
1479
2489
|
"constraints": [
|
|
@@ -1493,6 +2503,43 @@
|
|
|
1493
2503
|
"supersedes": [],
|
|
1494
2504
|
"symbol": "replay"
|
|
1495
2505
|
},
|
|
2506
|
+
{
|
|
2507
|
+
"aliases": [],
|
|
2508
|
+
"constraints": [],
|
|
2509
|
+
"example": "resetAudioCueLedger();",
|
|
2510
|
+
"importPath": "@threenative/core",
|
|
2511
|
+
"kind": "function",
|
|
2512
|
+
"overrides": [],
|
|
2513
|
+
"package": "@threenative/core",
|
|
2514
|
+
"signature": "export function resetAudioCueLedger(): void { … }",
|
|
2515
|
+
"situations": [
|
|
2516
|
+
"clear the recorded audio cue counts between tests so one test cannot read another's plays"
|
|
2517
|
+
],
|
|
2518
|
+
"summary": "Forgets every recorded cue.",
|
|
2519
|
+
"supersedes": [],
|
|
2520
|
+
"symbol": "resetAudioCueLedger"
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
"aliases": [],
|
|
2524
|
+
"constraints": [
|
|
2525
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
2526
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
2527
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
2528
|
+
],
|
|
2529
|
+
"example": "invalidateStatic(drawbridge);",
|
|
2530
|
+
"importPath": "@threenative/core",
|
|
2531
|
+
"kind": "function",
|
|
2532
|
+
"overrides": [],
|
|
2533
|
+
"package": "@threenative/core",
|
|
2534
|
+
"signature": "export function resetStaticTransforms(): void { … }",
|
|
2535
|
+
"situations": [
|
|
2536
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
2537
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
2538
|
+
],
|
|
2539
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
2540
|
+
"supersedes": [],
|
|
2541
|
+
"symbol": "resetStaticTransforms"
|
|
2542
|
+
},
|
|
1496
2543
|
{
|
|
1497
2544
|
"aliases": [],
|
|
1498
2545
|
"constraints": [
|
|
@@ -1525,10 +2572,53 @@
|
|
|
1525
2572
|
"supersedes": [],
|
|
1526
2573
|
"symbol": "resolveAtmosphereParameters"
|
|
1527
2574
|
},
|
|
2575
|
+
{
|
|
2576
|
+
"aliases": [],
|
|
2577
|
+
"constraints": [
|
|
2578
|
+
"pass the measured display rate when you have one; without it the answer is the 60 fallback and says so"
|
|
2579
|
+
],
|
|
2580
|
+
"example": "resolveTargetFps(config, getPlatform()).targetFps;",
|
|
2581
|
+
"importPath": "@threenative/core",
|
|
2582
|
+
"kind": "function",
|
|
2583
|
+
"overrides": [],
|
|
2584
|
+
"package": "@threenative/core",
|
|
2585
|
+
"signature": "export function resolveTargetFps( config: ITargetFpsConfig | undefined, platform: ITargetFpsPlatform | undefined, measuredRefreshHz?: number, ): ITargetFps { … }",
|
|
2586
|
+
"situations": ["my game does frame-rate-dependent work and must not hardcode 60"],
|
|
2587
|
+
"summary": "Read what frame rate a game gets when its config does not say, and why. `display.maxFps` follows the display — capped at 120 on desktop and web, 60 on mobile — rather than the 60 every template used to ship, and an explicit number still wins with `0` still uncapping. The engine calls this itself; a game calls it when it needs the same number for its own frame-rate-dependent work, and `TN_FRAME_BUDGET` reports the resolved target and its source on every window.",
|
|
2588
|
+
"supersedes": [],
|
|
2589
|
+
"symbol": "resolveTargetFps"
|
|
2590
|
+
},
|
|
2591
|
+
{
|
|
2592
|
+
"aliases": [],
|
|
2593
|
+
"constraints": [
|
|
2594
|
+
"it draws nothing; the game supplies the mesh, the material and every colour",
|
|
2595
|
+
"add its height to an analytic swell, never in place of one",
|
|
2596
|
+
"the patch is finite and its rim absorbs; call recenter to keep it over the action",
|
|
2597
|
+
"there is no obstacle mask, because a mask is only correct for a body that never moves"
|
|
2598
|
+
],
|
|
2599
|
+
"example": "const ripples = new RippleField({ resolution: 128, size: 400 });\nripples.impulse(hit.x, hit.z, 6, -40, 0.5);\nripples.advance(dt);\nconst lift = ripples.heightAt(boat.x, boat.z);",
|
|
2600
|
+
"importPath": "@threenative/core",
|
|
2601
|
+
"kind": "class",
|
|
2602
|
+
"overrides": [
|
|
2603
|
+
"speed, damping, foamHalfLife, current, step and maxSteps tune the solve; the default"
|
|
2604
|
+
],
|
|
2605
|
+
"package": "@threenative/core",
|
|
2606
|
+
"signature": "export class RippleField { … }",
|
|
2607
|
+
"situations": [
|
|
2608
|
+
"make a splash or explosion ripple outward across water",
|
|
2609
|
+
"show the sea reacting to a bomb, shell, or torpedo hitting it",
|
|
2610
|
+
"leave a foam trail behind something moving through water",
|
|
2611
|
+
"disturb a water surface the player can see respond",
|
|
2612
|
+
"spread and drift foam on a water surface over time"
|
|
2613
|
+
],
|
|
2614
|
+
"summary": "Propagate a disturbance across a patch of water surface and let it fade. step is 1/60s or the CFL stability limit for the given resolution and speed, whichever is smaller",
|
|
2615
|
+
"supersedes": [],
|
|
2616
|
+
"symbol": "RippleField"
|
|
2617
|
+
},
|
|
1528
2618
|
{
|
|
1529
2619
|
"aliases": [],
|
|
1530
2620
|
"constraints": ["scene code must stay portable across web and native"],
|
|
1531
|
-
"example": "class Play extends Scene { update(ctx, dt) {} }",
|
|
2621
|
+
"example": "class Play extends Scene { update(ctx, dt) {} }\nctx.beforeRender(() => packBatches()); // cleared on scene change and stop, like ctx.afterPhysics",
|
|
1532
2622
|
"importPath": "@threenative/core",
|
|
1533
2623
|
"kind": "class",
|
|
1534
2624
|
"overrides": [],
|
|
@@ -1536,7 +2626,8 @@
|
|
|
1536
2626
|
"signature": "export abstract class Scene< TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined, > { … }",
|
|
1537
2627
|
"situations": [
|
|
1538
2628
|
"add a playable level or menu scene",
|
|
1539
|
-
"move scene setup and per-frame gameplay out of the entry point"
|
|
2629
|
+
"move scene setup and per-frame gameplay out of the entry point",
|
|
2630
|
+
"run scene work once per actual world draw, after the frame's last fixed update and before the projection packs"
|
|
1540
2631
|
],
|
|
1541
2632
|
"summary": "Implement a portable Godot-shaped game scene lifecycle.",
|
|
1542
2633
|
"supersedes": [],
|
|
@@ -1559,6 +2650,27 @@
|
|
|
1559
2650
|
"supersedes": ["new Raycaster("],
|
|
1560
2651
|
"symbol": "ScenePicker"
|
|
1561
2652
|
},
|
|
2653
|
+
{
|
|
2654
|
+
"aliases": [],
|
|
2655
|
+
"constraints": [
|
|
2656
|
+
"the rule is derived from the frame's own numbers; the display period comes from the host's presentation cap or the game's declared target, never a constant",
|
|
2657
|
+
"no verdict without a measured GPU reading and a pass census — an absent measurement is not evidence",
|
|
2658
|
+
"the record carries objects considered, draws per pass, triangles per draw and shadow-exempt casters, so the reader is not dependent on the ranking"
|
|
2659
|
+
],
|
|
2660
|
+
"example": "defineGame({ display: { maxFps: 60 }, scenes: { Play } });",
|
|
2661
|
+
"importPath": "@threenative/core",
|
|
2662
|
+
"kind": "function",
|
|
2663
|
+
"overrides": [],
|
|
2664
|
+
"package": "@threenative/core",
|
|
2665
|
+
"signature": "export function sceneWarning( window: IFrameBudgetWindow, shape: ISceneShape | undefined, declaredTargetFps: number | undefined, ): ISceneWarning | undefined { … }",
|
|
2666
|
+
"situations": [
|
|
2667
|
+
"find out whether a slow frame is the scene's shape or the device",
|
|
2668
|
+
"tell an authoring agent what to reduce before it promises a merge"
|
|
2669
|
+
],
|
|
2670
|
+
"summary": "Warn the agent that built the scene before a human plays it: a frame whose GPU is idle while its JS render phase is longer than the display's own period is a scene-shape problem, and the engine already has the shape. On by default, printed at most once per reported window as `TN_SCENE_WARNING`, and silent on a scene that is honestly GPU-bound.",
|
|
2671
|
+
"supersedes": [],
|
|
2672
|
+
"symbol": "sceneWarning"
|
|
2673
|
+
},
|
|
1562
2674
|
{
|
|
1563
2675
|
"aliases": ["tower defense game", "spawn waves"],
|
|
1564
2676
|
"constraints": [
|
|
@@ -1580,6 +2692,49 @@
|
|
|
1580
2692
|
"supersedes": [],
|
|
1581
2693
|
"symbol": "Scheduler"
|
|
1582
2694
|
},
|
|
2695
|
+
{
|
|
2696
|
+
"aliases": [],
|
|
2697
|
+
"constraints": [
|
|
2698
|
+
"every mass, area, power and inertia value comes from the game's airframe",
|
|
2699
|
+
"damage, stores and configuration arrive as the game's own modifier sample"
|
|
2700
|
+
],
|
|
2701
|
+
"example": "const model = new FlightModel({ airframe: sbd, state: aircraft, wind: seaWind });\nmodel.step(1 / 60, { turn: -1, pitch: 0.4 });",
|
|
2702
|
+
"importPath": "@threenative/core",
|
|
2703
|
+
"kind": "function",
|
|
2704
|
+
"overrides": [],
|
|
2705
|
+
"package": "@threenative/core",
|
|
2706
|
+
"signature": "export function setAttitude( state: IFlightState, heading = 0, pitch = 0, roll = 0, ): IFlightQuaternion { … }",
|
|
2707
|
+
"situations": [
|
|
2708
|
+
"fly an airplane with lift, drag, stall and control authority",
|
|
2709
|
+
"launch an aircraft off a moving carrier deck",
|
|
2710
|
+
"apply component damage or a loadout to an aircraft's performance"
|
|
2711
|
+
],
|
|
2712
|
+
"summary": "Fly a fixed-wing aircraft with a real force balance instead of a steered velocity.",
|
|
2713
|
+
"supersedes": [],
|
|
2714
|
+
"symbol": "setAttitude"
|
|
2715
|
+
},
|
|
2716
|
+
{
|
|
2717
|
+
"aliases": [],
|
|
2718
|
+
"constraints": [
|
|
2719
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
2720
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
2721
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
2722
|
+
],
|
|
2723
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
2724
|
+
"importPath": "@threenative/core",
|
|
2725
|
+
"kind": "function",
|
|
2726
|
+
"overrides": [],
|
|
2727
|
+
"package": "@threenative/core",
|
|
2728
|
+
"signature": "export function setSpanRecorder(next: SpanRecorder | undefined): void { … }",
|
|
2729
|
+
"situations": [
|
|
2730
|
+
"find out what inside the render phase is actually costing the frame",
|
|
2731
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
2732
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
2733
|
+
],
|
|
2734
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
2735
|
+
"supersedes": [],
|
|
2736
|
+
"symbol": "setSpanRecorder"
|
|
2737
|
+
},
|
|
1583
2738
|
{
|
|
1584
2739
|
"aliases": [],
|
|
1585
2740
|
"constraints": [
|
|
@@ -1623,6 +2778,22 @@
|
|
|
1623
2778
|
"supersedes": [],
|
|
1624
2779
|
"symbol": "skeletonBones"
|
|
1625
2780
|
},
|
|
2781
|
+
{
|
|
2782
|
+
"aliases": [],
|
|
2783
|
+
"constraints": [
|
|
2784
|
+
"pass the measured display rate when you have one; without it the answer is the 60 fallback and says so"
|
|
2785
|
+
],
|
|
2786
|
+
"example": "resolveTargetFps(config, getPlatform()).targetFps;",
|
|
2787
|
+
"importPath": "@threenative/core",
|
|
2788
|
+
"kind": "function",
|
|
2789
|
+
"overrides": [],
|
|
2790
|
+
"package": "@threenative/core",
|
|
2791
|
+
"signature": "export function snapRefreshRate(refreshHz: number): number { … }",
|
|
2792
|
+
"situations": ["my game does frame-rate-dependent work and must not hardcode 60"],
|
|
2793
|
+
"summary": "Read what frame rate a game gets when its config does not say, and why. `display.maxFps` follows the display — capped at 120 on desktop and web, 60 on mobile — rather than the 60 every template used to ship, and an explicit number still wins with `0` still uncapping. The engine calls this itself; a game calls it when it needs the same number for its own frame-rate-dependent work, and `TN_FRAME_BUDGET` reports the resolved target and its source on every window.",
|
|
2794
|
+
"supersedes": [],
|
|
2795
|
+
"symbol": "snapRefreshRate"
|
|
2796
|
+
},
|
|
1626
2797
|
{
|
|
1627
2798
|
"aliases": [],
|
|
1628
2799
|
"constraints": [
|
|
@@ -1704,6 +2875,94 @@
|
|
|
1704
2875
|
"supersedes": [],
|
|
1705
2876
|
"symbol": "solarPositionAt"
|
|
1706
2877
|
},
|
|
2878
|
+
{
|
|
2879
|
+
"aliases": [],
|
|
2880
|
+
"constraints": [
|
|
2881
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
2882
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
2883
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
2884
|
+
],
|
|
2885
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
2886
|
+
"importPath": "@threenative/core",
|
|
2887
|
+
"kind": "function",
|
|
2888
|
+
"overrides": [],
|
|
2889
|
+
"package": "@threenative/core",
|
|
2890
|
+
"signature": "export function spanNow(): number { … }",
|
|
2891
|
+
"situations": [
|
|
2892
|
+
"find out what inside the render phase is actually costing the frame",
|
|
2893
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
2894
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
2895
|
+
],
|
|
2896
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
2897
|
+
"supersedes": [],
|
|
2898
|
+
"symbol": "spanNow"
|
|
2899
|
+
},
|
|
2900
|
+
{
|
|
2901
|
+
"aliases": [],
|
|
2902
|
+
"constraints": [
|
|
2903
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
2904
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
2905
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
2906
|
+
],
|
|
2907
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
2908
|
+
"importPath": "@threenative/core",
|
|
2909
|
+
"kind": "function",
|
|
2910
|
+
"overrides": [],
|
|
2911
|
+
"package": "@threenative/core",
|
|
2912
|
+
"signature": "export function spanRecorder(): SpanRecorder | undefined { … }",
|
|
2913
|
+
"situations": [
|
|
2914
|
+
"find out what inside the render phase is actually costing the frame",
|
|
2915
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
2916
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
2917
|
+
],
|
|
2918
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
2919
|
+
"supersedes": [],
|
|
2920
|
+
"symbol": "spanRecorder"
|
|
2921
|
+
},
|
|
2922
|
+
{
|
|
2923
|
+
"aliases": [],
|
|
2924
|
+
"constraints": [
|
|
2925
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
2926
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
2927
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
2928
|
+
],
|
|
2929
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
2930
|
+
"importPath": "@threenative/core",
|
|
2931
|
+
"kind": "class",
|
|
2932
|
+
"overrides": [],
|
|
2933
|
+
"package": "@threenative/core",
|
|
2934
|
+
"signature": "export class SpanRecorder { … }",
|
|
2935
|
+
"situations": [
|
|
2936
|
+
"find out what inside the render phase is actually costing the frame",
|
|
2937
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
2938
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
2939
|
+
],
|
|
2940
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
2941
|
+
"supersedes": [],
|
|
2942
|
+
"symbol": "SpanRecorder"
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
"aliases": [],
|
|
2946
|
+
"constraints": [
|
|
2947
|
+
"off by default and installed by `TN_FRAME_SPANS=1`; unset, every call site is one guarded return",
|
|
2948
|
+
"the tree is closed against the frame budget's own render phase, so `TN_FRAME_SPANS` and `TN_FRAME_BUDGET` describe the same frames",
|
|
2949
|
+
"measurement only: no span changes what is drawn, in what order, or with which renderer"
|
|
2950
|
+
],
|
|
2951
|
+
"example": "if (spansRequested()) setSpanRecorder(new SpanRecorder());",
|
|
2952
|
+
"importPath": "@threenative/core",
|
|
2953
|
+
"kind": "function",
|
|
2954
|
+
"overrides": [],
|
|
2955
|
+
"package": "@threenative/core",
|
|
2956
|
+
"signature": "export function spansRequested(): boolean { … }",
|
|
2957
|
+
"situations": [
|
|
2958
|
+
"find out what inside the render phase is actually costing the frame",
|
|
2959
|
+
"tell a shadow pass's traversal from the main pass's, with the residual computed",
|
|
2960
|
+
"price an optimisation against a measured part of the phase rather than the whole of it"
|
|
2961
|
+
],
|
|
2962
|
+
"summary": "Attribute the render phase to 100% with a nested span tree, off unless `TN_FRAME_SPANS` asks. Every non-leaf reports its own time minus the spans inside it, so an unmeasured part shows up as a residual instead of being filed under \"other\"; a child that outlives its parent reports a negative residual rather than a clamped zero.",
|
|
2963
|
+
"supersedes": [],
|
|
2964
|
+
"symbol": "spansRequested"
|
|
2965
|
+
},
|
|
1707
2966
|
{
|
|
1708
2967
|
"aliases": [],
|
|
1709
2968
|
"constraints": [
|
|
@@ -1748,6 +3007,27 @@
|
|
|
1748
3007
|
"supersedes": [],
|
|
1749
3008
|
"symbol": "SpriteAnimator3D"
|
|
1750
3009
|
},
|
|
3010
|
+
{
|
|
3011
|
+
"aliases": [],
|
|
3012
|
+
"constraints": [
|
|
3013
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
3014
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
3015
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
3016
|
+
],
|
|
3017
|
+
"example": "invalidateStatic(drawbridge);",
|
|
3018
|
+
"importPath": "@threenative/core",
|
|
3019
|
+
"kind": "function",
|
|
3020
|
+
"overrides": [],
|
|
3021
|
+
"package": "@threenative/core",
|
|
3022
|
+
"signature": "export function staticTransformCensus(): IStaticTransformCensus { … }",
|
|
3023
|
+
"situations": [
|
|
3024
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
3025
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
3026
|
+
],
|
|
3027
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
3028
|
+
"supersedes": [],
|
|
3029
|
+
"symbol": "staticTransformCensus"
|
|
3030
|
+
},
|
|
1751
3031
|
{
|
|
1752
3032
|
"aliases": [],
|
|
1753
3033
|
"constraints": [
|
|
@@ -1768,6 +3048,27 @@
|
|
|
1768
3048
|
"supersedes": [],
|
|
1769
3049
|
"symbol": "TracerPool3D"
|
|
1770
3050
|
},
|
|
3051
|
+
{
|
|
3052
|
+
"aliases": [],
|
|
3053
|
+
"constraints": [
|
|
3054
|
+
"staticness is authored, never guessed; no heuristic watches gameplay and decides for you",
|
|
3055
|
+
"it deletes the local and world matrix composes, not the walk itself — three 0.185 recurses into every child regardless",
|
|
3056
|
+
"a write deeper inside a frozen subtree must call `invalidateStatic`; `TN_RENDERLIST_VALIDATE=1` is what proves it happened"
|
|
3057
|
+
],
|
|
3058
|
+
"example": "invalidateStatic(drawbridge);",
|
|
3059
|
+
"importPath": "@threenative/core",
|
|
3060
|
+
"kind": "function",
|
|
3061
|
+
"overrides": [],
|
|
3062
|
+
"package": "@threenative/core",
|
|
3063
|
+
"signature": "export function unmarkStatic(root: Object3D): void { … }",
|
|
3064
|
+
"situations": [
|
|
3065
|
+
"cut the per-frame matrix work of terrain, buildings, props and other scenery that never moves",
|
|
3066
|
+
"keep a frozen subtree correct when the game does move it after all"
|
|
3067
|
+
],
|
|
3068
|
+
"summary": "Stop recomposing the transforms of a subtree nobody moves. `markStatic(root)` composes the subtree once and freezes it; the engine re-arms a root whose own transform the game changes, and `invalidateStatic(object)` announces a write deeper inside one.",
|
|
3069
|
+
"supersedes": [],
|
|
3070
|
+
"symbol": "unmarkStatic"
|
|
3071
|
+
},
|
|
1771
3072
|
{
|
|
1772
3073
|
"aliases": [],
|
|
1773
3074
|
"constraints": ["patches cannot introduce omitted, negative, or non-finite physical values"],
|
|
@@ -1784,17 +3085,63 @@
|
|
|
1784
3085
|
},
|
|
1785
3086
|
{
|
|
1786
3087
|
"aliases": [],
|
|
1787
|
-
"constraints": [],
|
|
1788
|
-
"example": "updateClusteredMeshes(stagedRoot, myCamera, ctx.renderer.domElement.height);",
|
|
3088
|
+
"constraints": [],
|
|
3089
|
+
"example": "updateClusteredMeshes(stagedRoot, myCamera, ctx.renderer.domElement.height);",
|
|
3090
|
+
"importPath": "@threenative/core",
|
|
3091
|
+
"kind": "function",
|
|
3092
|
+
"overrides": [],
|
|
3093
|
+
"package": "@threenative/core",
|
|
3094
|
+
"signature": "export function updateClusteredMeshes( root: { … }",
|
|
3095
|
+
"situations": ["cut a virtual-geometry subtree the engine does not render itself"],
|
|
3096
|
+
"summary": "Take every clustered mesh under a root through this frame's cut, before the render. The engine already does this for the scene it renders; reach for it only to cut a subtree the engine does not render, such as one staged for a camera of your own.",
|
|
3097
|
+
"supersedes": [],
|
|
3098
|
+
"symbol": "updateClusteredMeshes"
|
|
3099
|
+
},
|
|
3100
|
+
{
|
|
3101
|
+
"aliases": [],
|
|
3102
|
+
"constraints": [
|
|
3103
|
+
"the chain is baked by the asset cook; there is no runtime generation and no runtime flag",
|
|
3104
|
+
"`assets.lod: false` opts out globally and `assets.lod.overrides` per asset, with no runtime controller installed",
|
|
3105
|
+
"only static indexed triangles are eligible; skinned, morphed, alpha-blended or authored-LOD meshes keep full detail",
|
|
3106
|
+
"the real gate is measured benefit: a level must save at least `minSaving` (20% default) of its predecessor, and a mesh that cannot is skipped, not forced"
|
|
3107
|
+
],
|
|
3108
|
+
"example": "// Nothing to call: the loader returns a mesh with the chain and the engine selects every frame.\nconst hull = await ctx.assets.model(\"hull.glb\");\nctx.scene.add(hull.scene);",
|
|
3109
|
+
"importPath": "@threenative/core",
|
|
3110
|
+
"kind": "function",
|
|
3111
|
+
"overrides": [
|
|
3112
|
+
"`assets.lod.generation.maxLevels`, `.minTriangles`, `.minTrianglesScope`, `.minSaving` and `.errorTargets` move the bake's ceiling, pre-filter, its scope, its saving rule and its error ladder; `assets.lod.runtime.maxPixelError` and `.hysteresis` move the runtime budget and coarsen band; all by project, preset or asset"
|
|
3113
|
+
],
|
|
3114
|
+
"package": "@threenative/core",
|
|
3115
|
+
"signature": "export function updateModelLods( root: { … }",
|
|
3116
|
+
"situations": [
|
|
3117
|
+
"draw a vehicle or hull built from many small meshes at distance without its full triangles",
|
|
3118
|
+
"stop a distant model from costing its authored mesh count and triangle count",
|
|
3119
|
+
"one source model, right detail by default, no hand-authored LOD files"
|
|
3120
|
+
],
|
|
3121
|
+
"summary": "Draw a model at the detail its projected geometric error earns, from a chain the asset cook baked. **This is engine-owned, and a game does not call it.** `assets.lod: {}` opts in — the default-on front door opens after qualification — the `model` pass bakes `TN_discrete_lod` into eligible models, the loader registers the reader, and the engine runs the selection every frame before it renders. Each frame the mesh picks the cheapest baked level whose measured error projects to fewer than the resolved pixel budget, taking the camera's own projection, zoom, viewport and a conservative nearest depth into account. Refinement is immediate; coarsening waits for the resolved hysteresis. A mesh with no baked chain draws its full geometry.",
|
|
3122
|
+
"supersedes": [],
|
|
3123
|
+
"symbol": "updateModelLods"
|
|
3124
|
+
},
|
|
3125
|
+
{
|
|
3126
|
+
"aliases": [],
|
|
3127
|
+
"constraints": [
|
|
3128
|
+
"off by default and expensive by construction — it does the work it is checking, twice",
|
|
3129
|
+
"it throws on the first divergence rather than logging; a validation mode that continues is one nobody reads",
|
|
3130
|
+
"it proves the frames it ran on and nothing else"
|
|
3131
|
+
],
|
|
3132
|
+
"example": "if (renderListValidationRequested()) console.log(formatValidationReport(report));",
|
|
1789
3133
|
"importPath": "@threenative/core",
|
|
1790
3134
|
"kind": "function",
|
|
1791
3135
|
"overrides": [],
|
|
1792
3136
|
"package": "@threenative/core",
|
|
1793
|
-
"signature": "export function
|
|
1794
|
-
"situations": [
|
|
1795
|
-
|
|
3137
|
+
"signature": "export function validateWorldMatrices(root: Object3D): { … }",
|
|
3138
|
+
"situations": [
|
|
3139
|
+
"prove a static freeze did not leave a stale transform on screen",
|
|
3140
|
+
"gate a scene in CI against silent transform divergence"
|
|
3141
|
+
],
|
|
3142
|
+
"summary": "Prove that nothing a cache skipped changed the picture: `TN_RENDERLIST_VALIDATE=1` recomputes every world matrix the long way, every frame, and throws on the first element that disagrees with what the frame is about to draw.",
|
|
1796
3143
|
"supersedes": [],
|
|
1797
|
-
"symbol": "
|
|
3144
|
+
"symbol": "validateWorldMatrices"
|
|
1798
3145
|
},
|
|
1799
3146
|
{
|
|
1800
3147
|
"aliases": [],
|
|
@@ -1844,7 +3191,8 @@
|
|
|
1844
3191
|
"constraints": [
|
|
1845
3192
|
"the light must be a DirectionalLight with `castShadow` and a target in the scene",
|
|
1846
3193
|
"clipExtents are half-widths in world units, finest first, strictly increasing",
|
|
1847
|
-
"call `trackCaster(object)` for movers; it enables layer `VIRTUAL_SHADOW_MOVER_LAYER` on the object and its descendants, tracking or untracking refreshes cached levels once, and subsequent mover movement refreshes only when a window moves"
|
|
3194
|
+
"call `trackCaster(object)` for movers; it enables layer `VIRTUAL_SHADOW_MOVER_LAYER` on the object and its descendants, tracking or untracking refreshes cached levels once, and subsequent mover movement refreshes only when a window moves",
|
|
3195
|
+
"call `object.layers.set(VIRTUAL_SHADOW_CASTER_LAYER)` for a mesh that exists only to cast; the level cameras already render that layer and the main camera never does"
|
|
1848
3196
|
],
|
|
1849
3197
|
"example": "const sun = new DirectionalLight(0xffffff, 3);\nsun.castShadow = true;\nsun.shadow.shadowNode = new VirtualShadowNode(sun, { clipExtents: [12, 40, 120] });",
|
|
1850
3198
|
"importPath": "@threenative/core",
|
|
@@ -1888,10 +3236,12 @@
|
|
|
1888
3236
|
"it draws nothing; the game supplies the mesh, the material and every colour",
|
|
1889
3237
|
"the material must be transparent so the frame beneath it is already drawn",
|
|
1890
3238
|
"thickness is metres, saturating at maxThickness; sky behind the surface reads deep",
|
|
1891
|
-
"one reflection is a second draw of the world
|
|
3239
|
+
"one reflection is a second draw of the world; resolutionScale is its pixels only",
|
|
3240
|
+
"on a crowded scene the mirrored pass is draw-bound: name reflection.layers or pay twice",
|
|
3241
|
+
"reflection.refreshInterval is in presented frames; 1 is every frame, and the default",
|
|
1892
3242
|
"the mirror plane is level, from level alone; do not parent target to a scaled mesh"
|
|
1893
3243
|
],
|
|
1894
|
-
"example": "const surface = new WaterSurface3D({ level: 0, maxThickness: 3, reflection: { resolutionScale: 0.5 } });\nmaterial.colorNode = mix(surface.refractionAt(offset), surface.reflectionAt(offset), fresnel);",
|
|
3244
|
+
"example": "const REFLECTED = 1; // the layer the big silhouettes sit on\nconst surface = new WaterSurface3D({ level: 0, maxThickness: 3, reflection: { resolutionScale: 0.5, layers: (1 << 0) | (1 << REFLECTED) } });\nmaterial.colorNode = mix(surface.refractionAt(offset), surface.reflectionAt(offset), fresnel);",
|
|
1895
3245
|
"importPath": "@threenative/core",
|
|
1896
3246
|
"kind": "class",
|
|
1897
3247
|
"overrides": [],
|
|
@@ -1901,7 +3251,9 @@
|
|
|
1901
3251
|
"reflect the sky and the shoreline in a lake, pond or river",
|
|
1902
3252
|
"see the bed through the water and have the shallows fade at the shore",
|
|
1903
3253
|
"know how deep the water is under a pixel without a second render pass",
|
|
1904
|
-
"stop a water surface repeating in visible bands or stripes"
|
|
3254
|
+
"stop a water surface repeating in visible bands or stripes",
|
|
3255
|
+
"keep a crowd of small actors out of the water's reflection so the frame can afford it",
|
|
3256
|
+
"stop the water reflection redrawing the whole world every frame"
|
|
1905
3257
|
],
|
|
1906
3258
|
"summary": "Give a horizontal water surface the world mirrored in it, the world beneath it, and the metres of water between them.",
|
|
1907
3259
|
"supersedes": [],
|
|
@@ -1911,9 +3263,10 @@
|
|
|
1911
3263
|
"aliases": [],
|
|
1912
3264
|
"constraints": [
|
|
1913
3265
|
"supply every wave amplitude, wavelength, direction, speed and warp value",
|
|
1914
|
-
"call setTime for the default graph clock when the game advances its own time"
|
|
3266
|
+
"call setTime for the default graph clock when the game advances its own time",
|
|
3267
|
+
"sample allocates a result and a normal vector; ask heightAt when only the height is"
|
|
1915
3268
|
],
|
|
1916
|
-
"example": "const field = new WaveField({ waves });\nconst { height, normal } = field.sample(x, z, elapsed);",
|
|
3269
|
+
"example": "const field = new WaveField({ waves });\nconst { height, normal } = field.sample(x, z, elapsed);\nconst lift = field.heightAt(x, z, elapsed);",
|
|
1917
3270
|
"importPath": "@threenative/core",
|
|
1918
3271
|
"kind": "class",
|
|
1919
3272
|
"overrides": [],
|
|
@@ -1924,7 +3277,7 @@
|
|
|
1924
3277
|
"make water move",
|
|
1925
3278
|
"find the water surface height at a point"
|
|
1926
3279
|
],
|
|
1927
|
-
"summary": "Evaluate analytic waves on CPU and displace game-owned vertices with the matching TSL graph.",
|
|
3280
|
+
"summary": "Evaluate analytic waves on CPU and displace game-owned vertices with the matching TSL graph. wanted, and the warp jacobian, every slope and both allocations are skipped",
|
|
1928
3281
|
"supersedes": [],
|
|
1929
3282
|
"symbol": "WaveField"
|
|
1930
3283
|
},
|
|
@@ -2194,7 +3547,7 @@
|
|
|
2194
3547
|
{
|
|
2195
3548
|
"aliases": ["journal objective panel", "readable HUD"],
|
|
2196
3549
|
"constraints": [
|
|
2197
|
-
"publishes
|
|
3550
|
+
"publishes once per rendered frame unless stateFlushMs selects a slower interval, and not at all with no UI listening"
|
|
2198
3551
|
],
|
|
2199
3552
|
"example": "publishUiState(bridge, game.state);",
|
|
2200
3553
|
"importPath": "@threenative/core/ui-layer",
|
|
@@ -2206,7 +3559,7 @@
|
|
|
2206
3559
|
"show score or health in a UI rendered over the game surface",
|
|
2207
3560
|
"keep a HUD in step with the game without re-rendering on the loop",
|
|
2208
3561
|
"publish game state to a HUD in another realm",
|
|
2209
|
-
"keep a web and native UI mirror on the same
|
|
3562
|
+
"keep a web and native UI mirror on the same coalesced state stream"
|
|
2210
3563
|
],
|
|
2211
3564
|
"summary": "Publish the game's state so a UI in another process can mirror it.",
|
|
2212
3565
|
"supersedes": [],
|
|
@@ -2249,6 +3602,22 @@
|
|
|
2249
3602
|
"supersedes": [],
|
|
2250
3603
|
"symbol": "subscribeUiState"
|
|
2251
3604
|
},
|
|
3605
|
+
{
|
|
3606
|
+
"aliases": [],
|
|
3607
|
+
"constraints": [
|
|
3608
|
+
"the returned view aliases the caller's buffer; writing to it mutates the source"
|
|
3609
|
+
],
|
|
3610
|
+
"example": "const records = cellPlacements(buffer, { asset: \"tree\", offset: 0, count: 120 });",
|
|
3611
|
+
"importPath": "@threenative/core/world",
|
|
3612
|
+
"kind": "function",
|
|
3613
|
+
"overrides": [],
|
|
3614
|
+
"package": "@threenative/core",
|
|
3615
|
+
"signature": "export function cellPlacements(placements: ArrayBuffer, run: IWorldRun): Float32Array { … }",
|
|
3616
|
+
"situations": ["feed one cell's instance transforms into a batch without copying"],
|
|
3617
|
+
"summary": "Borrow the run's placement records as a live view over the placement buffer.",
|
|
3618
|
+
"supersedes": [],
|
|
3619
|
+
"symbol": "cellPlacements"
|
|
3620
|
+
},
|
|
2252
3621
|
{
|
|
2253
3622
|
"aliases": [],
|
|
2254
3623
|
"constraints": [
|
|
@@ -2295,17 +3664,73 @@
|
|
|
2295
3664
|
"supersedes": [],
|
|
2296
3665
|
"symbol": "Heightfield"
|
|
2297
3666
|
},
|
|
3667
|
+
{
|
|
3668
|
+
"aliases": [],
|
|
3669
|
+
"constraints": [
|
|
3670
|
+
"the sampler reads the game's data; the framework never selects a terrain shape"
|
|
3671
|
+
],
|
|
3672
|
+
"example": "const sampleHeight = heightSamplerFromHeightmap(terrain, extent, await loadWorldHeightmap(url));",
|
|
3673
|
+
"importPath": "@threenative/core/world",
|
|
3674
|
+
"kind": "function",
|
|
3675
|
+
"overrides": [],
|
|
3676
|
+
"package": "@threenative/core",
|
|
3677
|
+
"signature": "export function heightSamplerFromHeightmap( terrain: IWorldTerrain, extent: IWorldExtent, data: Uint16Array, ): (x: number, z: number) => number { … }",
|
|
3678
|
+
"situations": [
|
|
3679
|
+
"turn an exported raw heightmap into terrain collision and rendering",
|
|
3680
|
+
"query ground height from a Blender-authored world package"
|
|
3681
|
+
],
|
|
3682
|
+
"summary": "Build a game-usable `sampleHeight` from a raw v1 heightmap. The returned function interpolates bilinearly in world units and clamps to the map edges, so it plugs straight into `Heightfield.fromSampler` and `TerrainTiles`. Height is `heightMin + v / 65535 * (heightMax - heightMin)` at vertex `(column, row)`.",
|
|
3683
|
+
"supersedes": [],
|
|
3684
|
+
"symbol": "heightSamplerFromHeightmap"
|
|
3685
|
+
},
|
|
3686
|
+
{
|
|
3687
|
+
"aliases": [],
|
|
3688
|
+
"constraints": [
|
|
3689
|
+
"the package's world.json must carry `terrain.layers.table` and `terrain.layers.splat`, written by the `export_terrain_layers.py` recipe",
|
|
3690
|
+
"WebGPU allows 16 sampled textures per stage: planes + diffuse maps + normal maps must fit"
|
|
3691
|
+
],
|
|
3692
|
+
"example": "const surface = await loadTerrainSplat({ assets: ctx.assets, url: \"world/world.json\" });\nconst world = await WorldCells.load({ assets: ctx.assets, url: \"world/world.json\", surface, follow, ring: 2 });",
|
|
3693
|
+
"importPath": "@threenative/core/world",
|
|
3694
|
+
"kind": "function",
|
|
3695
|
+
"overrides": [
|
|
3696
|
+
"every value comes from the package's table; the returned material is the game's to adjust"
|
|
3697
|
+
],
|
|
3698
|
+
"package": "@threenative/core",
|
|
3699
|
+
"signature": "export async function loadTerrainSplat(options: ILoadTerrainSplatOptions): Promise<Material> { … }",
|
|
3700
|
+
"situations": [
|
|
3701
|
+
"terrain textured by splat masks exported from Blender with the world package",
|
|
3702
|
+
"the game's terrain should match the DCC's terrain material without a second copy"
|
|
3703
|
+
],
|
|
3704
|
+
"summary": "The splat terrain surface a world package describes, for `WorldCells.load({ surface })`. Layers blend over a base by mask channels read as linear data (the masks ship raw, beside the heightmap, so no cook moves a blend threshold), with noise-broken edges and macro brightness variation. Texture sets tile in world metres on the package's ground plane (x, -z: a Z-up authoring tool's x and y), cliffs can be triplanar, and the base plus any layer that asks carries a normal map. Nothing here is a look choice: textures, tiles, tints, thresholds and noise scales all come from the package's table, which the game authors once and its DCC shares.",
|
|
3705
|
+
"supersedes": [],
|
|
3706
|
+
"symbol": "loadTerrainSplat"
|
|
3707
|
+
},
|
|
3708
|
+
{
|
|
3709
|
+
"aliases": [],
|
|
3710
|
+
"constraints": ["a non-OK response throws; bytes are byte-swapped only on a big-endian host"],
|
|
3711
|
+
"example": "const data = await loadWorldHeightmap(\"/world/terrain/heightmap.u16\");",
|
|
3712
|
+
"importPath": "@threenative/core/world",
|
|
3713
|
+
"kind": "function",
|
|
3714
|
+
"overrides": [],
|
|
3715
|
+
"package": "@threenative/core",
|
|
3716
|
+
"signature": "export async function loadWorldHeightmap(url: string): Promise<Uint16Array> { … }",
|
|
3717
|
+
"situations": ["load a world package's heightmap once before building terrain"],
|
|
3718
|
+
"summary": "Fetch a raw little-endian uint16 heightmap and expose it as samples.",
|
|
3719
|
+
"supersedes": [],
|
|
3720
|
+
"symbol": "loadWorldHeightmap"
|
|
3721
|
+
},
|
|
2298
3722
|
{
|
|
2299
3723
|
"aliases": ["stream terrain across chunks"],
|
|
2300
3724
|
"constraints": [
|
|
2301
3725
|
"sampleHeight and surface are required game choices; no landform or surface preset is installed",
|
|
2302
|
-
"residentTileBudget and residentByteBudget are hard caps; a tile that cannot fit throws"
|
|
3726
|
+
"residentTileBudget and residentByteBudget are hard caps; a tile that cannot fit throws",
|
|
3727
|
+
"seam gap, LOD pop and the rendered-vertex finiteness scan are measurements that are off by default; TN_TERRAIN_VALIDATE=1, ?tnTerrainValidate=1 or validate: true runs them, and maxSeamGap, maxVisualSeamGap and maxLodPop report undefined while they are off"
|
|
2303
3728
|
],
|
|
2304
3729
|
"example": "const tiles = new TerrainTiles({ sampleHeight, surface: gameSurface(), tileSize: 256, tileResolution: 129, residentTileBudget: 25, residentByteBudget: 32_000_000 });",
|
|
2305
3730
|
"importPath": "@threenative/core/world",
|
|
2306
3731
|
"kind": "class",
|
|
2307
3732
|
"overrides": [
|
|
2308
|
-
"tileSize, tileResolution, lodFactors, lodDistances, skirtDepth, streamRadius, and budgets"
|
|
3733
|
+
"tileSize, tileResolution, lodFactors, lodDistances, skirtDepth, streamRadius, colliderRadius, validate, and budgets"
|
|
2309
3734
|
],
|
|
2310
3735
|
"package": "@threenative/core",
|
|
2311
3736
|
"signature": "export class TerrainTiles extends Object3D implements IComputeDriven { … }",
|
|
@@ -2318,9 +3743,177 @@
|
|
|
2318
3743
|
"supersedes": [],
|
|
2319
3744
|
"symbol": "TerrainTiles"
|
|
2320
3745
|
},
|
|
3746
|
+
{
|
|
3747
|
+
"aliases": [],
|
|
3748
|
+
"constraints": ["off by default: it is the work it checks, every frame"],
|
|
3749
|
+
"example": "// Reads its own the way `renderListValidationRequested` does: a native launch sets the\n// environment variable, a browser asks with the query string, a test or harness sets the\n// global. `0` and `false` are off, so a saved URL that enabled it still says \"off\".\nconst tiles = new TerrainTiles({ ...options, validate: terrainValidationRequested() });",
|
|
3750
|
+
"importPath": "@threenative/core/world",
|
|
3751
|
+
"kind": "function",
|
|
3752
|
+
"overrides": [],
|
|
3753
|
+
"package": "@threenative/core",
|
|
3754
|
+
"signature": "export function terrainValidationRequested(): boolean { … }",
|
|
3755
|
+
"situations": [
|
|
3756
|
+
"turn terrain's per-frame seam, LOD pop and vertex checks on for one run",
|
|
3757
|
+
"assert the terrain geometry a game streams before it ships"
|
|
3758
|
+
],
|
|
3759
|
+
"summary": "Whether `TN_TERRAIN_VALIDATE` asks for terrain validation on this launch.",
|
|
3760
|
+
"supersedes": [],
|
|
3761
|
+
"symbol": "terrainValidationRequested"
|
|
3762
|
+
},
|
|
3763
|
+
{
|
|
3764
|
+
"aliases": [],
|
|
3765
|
+
"constraints": [
|
|
3766
|
+
"validation only checks structure and ranges; it never fetches the heightmap or GLBs"
|
|
3767
|
+
],
|
|
3768
|
+
"example": "const { ok, errors } = validateWorldPackage(json, { placementsByteLength: buffer.byteLength });",
|
|
3769
|
+
"importPath": "@threenative/core/world",
|
|
3770
|
+
"kind": "function",
|
|
3771
|
+
"overrides": [],
|
|
3772
|
+
"package": "@threenative/core",
|
|
3773
|
+
"signature": "export function validateWorldPackage( manifest: unknown, options: IWorldPackageValidationOptions, ): { … }",
|
|
3774
|
+
"situations": [
|
|
3775
|
+
"check a Blender-exported world package before the runtime attaches anything",
|
|
3776
|
+
"report why a world package cannot be streamed"
|
|
3777
|
+
],
|
|
3778
|
+
"summary": "Validate a `world.json` manifest against the v1 contract. Never throws on garbage input: a non-object manifest is `WORLD_MALFORMED`. Every problem is collected, so an exporter sees the complete list at once.",
|
|
3779
|
+
"supersedes": [],
|
|
3780
|
+
"symbol": "validateWorldPackage"
|
|
3781
|
+
},
|
|
3782
|
+
{
|
|
3783
|
+
"aliases": [],
|
|
3784
|
+
"constraints": [
|
|
3785
|
+
"surface is the game's; this class creates no material, colour or geometry",
|
|
3786
|
+
"budgets are hard caps that report pressure instead of over-committing",
|
|
3787
|
+
"model loads are bounded by `concurrency` (default 12) across every resident cell, not per cell",
|
|
3788
|
+
"refilters are bounded by `rebuildsPerUpdate` (default 16) per update, nearest cell first",
|
|
3789
|
+
"admission is bounded by `admissionBudgetMs` (default 2) per update across every path, plus at most one unit each for terrain and props; while props are queued terrain takes at most half, so neither starves the other, and a deferred cell keeps drawing what it has",
|
|
3790
|
+
"SkinnedMesh parts are skipped; an instanced copy would draw one rest pose",
|
|
3791
|
+
"a baked chain's switch distances are measured against `autoLod` (default 4 px of error over 60° and 1080 raster rows), because an instanced draw cannot select a level per instance; an asset with authored `lods` never consults it",
|
|
3792
|
+
"`prewarmed` resolves once every prewarmed shared batch has been drawn; a game with a loading screen waits on it, and `stats().pendingPrewarm` is the same gate as a number",
|
|
3793
|
+
"every `asset:level:part` is one InstancedMesh for the main pass, plus one caster InstancedMesh per world-grid square of `clusterSize` on the shadow caster layer, so the main pass draws one mesh per key and a shadow level submits only the squares it covers",
|
|
3794
|
+
"two definitions the asset loader resolves to one model — the same cooked `glb`, the same `lods` at the same distances, the same `maxDistance` and bounds — are one asset under the lexicographically smallest id: one model load, one set of `asset:level:part` keys, one prewarm and one refcount, released when the last cell holding any member of the group leaves the ring; `TN_WORLD_ASSET_ALIAS` reports how many of the package's assets are really distinct",
|
|
3795
|
+
"the main pass mesh draws only the squares the render camera's frustum covers — on by default, narrowed once per frame for every main batch by the engine's render-cadence dispatch, never for an orthographic camera — a batch with nothing to draw is hidden rather than submitted at `count 0`, and `TN_WORLD_MAIN_CULL` reports both every five seconds",
|
|
3796
|
+
"a loaded chunk is merged by material before it is added, so it submits one draw per material rather than one per node; a skinned, multi-material or morph-target mesh, one carrying a baked AutoLOD chain, and an instanced mesh past `chunkMergeMaxTriangles` (default 43,690 triangles) or with a shape over 2,048 triangles, all keep their own geometry; a material group crossing 131,072 vertices (4 MiB of position + normal + uv) is split into several meshes in traversal order instead of one giant upload, indexed parts keep their index, and `TN_WORLD_CHUNK_MERGE` reports what the merge did and the bytes it left",
|
|
3797
|
+
"`shadows.castDistance` is accepted and ignored (clusters replaced it); `shadows.invalidate` is called at most once a second after streamed records changed"
|
|
3798
|
+
],
|
|
3799
|
+
"example": "const world = await WorldCells.load({ url: \"/world/world.json\", surface, follow, ring: 1, budgets: { residentCells: 25, instances: 20000, bytes: 8000000 } });\nscene.add(world);\nworld.update();",
|
|
3800
|
+
"importPath": "@threenative/core/world",
|
|
3801
|
+
"kind": "class",
|
|
3802
|
+
"overrides": [
|
|
3803
|
+
"ring, budgets, terrain tile size/resolution, terrain stream and collider radius, `transparentScatter`, `clusterSize` and `shadows.invalidate`, load `concurrency`, `rebuildsPerUpdate`, `admissionBudgetMs` and the package's per-asset maxDistance"
|
|
3804
|
+
],
|
|
3805
|
+
"package": "@threenative/core",
|
|
3806
|
+
"signature": "export class WorldCells extends Group implements IComputeDriven { … }",
|
|
3807
|
+
"situations": [
|
|
3808
|
+
"stream a large Blender-authored world by cell instead of one huge GLB",
|
|
3809
|
+
"keep scattered props and hand-placed chunks resident around a moving player",
|
|
3810
|
+
"honour per-asset draw distances and hard streaming budgets without a mid-frame throw"
|
|
3811
|
+
],
|
|
3812
|
+
"summary": "Stream a Blender-authored world package by cell and keep it resident around a followed point. The class composes `TerrainTiles` for the package's heightmap, builds one `InstancedBatch` per resident cell asset run, distance level and mesh part, and loads hand-placed chunk GLBs through `loadAll` + `addInSlices`. Ring residency, per-asset `maxDistance` filtering, the per-asset `lods` levels, hard budgets and generation-tokened cancellation all live here; every geometry, material and surface still comes from the package's GLBs and the game. An asset is drawn per part, not per model: a GLB with several primitives is one `InstancedBatch` each, and a scattered part whose own material is transparent draws as an alpha cutout unless the game asks for blending, because an `InstancedMesh` cannot sort its instances. An asset whose package entry names no `lods` is drawn at the levels its own model carries a baked AutoLOD chain for: the levels an instanced draw cannot reach by itself, switched at the distance their error projects over the `autoLod` viewport. Authored `lods` win; a chain is only a fallback.",
|
|
3813
|
+
"supersedes": [],
|
|
3814
|
+
"symbol": "WorldCells"
|
|
3815
|
+
},
|
|
3816
|
+
{
|
|
3817
|
+
"aliases": [],
|
|
3818
|
+
"constraints": [
|
|
3819
|
+
"absolute paths, Windows drive letters, backslashes and any `..` segment throw MetaHumanAssetError with TN_MH_PATH_ESCAPE before the path is joined onto a root"
|
|
3820
|
+
],
|
|
3821
|
+
"example": "assertAssetPath(\"metahuman/specimen.glb\");",
|
|
3822
|
+
"importPath": "@threenative/metahuman",
|
|
3823
|
+
"kind": "function",
|
|
3824
|
+
"overrides": [],
|
|
3825
|
+
"package": "@threenative/metahuman",
|
|
3826
|
+
"requires": ["npm i @threenative/metahuman"],
|
|
3827
|
+
"signature": "export function assertAssetPath(path: string): string { … }",
|
|
3828
|
+
"situations": ["reject a MetaHuman asset path that would read outside the game's asset root"],
|
|
3829
|
+
"summary": "Keeps a caller-supplied asset path inside the asset directory.",
|
|
3830
|
+
"supersedes": [],
|
|
3831
|
+
"symbol": "assertAssetPath"
|
|
3832
|
+
},
|
|
3833
|
+
{
|
|
3834
|
+
"aliases": [],
|
|
3835
|
+
"constraints": [
|
|
3836
|
+
"the model is loaded through the game's own asset loader and cloned per instance, so",
|
|
3837
|
+
"the rig's own GUI-to-raw mapping runs; the adapter never re-derives it, and a LOD",
|
|
3838
|
+
"an undeclared control, an out-of-domain value, an undeclared LOD and any call after",
|
|
3839
|
+
"in a native host that installed the MetaHuman resident the C++ evaluator runs and"
|
|
3840
|
+
],
|
|
3841
|
+
"example": "const human = await loadMetaHuman({ assets: ctx.assets, model: \"metahuman/head.glb\",\n dna: \"metahuman/head.dna\", bindings: \"metahuman/bindings.json\" });\n human.setControls({ jawOpen: 0.4 });\n// in the scene update, after any body animation\n human.update();",
|
|
3842
|
+
"importPath": "@threenative/metahuman",
|
|
3843
|
+
"kind": "function",
|
|
3844
|
+
"overrides": [],
|
|
3845
|
+
"package": "@threenative/metahuman",
|
|
3846
|
+
"requires": ["npm i @threenative/metahuman"],
|
|
3847
|
+
"signature": "loadMetaHuman = (options: ILoadMetaHumanOptions): Promise<IMetaHuman> => createMetaHuman( { … }",
|
|
3848
|
+
"situations": [
|
|
3849
|
+
"put a MetaHuman head in a browser game without an Unreal import or a baked clip"
|
|
3850
|
+
],
|
|
3851
|
+
"summary": "Load a prepared MetaHuman head and drive its expression from the browser's WASM evaluator: declared faceboard controls in, joint deltas and morph weights out, applied to an ordinary Three.js object graph. two characters never write each other's face and nothing is disposed that `ctx.assets` still owns switch re-evaluates the current controls before the replacement mesh is shown `dispose()` throw, each with a stable `code`; nothing is clamped or coerced `diagnostics().backend` reads \"native\"; the game code does not change",
|
|
3852
|
+
"supersedes": [],
|
|
3853
|
+
"symbol": "loadMetaHuman"
|
|
3854
|
+
},
|
|
3855
|
+
{
|
|
3856
|
+
"aliases": [],
|
|
3857
|
+
"constraints": ["`code` is part of the public surface; renaming one is a breaking change"],
|
|
3858
|
+
"example": "if (error instanceof MetaHumanAssetError && error.code === \"TN_MH_HASH_MISMATCH\") refetch();",
|
|
3859
|
+
"importPath": "@threenative/metahuman",
|
|
3860
|
+
"kind": "class",
|
|
3861
|
+
"overrides": [],
|
|
3862
|
+
"package": "@threenative/metahuman",
|
|
3863
|
+
"requires": ["npm i @threenative/metahuman"],
|
|
3864
|
+
"signature": "export class MetaHumanAssetError extends Error { … }",
|
|
3865
|
+
"situations": ["branch on why a MetaHuman asset or evaluator call was refused"],
|
|
3866
|
+
"summary": "The rejection every check in this package raises, carrying a stable machine-readable code.",
|
|
3867
|
+
"supersedes": [],
|
|
3868
|
+
"symbol": "MetaHumanAssetError"
|
|
3869
|
+
},
|
|
3870
|
+
{
|
|
3871
|
+
"aliases": [],
|
|
3872
|
+
"constraints": [
|
|
3873
|
+
"the binary's SHA-256 is checked against the shipped manifest before it is",
|
|
3874
|
+
"every returned array is a copy, so no view survives a memory growth; a disposed",
|
|
3875
|
+
"a native host that installed the MetaHuman resident gets its C++ evaluator from"
|
|
3876
|
+
],
|
|
3877
|
+
"example": "const rig = await RigEvaluator.create(dna); rig.setGuiControls(gui); rig.evaluate(true); rig.jointOutputs();",
|
|
3878
|
+
"importPath": "@threenative/metahuman",
|
|
3879
|
+
"kind": "class",
|
|
3880
|
+
"overrides": [],
|
|
3881
|
+
"package": "@threenative/metahuman",
|
|
3882
|
+
"requires": ["npm i @threenative/metahuman"],
|
|
3883
|
+
"signature": "export class RigEvaluator implements IRigEvaluator { … }",
|
|
3884
|
+
"situations": [
|
|
3885
|
+
"drive a prepared MetaHuman head's expression from the browser without an Unreal import"
|
|
3886
|
+
],
|
|
3887
|
+
"summary": "One MetaHuman head rig over the checksum-verified browser WASM build of the shared OpenRigLogic ABI: faceboard GUI controls in, joint deltas, blend shape weights and animated map weights out. instantiated, and nothing is fetched from a CDN evaluator throws instead of reading freed memory `create` and the WASM is never fetched; `RigEvaluator.backend()` says which one runs",
|
|
3888
|
+
"supersedes": [],
|
|
3889
|
+
"symbol": "RigEvaluator"
|
|
3890
|
+
},
|
|
3891
|
+
{
|
|
3892
|
+
"aliases": [],
|
|
3893
|
+
"constraints": [
|
|
3894
|
+
"fails closed: a missing key, a wrong type, a hash that differs from the bytes on",
|
|
3895
|
+
"reads the rig's real names and the GLB's real node, mesh and target counts, so it"
|
|
3896
|
+
],
|
|
3897
|
+
"example": "const bindings = validateMetaHumanAssets({ bindings: parsed, dnaSha256, glbSha256, rig, gltf });",
|
|
3898
|
+
"importPath": "@threenative/metahuman",
|
|
3899
|
+
"kind": "function",
|
|
3900
|
+
"overrides": [],
|
|
3901
|
+
"package": "@threenative/metahuman",
|
|
3902
|
+
"requires": ["npm i @threenative/metahuman"],
|
|
3903
|
+
"signature": "export function validateMetaHumanAssets(input: IMetaHumanAssetInput): IMetaHumanBindings { … }",
|
|
3904
|
+
"situations": [
|
|
3905
|
+
"refuse a MetaHuman specimen whose bindings point at joints, nodes, blend shape"
|
|
3906
|
+
],
|
|
3907
|
+
"summary": "Checks one prepared specimen — bindings sidecar, DNA and GLB — against the rig it claims to drive, and returns the bindings only when every name, index, domain and hash holds up. channels, morph targets or LODs the loaded files do not contain disk and an index past the end of its array are rejections, each with a stable code cannot pass a hand-written sidecar the rig cannot drive",
|
|
3908
|
+
"supersedes": [],
|
|
3909
|
+
"symbol": "validateMetaHumanAssets"
|
|
3910
|
+
},
|
|
2321
3911
|
{
|
|
2322
3912
|
"aliases": ["pick up item"],
|
|
2323
3913
|
"constraints": ["add the area to the physics context before stepping the world"],
|
|
3914
|
+
"deprecated": [
|
|
3915
|
+
"Constructor option `world` is deprecated; pass an IPhysicsContext as `physics` instead. Area3D itself is not deprecated."
|
|
3916
|
+
],
|
|
2324
3917
|
"example": "const goal = new Area3D({ physics: ctx.physics, shape: CollisionShape3D.sphere(1.2), position: { x: 0, y: 0.5, z: -8 } });",
|
|
2325
3918
|
"importPath": "@threenative/physics",
|
|
2326
3919
|
"kind": "class",
|
|
@@ -2378,6 +3971,9 @@
|
|
|
2378
3971
|
"run jump coins goal"
|
|
2379
3972
|
],
|
|
2380
3973
|
"constraints": ["use moveAndSlide inside the physics update"],
|
|
3974
|
+
"deprecated": [
|
|
3975
|
+
"Constructor option `world` is deprecated; pass an IPhysicsContext as `physics` instead. CharacterBody3D itself is not deprecated."
|
|
3976
|
+
],
|
|
2381
3977
|
"example": "const body = new CharacterBody3D({ object: hero, physics: ctx.physics, shape: CollisionShape3D.capsule(0.5, 0.35) });",
|
|
2382
3978
|
"importPath": "@threenative/physics",
|
|
2383
3979
|
"kind": "class",
|
|
@@ -2429,6 +4025,9 @@
|
|
|
2429
4025
|
{
|
|
2430
4026
|
"aliases": [],
|
|
2431
4027
|
"constraints": ["both bodies must belong to the same physics context"],
|
|
4028
|
+
"deprecated": [
|
|
4029
|
+
"Constructor option `world` is deprecated; pass an IPhysicsContext as `physics` instead. Joint3D itself is not deprecated."
|
|
4030
|
+
],
|
|
2432
4031
|
"example": "const hinge = Joint3D.hinge({ physics: ctx.physics, bodyA: beam, bodyB: bob, anchorA: { x: 0, y: 0, z: 0 }, anchorB: { x: 0, y: 2.4, z: 0 }, axis: { x: 1, y: 0, z: 0 } });",
|
|
2433
4032
|
"importPath": "@threenative/physics",
|
|
2434
4033
|
"kind": "class",
|
|
@@ -2481,6 +4080,9 @@
|
|
|
2481
4080
|
{
|
|
2482
4081
|
"aliases": [],
|
|
2483
4082
|
"constraints": ["register rapier() in the game plugin list before using bodies"],
|
|
4083
|
+
"deprecated": [
|
|
4084
|
+
"Constructor option `world` is deprecated; pass an IPhysicsContext as `physics` instead. RigidBody3D itself is not deprecated."
|
|
4085
|
+
],
|
|
2484
4086
|
"example": "const crate = new RigidBody3D({ object, physics: ctx.physics, shape: CollisionShape3D.box(1, 1, 1), mass: 8 });",
|
|
2485
4087
|
"importPath": "@threenative/physics",
|
|
2486
4088
|
"kind": "class",
|
|
@@ -2520,6 +4122,34 @@
|
|
|
2520
4122
|
"supersedes": [],
|
|
2521
4123
|
"symbol": "softBodyCollision"
|
|
2522
4124
|
},
|
|
4125
|
+
{
|
|
4126
|
+
"aliases": [
|
|
4127
|
+
"racing car racing kart drift vehicle go-kart",
|
|
4128
|
+
"suspension wheel traction tyre grip",
|
|
4129
|
+
"accelerator pedal handbrake steering wheel",
|
|
4130
|
+
"rescue respawn flip back on track"
|
|
4131
|
+
],
|
|
4132
|
+
"constraints": [
|
|
4133
|
+
"write engineForce, brake and steering every physics update; a car with no input does not move",
|
|
4134
|
+
"suspensionStiffness is a frequency squared, not newtons per metre; 100 is a road car and 20 bottoms out"
|
|
4135
|
+
],
|
|
4136
|
+
"example": "const car = new VehicleBody3D({ object: chassis, physics: ctx.physics, shape: CollisionShape3D.box(1.6, 0.5, 3.6), mass: 900, wheels: [{ position: { x: 0.8, y: -0.15, z: -1.2 }, wheelRadius: 0.34, suspensionRestLength: 0.3, suspensionStiffness: 100, dampingCompression: 2.3, dampingRelaxation: 4.4, wheelFrictionSlip: 10.5, maxSuspensionTravel: 0.3, useAsSteering: true, useAsTraction: false }] });",
|
|
4137
|
+
"importPath": "@threenative/physics",
|
|
4138
|
+
"kind": "class",
|
|
4139
|
+
"overrides": [
|
|
4140
|
+
"a wheel ray never hits the chassis it hangs from, and it honours the chassis collision mask",
|
|
4141
|
+
"continuousCollision is on for the chassis, so a fast car cannot tunnel through a wall"
|
|
4142
|
+
],
|
|
4143
|
+
"package": "@threenative/physics",
|
|
4144
|
+
"signature": "export class VehicleBody3D extends RigidBody3D { … }",
|
|
4145
|
+
"situations": [
|
|
4146
|
+
"drive a car, truck or bike around a track",
|
|
4147
|
+
"make a vehicle roll over kerbs, brake into a corner or stop at a wall"
|
|
4148
|
+
],
|
|
4149
|
+
"summary": "Drive a car on ray-cast suspension instead of faking speed and heading.",
|
|
4150
|
+
"supersedes": [],
|
|
4151
|
+
"symbol": "VehicleBody3D"
|
|
4152
|
+
},
|
|
2523
4153
|
{
|
|
2524
4154
|
"aliases": ["close engagement range"],
|
|
2525
4155
|
"constraints": [
|
|
@@ -2672,18 +4302,15 @@
|
|
|
2672
4302
|
},
|
|
2673
4303
|
{
|
|
2674
4304
|
"aliases": [],
|
|
2675
|
-
"constraints": ["
|
|
2676
|
-
"example": "
|
|
4305
|
+
"constraints": ["returns an error; the caller must throw it"],
|
|
4306
|
+
"example": "import { invalidScenario } from \"@threenative/playtest\";\nthrow invalidScenario(\"smoke.playtest.json\", \"Expected a non-empty assertion set\");",
|
|
2677
4307
|
"importPath": "@threenative/playtest",
|
|
2678
4308
|
"kind": "function",
|
|
2679
4309
|
"overrides": [],
|
|
2680
4310
|
"package": "@threenative/playtest",
|
|
2681
4311
|
"signature": "export function invalidScenario(scenarioPath: string, message: string): PlaytestScenarioError { … }",
|
|
2682
|
-
"situations": [
|
|
2683
|
-
|
|
2684
|
-
"wait or hold a game for a deterministic number of ticks"
|
|
2685
|
-
],
|
|
2686
|
-
"summary": "Load and validate a scenario, then control its tick steps.",
|
|
4312
|
+
"situations": ["construct a validation error for malformed scenario input"],
|
|
4313
|
+
"summary": "Construct a named invalid-scenario error without loading or executing a scenario.",
|
|
2687
4314
|
"supersedes": [],
|
|
2688
4315
|
"symbol": "invalidScenario"
|
|
2689
4316
|
},
|
|
@@ -2707,8 +4334,11 @@
|
|
|
2707
4334
|
},
|
|
2708
4335
|
{
|
|
2709
4336
|
"aliases": [],
|
|
2710
|
-
"constraints": [
|
|
2711
|
-
|
|
4337
|
+
"constraints": [
|
|
4338
|
+
"unknown scenario keys and missing referenced evidence fail closed",
|
|
4339
|
+
"loading validates the fixture; use the runner to execute it"
|
|
4340
|
+
],
|
|
4341
|
+
"example": "import { loadPlaytestScenario } from \"@threenative/playtest\";\nconst scenario = await loadPlaytestScenario(process.cwd(), \"playtests/smoke.playtest.json\");",
|
|
2712
4342
|
"importPath": "@threenative/playtest",
|
|
2713
4343
|
"kind": "function",
|
|
2714
4344
|
"overrides": [],
|
|
@@ -2716,9 +4346,9 @@
|
|
|
2716
4346
|
"signature": "export async function loadPlaytestScenario(projectPath: string, scenarioPath: string): Promise<IPlaytestScenario> { … }",
|
|
2717
4347
|
"situations": [
|
|
2718
4348
|
"create a browser or device playtest scenario",
|
|
2719
|
-
"
|
|
4349
|
+
"load a deterministic tick-based playtest scenario"
|
|
2720
4350
|
],
|
|
2721
|
-
"summary": "Load and validate a scenario
|
|
4351
|
+
"summary": "Load and validate a scenario and its referenced evidence before running it.",
|
|
2722
4352
|
"supersedes": [],
|
|
2723
4353
|
"symbol": "loadPlaytestScenario"
|
|
2724
4354
|
},
|
|
@@ -2838,69 +4468,59 @@
|
|
|
2838
4468
|
},
|
|
2839
4469
|
{
|
|
2840
4470
|
"aliases": [],
|
|
2841
|
-
"constraints": [
|
|
2842
|
-
|
|
4471
|
+
"constraints": [
|
|
4472
|
+
"the diagnostic describes a failed load, not a successfully executed scenario"
|
|
4473
|
+
],
|
|
4474
|
+
"example": "import { PlaytestScenarioError } from \"@threenative/playtest\";\nconst error = new PlaytestScenarioError({ code: \"TN_PLAYTEST_SCENARIO_INVALID\", message: \"Invalid fixture\", severity: \"error\", suggestion: \"Fix the fixture\" });",
|
|
2843
4475
|
"importPath": "@threenative/playtest",
|
|
2844
4476
|
"kind": "class",
|
|
2845
4477
|
"overrides": [],
|
|
2846
4478
|
"package": "@threenative/playtest",
|
|
2847
4479
|
"signature": "export class PlaytestScenarioError extends Error { … }",
|
|
2848
|
-
"situations": [
|
|
2849
|
-
|
|
2850
|
-
"wait or hold a game for a deterministic number of ticks"
|
|
2851
|
-
],
|
|
2852
|
-
"summary": "Load and validate a scenario, then control its tick steps.",
|
|
4480
|
+
"situations": ["catch a structured playtest scenario validation error"],
|
|
4481
|
+
"summary": "Carry a structured scenario validation diagnostic as an error.",
|
|
2853
4482
|
"supersedes": [],
|
|
2854
4483
|
"symbol": "PlaytestScenarioError"
|
|
2855
4484
|
},
|
|
2856
4485
|
{
|
|
2857
4486
|
"aliases": [],
|
|
2858
|
-
"constraints": ["
|
|
2859
|
-
"example": "
|
|
4487
|
+
"constraints": ["reads the duration only; the runner advances the simulation"],
|
|
4488
|
+
"example": "import { playtestStepHoldTicks } from \"@threenative/playtest\";\nconst ticks = playtestStepHoldTicks({ kind: \"input\", press: \"KeyW\", holdTicks: 30, release: true });",
|
|
2860
4489
|
"importPath": "@threenative/playtest",
|
|
2861
4490
|
"kind": "function",
|
|
2862
4491
|
"overrides": [],
|
|
2863
4492
|
"package": "@threenative/playtest",
|
|
2864
4493
|
"signature": "export function playtestStepHoldTicks(step: IPlaytestStep, fallback = 1): number { … }",
|
|
2865
|
-
"situations": [
|
|
2866
|
-
|
|
2867
|
-
"wait or hold a game for a deterministic number of ticks"
|
|
2868
|
-
],
|
|
2869
|
-
"summary": "Load and validate a scenario, then control its tick steps.",
|
|
4494
|
+
"situations": ["read the deterministic number of ticks to hold a playtest input"],
|
|
4495
|
+
"summary": "Read a validated step's input-hold duration in simulation ticks.",
|
|
2870
4496
|
"supersedes": [],
|
|
2871
4497
|
"symbol": "playtestStepHoldTicks"
|
|
2872
4498
|
},
|
|
2873
4499
|
{
|
|
2874
4500
|
"aliases": [],
|
|
2875
|
-
"constraints": ["
|
|
2876
|
-
"example": "
|
|
4501
|
+
"constraints": ["reads the wait duration only; the runner advances the simulation"],
|
|
4502
|
+
"example": "import { playtestStepWaitTicks } from \"@threenative/playtest\";\nconst ticks = playtestStepWaitTicks({ kind: \"wait\", waitTicks: 30, release: true });",
|
|
2877
4503
|
"importPath": "@threenative/playtest",
|
|
2878
4504
|
"kind": "function",
|
|
2879
4505
|
"overrides": [],
|
|
2880
4506
|
"package": "@threenative/playtest",
|
|
2881
4507
|
"signature": "export function playtestStepWaitTicks(step: IPlaytestStep): number { … }",
|
|
2882
|
-
"situations": [
|
|
2883
|
-
|
|
2884
|
-
"wait or hold a game for a deterministic number of ticks"
|
|
2885
|
-
],
|
|
2886
|
-
"summary": "Load and validate a scenario, then control its tick steps.",
|
|
4508
|
+
"situations": ["wait or hold a game for a deterministic number of ticks"],
|
|
4509
|
+
"summary": "Read a validated step's no-input duration in simulation ticks.",
|
|
2887
4510
|
"supersedes": [],
|
|
2888
4511
|
"symbol": "playtestStepWaitTicks"
|
|
2889
4512
|
},
|
|
2890
4513
|
{
|
|
2891
4514
|
"aliases": [],
|
|
2892
|
-
"constraints": ["
|
|
2893
|
-
"example": "
|
|
4515
|
+
"constraints": ["throws an invalid-scenario error on the first unknown key"],
|
|
4516
|
+
"example": "import { rejectUnknownKeys } from \"@threenative/playtest\";\nrejectUnknownKeys({ name: \"smoke\" }, [\"name\"], \"smoke.playtest.json\", \"scenario\");",
|
|
2894
4517
|
"importPath": "@threenative/playtest",
|
|
2895
4518
|
"kind": "function",
|
|
2896
4519
|
"overrides": [],
|
|
2897
4520
|
"package": "@threenative/playtest",
|
|
2898
4521
|
"signature": "export function rejectUnknownKeys( value: Record<string, unknown>, allowedKeys: readonly string[], scenarioPath: string, objectPath: string, ): void { … }",
|
|
2899
|
-
"situations": [
|
|
2900
|
-
|
|
2901
|
-
"wait or hold a game for a deterministic number of ticks"
|
|
2902
|
-
],
|
|
2903
|
-
"summary": "Load and validate a scenario, then control its tick steps.",
|
|
4522
|
+
"situations": ["reject an unknown field while validating a scenario object"],
|
|
4523
|
+
"summary": "Reject object keys outside the explicitly allowed scenario fields.",
|
|
2904
4524
|
"supersedes": [],
|
|
2905
4525
|
"symbol": "rejectUnknownKeys"
|
|
2906
4526
|
},
|
|
@@ -2989,6 +4609,23 @@
|
|
|
2989
4609
|
"supersedes": [],
|
|
2990
4610
|
"symbol": "assertCaptureNotBlank"
|
|
2991
4611
|
},
|
|
4612
|
+
{
|
|
4613
|
+
"aliases": [],
|
|
4614
|
+
"constraints": ["the assertion throws instead of returning a false pass"],
|
|
4615
|
+
"example": "assertFrameShowsSomething(png, \"first frame\");",
|
|
4616
|
+
"importPath": "@threenative/playtest/capture",
|
|
4617
|
+
"kind": "function",
|
|
4618
|
+
"overrides": [],
|
|
4619
|
+
"package": "@threenative/playtest",
|
|
4620
|
+
"signature": "assertFrameShowsSomething = assertCaptureNotBlank",
|
|
4621
|
+
"situations": [
|
|
4622
|
+
"guard a visual playtest against a blank frame",
|
|
4623
|
+
"prove a screenshot contains more than a loading surface"
|
|
4624
|
+
],
|
|
4625
|
+
"summary": "Fail closed when a screenshot is blank or uniform.",
|
|
4626
|
+
"supersedes": [],
|
|
4627
|
+
"symbol": "assertFrameShowsSomething"
|
|
4628
|
+
},
|
|
2992
4629
|
{
|
|
2993
4630
|
"aliases": [],
|
|
2994
4631
|
"constraints": [],
|
|
@@ -3259,6 +4896,20 @@
|
|
|
3259
4896
|
"supersedes": [],
|
|
3260
4897
|
"symbol": "connectPlaytestBridgeTransport"
|
|
3261
4898
|
},
|
|
4899
|
+
{
|
|
4900
|
+
"aliases": [],
|
|
4901
|
+
"constraints": ["a private Xvfb is software, so a rate read there measures the X server"],
|
|
4902
|
+
"example": "import { decideDisplayStrategy } from \"@threenative/playtest/runner\";\nconst lane = decideDisplayStrategy({ env: process.env, platform: \"linux\" });\nif (lane.kind === \"private-xvfb\") throw new Error(\"refuse to judge this frame rate\");",
|
|
4903
|
+
"importPath": "@threenative/playtest/runner",
|
|
4904
|
+
"kind": "function",
|
|
4905
|
+
"overrides": [],
|
|
4906
|
+
"package": "@threenative/playtest",
|
|
4907
|
+
"signature": "export function decideDisplayStrategy(input: IDisplayDecisionInput): IDisplayStrategy { … }",
|
|
4908
|
+
"situations": ["judge whether a measured frame rate came from a display that can carry one"],
|
|
4909
|
+
"summary": "Decide which display a pixel-producing run paints on, the same decision the runner makes.",
|
|
4910
|
+
"supersedes": [],
|
|
4911
|
+
"symbol": "decideDisplayStrategy"
|
|
4912
|
+
},
|
|
3262
4913
|
{
|
|
3263
4914
|
"aliases": [],
|
|
3264
4915
|
"constraints": ["the mailbox lifecycle must be disposed after the run"],
|
|
@@ -3734,18 +5385,17 @@
|
|
|
3734
5385
|
},
|
|
3735
5386
|
{
|
|
3736
5387
|
"aliases": [],
|
|
3737
|
-
"constraints": [
|
|
3738
|
-
|
|
5388
|
+
"constraints": [
|
|
5389
|
+
"returns changes only; the caller dispatches them and retains the next snapshot"
|
|
5390
|
+
],
|
|
5391
|
+
"example": "import { reconcileBrowserPointers } from \"@threenative/playtest/runner\";\nconst changes = reconcileBrowserPointers(new Map(), [{ id: 1, x: 20, y: 30 }]);",
|
|
3739
5392
|
"importPath": "@threenative/playtest/runner",
|
|
3740
5393
|
"kind": "function",
|
|
3741
5394
|
"overrides": [],
|
|
3742
5395
|
"package": "@threenative/playtest",
|
|
3743
5396
|
"signature": "export function reconcileBrowserPointers( previous: ReadonlyMap<number, Required<IPlaytestPointer>>, next: readonly IPlaytestPointer[], ): IBrowserPointerChange[] { … }",
|
|
3744
|
-
"situations": [
|
|
3745
|
-
|
|
3746
|
-
"reject a SwiftShader adapter as evidence"
|
|
3747
|
-
],
|
|
3748
|
-
"summary": "Select safe Chromium arguments for WebGPU playtests.",
|
|
5397
|
+
"situations": ["reconcile pointer contacts into down move and up events"],
|
|
5398
|
+
"summary": "Compare pointer snapshots and produce down, move, and up transitions.",
|
|
3749
5399
|
"supersedes": [],
|
|
3750
5400
|
"symbol": "reconcileBrowserPointers"
|
|
3751
5401
|
},
|
|
@@ -3785,18 +5435,18 @@
|
|
|
3785
5435
|
},
|
|
3786
5436
|
{
|
|
3787
5437
|
"aliases": [],
|
|
3788
|
-
"constraints": [
|
|
3789
|
-
|
|
5438
|
+
"constraints": [
|
|
5439
|
+
"pass WEBGPU_BROWSER_ARGS explicitly; undefined selects no additional arguments",
|
|
5440
|
+
"inspect the observed adapter before claiming hardware GPU evidence"
|
|
5441
|
+
],
|
|
5442
|
+
"example": "import { resolveBrowserArguments, WEBGPU_BROWSER_ARGS } from \"@threenative/playtest/runner\";\nconst args = resolveBrowserArguments(WEBGPU_BROWSER_ARGS);",
|
|
3790
5443
|
"importPath": "@threenative/playtest/runner",
|
|
3791
5444
|
"kind": "function",
|
|
3792
5445
|
"overrides": [],
|
|
3793
5446
|
"package": "@threenative/playtest",
|
|
3794
5447
|
"signature": "export function resolveBrowserArguments(browserArgs: readonly string[] | undefined): string[] { … }",
|
|
3795
|
-
"situations": [
|
|
3796
|
-
|
|
3797
|
-
"reject a SwiftShader adapter as evidence"
|
|
3798
|
-
],
|
|
3799
|
-
"summary": "Select safe Chromium arguments for WebGPU playtests.",
|
|
5448
|
+
"situations": ["run a browser playtest with Vulkan WebGPU"],
|
|
5449
|
+
"summary": "Copy the selected Chromium arguments without silently enabling a rendering recipe.",
|
|
3800
5450
|
"supersedes": [],
|
|
3801
5451
|
"symbol": "resolveBrowserArguments"
|
|
3802
5452
|
},
|
|
@@ -3952,18 +5602,17 @@
|
|
|
3952
5602
|
},
|
|
3953
5603
|
{
|
|
3954
5604
|
"aliases": [],
|
|
3955
|
-
"constraints": [
|
|
3956
|
-
|
|
5605
|
+
"constraints": [
|
|
5606
|
+
"undefined means no software name was found, not proof of a hardware adapter"
|
|
5607
|
+
],
|
|
5608
|
+
"example": "import { softwareAdapterName } from \"@threenative/playtest/runner\";\nconst software = softwareAdapterName({ architecture: \"swiftshader\" });",
|
|
3957
5609
|
"importPath": "@threenative/playtest/runner",
|
|
3958
5610
|
"kind": "function",
|
|
3959
5611
|
"overrides": [],
|
|
3960
5612
|
"package": "@threenative/playtest",
|
|
3961
5613
|
"signature": "export function softwareAdapterName(adapter: Readonly<Record<string, string>> | undefined): string | undefined { … }",
|
|
3962
|
-
"situations": [
|
|
3963
|
-
|
|
3964
|
-
"reject a SwiftShader adapter as evidence"
|
|
3965
|
-
],
|
|
3966
|
-
"summary": "Select safe Chromium arguments for WebGPU playtests.",
|
|
5614
|
+
"situations": ["reject a SwiftShader adapter as evidence"],
|
|
5615
|
+
"summary": "Identify a software renderer in the fields reported by adapter.info.",
|
|
3967
5616
|
"supersedes": [],
|
|
3968
5617
|
"symbol": "softwareAdapterName"
|
|
3969
5618
|
},
|
|
@@ -4122,6 +5771,25 @@
|
|
|
4122
5771
|
"supersedes": [],
|
|
4123
5772
|
"symbol": "viewportRestoreCommands"
|
|
4124
5773
|
},
|
|
5774
|
+
{
|
|
5775
|
+
"aliases": [],
|
|
5776
|
+
"constraints": [
|
|
5777
|
+
"browser only; requires a scenario and runtime.startup; does not execute scenario steps or assertions",
|
|
5778
|
+
"cancellation is checked between resource acquisitions; lock waiting retains its own bounded queue policy",
|
|
5779
|
+
"use session.screenshot for nonblank PNGs; private-display captures are not FPS evidence",
|
|
5780
|
+
"use threenative-playtest trace --url <url> for slow-frame attribution instead of creating another profiler"
|
|
5781
|
+
],
|
|
5782
|
+
"example": "import { parseStandalonePlaytestArgs, withBrowserCapture } from \"@threenative/playtest/runner\";\nconst config = parseStandalonePlaytestArgs([\"--scenario\", \"playtests/smoke.playtest.json\", \"--url\", \"http://127.0.0.1:5173\"]);\nawait withBrowserCapture(config, async (session) => session.screenshot(\"ready\"));",
|
|
5783
|
+
"importPath": "@threenative/playtest/runner",
|
|
5784
|
+
"kind": "function",
|
|
5785
|
+
"overrides": [],
|
|
5786
|
+
"package": "@threenative/playtest",
|
|
5787
|
+
"signature": "export async function withBrowserCapture<T>( config: IStandalonePlaytestConfig, capture: (session: IBrowserCaptureSession) => Promise<T>, signal?: AbortSignal, ): Promise<T> { … }",
|
|
5788
|
+
"situations": ["write a custom browser capture without owning Xvfb or Chromium cleanup"],
|
|
5789
|
+
"summary": "Capture a ready ThreeNative game with the runner's display, lock, server and browser ownership.",
|
|
5790
|
+
"supersedes": [],
|
|
5791
|
+
"symbol": "withBrowserCapture"
|
|
5792
|
+
},
|
|
4125
5793
|
{
|
|
4126
5794
|
"aliases": [],
|
|
4127
5795
|
"constraints": ["missing observations and malformed assertions fail closed"],
|
|
@@ -4853,7 +6521,10 @@
|
|
|
4853
6521
|
},
|
|
4854
6522
|
{
|
|
4855
6523
|
"aliases": [],
|
|
4856
|
-
"constraints": [
|
|
6524
|
+
"constraints": [
|
|
6525
|
+
"the Geometry tab captures only on an explicit press; nothing is collected while idle",
|
|
6526
|
+
"per-object numbers are measured submissions reconciled against the frame's own pass totals, and the remainder is reported rather than hidden"
|
|
6527
|
+
],
|
|
4857
6528
|
"example": "<DebugOverlay />",
|
|
4858
6529
|
"importPath": "@threenative/ui",
|
|
4859
6530
|
"kind": "function",
|
|
@@ -4862,9 +6533,11 @@
|
|
|
4862
6533
|
"signature": "export function DebugOverlay() { … }",
|
|
4863
6534
|
"situations": [
|
|
4864
6535
|
"display runtime and playtest diagnostics in a React HUD",
|
|
4865
|
-
"inspect a game without changing its scene"
|
|
6536
|
+
"inspect a game without changing its scene",
|
|
6537
|
+
"find out which scene object is submitting the frame's triangles",
|
|
6538
|
+
"tell a cheap foreground character from an expensive distant prop"
|
|
4866
6539
|
],
|
|
4867
|
-
"summary": "Show framework diagnostics while developing a game.",
|
|
6540
|
+
"summary": "Show framework diagnostics while developing a game. Backtick opens it; the Entities tab lists registered entity fields, and the Geometry tab captures one frame and ranks the objects that submitted its triangles beside their projected size on screen.",
|
|
4868
6541
|
"supersedes": [],
|
|
4869
6542
|
"symbol": "DebugOverlay"
|
|
4870
6543
|
},
|
|
@@ -4915,7 +6588,7 @@
|
|
|
4915
6588
|
"bind a HUD component to game state",
|
|
4916
6589
|
"select a slice of state for a React panel"
|
|
4917
6590
|
],
|
|
4918
|
-
"summary": "Read
|
|
6591
|
+
"summary": "Read the game's coalesced frame snapshot from React.",
|
|
4919
6592
|
"supersedes": [],
|
|
4920
6593
|
"symbol": "useGameState"
|
|
4921
6594
|
},
|
|
@@ -4953,6 +6626,101 @@
|
|
|
4953
6626
|
"supersedes": [],
|
|
4954
6627
|
"symbol": "useUiState"
|
|
4955
6628
|
},
|
|
6629
|
+
{
|
|
6630
|
+
"symbol": "renderer.matrixWorld",
|
|
6631
|
+
"package": "@threenative/core",
|
|
6632
|
+
"importPath": "src/game.ts",
|
|
6633
|
+
"kind": "function",
|
|
6634
|
+
"signature": "renderer.matrixWorld?: \"visible\" | \"all\"",
|
|
6635
|
+
"summary": "The per-frame world-matrix walk, on by default as `\"visible\"`: a hidden subtree — a full-detail body behind a merged stand-in, a hidden LOD level, a parked or hangared model — is not recursed into, so nothing that cannot draw pays a world-matrix multiply. Every visible node is composed exactly as three does, a class that overrides `updateMatrixWorld` (`SkinnedMesh`, `Camera`) runs its own, and a hidden node that holds bones is still walked. `\"all\"` restores three's every-node walk.",
|
|
6636
|
+
"situations": [
|
|
6637
|
+
"updateMatrixWorld and multiplyMatrices are hot in a profile",
|
|
6638
|
+
"the per-frame matrix walk is slow with many hidden LOD bodies or paired full/hull models",
|
|
6639
|
+
"stop paying for matrices of models nothing can draw",
|
|
6640
|
+
"a skinned mesh or camera goes stale because its world matrix was not refreshed",
|
|
6641
|
+
"restore three's own full updateMatrixWorld walk",
|
|
6642
|
+
"compare how many scene nodes the engine walks per frame"
|
|
6643
|
+
],
|
|
6644
|
+
"example": "renderer: { matrixWorld: \"all\" } // in threenative.config.ts; omit for the visible-only default",
|
|
6645
|
+
"constraints": [
|
|
6646
|
+
"Unset is the shipping behaviour: `\"visible\"`, which does not recurse into a hidden subtree. `\"all\"` visits every node exactly as three's own `updateMatrixWorld` does.",
|
|
6647
|
+
"A game that reads a hidden object's `matrixWorld` directly must not rely on the walk reaching it: use `getWorldPosition`/`getWorldQuaternion`/`getWorldScale` or call `object.updateWorldMatrix(true, false)` first.",
|
|
6648
|
+
"The walk is the engine's either way, so three's renderer never walks the scene a second time; the count of nodes visited is reported as `matrixWorld` in every `TN_PROJECTION` window.",
|
|
6649
|
+
"Bones are never skipped: a hidden node that holds a `Bone` is walked, because a visible `SkinnedMesh` draws with its skeleton's matrices wherever the armature sits."
|
|
6650
|
+
],
|
|
6651
|
+
"overrides": [
|
|
6652
|
+
"renderer.matrixWorld: \"all\" restores three's every-node walk; the visited count still reports"
|
|
6653
|
+
],
|
|
6654
|
+
"supersedes": [],
|
|
6655
|
+
"aliases": []
|
|
6656
|
+
},
|
|
6657
|
+
{
|
|
6658
|
+
"symbol": "renderer.minimumProjectedPixels",
|
|
6659
|
+
"package": "@threenative/core",
|
|
6660
|
+
"importPath": "src/game.ts",
|
|
6661
|
+
"kind": "function",
|
|
6662
|
+
"signature": "renderer.minimumProjectedPixels?: number | false",
|
|
6663
|
+
"summary": "Do not submit what the render camera cannot resolve. On by default at a conservative 0.5 projected pixel; an object below it is skipped per render camera. Raise the number to cull more, set `false` to leave every object drawn — the count of what was skipped still reports in `TN_PROJECTION`.",
|
|
6664
|
+
"situations": [
|
|
6665
|
+
"my frame is slow with many distant objects",
|
|
6666
|
+
"draw count is high but the screen is mostly empty",
|
|
6667
|
+
"far away models, aircraft, boats or props cost draw calls but are specks",
|
|
6668
|
+
"a large roster or fleet drops the frame rate while barely visible",
|
|
6669
|
+
"stop submitting objects smaller than a pixel to the camera",
|
|
6670
|
+
"cull by how big something looks to the camera rather than how far it is from the player",
|
|
6671
|
+
"tune how aggressively distant objects are skipped",
|
|
6672
|
+
"a small object I need disappeared at range"
|
|
6673
|
+
],
|
|
6674
|
+
"example": "renderer: { minimumProjectedPixels: 2 } // in threenative.config.ts",
|
|
6675
|
+
"constraints": [
|
|
6676
|
+
"Unset is the shipping behaviour: the gate runs at 0.5 px. A game that wants the cut a shipped title tuned names 2; `false` leaves every object drawn.",
|
|
6677
|
+
"The decision reads the render camera's projection and viewport, not the player's distance — a camera far from the player still culls its own specks.",
|
|
6678
|
+
"It writes only `object.visible`, which the projection's batch key ignores; `castShadow`, `layers` and `frustumCulled` are never flipped, because that churns batch grouping.",
|
|
6679
|
+
"Shadow casters and objects attached to the render camera are never dropped on the main view alone. Exempt any other object with `alwaysRender`.",
|
|
6680
|
+
"Turning the gate off with `false` does not turn its measurement off: `TN_PROJECTION` still reports considered and skipped counts as `cull`."
|
|
6681
|
+
],
|
|
6682
|
+
"overrides": [
|
|
6683
|
+
"alwaysRender(object) keeps one object drawn whatever the render camera resolves",
|
|
6684
|
+
"renderer.minimumProjectedPixels: false leaves the scene drawn and keeps the measurement on"
|
|
6685
|
+
],
|
|
6686
|
+
"supersedes": [],
|
|
6687
|
+
"aliases": []
|
|
6688
|
+
},
|
|
6689
|
+
{
|
|
6690
|
+
"symbol": "renderer.projection",
|
|
6691
|
+
"package": "@threenative/core",
|
|
6692
|
+
"importPath": "src/game.ts",
|
|
6693
|
+
"kind": "function",
|
|
6694
|
+
"signature": "renderer.projection?: boolean | { materialChecks?: 'spread' | 'everyFrame' }",
|
|
6695
|
+
"summary": "The engine's scene-render projection — an internal mirror that collapses repeated draws, including animated skinned rigs that share a geometry and material into one palette draw per pass — on by default. Set `renderer.projection: false` to decline it, or `projection: { materialChecks: 'everyFrame' }` to keep it and pay for a per-material check on every frame.",
|
|
6696
|
+
"situations": [
|
|
6697
|
+
"a crowd of animated characters draws slowly",
|
|
6698
|
+
"many SkinnedMesh copies of one rig, each its own draw call",
|
|
6699
|
+
"the game got slower after the projection engaged",
|
|
6700
|
+
"turn off the render projection, batching, or the instanced mirror",
|
|
6701
|
+
"draw count fell but frame time did not",
|
|
6702
|
+
"a multi-second freeze when the mirror first engages",
|
|
6703
|
+
"opt out of an engine render optimizer",
|
|
6704
|
+
"thousands of props each with their own material, one colour apart",
|
|
6705
|
+
"a material edit takes a few frames to show up",
|
|
6706
|
+
"check every batched material every frame anyway"
|
|
6707
|
+
],
|
|
6708
|
+
"example": "renderer: { projection: false } // in threenative.config.ts",
|
|
6709
|
+
"constraints": [
|
|
6710
|
+
"Unset is the shipping behaviour: the projection runs. Only an explicit `false` declines it.",
|
|
6711
|
+
"An opted-out game builds no mirror and runs no eligibility scan; the authored scene is what renders, so declining costs nothing rather than being re-judged each frame.",
|
|
6712
|
+
"TN_RENDER_PROJECTION still reports the verdict, with reasonCode `disabled` rather than one of the measured declines.",
|
|
6713
|
+
"`materialChecks: 'spread'` is the default: a bounded slice of the batched materials is proved per frame instead of all of them, so a frame of 4,096 colour-only materials costs 512 checks rather than 4,096. A base-colour edit is never delayed — that write is O(1) per member.",
|
|
6714
|
+
"The price of `spread` is staleness on every other material edit: a material that gains a roughness, a map or a define still leaves its group and is drawn exactly, up to `materialCheckStaleFrames` frames later. TN_RENDER_PROJECTION reports that bound; `materialChecks: 'everyFrame'` sets it to 0 and restores the per-member, per-frame check.",
|
|
6715
|
+
"Any other `materialChecks` value throws at startup rather than falling back to a default."
|
|
6716
|
+
],
|
|
6717
|
+
"overrides": [
|
|
6718
|
+
"renderer.projection: false declines the whole mirror and costs nothing to decline",
|
|
6719
|
+
"renderer: { projection: { materialChecks: 'everyFrame' } } proves every batched material every frame instead of the default bounded slice"
|
|
6720
|
+
],
|
|
6721
|
+
"supersedes": [],
|
|
6722
|
+
"aliases": []
|
|
6723
|
+
},
|
|
4956
6724
|
{
|
|
4957
6725
|
"symbol": "WorldEnvironment",
|
|
4958
6726
|
"package": "three",
|
|
@@ -5264,7 +7032,7 @@
|
|
|
5264
7032
|
],
|
|
5265
7033
|
"notOwned": [
|
|
5266
7034
|
{
|
|
5267
|
-
"guidance": "The framework owns no save/load system. Write a save module in your project's src/ using your own plain state shape (for example, ctx.state), and read agent-docs/gameplay-recipes.md for the template recipe.",
|
|
7035
|
+
"guidance": "The framework owns no save/load system. Write a save module in your project's src/ using your own plain state shape (for example, ctx.state), and read node_modules/create-threenative/agent-docs/references/gameplay-recipes.md for the template recipe.",
|
|
5268
7036
|
"id": "save-load",
|
|
5269
7037
|
"situations": [
|
|
5270
7038
|
"persist a player's progress between sessions",
|
|
@@ -5273,12 +7041,12 @@
|
|
|
5273
7041
|
]
|
|
5274
7042
|
},
|
|
5275
7043
|
{
|
|
5276
|
-
"guidance": "The framework owns no inventory system. Write inventory state in your project's src/ with plain objects under ctx.state, and read agent-docs/gameplay-recipes.md for the template recipe.",
|
|
7044
|
+
"guidance": "The framework owns no inventory system. Write inventory state in your project's src/ with plain objects under ctx.state, and read node_modules/create-threenative/agent-docs/references/gameplay-recipes.md for the template recipe.",
|
|
5277
7045
|
"id": "inventory",
|
|
5278
7046
|
"situations": ["inventory system", "manage inventory contents"]
|
|
5279
7047
|
},
|
|
5280
7048
|
{
|
|
5281
|
-
"guidance": "The framework owns no dialogue system. Write the conversation data and state in your project's src/; render it with the template UI (starter uses src/ui/), and read agent-docs/gameplay-recipes.md.",
|
|
7049
|
+
"guidance": "The framework owns no dialogue system. Write the conversation data and state in your project's src/; render it with the template UI (starter uses src/ui/), and read node_modules/create-threenative/agent-docs/references/gameplay-recipes.md.",
|
|
5282
7050
|
"id": "dialogue",
|
|
5283
7051
|
"situations": ["NPC dialogue system", "write conversation choices for an NPC"]
|
|
5284
7052
|
},
|
|
@@ -5286,6 +7054,28 @@
|
|
|
5286
7054
|
"guidance": "The framework owns the optional authenticated transport seam at @threenative/core/net. Import connect with an HTTPS endpoint and a game-issued credential; it validates channels, message sizes, and bounded queues, but reliable overflow returns false and native qualification depends on the installed bridge (iOS remains unverified). Write authoritative replication, snapshots, prediction, interpolation, and rejoin policy in your project's src/ and server code.",
|
|
5287
7055
|
"id": "networked-multiplayer",
|
|
5288
7056
|
"situations": ["authoritative replication", "client prediction"]
|
|
7057
|
+
},
|
|
7058
|
+
{
|
|
7059
|
+
"guidance": "The framework owns no vegetation system. Copy the MIT source in examples/integrations/vegetation/src/ (https://github.com/ThreeNativeHQ/threenative/tree/develop/examples/integrations/vegetation) into your src/ and follow its README: generate seeded, vertex-budgeted EZ Tree variants offline (tree.ts, from the pinned ez-tree source, not the npm 1.1.0 bundle), write them with treeToGlb into assets/, cook them with assets.models.passes.prune: false (prune strips the uv and _WIND weight your materials read), load with ctx.assets.model and re-material by glTF material name. render/wind.ts is editable TSL wind in world metres; call expandBounds(geometry, minWorldScale) per variant geometry. assets.lod bakes bark levels and keeps alpha-masked leaves at LOD0. Worked sample: the grove game in github.com/ThreeNativeHQ/examples.",
|
|
7060
|
+
"id": "procedural-vegetation",
|
|
7061
|
+
"situations": [
|
|
7062
|
+
"procedural trees",
|
|
7063
|
+
"swaying trees",
|
|
7064
|
+
"tree foliage wind sway",
|
|
7065
|
+
"generate a forest of trees"
|
|
7066
|
+
]
|
|
7067
|
+
},
|
|
7068
|
+
{
|
|
7069
|
+
"guidance": "The framework owns no IK system. Copy the MIT source in examples/integrations/ik/src/ (https://github.com/ThreeNativeHQ/threenative/tree/develop/examples/integrations/ik) into your src/ and follow its README: it needs the closed-chain-ik/core dependency its own package.json pins, three keeps the only rendered pose, and new ConstrainedIK({root, joints: [{bone, axes, min, max}], effectors: [{bone, orientation?}], iterations, positionTolerance, rotationTolerance}) admits direct Bone hierarchies under rigid or positive-uniform-scale parents (shear, reflection, non-uniform or singular scale throws before the pose is touched). Call ik.update(targets) once per frame after AnimationPlayer/mixer and before render, with one world-space metre position per effector in order plus an optional quaternion only where the effector declared orientation: true; it never starts a loop, never moves the root and installs no bone translation or root-motion controller. Joint limits are X/Y/Z offsets relative to the animation pose supplied that call, so reapply the animation pose before solving to avoid accumulation, and the solver only rotates: bone lengths hold to under 1e-15 m. Iterations are 1-128; an unreachable goal returns converged false with finite residuals and never throws, a solver failure restores the original pose, and dispose() at scene teardown. Worked demo: the rifle grip game in examples/constrained-ik/, which plays on web, desktop native and Android.",
|
|
7070
|
+
"id": "constrained-ik",
|
|
7071
|
+
"situations": [
|
|
7072
|
+
"two-handed grip on a prop",
|
|
7073
|
+
"grip shared by two hands",
|
|
7074
|
+
"rifle grip follows sway",
|
|
7075
|
+
"inverse kinematics for a hand on a target",
|
|
7076
|
+
"hand IK pose correction",
|
|
7077
|
+
"elbow limits during an aiming pose"
|
|
7078
|
+
]
|
|
5289
7079
|
}
|
|
5290
7080
|
],
|
|
5291
7081
|
"version": 2
|