@threenative/assets 0.3.2 → 0.3.5
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/README.md +22 -0
- package/dist/apply-worker.js +9293 -6395
- package/dist/blender-gpl/fixtures/make_world_fixture.py +184 -0
- package/dist/blender-gpl/recipes/_common.py +11 -0
- package/dist/blender-gpl/recipes/decimate.py +11 -5
- package/dist/blender-gpl/recipes/export_world.py +682 -0
- package/dist/index.d.ts +533 -2
- package/dist/index.js +11322 -7087
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Document, Material } from '@gltf-transform/core';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Encodes compiled textures to KTX2/Basis so the GPU stores them compressed instead of as
|
|
3
5
|
* decoded RGBA (a 2048x2048 PNG is 16 MB in VRAM; BC7/ASTC hold it in a quarter of that).
|
|
@@ -46,6 +48,20 @@ interface IAssetBudget {
|
|
|
46
48
|
readonly uncooked: number | "none";
|
|
47
49
|
readonly total: number | "none";
|
|
48
50
|
}
|
|
51
|
+
/** Runtime decoder capabilities that decide which emitted bytes can be compressed on-device. */
|
|
52
|
+
interface IAssetRuntimeDecoderCapabilities {
|
|
53
|
+
readonly ktx2: boolean;
|
|
54
|
+
readonly meshopt: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface IModelDedupeSummary {
|
|
58
|
+
/** Distinct cooked outputs among the model sources that took part. */
|
|
59
|
+
readonly cooked: number;
|
|
60
|
+
/** Cooked bytes the duplicates would have written on their own. */
|
|
61
|
+
readonly savedBytes: number;
|
|
62
|
+
/** Model sources that took part, canonicals and duplicates alike. */
|
|
63
|
+
readonly sources: number;
|
|
64
|
+
}
|
|
49
65
|
|
|
50
66
|
type AssetFindingGrade = "fail" | "ok" | "warn";
|
|
51
67
|
interface IAssetFinding {
|
|
@@ -99,6 +115,89 @@ declare function runHealthReport(inputs: readonly IAssetHealthInput[], targets?:
|
|
|
99
115
|
/** The person-readable form printed by the compile step, one line per finding plus a summary. */
|
|
100
116
|
declare function formatHealthReport(report: IAssetHealthReport): readonly string[];
|
|
101
117
|
|
|
118
|
+
/**
|
|
119
|
+
* What the `minTriangles` pre-filter is measured against.
|
|
120
|
+
*
|
|
121
|
+
* `"primitive"` compares each primitive on its own; `"asset"` compares the whole source asset's
|
|
122
|
+
* total. The asset total is the evidence-backed default: a carrier can be 347,497 triangles split
|
|
123
|
+
* over 300 primitives of ~1,200, where an absolute per-primitive floor measures how the artist
|
|
124
|
+
* happened to split the mesh rather than whether simplification would pay. Whichever is chosen, the
|
|
125
|
+
* floor is only a cheap pre-filter — the measured saving rule below is the gate.
|
|
126
|
+
*/
|
|
127
|
+
type LodMinTrianglesScope = "primitive" | "asset";
|
|
128
|
+
|
|
129
|
+
/** Generation knobs, shared by every preset (PRD-377 §3.2); both are ceilings, not promises. */
|
|
130
|
+
interface IModelLodGenerationOptions {
|
|
131
|
+
/**
|
|
132
|
+
* Versioned increasing geometric-error targets, in normalized mesh-extent units. Each target is
|
|
133
|
+
* simplified from LOD0 independently; a target that cannot reduce is dropped. Default
|
|
134
|
+
* {@link LOD_ERROR_TARGETS}.
|
|
135
|
+
*/
|
|
136
|
+
readonly errorTargets?: readonly number[];
|
|
137
|
+
/** Levels in the chain including LOD0; integer 1–8. Default {@link DEFAULT_LOD_MAX_LEVELS}. */
|
|
138
|
+
readonly maxLevels?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Fraction of its predecessor's triangles a derived level must save to be kept. Default
|
|
141
|
+
* {@link LOD_MIN_SAVING}. This is the benefit gate: an inability to reach it is a normal skip.
|
|
142
|
+
*/
|
|
143
|
+
readonly minSaving?: number;
|
|
144
|
+
/**
|
|
145
|
+
* Cheap pre-filter floor in triangles. It is not the benefit gate — {@link minSaving} is — and
|
|
146
|
+
* only skips work where the simplifier's fixed per-call cost would dominate. Default
|
|
147
|
+
* {@link DEFAULT_LOD_MIN_TRIANGLES}.
|
|
148
|
+
*/
|
|
149
|
+
readonly minTriangles?: number;
|
|
150
|
+
/**
|
|
151
|
+
* What {@link minTriangles} is measured against. Default
|
|
152
|
+
* {@link DEFAULT_LOD_MIN_TRIANGLES_SCOPE}, which floors the whole asset rather than each
|
|
153
|
+
* primitive, so a model split into many small primitives is still measured by its total.
|
|
154
|
+
*/
|
|
155
|
+
readonly minTrianglesScope?: LodMinTrianglesScope;
|
|
156
|
+
/**
|
|
157
|
+
* Opt-in far rung that joins a mesh's same-material primitives into one draw per material group
|
|
158
|
+
* (PRD-377 §4.4 extension). **Default `false`: with no option the cook is byte-identical to
|
|
159
|
+
* today.** A join never crosses a material, never touches a skinned, morph-target or animated
|
|
160
|
+
* node, and never leaves the mesh's own node, so authored LOD0, node identity and picking are
|
|
161
|
+
* untouched; the joined geometry is an additional far rung beside LOD0.
|
|
162
|
+
*/
|
|
163
|
+
readonly join?: boolean;
|
|
164
|
+
}
|
|
165
|
+
/** Quality policy for automatic LOD; it selects the projected pixel-error budget (PRD-377 §3.2). */
|
|
166
|
+
type LodPreset = "aggressive" | "balanced" | "quality";
|
|
167
|
+
/** Screen-space selection knobs; they change what the runtime picks, never what is baked. */
|
|
168
|
+
interface IModelLodRuntimeOptions {
|
|
169
|
+
/** Projected geometric-error budget in raster pixels; positive finite. */
|
|
170
|
+
readonly maxPixelError?: number;
|
|
171
|
+
/** Fraction in `[0, 0.5)`, default 0.15, that stabilizes coarsening at a boundary. */
|
|
172
|
+
readonly hysteresis?: number;
|
|
173
|
+
}
|
|
174
|
+
/** A partial override for one asset; nested objects overlay, they never replace. */
|
|
175
|
+
interface IModelLodOverride {
|
|
176
|
+
readonly cutout?: boolean;
|
|
177
|
+
readonly enabled?: boolean;
|
|
178
|
+
readonly generation?: IModelLodGenerationOptions;
|
|
179
|
+
readonly preset?: LodPreset;
|
|
180
|
+
readonly runtime?: IModelLodRuntimeOptions;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* `assets.lod`, as the compile step reads it. Absent or `{}` resolves to enabled/balanced; `false`
|
|
184
|
+
* and `{ enabled: false }` are equivalent absolute kill switches that no per-asset override can
|
|
185
|
+
* re-enable. Resolution is per asset and happens where the asset is known (PRD-377 §3.2, §5).
|
|
186
|
+
*/
|
|
187
|
+
interface IModelLodOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Convert eligible `BLEND` foliage to alpha-tested in the cook (PRD-458 §4). Default `true`,
|
|
190
|
+
* because a needle card exported as `BLEND` is refused a chain by rule and the tree then draws
|
|
191
|
+
* LOD0 forever. `false` keeps the authored blending; per asset, `cutout` in an override entry.
|
|
192
|
+
*/
|
|
193
|
+
readonly cutout?: boolean;
|
|
194
|
+
readonly enabled?: boolean;
|
|
195
|
+
readonly generation?: IModelLodGenerationOptions;
|
|
196
|
+
readonly overrides?: Readonly<Record<string, boolean | IModelLodOverride>>;
|
|
197
|
+
readonly preset?: LodPreset;
|
|
198
|
+
readonly runtime?: IModelLodRuntimeOptions;
|
|
199
|
+
}
|
|
200
|
+
|
|
102
201
|
/**
|
|
103
202
|
* The five bands a clip's content is judged in, contiguous and gapless up to Nyquist.
|
|
104
203
|
*
|
|
@@ -192,6 +291,74 @@ interface IAudioPassOptions {
|
|
|
192
291
|
/** `"none"` drops the pass; an object configures it; absent means the defaults run. */
|
|
193
292
|
declare function parseAudioConfig(raw: unknown): IAudioPassOptions | undefined;
|
|
194
293
|
|
|
294
|
+
interface IModelCompactInstanceOptions {
|
|
295
|
+
/**
|
|
296
|
+
* Minimum number of nodes that must share one mesh before it is instanced, default 2.
|
|
297
|
+
* gltf-transform's own default is 5; the lower floor is what lets a paired prop instance.
|
|
298
|
+
*/
|
|
299
|
+
readonly min?: number;
|
|
300
|
+
}
|
|
301
|
+
interface IModelCompactOptions {
|
|
302
|
+
readonly flatten?: boolean;
|
|
303
|
+
readonly instance?: boolean | IModelCompactInstanceOptions;
|
|
304
|
+
readonly join?: boolean;
|
|
305
|
+
/** Extra exact node names to protect, on top of the regex and the structural rules. */
|
|
306
|
+
readonly protectedNames?: readonly string[];
|
|
307
|
+
/**
|
|
308
|
+
* Case-insensitive node-name regex source for nodes the game looks up or moves. Defaults to
|
|
309
|
+
* {@link DEFAULT_PROTECTED_PATTERN}; an invalid source throws rather than silently protecting nothing.
|
|
310
|
+
*/
|
|
311
|
+
readonly protectedPattern?: string;
|
|
312
|
+
}
|
|
313
|
+
/** Why a node was kept out of join/instance. */
|
|
314
|
+
type TModelProtectedRule = "allow-list" | "animation-ancestor" | "animation-target" | "regex" | "skin-joint";
|
|
315
|
+
interface IModelProtectedNode {
|
|
316
|
+
readonly name: string;
|
|
317
|
+
readonly rule: TModelProtectedRule;
|
|
318
|
+
}
|
|
319
|
+
interface IModelCompactSummary {
|
|
320
|
+
/** Whether each pass ran and how many primitives/nodes it removed. */
|
|
321
|
+
readonly flatten: {
|
|
322
|
+
readonly enabled: boolean;
|
|
323
|
+
/** Nodes reparented toward the scene root; a node left in place is not counted. */
|
|
324
|
+
readonly reparented: number;
|
|
325
|
+
};
|
|
326
|
+
readonly instance: {
|
|
327
|
+
readonly batches: number;
|
|
328
|
+
readonly enabled: boolean;
|
|
329
|
+
readonly instances: number;
|
|
330
|
+
/** Why no batch was created, when none was. */
|
|
331
|
+
readonly reason?: string;
|
|
332
|
+
};
|
|
333
|
+
readonly join: {
|
|
334
|
+
readonly enabled: boolean;
|
|
335
|
+
readonly primitivesAfter: number;
|
|
336
|
+
readonly primitivesBefore: number;
|
|
337
|
+
};
|
|
338
|
+
readonly nodesAfter: number;
|
|
339
|
+
readonly nodesBefore: number;
|
|
340
|
+
readonly primitivesAfter: number;
|
|
341
|
+
readonly primitivesBefore: number;
|
|
342
|
+
/**
|
|
343
|
+
* Named scene nodes that existed before compaction and are gone after it. Empty is the
|
|
344
|
+
* expected result of a run whose protected set covered every name the game addresses; a
|
|
345
|
+
* non-empty list is the report's warning that a `getObjectByName` can now return undefined.
|
|
346
|
+
*/
|
|
347
|
+
readonly removed: readonly string[];
|
|
348
|
+
/** Every node kept out of compaction, with the rule that protected it, sorted by name. */
|
|
349
|
+
readonly protected: readonly IModelProtectedNode[];
|
|
350
|
+
}
|
|
351
|
+
/** The compaction policy with every default resolved, so it is a stable cache key. */
|
|
352
|
+
interface IResolvedCompactOptions {
|
|
353
|
+
readonly flatten: boolean;
|
|
354
|
+
readonly instance: false | IModelCompactInstanceOptions;
|
|
355
|
+
readonly join: boolean;
|
|
356
|
+
readonly protectedNames: readonly string[];
|
|
357
|
+
readonly protectedPattern: string;
|
|
358
|
+
/** Algorithm identity, so an upgraded pass never re-serves a cache from an older build. */
|
|
359
|
+
readonly version: number;
|
|
360
|
+
}
|
|
361
|
+
|
|
195
362
|
/**
|
|
196
363
|
* Atlas integration follows the mesh → xatlas → remapped-attribute split documented by
|
|
197
364
|
* xatlas-three 0.2.1 (MIT, copyright 2022 Palash Bansal). This implementation uses xatlasjs's
|
|
@@ -267,6 +434,13 @@ interface IModelTextureOverride {
|
|
|
267
434
|
readonly slot: string;
|
|
268
435
|
}
|
|
269
436
|
interface IModelTexturesOptions {
|
|
437
|
+
/**
|
|
438
|
+
* Resolved by the compiler from the target's runtime capabilities, never by a project: the
|
|
439
|
+
* target has no KTX2 decoder, so an over-cap embedded image is box-resampled and re-emitted as
|
|
440
|
+
* a PNG instead of being encoded. Everything else — authored-byte retention under the cap,
|
|
441
|
+
* per-image deduplication, colour space — is unchanged.
|
|
442
|
+
*/
|
|
443
|
+
readonly decoderFree?: boolean;
|
|
270
444
|
/**
|
|
271
445
|
* Longest edge an embedded image may keep, default 2048. Larger images are box-resampled
|
|
272
446
|
* down preserving aspect ratio; smaller ones are never upscaled.
|
|
@@ -387,6 +561,12 @@ interface IModelSimplifyOptions {
|
|
|
387
561
|
}
|
|
388
562
|
interface IModelPassOptions {
|
|
389
563
|
readonly passes?: IModelPassesOptions;
|
|
564
|
+
/**
|
|
565
|
+
* Lossless scene-graph compaction (flatten → instance → join) against one protected-node set.
|
|
566
|
+
* Absent means on with defaults; `false` is the kill switch; an object configures the
|
|
567
|
+
* sub-passes, the protected-name allow-list and the name regex. It never decimates geometry.
|
|
568
|
+
*/
|
|
569
|
+
readonly compact?: boolean | IModelCompactOptions;
|
|
390
570
|
/** Native WebGPU rejects interleaved vertex attributes; mobile output uses separate buffers. */
|
|
391
571
|
readonly vertexLayout?: "interleaved" | "separate";
|
|
392
572
|
/** Preserve generated TEXCOORD_1 data that is consumed by a runtime-attached lightmap. */
|
|
@@ -414,6 +594,13 @@ interface IModelPassOptions {
|
|
|
414
594
|
* would hide that.
|
|
415
595
|
*/
|
|
416
596
|
readonly virtual?: IModelVirtualOptions | "none";
|
|
597
|
+
/**
|
|
598
|
+
* Automatic discrete LOD (`assets.lod`). Absent means no automatic generation in this phase;
|
|
599
|
+
* `false` / `"none"` is the kill switch, and an object carries the resolved policy. Per-asset
|
|
600
|
+
* overrides key off the canonical source asset path, which is why the pass resolves them here
|
|
601
|
+
* rather than at registry construction.
|
|
602
|
+
*/
|
|
603
|
+
readonly lod?: boolean | IModelLodOptions | "none";
|
|
417
604
|
}
|
|
418
605
|
|
|
419
606
|
/**
|
|
@@ -475,6 +662,63 @@ interface ISimplifyRow {
|
|
|
475
662
|
readonly trianglesAfter: number;
|
|
476
663
|
readonly trianglesBefore: number;
|
|
477
664
|
}
|
|
665
|
+
/** What automatic discrete generation produced for one model (PRD-377 §4.3). */
|
|
666
|
+
/** One material group inside an opt-in joined far rung: what it collapsed and how much it draws. */
|
|
667
|
+
interface ILodJoinedGroupRow {
|
|
668
|
+
readonly material: string;
|
|
669
|
+
readonly primitives: number;
|
|
670
|
+
readonly triangles: number;
|
|
671
|
+
}
|
|
672
|
+
/** The opt-in joined far rung as the manifest carries it: draws and the primitives collapsed. */
|
|
673
|
+
interface ILodJoinedRow {
|
|
674
|
+
readonly draws: number;
|
|
675
|
+
readonly groups: readonly ILodJoinedGroupRow[];
|
|
676
|
+
readonly primitives: number;
|
|
677
|
+
readonly triangles: number;
|
|
678
|
+
readonly trianglesBefore: number;
|
|
679
|
+
}
|
|
680
|
+
interface ILodRow {
|
|
681
|
+
/** Bytes the derived index buffers add, before compression. */
|
|
682
|
+
readonly byteOverhead: number;
|
|
683
|
+
/**
|
|
684
|
+
* The foliage cutout conversion the cook ran before generating (PRD-458 §4): the `BLEND`
|
|
685
|
+
* materials that became alpha-tested, and the ones that kept blending and why.
|
|
686
|
+
*/
|
|
687
|
+
readonly cutout?: {
|
|
688
|
+
readonly converted: readonly string[];
|
|
689
|
+
readonly kept: readonly {
|
|
690
|
+
readonly name: string;
|
|
691
|
+
readonly reason: string;
|
|
692
|
+
}[];
|
|
693
|
+
};
|
|
694
|
+
/** Migration/legacy notes the resolver raised, by code. */
|
|
695
|
+
readonly diagnostics: readonly string[];
|
|
696
|
+
/** The resolved increasing geometric-error targets, as the bake consumed them. */
|
|
697
|
+
readonly errorTargets: readonly number[];
|
|
698
|
+
readonly fingerprint: string;
|
|
699
|
+
readonly generated: number;
|
|
700
|
+
/** Whether the opt-in join was requested; {@link joined} is present only when it did something. */
|
|
701
|
+
readonly join: boolean;
|
|
702
|
+
/** The opt-in joined far rung's honesty record, when one was produced. */
|
|
703
|
+
readonly joined?: ILodJoinedRow;
|
|
704
|
+
readonly levels: number;
|
|
705
|
+
readonly maxLevels: number;
|
|
706
|
+
/** Fraction of its predecessor's triangles a derived level had to save. */
|
|
707
|
+
readonly minSaving: number;
|
|
708
|
+
readonly minTriangles: number;
|
|
709
|
+
/** What `minTriangles` measured against: `"primitive"` or `"asset"`. */
|
|
710
|
+
readonly minTrianglesScope: string;
|
|
711
|
+
readonly preset: string;
|
|
712
|
+
readonly reasons: readonly string[];
|
|
713
|
+
/** The runtime selection budget this asset ships with; the loader reads it from the manifest. */
|
|
714
|
+
readonly runtime: {
|
|
715
|
+
readonly hysteresis: number;
|
|
716
|
+
readonly maxPixelError: number;
|
|
717
|
+
};
|
|
718
|
+
readonly skipped: number;
|
|
719
|
+
readonly trianglesAfter: number;
|
|
720
|
+
readonly trianglesBefore: number;
|
|
721
|
+
}
|
|
478
722
|
/** Whether the pass executed this bake or every input that reached it was compile-cache-served. */
|
|
479
723
|
type PassCostStatus = "cached" | "ran";
|
|
480
724
|
/** What one input cost the pass that processed it, as the pass driver measured it. */
|
|
@@ -581,6 +825,19 @@ interface IAudioRow {
|
|
|
581
825
|
*/
|
|
582
826
|
declare function formatAudioSizes(rows: readonly IAudioRow[]): readonly string[];
|
|
583
827
|
/** One compiled model plus a total, before against after the optimization pass. */
|
|
828
|
+
/** The cook's material merge (PRD-458 §5): what collapsed, and the counts either side of it. */
|
|
829
|
+
interface IMaterialsRow {
|
|
830
|
+
/** Distinct signatures — the count a draw call is bound by. */
|
|
831
|
+
readonly distinct: {
|
|
832
|
+
readonly after: number;
|
|
833
|
+
readonly before: number;
|
|
834
|
+
};
|
|
835
|
+
readonly materials: {
|
|
836
|
+
readonly after: number;
|
|
837
|
+
readonly before: number;
|
|
838
|
+
};
|
|
839
|
+
readonly merged: readonly string[];
|
|
840
|
+
}
|
|
584
841
|
interface IModelSizeRow {
|
|
585
842
|
readonly after: number;
|
|
586
843
|
readonly before: number;
|
|
@@ -601,8 +858,14 @@ interface IModelSizeRow {
|
|
|
601
858
|
};
|
|
602
859
|
/** LOD simplification, when it was configured for this model. */
|
|
603
860
|
readonly simplify?: ISimplifyRow;
|
|
861
|
+
/** Automatic discrete LOD generation (PRD-377), when the effective policy ran. */
|
|
862
|
+
readonly lod?: ILodRow;
|
|
863
|
+
/** Lossless scene-graph compaction (PRD-443), when it ran. */
|
|
864
|
+
readonly compact?: IModelCompactSummary;
|
|
604
865
|
/** The cluster-DAG bake, when it was configured for this model. */
|
|
605
866
|
readonly virtual?: IVirtualRow;
|
|
867
|
+
/** The material merge the cook ran unconditionally, before any other geometry stage. */
|
|
868
|
+
readonly materials?: IMaterialsRow;
|
|
606
869
|
/** Triangle count of the compiled output, recorded in the manifest. */
|
|
607
870
|
readonly triangles?: number;
|
|
608
871
|
}
|
|
@@ -666,6 +929,13 @@ interface IAssetPass {
|
|
|
666
929
|
/** True when this pass's emitted output requires a runtime decoder unavailable on mobile. */
|
|
667
930
|
readonly needsRuntimeDecoder?: boolean;
|
|
668
931
|
readonly name: string;
|
|
932
|
+
/**
|
|
933
|
+
* Input kinds whose bytes this pass can change. The compile hashes only the passes that can
|
|
934
|
+
* change an asset into that asset's digest, so a model-only option edit does not rename every
|
|
935
|
+
* texture. `undefined` (the default for a caller-supplied pass) means any kind, which stays
|
|
936
|
+
* conservative: an unknown pass can change every asset.
|
|
937
|
+
*/
|
|
938
|
+
readonly appliesTo?: readonly AssetKind[];
|
|
669
939
|
/**
|
|
670
940
|
* JSON-serializable snapshot of every option that changes this pass's output. Part of the
|
|
671
941
|
* compile cache key: without it, editing a config value (texture quality, quantize bits, an
|
|
@@ -687,7 +957,7 @@ interface IAssetSourceConfig {
|
|
|
687
957
|
readonly audio?: IAudioConfig | "none";
|
|
688
958
|
/**
|
|
689
959
|
* The bound on how many workers a bake may use. A direct `concurrency` option overrides it;
|
|
690
|
-
* absent means the driver's default (
|
|
960
|
+
* absent means the driver's default (cores - 1, bounded by ~2 GB of memory a worker, at most 12). CI boxes and laptops differ, and
|
|
691
961
|
* a 6.8 GB pack does not get to decide the machine's fate.
|
|
692
962
|
*/
|
|
693
963
|
readonly concurrency?: number;
|
|
@@ -696,6 +966,12 @@ interface IAssetSourceConfig {
|
|
|
696
966
|
* exactly as committed. Absent means optimization runs with defaults.
|
|
697
967
|
*/
|
|
698
968
|
readonly models?: IModelsConfig | "none";
|
|
969
|
+
/**
|
|
970
|
+
* Automatic discrete LOD (`assets.lod`): a boolean kill switch/enable, an object carrying the
|
|
971
|
+
* resolved generation policy, or `"none"`. Absent means this phase generates nothing; the
|
|
972
|
+
* default-on decision is a later phase (PRD-377 §8).
|
|
973
|
+
*/
|
|
974
|
+
readonly lod?: boolean | IModelLodOptions | "none";
|
|
699
975
|
readonly output?: string;
|
|
700
976
|
readonly source?: string;
|
|
701
977
|
readonly targets?: IAssetTargets;
|
|
@@ -706,6 +982,11 @@ interface IAssetSourceConfig {
|
|
|
706
982
|
readonly textures?: ITexturesConfig | "none";
|
|
707
983
|
}
|
|
708
984
|
interface IModelsConfig {
|
|
985
|
+
/**
|
|
986
|
+
* Lossless scene-graph compaction (flatten → instance → join) against one protected-node set.
|
|
987
|
+
* Default true ({@link IModelCompactOptions}); `false` ships the scene graph as authored.
|
|
988
|
+
*/
|
|
989
|
+
readonly compact?: boolean | IModelCompactOptions;
|
|
709
990
|
/** Generate standard TEXCOORD_1 lightmap UVs. Absent means no lightmap pass. */
|
|
710
991
|
readonly lightmap?: ILightmapPassOptions;
|
|
711
992
|
readonly passes?: IModelPassesOptions;
|
|
@@ -780,6 +1061,16 @@ interface IAssetCompileOptions {
|
|
|
780
1061
|
* build uncompressed too. The build knows its target; it decides.
|
|
781
1062
|
*/
|
|
782
1063
|
readonly platform?: "android" | "desktop" | "ios" | "web";
|
|
1064
|
+
/**
|
|
1065
|
+
* What the runtime that will execute this bake can decode, when the caller has probed it.
|
|
1066
|
+
*
|
|
1067
|
+
* `platform` is a proxy: it answers "android and iOS have no WebAssembly" for the targets this
|
|
1068
|
+
* repository ships. A desktop host can also lack WebAssembly — the Linux arm64 lane builds
|
|
1069
|
+
* QuickJS over wgpu-native — and then the same meshopt/KTX2 passes it would keep produce an
|
|
1070
|
+
* asset nothing can decode. `threenative build` reads the engine from the runtime binary it will
|
|
1071
|
+
* package and passes the real capability here; absent, the platform-derived default stands.
|
|
1072
|
+
*/
|
|
1073
|
+
readonly runtimeDecoders?: IAssetRuntimeDecoderCapabilities;
|
|
783
1074
|
readonly source?: string;
|
|
784
1075
|
/** Overrides resolution of three's Basis transcoder for the copy into the output root. */
|
|
785
1076
|
readonly transcoder?: IBasisTranscoder;
|
|
@@ -801,6 +1092,11 @@ interface IAssetCompileResult {
|
|
|
801
1092
|
readonly skippedCompression: readonly ISkippedReportRow[];
|
|
802
1093
|
readonly receipt?: IBakeReceipt;
|
|
803
1094
|
readonly report?: IAssetHealthReport;
|
|
1095
|
+
/**
|
|
1096
|
+
* What the model cook saved by cooking content-identical sources once, when this project has
|
|
1097
|
+
* any model source. `cooked` is the number of distinct outputs those sources produced.
|
|
1098
|
+
*/
|
|
1099
|
+
readonly dedupe?: IModelDedupeSummary;
|
|
804
1100
|
readonly skipped: number;
|
|
805
1101
|
readonly written: number;
|
|
806
1102
|
}
|
|
@@ -928,4 +1224,239 @@ interface IAssetWatchHandle {
|
|
|
928
1224
|
}
|
|
929
1225
|
declare function watchAssets(options?: IAssetWatchOptions): IAssetWatchHandle;
|
|
930
1226
|
|
|
931
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* A deterministic texture atlas packer.
|
|
1229
|
+
*
|
|
1230
|
+
* The reference game's carrier is 95 meshes over 76 materials and **73 distinct texture sources** —
|
|
1231
|
+
* one texture per material, because an imported model carries a material per part. A per-material
|
|
1232
|
+
* merge over content shaped like that has nothing to collapse: it found 274 buckets holding 213
|
|
1233
|
+
* singletons and moved no frame time. That null result is the evidence for this file. Materials
|
|
1234
|
+
* cannot be deduplicated while each one owns a private texture, so the atlas is the prerequisite
|
|
1235
|
+
* and everything downstream — dedupe, merge, instancing — is priced against it.
|
|
1236
|
+
*
|
|
1237
|
+
* Two properties, both load-bearing:
|
|
1238
|
+
*
|
|
1239
|
+
* - **Deterministic.** Two runs over the same inputs place every source at the same pixel. The
|
|
1240
|
+
* order is derived from the sources themselves (taller first, then wider, then the key), never
|
|
1241
|
+
* from map iteration or filesystem order, because a build that packs differently on a rebuild
|
|
1242
|
+
* invalidates every downstream cache and makes a screenshot-parity gate meaningless.
|
|
1243
|
+
* - **It refuses what it cannot do.** A source that tiles is excluded and reported, never packed
|
|
1244
|
+
* and silently clamped: a repeating surface whose UVs leave `[0, 1]` reads neighbouring pages
|
|
1245
|
+
* once it shares one, which is a visible bug that looks like corruption rather than like a
|
|
1246
|
+
* packing decision.
|
|
1247
|
+
*
|
|
1248
|
+
* Shelf packing, not a perfect bin: sorted by height it wastes a few percent of a page against an
|
|
1249
|
+
* optimal solver nobody can read, and the cost of a wasted page is bytes, while the cost of a
|
|
1250
|
+
* wrong one is a visual bug. `ponytail: shelf packer, revisit only if page count is measured to
|
|
1251
|
+
* matter.`
|
|
1252
|
+
*/
|
|
1253
|
+
/** One image offered to the packer. */
|
|
1254
|
+
interface IAtlasSource {
|
|
1255
|
+
/** Stable identity — the content hash or the texture's URI. Ties in the sort break on this. */
|
|
1256
|
+
readonly key: string;
|
|
1257
|
+
readonly width: number;
|
|
1258
|
+
readonly height: number;
|
|
1259
|
+
/**
|
|
1260
|
+
* Whether the material samples this source outside `[0, 1]`. A tiling source cannot share a
|
|
1261
|
+
* page, and saying so is the source's job, not a guess the packer makes from pixels.
|
|
1262
|
+
*/
|
|
1263
|
+
readonly tiles?: boolean;
|
|
1264
|
+
}
|
|
1265
|
+
/** Where one source landed. */
|
|
1266
|
+
interface IAtlasPlacement {
|
|
1267
|
+
readonly key: string;
|
|
1268
|
+
readonly page: number;
|
|
1269
|
+
readonly x: number;
|
|
1270
|
+
readonly y: number;
|
|
1271
|
+
readonly width: number;
|
|
1272
|
+
readonly height: number;
|
|
1273
|
+
}
|
|
1274
|
+
/** The UV transform for one source: `uv' = uv * scale + offset`. */
|
|
1275
|
+
interface IAtlasTransform {
|
|
1276
|
+
readonly page: number;
|
|
1277
|
+
readonly offsetX: number;
|
|
1278
|
+
readonly offsetY: number;
|
|
1279
|
+
readonly scaleX: number;
|
|
1280
|
+
readonly scaleY: number;
|
|
1281
|
+
}
|
|
1282
|
+
/** Why a source was left out. Every exclusion is reported; none is silent. */
|
|
1283
|
+
type AtlasExclusionReason = "tiles" | "too-large";
|
|
1284
|
+
interface IAtlasExclusion {
|
|
1285
|
+
readonly key: string;
|
|
1286
|
+
readonly reason: AtlasExclusionReason;
|
|
1287
|
+
}
|
|
1288
|
+
interface IAtlasPage {
|
|
1289
|
+
readonly index: number;
|
|
1290
|
+
readonly width: number;
|
|
1291
|
+
readonly height: number;
|
|
1292
|
+
readonly placements: readonly IAtlasPlacement[];
|
|
1293
|
+
}
|
|
1294
|
+
interface IAtlasResult {
|
|
1295
|
+
readonly pages: readonly IAtlasPage[];
|
|
1296
|
+
/** Keyed by source key, in the packer's own deterministic order. */
|
|
1297
|
+
readonly transforms: ReadonlyMap<string, IAtlasTransform>;
|
|
1298
|
+
readonly excluded: readonly IAtlasExclusion[];
|
|
1299
|
+
}
|
|
1300
|
+
interface IAtlasOptions {
|
|
1301
|
+
/** Page edge in pixels. Default 4096. */
|
|
1302
|
+
readonly pageSize?: number;
|
|
1303
|
+
/**
|
|
1304
|
+
* Transparent pixels kept between neighbours, so a mip level or a bilinear tap cannot reach the
|
|
1305
|
+
* next source. Default 4 — two levels of mip bleed at the sizes this packs.
|
|
1306
|
+
*/
|
|
1307
|
+
readonly padding?: number;
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Packs sources into pages and answers the UV transform for each.
|
|
1311
|
+
*
|
|
1312
|
+
* Fails closed on a malformed source: a zero or non-integer dimension is a decoder that did not
|
|
1313
|
+
* decode, and packing it would put a divide-by-zero in every UV it rewrites.
|
|
1314
|
+
*/
|
|
1315
|
+
declare function packAtlas(sources: readonly IAtlasSource[], options?: IAtlasOptions): IAtlasResult;
|
|
1316
|
+
/**
|
|
1317
|
+
* The manifest a build writes beside the pages.
|
|
1318
|
+
*
|
|
1319
|
+
* Sorted by key so two runs produce byte-identical JSON — the same reason the packing order is
|
|
1320
|
+
* derived rather than inherited.
|
|
1321
|
+
*/
|
|
1322
|
+
declare function atlasManifest(result: IAtlasResult): string;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Moving a mesh's UVs onto its atlas page, and deciding which meshes may not go.
|
|
1326
|
+
*
|
|
1327
|
+
* The rewrite itself is two multiplies and two adds per vertex. The decision in front of it is the
|
|
1328
|
+
* part that matters: a surface that samples outside `[0, 1]` tiles, and a tiling surface on a
|
|
1329
|
+
* shared page reads its neighbours. That is a visible bug that looks like corruption, so the
|
|
1330
|
+
* policy here is to detect it from the geometry's own UVs and from the sampler's wrap mode, refuse
|
|
1331
|
+
* to pack the source, and report it — never to clamp, and never to guess from the image.
|
|
1332
|
+
*/
|
|
1333
|
+
|
|
1334
|
+
/** glTF sampler wrap modes; `10497` is REPEAT and `33648` is MIRRORED_REPEAT. */
|
|
1335
|
+
declare const WRAP_REPEAT = 10497;
|
|
1336
|
+
declare const WRAP_MIRRORED_REPEAT = 33648;
|
|
1337
|
+
declare const WRAP_CLAMP_TO_EDGE = 33071;
|
|
1338
|
+
/** Whether this UV set samples outside the unit square, and therefore tiles. */
|
|
1339
|
+
declare function uvsTile(uv: ArrayLike<number>): boolean;
|
|
1340
|
+
/** Whether the sampler asks to repeat. A wrap mode the packer does not know is treated as tiling. */
|
|
1341
|
+
declare function wrapTiles(wrapS: number | undefined, wrapT: number | undefined): boolean;
|
|
1342
|
+
/**
|
|
1343
|
+
* Rewrites a UV buffer in place onto its atlas page.
|
|
1344
|
+
*
|
|
1345
|
+
* In place because the caller owns a freshly-decoded accessor and a second buffer per mesh is a
|
|
1346
|
+
* copy of the whole scene's UVs for nothing. Throws on an odd-length buffer rather than rewriting
|
|
1347
|
+
* half a coordinate.
|
|
1348
|
+
*/
|
|
1349
|
+
declare function rewriteUvs(uv: Float32Array, transform: IAtlasTransform): Float32Array;
|
|
1350
|
+
/**
|
|
1351
|
+
* The texel a rewritten UV resolves to, in the source image's own pixel space.
|
|
1352
|
+
*
|
|
1353
|
+
* This is the inverse of `rewriteUvs` composed with the page geometry, and it exists so a test can
|
|
1354
|
+
* assert the round trip rather than assert the arithmetic against itself: sample a texel, rewrite,
|
|
1355
|
+
* resolve, and land within a texel of where you started.
|
|
1356
|
+
*/
|
|
1357
|
+
declare function resolveSourceTexel(atlasUv: readonly [number, number], transform: IAtlasTransform, source: {
|
|
1358
|
+
readonly width: number;
|
|
1359
|
+
readonly height: number;
|
|
1360
|
+
}): {
|
|
1361
|
+
x: number;
|
|
1362
|
+
y: number;
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Collapsing materials that became identical once their textures shared a page.
|
|
1367
|
+
*
|
|
1368
|
+
* This is the step the atlas exists to enable, and it is also the step that falsifies the atlas.
|
|
1369
|
+
* The reference game's merge found 274 buckets holding 213 singletons because every material owned
|
|
1370
|
+
* a private texture; if the singleton count does not fall once those textures share pages, the
|
|
1371
|
+
* atlas hypothesis is wrong and everything priced on it has to be repriced. So the signature here
|
|
1372
|
+
* is deliberately conservative: two materials collapse only when every field that can change a
|
|
1373
|
+
* pixel agrees, and anything the signature does not know about keeps them apart.
|
|
1374
|
+
*/
|
|
1375
|
+
/** The material state a signature is taken over. Structural, so a glTF or a three material fits. */
|
|
1376
|
+
interface IMaterialState {
|
|
1377
|
+
readonly name?: string;
|
|
1378
|
+
/** Texture identity per slot *after* atlasing — the page, not the original image. */
|
|
1379
|
+
readonly textures: Readonly<Record<string, string | undefined>>;
|
|
1380
|
+
/** Scalar and vector uniforms: base colour, metallic, roughness, emissive, alpha cutoff, … */
|
|
1381
|
+
readonly uniforms: Readonly<Record<string, number | readonly number[] | string | boolean | undefined>>;
|
|
1382
|
+
/** Anything that changes how the surface is drawn rather than what it samples. */
|
|
1383
|
+
readonly flags: Readonly<Record<string, number | string | boolean | undefined>>;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* A stable signature over everything that decides how this material looks.
|
|
1387
|
+
*
|
|
1388
|
+
* The name is deliberately **not** in it: an imported pack names a material per part, and letting
|
|
1389
|
+
* the name separate them is exactly the property that made 213 singletons. Every other field is,
|
|
1390
|
+
* including ones this pipeline does not understand, because an unknown field that differs is a
|
|
1391
|
+
* reason not to collapse.
|
|
1392
|
+
*/
|
|
1393
|
+
declare function materialSignature(material: IMaterialState): string;
|
|
1394
|
+
/** One group of materials that became the same material. */
|
|
1395
|
+
interface IMaterialBucket {
|
|
1396
|
+
readonly signature: string;
|
|
1397
|
+
/** Names in input order, so a report can say which materials collapsed into which. */
|
|
1398
|
+
readonly members: readonly string[];
|
|
1399
|
+
}
|
|
1400
|
+
interface IDedupeCensus {
|
|
1401
|
+
/** Distinct signatures — the material count after deduplication. */
|
|
1402
|
+
readonly buckets: number;
|
|
1403
|
+
/** Buckets holding exactly one material: the count the atlas has to move. */
|
|
1404
|
+
readonly singletons: number;
|
|
1405
|
+
/** Materials in. */
|
|
1406
|
+
readonly materials: number;
|
|
1407
|
+
}
|
|
1408
|
+
/** Groups materials by signature, in first-seen order so two runs report the same thing. */
|
|
1409
|
+
declare function dedupeMaterials(materials: readonly IMaterialState[]): {
|
|
1410
|
+
buckets: readonly IMaterialBucket[];
|
|
1411
|
+
census: IDedupeCensus;
|
|
1412
|
+
};
|
|
1413
|
+
/**
|
|
1414
|
+
* The same materials with every texture slot repointed at its atlas page.
|
|
1415
|
+
*
|
|
1416
|
+
* A slot whose source was excluded from the atlas keeps its original texture, which is what makes
|
|
1417
|
+
* the census honest: those materials stay singletons, and the report says how many.
|
|
1418
|
+
*/
|
|
1419
|
+
declare function withAtlasTextures(materials: readonly IMaterialState[], pageOf: (texture: string) => string | undefined): IMaterialState[];
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* The bucket census: what a merge could collapse, before and after an atlas.
|
|
1423
|
+
*
|
|
1424
|
+
* "Fewer objects" was the obvious lever on the reference game and it recovered nothing — a runtime
|
|
1425
|
+
* per-material merge replaced 246 of 1,561 meshes and moved no frame time, because 274 buckets
|
|
1426
|
+
* held 213 singletons. The census is the number to read before promising a merge, and this is the
|
|
1427
|
+
* thing that reads it: it walks a glTF document's materials, takes the same signature the dedupe
|
|
1428
|
+
* takes, and reports the buckets twice — once as authored, once with every atlasable texture
|
|
1429
|
+
* repointed at a page.
|
|
1430
|
+
*
|
|
1431
|
+
* The second number is the falsification point for the whole atlas argument. If singletons do not
|
|
1432
|
+
* fall, the atlas is not the lever and nothing downstream of it should be built.
|
|
1433
|
+
*/
|
|
1434
|
+
|
|
1435
|
+
/** One model's census. */
|
|
1436
|
+
interface IContentCensus {
|
|
1437
|
+
readonly model: string;
|
|
1438
|
+
readonly meshes: number;
|
|
1439
|
+
readonly primitives: number;
|
|
1440
|
+
readonly materials: number;
|
|
1441
|
+
/** Distinct texture images the materials reference. */
|
|
1442
|
+
readonly textureSources: number;
|
|
1443
|
+
/** Sources the packer refused, with the reason — tiling surfaces, mostly. */
|
|
1444
|
+
readonly excluded: number;
|
|
1445
|
+
readonly atlasPages: number;
|
|
1446
|
+
readonly before: IDedupeCensus;
|
|
1447
|
+
readonly after: IDedupeCensus;
|
|
1448
|
+
}
|
|
1449
|
+
/** Reads a material into the state the signature is taken over. */
|
|
1450
|
+
declare function materialStateOf(material: Material): IMaterialState;
|
|
1451
|
+
/**
|
|
1452
|
+
* Censuses one document: what its materials are now, and what they would be once their textures
|
|
1453
|
+
* shared atlas pages.
|
|
1454
|
+
*
|
|
1455
|
+
* A texture whose size the document does not state cannot be packed, so it is reported as excluded
|
|
1456
|
+
* rather than assumed square — an assumed size is an assumed page layout.
|
|
1457
|
+
*/
|
|
1458
|
+
declare function censusDocument(model: string, document: Document, pageSize?: number): IContentCensus;
|
|
1459
|
+
/** Totals across a set of models, so a scene's census is one line rather than twenty-nine. */
|
|
1460
|
+
declare function totalCensus(entries: readonly IContentCensus[]): Omit<IContentCensus, "model">;
|
|
1461
|
+
|
|
1462
|
+
export { type AssetFindingGrade, type AssetKind, type AtlasExclusionReason, type AudioBand, type AudioConditioning, type AudioNormalisation, type IAssetCompileOptions, type IAssetCompileResult, type IAssetFinding, type IAssetHealthEntry, type IAssetHealthInput, type IAssetHealthReport, type IAssetPass, type IAssetPassOutput, type IAssetSourceConfig, type IAssetTargets, type IAssetWatchHandle, type IAssetWatchOptions, type IAssetWatchSummary, type IAtlasExclusion, type IAtlasOptions, type IAtlasPage, type IAtlasPlacement, type IAtlasResult, type IAtlasSource, type IAudioConfig, type IAudioLoopOptions, type IAudioOverride, type IAudioPassOptions, type IAudioRow, type IAudioSpectrumExpectation, type IBakeReceipt, type IBakeReceiptOutput, type IBasisTranscoder, type IContentCensus, type IDedupeCensus, type IEmbeddedTextureRow, type IEmbeddedTextureSummary, type ILightmapPassOptions, type IMaterialBucket, type IMaterialState, type IModelCompactInstanceOptions, type IModelCompactOptions, type IModelCompactSummary, type IModelPassOptions, type IModelPassesOptions, type IModelProtectedNode, type IModelQuantizeOptions, type IModelSimplifyOptions, type IModelSimplifySummary, type IModelSizeRow, type IModelStats, type IModelTextureBinding, type IModelTextureBindings, type IModelTextureOverride, type IModelTexturesOptions, type IModelsConfig, type IPassCostAssetRow, type IPassCostRow, type IPngInfo, type IResolvedCompactOptions, type ISimplifyRow, type ITextureOverride, type ITexturePassOptions, type ITextureSizeRow, type ITextureStats, type ITexturesConfig, type PassCostStatus, type TModelProtectedRule, type TextureCodec, WRAP_CLAMP_TO_EDGE, WRAP_MIRRORED_REPEAT, WRAP_REPEAT, atlasManifest, audioPass, censusDocument, compileAssets, dedupeMaterials, formatAudioSizes, formatHealthReport, formatModelSizes, formatPassCosts, formatTextureSizes, lightmapPass, materialSignature, materialStateOf, modelPass, packAtlas, parseAudioConfig, parsePng, resolveBasisTranscoder, resolveSourceTexel, rewriteUvs, runHealthReport, texturePass, totalCensus, uvsTile, watchAssets, withAtlasTextures, wrapTiles };
|