@threenative/assets 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apply-worker.js +7742 -6368
- package/dist/index.d.ts +390 -1
- package/dist/index.js +8889 -6935
- 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,11 @@ 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
|
+
}
|
|
49
56
|
|
|
50
57
|
type AssetFindingGrade = "fail" | "ok" | "warn";
|
|
51
58
|
interface IAssetFinding {
|
|
@@ -99,6 +106,82 @@ declare function runHealthReport(inputs: readonly IAssetHealthInput[], targets?:
|
|
|
99
106
|
/** The person-readable form printed by the compile step, one line per finding plus a summary. */
|
|
100
107
|
declare function formatHealthReport(report: IAssetHealthReport): readonly string[];
|
|
101
108
|
|
|
109
|
+
/**
|
|
110
|
+
* What the `minTriangles` pre-filter is measured against.
|
|
111
|
+
*
|
|
112
|
+
* `"primitive"` compares each primitive on its own; `"asset"` compares the whole source asset's
|
|
113
|
+
* total. The asset total is the evidence-backed default: a carrier can be 347,497 triangles split
|
|
114
|
+
* over 300 primitives of ~1,200, where an absolute per-primitive floor measures how the artist
|
|
115
|
+
* happened to split the mesh rather than whether simplification would pay. Whichever is chosen, the
|
|
116
|
+
* floor is only a cheap pre-filter — the measured saving rule below is the gate.
|
|
117
|
+
*/
|
|
118
|
+
type LodMinTrianglesScope = "primitive" | "asset";
|
|
119
|
+
|
|
120
|
+
/** Generation knobs, shared by every preset (PRD-377 §3.2); both are ceilings, not promises. */
|
|
121
|
+
interface IModelLodGenerationOptions {
|
|
122
|
+
/**
|
|
123
|
+
* Versioned increasing geometric-error targets, in normalized mesh-extent units. Each target is
|
|
124
|
+
* simplified from LOD0 independently; a target that cannot reduce is dropped. Default
|
|
125
|
+
* {@link LOD_ERROR_TARGETS}.
|
|
126
|
+
*/
|
|
127
|
+
readonly errorTargets?: readonly number[];
|
|
128
|
+
/** Levels in the chain including LOD0; integer 1–8. Default {@link DEFAULT_LOD_MAX_LEVELS}. */
|
|
129
|
+
readonly maxLevels?: number;
|
|
130
|
+
/**
|
|
131
|
+
* Fraction of its predecessor's triangles a derived level must save to be kept. Default
|
|
132
|
+
* {@link LOD_MIN_SAVING}. This is the benefit gate: an inability to reach it is a normal skip.
|
|
133
|
+
*/
|
|
134
|
+
readonly minSaving?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Cheap pre-filter floor in triangles. It is not the benefit gate — {@link minSaving} is — and
|
|
137
|
+
* only skips work where the simplifier's fixed per-call cost would dominate. Default
|
|
138
|
+
* {@link DEFAULT_LOD_MIN_TRIANGLES}.
|
|
139
|
+
*/
|
|
140
|
+
readonly minTriangles?: number;
|
|
141
|
+
/**
|
|
142
|
+
* What {@link minTriangles} is measured against. Default
|
|
143
|
+
* {@link DEFAULT_LOD_MIN_TRIANGLES_SCOPE}, which floors the whole asset rather than each
|
|
144
|
+
* primitive, so a model split into many small primitives is still measured by its total.
|
|
145
|
+
*/
|
|
146
|
+
readonly minTrianglesScope?: LodMinTrianglesScope;
|
|
147
|
+
/**
|
|
148
|
+
* Opt-in far rung that joins a mesh's same-material primitives into one draw per material group
|
|
149
|
+
* (PRD-377 §4.4 extension). **Default `false`: with no option the cook is byte-identical to
|
|
150
|
+
* today.** A join never crosses a material, never touches a skinned, morph-target or animated
|
|
151
|
+
* node, and never leaves the mesh's own node, so authored LOD0, node identity and picking are
|
|
152
|
+
* untouched; the joined geometry is an additional far rung beside LOD0.
|
|
153
|
+
*/
|
|
154
|
+
readonly join?: boolean;
|
|
155
|
+
}
|
|
156
|
+
/** Quality policy for automatic LOD; it selects the projected pixel-error budget (PRD-377 §3.2). */
|
|
157
|
+
type LodPreset = "aggressive" | "balanced" | "quality";
|
|
158
|
+
/** Screen-space selection knobs; they change what the runtime picks, never what is baked. */
|
|
159
|
+
interface IModelLodRuntimeOptions {
|
|
160
|
+
/** Projected geometric-error budget in raster pixels; positive finite. */
|
|
161
|
+
readonly maxPixelError?: number;
|
|
162
|
+
/** Fraction in `[0, 0.5)`, default 0.15, that stabilizes coarsening at a boundary. */
|
|
163
|
+
readonly hysteresis?: number;
|
|
164
|
+
}
|
|
165
|
+
/** A partial override for one asset; nested objects overlay, they never replace. */
|
|
166
|
+
interface IModelLodOverride {
|
|
167
|
+
readonly enabled?: boolean;
|
|
168
|
+
readonly generation?: IModelLodGenerationOptions;
|
|
169
|
+
readonly preset?: LodPreset;
|
|
170
|
+
readonly runtime?: IModelLodRuntimeOptions;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* `assets.lod`, as the compile step reads it. Absent or `{}` resolves to enabled/balanced; `false`
|
|
174
|
+
* and `{ enabled: false }` are equivalent absolute kill switches that no per-asset override can
|
|
175
|
+
* re-enable. Resolution is per asset and happens where the asset is known (PRD-377 §3.2, §5).
|
|
176
|
+
*/
|
|
177
|
+
interface IModelLodOptions {
|
|
178
|
+
readonly enabled?: boolean;
|
|
179
|
+
readonly generation?: IModelLodGenerationOptions;
|
|
180
|
+
readonly overrides?: Readonly<Record<string, boolean | IModelLodOverride>>;
|
|
181
|
+
readonly preset?: LodPreset;
|
|
182
|
+
readonly runtime?: IModelLodRuntimeOptions;
|
|
183
|
+
}
|
|
184
|
+
|
|
102
185
|
/**
|
|
103
186
|
* The five bands a clip's content is judged in, contiguous and gapless up to Nyquist.
|
|
104
187
|
*
|
|
@@ -414,6 +497,13 @@ interface IModelPassOptions {
|
|
|
414
497
|
* would hide that.
|
|
415
498
|
*/
|
|
416
499
|
readonly virtual?: IModelVirtualOptions | "none";
|
|
500
|
+
/**
|
|
501
|
+
* Automatic discrete LOD (`assets.lod`). Absent means no automatic generation in this phase;
|
|
502
|
+
* `false` / `"none"` is the kill switch, and an object carries the resolved policy. Per-asset
|
|
503
|
+
* overrides key off the canonical source asset path, which is why the pass resolves them here
|
|
504
|
+
* rather than at registry construction.
|
|
505
|
+
*/
|
|
506
|
+
readonly lod?: boolean | IModelLodOptions | "none";
|
|
417
507
|
}
|
|
418
508
|
|
|
419
509
|
/**
|
|
@@ -475,6 +565,52 @@ interface ISimplifyRow {
|
|
|
475
565
|
readonly trianglesAfter: number;
|
|
476
566
|
readonly trianglesBefore: number;
|
|
477
567
|
}
|
|
568
|
+
/** What automatic discrete generation produced for one model (PRD-377 §4.3). */
|
|
569
|
+
/** One material group inside an opt-in joined far rung: what it collapsed and how much it draws. */
|
|
570
|
+
interface ILodJoinedGroupRow {
|
|
571
|
+
readonly material: string;
|
|
572
|
+
readonly primitives: number;
|
|
573
|
+
readonly triangles: number;
|
|
574
|
+
}
|
|
575
|
+
/** The opt-in joined far rung as the manifest carries it: draws and the primitives collapsed. */
|
|
576
|
+
interface ILodJoinedRow {
|
|
577
|
+
readonly draws: number;
|
|
578
|
+
readonly groups: readonly ILodJoinedGroupRow[];
|
|
579
|
+
readonly primitives: number;
|
|
580
|
+
readonly triangles: number;
|
|
581
|
+
readonly trianglesBefore: number;
|
|
582
|
+
}
|
|
583
|
+
interface ILodRow {
|
|
584
|
+
/** Bytes the derived index buffers add, before compression. */
|
|
585
|
+
readonly byteOverhead: number;
|
|
586
|
+
/** Migration/legacy notes the resolver raised, by code. */
|
|
587
|
+
readonly diagnostics: readonly string[];
|
|
588
|
+
/** The resolved increasing geometric-error targets, as the bake consumed them. */
|
|
589
|
+
readonly errorTargets: readonly number[];
|
|
590
|
+
readonly fingerprint: string;
|
|
591
|
+
readonly generated: number;
|
|
592
|
+
/** Whether the opt-in join was requested; {@link joined} is present only when it did something. */
|
|
593
|
+
readonly join: boolean;
|
|
594
|
+
/** The opt-in joined far rung's honesty record, when one was produced. */
|
|
595
|
+
readonly joined?: ILodJoinedRow;
|
|
596
|
+
readonly levels: number;
|
|
597
|
+
readonly maxLevels: number;
|
|
598
|
+
/** Fraction of its predecessor's triangles a derived level had to save. */
|
|
599
|
+
readonly minSaving: number;
|
|
600
|
+
readonly minTriangles: number;
|
|
601
|
+
/** What `minTriangles` measured against: `"primitive"` or `"asset"`. */
|
|
602
|
+
readonly minTrianglesScope: string;
|
|
603
|
+
readonly preset: string;
|
|
604
|
+
readonly reasons: readonly string[];
|
|
605
|
+
/** The runtime selection budget this asset ships with; the loader reads it from the manifest. */
|
|
606
|
+
readonly runtime: {
|
|
607
|
+
readonly hysteresis: number;
|
|
608
|
+
readonly maxPixelError: number;
|
|
609
|
+
};
|
|
610
|
+
readonly skipped: number;
|
|
611
|
+
readonly trianglesAfter: number;
|
|
612
|
+
readonly trianglesBefore: number;
|
|
613
|
+
}
|
|
478
614
|
/** Whether the pass executed this bake or every input that reached it was compile-cache-served. */
|
|
479
615
|
type PassCostStatus = "cached" | "ran";
|
|
480
616
|
/** What one input cost the pass that processed it, as the pass driver measured it. */
|
|
@@ -601,6 +737,8 @@ interface IModelSizeRow {
|
|
|
601
737
|
};
|
|
602
738
|
/** LOD simplification, when it was configured for this model. */
|
|
603
739
|
readonly simplify?: ISimplifyRow;
|
|
740
|
+
/** Automatic discrete LOD generation (PRD-377), when the effective policy ran. */
|
|
741
|
+
readonly lod?: ILodRow;
|
|
604
742
|
/** The cluster-DAG bake, when it was configured for this model. */
|
|
605
743
|
readonly virtual?: IVirtualRow;
|
|
606
744
|
/** Triangle count of the compiled output, recorded in the manifest. */
|
|
@@ -696,6 +834,12 @@ interface IAssetSourceConfig {
|
|
|
696
834
|
* exactly as committed. Absent means optimization runs with defaults.
|
|
697
835
|
*/
|
|
698
836
|
readonly models?: IModelsConfig | "none";
|
|
837
|
+
/**
|
|
838
|
+
* Automatic discrete LOD (`assets.lod`): a boolean kill switch/enable, an object carrying the
|
|
839
|
+
* resolved generation policy, or `"none"`. Absent means this phase generates nothing; the
|
|
840
|
+
* default-on decision is a later phase (PRD-377 §8).
|
|
841
|
+
*/
|
|
842
|
+
readonly lod?: boolean | IModelLodOptions | "none";
|
|
699
843
|
readonly output?: string;
|
|
700
844
|
readonly source?: string;
|
|
701
845
|
readonly targets?: IAssetTargets;
|
|
@@ -780,6 +924,16 @@ interface IAssetCompileOptions {
|
|
|
780
924
|
* build uncompressed too. The build knows its target; it decides.
|
|
781
925
|
*/
|
|
782
926
|
readonly platform?: "android" | "desktop" | "ios" | "web";
|
|
927
|
+
/**
|
|
928
|
+
* What the runtime that will execute this bake can decode, when the caller has probed it.
|
|
929
|
+
*
|
|
930
|
+
* `platform` is a proxy: it answers "android and iOS have no WebAssembly" for the targets this
|
|
931
|
+
* repository ships. A desktop host can also lack WebAssembly — the Linux arm64 lane builds
|
|
932
|
+
* QuickJS over wgpu-native — and then the same meshopt/KTX2 passes it would keep produce an
|
|
933
|
+
* asset nothing can decode. `threenative build` reads the engine from the runtime binary it will
|
|
934
|
+
* package and passes the real capability here; absent, the platform-derived default stands.
|
|
935
|
+
*/
|
|
936
|
+
readonly runtimeDecoders?: IAssetRuntimeDecoderCapabilities;
|
|
783
937
|
readonly source?: string;
|
|
784
938
|
/** Overrides resolution of three's Basis transcoder for the copy into the output root. */
|
|
785
939
|
readonly transcoder?: IBasisTranscoder;
|
|
@@ -928,4 +1082,239 @@ interface IAssetWatchHandle {
|
|
|
928
1082
|
}
|
|
929
1083
|
declare function watchAssets(options?: IAssetWatchOptions): IAssetWatchHandle;
|
|
930
1084
|
|
|
931
|
-
|
|
1085
|
+
/**
|
|
1086
|
+
* A deterministic texture atlas packer.
|
|
1087
|
+
*
|
|
1088
|
+
* The reference game's carrier is 95 meshes over 76 materials and **73 distinct texture sources** —
|
|
1089
|
+
* one texture per material, because an imported model carries a material per part. A per-material
|
|
1090
|
+
* merge over content shaped like that has nothing to collapse: it found 274 buckets holding 213
|
|
1091
|
+
* singletons and moved no frame time. That null result is the evidence for this file. Materials
|
|
1092
|
+
* cannot be deduplicated while each one owns a private texture, so the atlas is the prerequisite
|
|
1093
|
+
* and everything downstream — dedupe, merge, instancing — is priced against it.
|
|
1094
|
+
*
|
|
1095
|
+
* Two properties, both load-bearing:
|
|
1096
|
+
*
|
|
1097
|
+
* - **Deterministic.** Two runs over the same inputs place every source at the same pixel. The
|
|
1098
|
+
* order is derived from the sources themselves (taller first, then wider, then the key), never
|
|
1099
|
+
* from map iteration or filesystem order, because a build that packs differently on a rebuild
|
|
1100
|
+
* invalidates every downstream cache and makes a screenshot-parity gate meaningless.
|
|
1101
|
+
* - **It refuses what it cannot do.** A source that tiles is excluded and reported, never packed
|
|
1102
|
+
* and silently clamped: a repeating surface whose UVs leave `[0, 1]` reads neighbouring pages
|
|
1103
|
+
* once it shares one, which is a visible bug that looks like corruption rather than like a
|
|
1104
|
+
* packing decision.
|
|
1105
|
+
*
|
|
1106
|
+
* Shelf packing, not a perfect bin: sorted by height it wastes a few percent of a page against an
|
|
1107
|
+
* optimal solver nobody can read, and the cost of a wasted page is bytes, while the cost of a
|
|
1108
|
+
* wrong one is a visual bug. `ponytail: shelf packer, revisit only if page count is measured to
|
|
1109
|
+
* matter.`
|
|
1110
|
+
*/
|
|
1111
|
+
/** One image offered to the packer. */
|
|
1112
|
+
interface IAtlasSource {
|
|
1113
|
+
/** Stable identity — the content hash or the texture's URI. Ties in the sort break on this. */
|
|
1114
|
+
readonly key: string;
|
|
1115
|
+
readonly width: number;
|
|
1116
|
+
readonly height: number;
|
|
1117
|
+
/**
|
|
1118
|
+
* Whether the material samples this source outside `[0, 1]`. A tiling source cannot share a
|
|
1119
|
+
* page, and saying so is the source's job, not a guess the packer makes from pixels.
|
|
1120
|
+
*/
|
|
1121
|
+
readonly tiles?: boolean;
|
|
1122
|
+
}
|
|
1123
|
+
/** Where one source landed. */
|
|
1124
|
+
interface IAtlasPlacement {
|
|
1125
|
+
readonly key: string;
|
|
1126
|
+
readonly page: number;
|
|
1127
|
+
readonly x: number;
|
|
1128
|
+
readonly y: number;
|
|
1129
|
+
readonly width: number;
|
|
1130
|
+
readonly height: number;
|
|
1131
|
+
}
|
|
1132
|
+
/** The UV transform for one source: `uv' = uv * scale + offset`. */
|
|
1133
|
+
interface IAtlasTransform {
|
|
1134
|
+
readonly page: number;
|
|
1135
|
+
readonly offsetX: number;
|
|
1136
|
+
readonly offsetY: number;
|
|
1137
|
+
readonly scaleX: number;
|
|
1138
|
+
readonly scaleY: number;
|
|
1139
|
+
}
|
|
1140
|
+
/** Why a source was left out. Every exclusion is reported; none is silent. */
|
|
1141
|
+
type AtlasExclusionReason = "tiles" | "too-large";
|
|
1142
|
+
interface IAtlasExclusion {
|
|
1143
|
+
readonly key: string;
|
|
1144
|
+
readonly reason: AtlasExclusionReason;
|
|
1145
|
+
}
|
|
1146
|
+
interface IAtlasPage {
|
|
1147
|
+
readonly index: number;
|
|
1148
|
+
readonly width: number;
|
|
1149
|
+
readonly height: number;
|
|
1150
|
+
readonly placements: readonly IAtlasPlacement[];
|
|
1151
|
+
}
|
|
1152
|
+
interface IAtlasResult {
|
|
1153
|
+
readonly pages: readonly IAtlasPage[];
|
|
1154
|
+
/** Keyed by source key, in the packer's own deterministic order. */
|
|
1155
|
+
readonly transforms: ReadonlyMap<string, IAtlasTransform>;
|
|
1156
|
+
readonly excluded: readonly IAtlasExclusion[];
|
|
1157
|
+
}
|
|
1158
|
+
interface IAtlasOptions {
|
|
1159
|
+
/** Page edge in pixels. Default 4096. */
|
|
1160
|
+
readonly pageSize?: number;
|
|
1161
|
+
/**
|
|
1162
|
+
* Transparent pixels kept between neighbours, so a mip level or a bilinear tap cannot reach the
|
|
1163
|
+
* next source. Default 4 — two levels of mip bleed at the sizes this packs.
|
|
1164
|
+
*/
|
|
1165
|
+
readonly padding?: number;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Packs sources into pages and answers the UV transform for each.
|
|
1169
|
+
*
|
|
1170
|
+
* Fails closed on a malformed source: a zero or non-integer dimension is a decoder that did not
|
|
1171
|
+
* decode, and packing it would put a divide-by-zero in every UV it rewrites.
|
|
1172
|
+
*/
|
|
1173
|
+
declare function packAtlas(sources: readonly IAtlasSource[], options?: IAtlasOptions): IAtlasResult;
|
|
1174
|
+
/**
|
|
1175
|
+
* The manifest a build writes beside the pages.
|
|
1176
|
+
*
|
|
1177
|
+
* Sorted by key so two runs produce byte-identical JSON — the same reason the packing order is
|
|
1178
|
+
* derived rather than inherited.
|
|
1179
|
+
*/
|
|
1180
|
+
declare function atlasManifest(result: IAtlasResult): string;
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Moving a mesh's UVs onto its atlas page, and deciding which meshes may not go.
|
|
1184
|
+
*
|
|
1185
|
+
* The rewrite itself is two multiplies and two adds per vertex. The decision in front of it is the
|
|
1186
|
+
* part that matters: a surface that samples outside `[0, 1]` tiles, and a tiling surface on a
|
|
1187
|
+
* shared page reads its neighbours. That is a visible bug that looks like corruption, so the
|
|
1188
|
+
* policy here is to detect it from the geometry's own UVs and from the sampler's wrap mode, refuse
|
|
1189
|
+
* to pack the source, and report it — never to clamp, and never to guess from the image.
|
|
1190
|
+
*/
|
|
1191
|
+
|
|
1192
|
+
/** glTF sampler wrap modes; `10497` is REPEAT and `33648` is MIRRORED_REPEAT. */
|
|
1193
|
+
declare const WRAP_REPEAT = 10497;
|
|
1194
|
+
declare const WRAP_MIRRORED_REPEAT = 33648;
|
|
1195
|
+
declare const WRAP_CLAMP_TO_EDGE = 33071;
|
|
1196
|
+
/** Whether this UV set samples outside the unit square, and therefore tiles. */
|
|
1197
|
+
declare function uvsTile(uv: ArrayLike<number>): boolean;
|
|
1198
|
+
/** Whether the sampler asks to repeat. A wrap mode the packer does not know is treated as tiling. */
|
|
1199
|
+
declare function wrapTiles(wrapS: number | undefined, wrapT: number | undefined): boolean;
|
|
1200
|
+
/**
|
|
1201
|
+
* Rewrites a UV buffer in place onto its atlas page.
|
|
1202
|
+
*
|
|
1203
|
+
* In place because the caller owns a freshly-decoded accessor and a second buffer per mesh is a
|
|
1204
|
+
* copy of the whole scene's UVs for nothing. Throws on an odd-length buffer rather than rewriting
|
|
1205
|
+
* half a coordinate.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function rewriteUvs(uv: Float32Array, transform: IAtlasTransform): Float32Array;
|
|
1208
|
+
/**
|
|
1209
|
+
* The texel a rewritten UV resolves to, in the source image's own pixel space.
|
|
1210
|
+
*
|
|
1211
|
+
* This is the inverse of `rewriteUvs` composed with the page geometry, and it exists so a test can
|
|
1212
|
+
* assert the round trip rather than assert the arithmetic against itself: sample a texel, rewrite,
|
|
1213
|
+
* resolve, and land within a texel of where you started.
|
|
1214
|
+
*/
|
|
1215
|
+
declare function resolveSourceTexel(atlasUv: readonly [number, number], transform: IAtlasTransform, source: {
|
|
1216
|
+
readonly width: number;
|
|
1217
|
+
readonly height: number;
|
|
1218
|
+
}): {
|
|
1219
|
+
x: number;
|
|
1220
|
+
y: number;
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Collapsing materials that became identical once their textures shared a page.
|
|
1225
|
+
*
|
|
1226
|
+
* This is the step the atlas exists to enable, and it is also the step that falsifies the atlas.
|
|
1227
|
+
* The reference game's merge found 274 buckets holding 213 singletons because every material owned
|
|
1228
|
+
* a private texture; if the singleton count does not fall once those textures share pages, the
|
|
1229
|
+
* atlas hypothesis is wrong and everything priced on it has to be repriced. So the signature here
|
|
1230
|
+
* is deliberately conservative: two materials collapse only when every field that can change a
|
|
1231
|
+
* pixel agrees, and anything the signature does not know about keeps them apart.
|
|
1232
|
+
*/
|
|
1233
|
+
/** The material state a signature is taken over. Structural, so a glTF or a three material fits. */
|
|
1234
|
+
interface IMaterialState {
|
|
1235
|
+
readonly name?: string;
|
|
1236
|
+
/** Texture identity per slot *after* atlasing — the page, not the original image. */
|
|
1237
|
+
readonly textures: Readonly<Record<string, string | undefined>>;
|
|
1238
|
+
/** Scalar and vector uniforms: base colour, metallic, roughness, emissive, alpha cutoff, … */
|
|
1239
|
+
readonly uniforms: Readonly<Record<string, number | readonly number[] | string | boolean | undefined>>;
|
|
1240
|
+
/** Anything that changes how the surface is drawn rather than what it samples. */
|
|
1241
|
+
readonly flags: Readonly<Record<string, number | string | boolean | undefined>>;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* A stable signature over everything that decides how this material looks.
|
|
1245
|
+
*
|
|
1246
|
+
* The name is deliberately **not** in it: an imported pack names a material per part, and letting
|
|
1247
|
+
* the name separate them is exactly the property that made 213 singletons. Every other field is,
|
|
1248
|
+
* including ones this pipeline does not understand, because an unknown field that differs is a
|
|
1249
|
+
* reason not to collapse.
|
|
1250
|
+
*/
|
|
1251
|
+
declare function materialSignature(material: IMaterialState): string;
|
|
1252
|
+
/** One group of materials that became the same material. */
|
|
1253
|
+
interface IMaterialBucket {
|
|
1254
|
+
readonly signature: string;
|
|
1255
|
+
/** Names in input order, so a report can say which materials collapsed into which. */
|
|
1256
|
+
readonly members: readonly string[];
|
|
1257
|
+
}
|
|
1258
|
+
interface IDedupeCensus {
|
|
1259
|
+
/** Distinct signatures — the material count after deduplication. */
|
|
1260
|
+
readonly buckets: number;
|
|
1261
|
+
/** Buckets holding exactly one material: the count the atlas has to move. */
|
|
1262
|
+
readonly singletons: number;
|
|
1263
|
+
/** Materials in. */
|
|
1264
|
+
readonly materials: number;
|
|
1265
|
+
}
|
|
1266
|
+
/** Groups materials by signature, in first-seen order so two runs report the same thing. */
|
|
1267
|
+
declare function dedupeMaterials(materials: readonly IMaterialState[]): {
|
|
1268
|
+
buckets: readonly IMaterialBucket[];
|
|
1269
|
+
census: IDedupeCensus;
|
|
1270
|
+
};
|
|
1271
|
+
/**
|
|
1272
|
+
* The same materials with every texture slot repointed at its atlas page.
|
|
1273
|
+
*
|
|
1274
|
+
* A slot whose source was excluded from the atlas keeps its original texture, which is what makes
|
|
1275
|
+
* the census honest: those materials stay singletons, and the report says how many.
|
|
1276
|
+
*/
|
|
1277
|
+
declare function withAtlasTextures(materials: readonly IMaterialState[], pageOf: (texture: string) => string | undefined): IMaterialState[];
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* The bucket census: what a merge could collapse, before and after an atlas.
|
|
1281
|
+
*
|
|
1282
|
+
* "Fewer objects" was the obvious lever on the reference game and it recovered nothing — a runtime
|
|
1283
|
+
* per-material merge replaced 246 of 1,561 meshes and moved no frame time, because 274 buckets
|
|
1284
|
+
* held 213 singletons. The census is the number to read before promising a merge, and this is the
|
|
1285
|
+
* thing that reads it: it walks a glTF document's materials, takes the same signature the dedupe
|
|
1286
|
+
* takes, and reports the buckets twice — once as authored, once with every atlasable texture
|
|
1287
|
+
* repointed at a page.
|
|
1288
|
+
*
|
|
1289
|
+
* The second number is the falsification point for the whole atlas argument. If singletons do not
|
|
1290
|
+
* fall, the atlas is not the lever and nothing downstream of it should be built.
|
|
1291
|
+
*/
|
|
1292
|
+
|
|
1293
|
+
/** One model's census. */
|
|
1294
|
+
interface IContentCensus {
|
|
1295
|
+
readonly model: string;
|
|
1296
|
+
readonly meshes: number;
|
|
1297
|
+
readonly primitives: number;
|
|
1298
|
+
readonly materials: number;
|
|
1299
|
+
/** Distinct texture images the materials reference. */
|
|
1300
|
+
readonly textureSources: number;
|
|
1301
|
+
/** Sources the packer refused, with the reason — tiling surfaces, mostly. */
|
|
1302
|
+
readonly excluded: number;
|
|
1303
|
+
readonly atlasPages: number;
|
|
1304
|
+
readonly before: IDedupeCensus;
|
|
1305
|
+
readonly after: IDedupeCensus;
|
|
1306
|
+
}
|
|
1307
|
+
/** Reads a material into the state the signature is taken over. */
|
|
1308
|
+
declare function materialStateOf(material: Material): IMaterialState;
|
|
1309
|
+
/**
|
|
1310
|
+
* Censuses one document: what its materials are now, and what they would be once their textures
|
|
1311
|
+
* shared atlas pages.
|
|
1312
|
+
*
|
|
1313
|
+
* A texture whose size the document does not state cannot be packed, so it is reported as excluded
|
|
1314
|
+
* rather than assumed square — an assumed size is an assumed page layout.
|
|
1315
|
+
*/
|
|
1316
|
+
declare function censusDocument(model: string, document: Document, pageSize?: number): IContentCensus;
|
|
1317
|
+
/** Totals across a set of models, so a scene's census is one line rather than twenty-nine. */
|
|
1318
|
+
declare function totalCensus(entries: readonly IContentCensus[]): Omit<IContentCensus, "model">;
|
|
1319
|
+
|
|
1320
|
+
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 IModelPassOptions, type IModelPassesOptions, 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 ISimplifyRow, type ITextureOverride, type ITexturePassOptions, type ITextureSizeRow, type ITextureStats, type ITexturesConfig, type PassCostStatus, 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 };
|