@aura3d/assets 1.4.0 → 1.4.1
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/package.json +38 -5
- package/AGENTS.md +0 -46
- package/src/AdvancedAssetCorpus.ts +0 -454
- package/src/AssetBundleCacheFixtures.ts +0 -290
- package/src/AssetCache.ts +0 -76
- package/src/AssetCompatibility.ts +0 -224
- package/src/AssetCorpus.ts +0 -329
- package/src/AssetDependencyGraph.ts +0 -83
- package/src/AssetHandle.ts +0 -74
- package/src/AssetImportPreflight.ts +0 -179
- package/src/AssetInspection.ts +0 -706
- package/src/AssetLoader.ts +0 -36
- package/src/AssetManager.ts +0 -211
- package/src/AssetRegistry.ts +0 -44
- package/src/AssetRenderDefaults.ts +0 -36
- package/src/AudioLoader.ts +0 -41
- package/src/BlenderExportValidation.ts +0 -227
- package/src/CarConceptMaterialStability.ts +0 -1405
- package/src/EXRLoader.ts +0 -1
- package/src/ExternalParityGLTFCorpus.ts +0 -65
- package/src/GLTFAnimationRuntime.ts +0 -1416
- package/src/GLTFAutoFit.ts +0 -133
- package/src/GLTFCompressionDecoders.ts +0 -190
- package/src/GLTFExtensionSupport.ts +0 -138
- package/src/GLTFLoader.ts +0 -4060
- package/src/GLTFRenderResources.ts +0 -2270
- package/src/HDRLoader.ts +0 -1
- package/src/ImageLoader.ts +0 -52
- package/src/ImportPipeline.ts +0 -148
- package/src/KTX2BasisTextureTranscoder.ts +0 -225
- package/src/LoadContext.ts +0 -60
- package/src/MaterialLoader.ts +0 -166
- package/src/MeshOptimization.ts +0 -93
- package/src/OBJLoader.ts +0 -551
- package/src/SceneAnalysisFixtures.ts +0 -438
- package/src/SceneLoader.ts +0 -62
- package/src/ShaderLoader.ts +0 -30
- package/src/TextureLoader.ts +0 -30
- package/src/TexturePipeline.ts +0 -96
- package/src/TextureStreaming.ts +0 -20
- package/src/WorkerAssetJobs.ts +0 -56
- package/src/advanced-gallery/index.ts +0 -18
- package/src/asset-corpus/GLTFSceneLoader.ts +0 -9
- package/src/asset-corpus/ProductionAssetCorpus.ts +0 -230
- package/src/asset-corpus/ProductionAssetPipeline.ts +0 -5
- package/src/asset-corpus/ProductionGLTFRenderPipeline.ts +0 -182
- package/src/asset-corpus/ProductionHDRTextureLoader.ts +0 -2
- package/src/asset-corpus/ProductionKTX2TextureLoader.ts +0 -2
- package/src/asset-corpus/ProductionTextureLoader.ts +0 -2
- package/src/asset-corpus/index.ts +0 -29
- package/src/browser-index.ts +0 -229
- package/src/createRenderableScene.ts +0 -128
- package/src/index.ts +0 -325
- package/src/loadRenderableAsset.ts +0 -67
- package/src/loaders/CubeTextureLoader.ts +0 -8
- package/src/loaders/EXRLoader.ts +0 -10
- package/src/loaders/HDRLoader.ts +0 -7
- package/src/loaders/KTX2Loader.ts +0 -7
- package/src/loaders/LoaderDiagnostics.ts +0 -36
- package/src/loaders/MTLLoader.ts +0 -20
- package/src/loaders/OBJLoader.ts +0 -24
- package/src/loaders/TextureLoader.ts +0 -14
- package/src/loaders/ThreeCompatGLTFLoader.ts +0 -19
- package/src/loaders/index.ts +0 -14
- package/src/threejs-compatibility/ThreeCompatAssetProvenance.ts +0 -51
- package/src/threejs-compatibility/ThreeCompatAssetRegistry.ts +0 -61
- package/src/threejs-example-parity/index.ts +0 -262
- package/tests/assets.test.ts +0 -393
- package/tsconfig.json +0 -7
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
export type AssetBundleCacheEvictionPolicy = "lru" | "lfu" | "fifo" | "ttl" | "size";
|
|
2
|
-
|
|
3
|
-
export interface AssetBundleCacheInput {
|
|
4
|
-
readonly assetId: string;
|
|
5
|
-
readonly url: string;
|
|
6
|
-
readonly meshCount: number;
|
|
7
|
-
readonly materialCount: number;
|
|
8
|
-
readonly textureCount: number;
|
|
9
|
-
readonly animationCount: number;
|
|
10
|
-
readonly skinCount: number;
|
|
11
|
-
readonly morphTargetCount: number;
|
|
12
|
-
readonly decodedTextureBytes?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AssetBundleManifestEvidenceEntry {
|
|
16
|
-
readonly id: string;
|
|
17
|
-
readonly path: string;
|
|
18
|
-
readonly type: "document" | "buffer" | "texture" | "material" | "animation" | "metadata";
|
|
19
|
-
readonly sizeBytes: number;
|
|
20
|
-
readonly dependencies: readonly string[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface AssetBundleCacheEvidence {
|
|
24
|
-
readonly source: "origin-master-asset-bundle-cache-adapted";
|
|
25
|
-
readonly manifest: {
|
|
26
|
-
readonly id: string;
|
|
27
|
-
readonly version: "external-parity-generated";
|
|
28
|
-
readonly assetCount: number;
|
|
29
|
-
readonly totalBytes: number;
|
|
30
|
-
readonly entries: readonly AssetBundleManifestEvidenceEntry[];
|
|
31
|
-
};
|
|
32
|
-
readonly dependencyGraph: {
|
|
33
|
-
readonly rootAssetId: string;
|
|
34
|
-
readonly loadOrder: readonly string[];
|
|
35
|
-
readonly releaseOrder: readonly string[];
|
|
36
|
-
readonly directDependencies: readonly string[];
|
|
37
|
-
readonly transitiveDependencies: readonly string[];
|
|
38
|
-
readonly cycleDetected: false;
|
|
39
|
-
};
|
|
40
|
-
readonly cache: {
|
|
41
|
-
readonly policy: AssetBundleCacheEvictionPolicy;
|
|
42
|
-
readonly maxEntries: number;
|
|
43
|
-
readonly maxBytes: number;
|
|
44
|
-
readonly cachedEntries: number;
|
|
45
|
-
readonly memoryBytes: number;
|
|
46
|
-
readonly hits: number;
|
|
47
|
-
readonly misses: number;
|
|
48
|
-
readonly evictions: number;
|
|
49
|
-
readonly hitRate: number;
|
|
50
|
-
readonly retainedAssetIds: readonly string[];
|
|
51
|
-
readonly evictedAssetIds: readonly string[];
|
|
52
|
-
};
|
|
53
|
-
readonly productionReadiness: {
|
|
54
|
-
readonly bundleManifest: true;
|
|
55
|
-
readonly dependencySorting: true;
|
|
56
|
-
readonly memoryBudgetEviction: true;
|
|
57
|
-
readonly cacheTelemetry: true;
|
|
58
|
-
readonly inFlightDeduplicationBoundary: true;
|
|
59
|
-
};
|
|
60
|
-
readonly blockedClaims: readonly string[];
|
|
61
|
-
readonly claimBoundary: string;
|
|
62
|
-
readonly hash: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function createAssetBundleCacheEvidence(input: AssetBundleCacheInput): AssetBundleCacheEvidence {
|
|
66
|
-
assertInput(input);
|
|
67
|
-
const rootId = normalizeAssetId(input.assetId);
|
|
68
|
-
const entries = createManifestEntries(rootId, input);
|
|
69
|
-
const loadOrder = topologicalSort(entries);
|
|
70
|
-
const releaseOrder = [...loadOrder].reverse();
|
|
71
|
-
const cache = simulateCache(loadOrder, entries);
|
|
72
|
-
const manifest = {
|
|
73
|
-
id: `${rootId}-bundle`,
|
|
74
|
-
version: "external-parity-generated" as const,
|
|
75
|
-
assetCount: entries.length,
|
|
76
|
-
totalBytes: sumBytes(entries),
|
|
77
|
-
entries
|
|
78
|
-
};
|
|
79
|
-
const directDependencies = entries.find((entry) => entry.id === rootId)?.dependencies ?? [];
|
|
80
|
-
const evidenceCore = {
|
|
81
|
-
manifest,
|
|
82
|
-
dependencyGraph: {
|
|
83
|
-
rootAssetId: rootId,
|
|
84
|
-
loadOrder,
|
|
85
|
-
releaseOrder,
|
|
86
|
-
directDependencies,
|
|
87
|
-
transitiveDependencies: loadOrder.filter((id) => id !== rootId),
|
|
88
|
-
cycleDetected: false as const
|
|
89
|
-
},
|
|
90
|
-
cache
|
|
91
|
-
};
|
|
92
|
-
return {
|
|
93
|
-
source: "origin-master-asset-bundle-cache-adapted",
|
|
94
|
-
...evidenceCore,
|
|
95
|
-
productionReadiness: {
|
|
96
|
-
bundleManifest: true,
|
|
97
|
-
dependencySorting: true,
|
|
98
|
-
memoryBudgetEviction: true,
|
|
99
|
-
cacheTelemetry: true,
|
|
100
|
-
inFlightDeduplicationBoundary: true
|
|
101
|
-
},
|
|
102
|
-
blockedClaims: [
|
|
103
|
-
"Unity Addressables catalog parity",
|
|
104
|
-
"Unreal Asset Manager primary asset parity",
|
|
105
|
-
"IndexedDB persistent cache certification",
|
|
106
|
-
"CDN cache invalidation and patch streaming parity",
|
|
107
|
-
"native platform asset bundle packaging parity"
|
|
108
|
-
],
|
|
109
|
-
claimBoundary: "This evidence ports the old branch asset bundle/cache concepts into deterministic ExternalParity manifest, dependency-order, and memory-budget telemetry for loaded glTF assets. It does not claim Unity Addressables, Unreal Asset Manager, persistent browser storage, CDN patch streaming, or native bundle packaging parity.",
|
|
110
|
-
hash: stableHash(JSON.stringify(evidenceCore))
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function createManifestEntries(rootId: string, input: AssetBundleCacheInput): readonly AssetBundleManifestEvidenceEntry[] {
|
|
115
|
-
const textureCount = Math.max(0, Math.trunc(input.textureCount));
|
|
116
|
-
const materialCount = Math.max(0, Math.trunc(input.materialCount));
|
|
117
|
-
const meshCount = Math.max(0, Math.trunc(input.meshCount));
|
|
118
|
-
const animationCount = Math.max(0, Math.trunc(input.animationCount));
|
|
119
|
-
const textureIds = Array.from({ length: Math.max(1, textureCount) }, (_, index) => `${rootId}:texture-${index + 1}`);
|
|
120
|
-
const entries: AssetBundleManifestEvidenceEntry[] = [
|
|
121
|
-
{
|
|
122
|
-
id: rootId,
|
|
123
|
-
path: input.url,
|
|
124
|
-
type: "document",
|
|
125
|
-
sizeBytes: 2048 + materialCount * 256 + meshCount * 512,
|
|
126
|
-
dependencies: [
|
|
127
|
-
`${rootId}:geometry-buffer`,
|
|
128
|
-
`${rootId}:material-manifest`,
|
|
129
|
-
`${rootId}:metadata`,
|
|
130
|
-
...(animationCount > 0 ? [`${rootId}:animation`] : [])
|
|
131
|
-
]
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
id: `${rootId}:geometry-buffer`,
|
|
135
|
-
path: `${input.url}#geometry-buffer`,
|
|
136
|
-
type: "buffer",
|
|
137
|
-
sizeBytes: 8192 + meshCount * 32768 + input.skinCount * 4096 + input.morphTargetCount * 8192,
|
|
138
|
-
dependencies: []
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: `${rootId}:material-manifest`,
|
|
142
|
-
path: `${input.url}#materials`,
|
|
143
|
-
type: "material",
|
|
144
|
-
sizeBytes: 1024 + materialCount * 768,
|
|
145
|
-
dependencies: textureIds
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
id: `${rootId}:metadata`,
|
|
149
|
-
path: `${input.url}#loader-diagnostics`,
|
|
150
|
-
type: "metadata",
|
|
151
|
-
sizeBytes: 1024,
|
|
152
|
-
dependencies: []
|
|
153
|
-
}
|
|
154
|
-
];
|
|
155
|
-
|
|
156
|
-
for (let index = 0; index < textureIds.length; index += 1) {
|
|
157
|
-
entries.push({
|
|
158
|
-
id: textureIds[index] ?? `${rootId}:texture-${index + 1}`,
|
|
159
|
-
path: `${input.url}#texture-${index + 1}`,
|
|
160
|
-
type: "texture",
|
|
161
|
-
sizeBytes: Math.max(4096, Math.trunc((input.decodedTextureBytes ?? textureCount * 65536) / Math.max(1, textureCount))),
|
|
162
|
-
dependencies: []
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (animationCount > 0) {
|
|
167
|
-
entries.push({
|
|
168
|
-
id: `${rootId}:animation`,
|
|
169
|
-
path: `${input.url}#animation`,
|
|
170
|
-
type: "animation",
|
|
171
|
-
sizeBytes: 2048 + animationCount * 4096,
|
|
172
|
-
dependencies: [`${rootId}:geometry-buffer`]
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return entries;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function topologicalSort(entries: readonly AssetBundleManifestEvidenceEntry[]): readonly string[] {
|
|
180
|
-
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
181
|
-
const sorted: string[] = [];
|
|
182
|
-
const visited = new Set<string>();
|
|
183
|
-
const visiting = new Set<string>();
|
|
184
|
-
const visit = (id: string): void => {
|
|
185
|
-
if (visited.has(id)) return;
|
|
186
|
-
if (visiting.has(id)) throw new Error(`Asset bundle dependency cycle detected: ${id}`);
|
|
187
|
-
visiting.add(id);
|
|
188
|
-
for (const dependency of byId.get(id)?.dependencies ?? []) {
|
|
189
|
-
visit(dependency);
|
|
190
|
-
}
|
|
191
|
-
visiting.delete(id);
|
|
192
|
-
visited.add(id);
|
|
193
|
-
sorted.push(id);
|
|
194
|
-
};
|
|
195
|
-
for (const entry of entries) visit(entry.id);
|
|
196
|
-
return sorted;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function simulateCache(loadOrder: readonly string[], entries: readonly AssetBundleManifestEvidenceEntry[]): AssetBundleCacheEvidence["cache"] {
|
|
200
|
-
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
201
|
-
const maxEntries = Math.max(3, Math.min(6, loadOrder.length - 1));
|
|
202
|
-
const maxBytes = Math.max(32768, Math.trunc(sumBytes(entries) * 0.72));
|
|
203
|
-
const retained = new Map<string, { readonly sizeBytes: number; accessCount: number; lastAccess: number }>();
|
|
204
|
-
const evicted: string[] = [];
|
|
205
|
-
let clock = 0;
|
|
206
|
-
let memoryBytes = 0;
|
|
207
|
-
let hits = 0;
|
|
208
|
-
let misses = 0;
|
|
209
|
-
let evictions = 0;
|
|
210
|
-
|
|
211
|
-
for (const id of loadOrder) {
|
|
212
|
-
clock += 1;
|
|
213
|
-
const hit = retained.get(id);
|
|
214
|
-
if (hit) {
|
|
215
|
-
hit.accessCount += 1;
|
|
216
|
-
hit.lastAccess = clock;
|
|
217
|
-
hits += 1;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
misses += 1;
|
|
221
|
-
const sizeBytes = byId.get(id)?.sizeBytes ?? 0;
|
|
222
|
-
while ((retained.size >= maxEntries || memoryBytes + sizeBytes > maxBytes) && retained.size > 0) {
|
|
223
|
-
const victim = [...retained.entries()].sort((left, right) => left[1].lastAccess - right[1].lastAccess)[0];
|
|
224
|
-
if (!victim) break;
|
|
225
|
-
retained.delete(victim[0]);
|
|
226
|
-
memoryBytes -= victim[1].sizeBytes;
|
|
227
|
-
evicted.push(victim[0]);
|
|
228
|
-
evictions += 1;
|
|
229
|
-
}
|
|
230
|
-
if (sizeBytes <= maxBytes) {
|
|
231
|
-
retained.set(id, { sizeBytes, accessCount: 1, lastAccess: clock });
|
|
232
|
-
memoryBytes += sizeBytes;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
for (const id of loadOrder.slice(-2)) {
|
|
237
|
-
if (retained.has(id)) {
|
|
238
|
-
retained.get(id)!.accessCount += 1;
|
|
239
|
-
hits += 1;
|
|
240
|
-
} else {
|
|
241
|
-
misses += 1;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return {
|
|
246
|
-
policy: "lru",
|
|
247
|
-
maxEntries,
|
|
248
|
-
maxBytes,
|
|
249
|
-
cachedEntries: retained.size,
|
|
250
|
-
memoryBytes,
|
|
251
|
-
hits,
|
|
252
|
-
misses,
|
|
253
|
-
evictions,
|
|
254
|
-
hitRate: Number((hits / Math.max(1, hits + misses)).toFixed(4)),
|
|
255
|
-
retainedAssetIds: [...retained.keys()],
|
|
256
|
-
evictedAssetIds: evicted
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function assertInput(input: AssetBundleCacheInput): void {
|
|
261
|
-
if (input.assetId.trim().length === 0) throw new Error("assetId is required.");
|
|
262
|
-
if (input.url.trim().length === 0) throw new Error("url is required.");
|
|
263
|
-
for (const [name, value] of Object.entries({
|
|
264
|
-
meshCount: input.meshCount,
|
|
265
|
-
materialCount: input.materialCount,
|
|
266
|
-
textureCount: input.textureCount,
|
|
267
|
-
animationCount: input.animationCount,
|
|
268
|
-
skinCount: input.skinCount,
|
|
269
|
-
morphTargetCount: input.morphTargetCount
|
|
270
|
-
})) {
|
|
271
|
-
if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be a non-negative finite number.`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function normalizeAssetId(id: string): string {
|
|
276
|
-
return id.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset";
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function sumBytes(entries: readonly AssetBundleManifestEvidenceEntry[]): number {
|
|
280
|
-
return entries.reduce((sum, entry) => sum + entry.sizeBytes, 0);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function stableHash(value: string): string {
|
|
284
|
-
let hash = 2166136261;
|
|
285
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
286
|
-
hash ^= value.charCodeAt(index);
|
|
287
|
-
hash = Math.imul(hash, 16777619);
|
|
288
|
-
}
|
|
289
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
290
|
-
}
|
package/src/AssetCache.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { AssetHandle } from "./AssetHandle";
|
|
2
|
-
|
|
3
|
-
interface CacheEntry<T> {
|
|
4
|
-
readonly handle: AssetHandle<T>;
|
|
5
|
-
readonly promise?: Promise<AssetHandle<T>>;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface AssetCacheSnapshot {
|
|
9
|
-
readonly cachedEntries: number;
|
|
10
|
-
readonly inFlightEntries: number;
|
|
11
|
-
readonly totalEntries: number;
|
|
12
|
-
readonly keys: readonly string[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class AssetCache {
|
|
16
|
-
private readonly entries = new Map<string, CacheEntry<unknown>>();
|
|
17
|
-
private readonly inFlight = new Map<string, Promise<AssetHandle<unknown>>>();
|
|
18
|
-
|
|
19
|
-
has(key: string): boolean {
|
|
20
|
-
return this.entries.has(key) || this.inFlight.has(key);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get<T>(key: string): AssetHandle<T> | undefined {
|
|
24
|
-
const entry = this.entries.get(key);
|
|
25
|
-
if (!entry) {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return entry.handle as AssetHandle<T>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getInFlight<T>(key: string): Promise<AssetHandle<T>> | undefined {
|
|
33
|
-
return this.inFlight.get(key) as Promise<AssetHandle<T>> | undefined;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
set<T>(key: string, handle: AssetHandle<T>): void {
|
|
37
|
-
this.entries.set(key, { handle: handle as unknown as AssetHandle<unknown> });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
setInFlight<T>(key: string, promise: Promise<AssetHandle<T>>): void {
|
|
41
|
-
this.inFlight.set(key, promise as Promise<AssetHandle<unknown>>);
|
|
42
|
-
promise
|
|
43
|
-
.then((handle) => {
|
|
44
|
-
this.entries.set(key, { handle: handle as unknown as AssetHandle<unknown> });
|
|
45
|
-
})
|
|
46
|
-
.catch(() => {
|
|
47
|
-
// Failed loads are intentionally not cached; callers receive the original rejection.
|
|
48
|
-
})
|
|
49
|
-
.finally(() => {
|
|
50
|
-
this.inFlight.delete(key);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
delete(key: string): boolean {
|
|
55
|
-
return this.entries.delete(key);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
clear(): void {
|
|
59
|
-
this.entries.clear();
|
|
60
|
-
this.inFlight.clear();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
keys(): readonly string[] {
|
|
64
|
-
return [...new Set([...this.entries.keys(), ...this.inFlight.keys()])];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
snapshot(): AssetCacheSnapshot {
|
|
68
|
-
const keys = this.keys();
|
|
69
|
-
return {
|
|
70
|
-
cachedEntries: this.entries.size,
|
|
71
|
-
inFlightEntries: this.inFlight.size,
|
|
72
|
-
totalEntries: keys.length,
|
|
73
|
-
keys
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
assertValidGLTFCorpusManifest,
|
|
3
|
-
createGLTFCorpusReport,
|
|
4
|
-
type AssetDiagnostic,
|
|
5
|
-
type AssetImportSettings,
|
|
6
|
-
type GLTFCorpusAssetReport,
|
|
7
|
-
type GLTFCorpusManifest,
|
|
8
|
-
type GLTFCorpusReport
|
|
9
|
-
} from "./AssetCorpus";
|
|
10
|
-
import type { BlenderExportValidationReport } from "./BlenderExportValidation";
|
|
11
|
-
|
|
12
|
-
export type AssetCompatibilityLoaderName = "aura3d" | "threejs" | "babylonjs" | "blender-export";
|
|
13
|
-
export type AssetCompatibilityStatus = "pass" | "warn" | "expected-fail" | "not-run";
|
|
14
|
-
|
|
15
|
-
export interface AssetLoaderCompatibilityResult {
|
|
16
|
-
readonly loader: AssetCompatibilityLoaderName;
|
|
17
|
-
readonly status: AssetCompatibilityStatus;
|
|
18
|
-
readonly diagnostics: readonly AssetDiagnostic[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface AssetCompatibilityReportAsset {
|
|
22
|
-
readonly id: string;
|
|
23
|
-
readonly name: string;
|
|
24
|
-
readonly format: string;
|
|
25
|
-
readonly tags: readonly string[];
|
|
26
|
-
readonly sourceUri: string;
|
|
27
|
-
readonly sourceRevision: string;
|
|
28
|
-
readonly sourceSha256: string;
|
|
29
|
-
readonly importSettings: AssetImportSettings;
|
|
30
|
-
readonly loaders: readonly AssetLoaderCompatibilityResult[];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface AssetCompatibilityReport {
|
|
34
|
-
readonly schemaVersion: "asset-compatibility-report";
|
|
35
|
-
readonly generatedAt: string;
|
|
36
|
-
readonly sourceManifest: GLTFCorpusReport["sourceManifest"];
|
|
37
|
-
readonly fixtureStatus: {
|
|
38
|
-
readonly blenderExportFixtures: "present" | "missing";
|
|
39
|
-
};
|
|
40
|
-
readonly blenderExportValidation?: BlenderExportValidationReport;
|
|
41
|
-
readonly summary: {
|
|
42
|
-
readonly assetCount: number;
|
|
43
|
-
readonly aura3d: Record<AssetCompatibilityStatus, number>;
|
|
44
|
-
readonly threejs: Record<AssetCompatibilityStatus, number>;
|
|
45
|
-
readonly babylonjs: Record<AssetCompatibilityStatus, number>;
|
|
46
|
-
readonly blenderExport: Record<AssetCompatibilityStatus, number>;
|
|
47
|
-
};
|
|
48
|
-
readonly assets: readonly AssetCompatibilityReportAsset[];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface AssetCompatibilityReportOptions {
|
|
52
|
-
readonly generatedAt?: string;
|
|
53
|
-
readonly blenderExportFixturesPresent?: boolean;
|
|
54
|
-
readonly blenderExportValidation?: BlenderExportValidationReport;
|
|
55
|
-
readonly externalLoaderResults?: readonly ExternalAssetLoaderCompatibilityResult[];
|
|
56
|
-
readonly blenderExportResults?: readonly BlenderExportCompatibilityResult[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface ExternalAssetLoaderCompatibilityResult extends AssetLoaderCompatibilityResult {
|
|
60
|
-
readonly assetId: string;
|
|
61
|
-
readonly loader: "threejs" | "babylonjs";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface BlenderExportCompatibilityResult extends AssetLoaderCompatibilityResult {
|
|
65
|
-
readonly assetId: string;
|
|
66
|
-
readonly loader: "blender-export";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function createAssetCompatibilityReport(
|
|
70
|
-
manifest: GLTFCorpusManifest,
|
|
71
|
-
options: AssetCompatibilityReportOptions = {}
|
|
72
|
-
): AssetCompatibilityReport {
|
|
73
|
-
const validated = assertValidGLTFCorpusManifest(manifest);
|
|
74
|
-
const corpusReport = createGLTFCorpusReport(validated, options.generatedAt ?? new Date().toISOString());
|
|
75
|
-
const blenderExportFixturesPresent = options.blenderExportFixturesPresent ?? options.blenderExportValidation !== undefined;
|
|
76
|
-
const externalResults = indexExternalLoaderResults(options.externalLoaderResults ?? []);
|
|
77
|
-
const blenderExportResults = indexBlenderExportResults(options.blenderExportResults ?? []);
|
|
78
|
-
const assets = corpusReport.assets.map((asset) => createCompatibilityAsset(asset, blenderExportFixturesPresent, externalResults, blenderExportResults));
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
schemaVersion: "asset-compatibility-report",
|
|
82
|
-
generatedAt: corpusReport.generatedAt,
|
|
83
|
-
sourceManifest: corpusReport.sourceManifest,
|
|
84
|
-
fixtureStatus: {
|
|
85
|
-
blenderExportFixtures: blenderExportFixturesPresent ? "present" : "missing"
|
|
86
|
-
},
|
|
87
|
-
...(options.blenderExportValidation ? { blenderExportValidation: options.blenderExportValidation } : {}),
|
|
88
|
-
summary: {
|
|
89
|
-
assetCount: assets.length,
|
|
90
|
-
aura3d: summarize(assets, "aura3d"),
|
|
91
|
-
threejs: summarize(assets, "threejs"),
|
|
92
|
-
babylonjs: summarize(assets, "babylonjs"),
|
|
93
|
-
blenderExport: summarize(assets, "blender-export")
|
|
94
|
-
},
|
|
95
|
-
assets
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function createCompatibilityAsset(
|
|
100
|
-
asset: GLTFCorpusAssetReport,
|
|
101
|
-
blenderExportFixturesPresent: boolean,
|
|
102
|
-
externalResults: ReadonlyMap<string, AssetLoaderCompatibilityResult>,
|
|
103
|
-
blenderExportResults: ReadonlyMap<string, AssetLoaderCompatibilityResult>
|
|
104
|
-
): AssetCompatibilityReportAsset {
|
|
105
|
-
return {
|
|
106
|
-
id: asset.id,
|
|
107
|
-
name: asset.name,
|
|
108
|
-
format: asset.format,
|
|
109
|
-
tags: asset.tags,
|
|
110
|
-
sourceUri: asset.sourceUri,
|
|
111
|
-
sourceRevision: asset.sourceRevision,
|
|
112
|
-
sourceSha256: asset.sourceSha256,
|
|
113
|
-
importSettings: asset.importSettings,
|
|
114
|
-
loaders: [
|
|
115
|
-
{
|
|
116
|
-
loader: "aura3d",
|
|
117
|
-
status: asset.expectedStatus === "expected-fail" ? "expected-fail" : asset.expectedStatus,
|
|
118
|
-
diagnostics: asset.diagnostics
|
|
119
|
-
},
|
|
120
|
-
externalResults.get(externalResultKey(asset.id, "threejs")) ?? externalLoaderScaffold("threejs", asset),
|
|
121
|
-
externalResults.get(externalResultKey(asset.id, "babylonjs")) ?? externalLoaderScaffold("babylonjs", asset),
|
|
122
|
-
blenderExportResults.get(asset.id) ?? blenderExportScaffold(asset, blenderExportFixturesPresent)
|
|
123
|
-
]
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function indexExternalLoaderResults(
|
|
128
|
-
results: readonly ExternalAssetLoaderCompatibilityResult[]
|
|
129
|
-
): ReadonlyMap<string, AssetLoaderCompatibilityResult> {
|
|
130
|
-
const indexed = new Map<string, AssetLoaderCompatibilityResult>();
|
|
131
|
-
for (const result of results) {
|
|
132
|
-
indexed.set(externalResultKey(result.assetId, result.loader), {
|
|
133
|
-
loader: result.loader,
|
|
134
|
-
status: result.status,
|
|
135
|
-
diagnostics: result.diagnostics
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
return indexed;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function indexBlenderExportResults(
|
|
142
|
-
results: readonly BlenderExportCompatibilityResult[]
|
|
143
|
-
): ReadonlyMap<string, AssetLoaderCompatibilityResult> {
|
|
144
|
-
const indexed = new Map<string, AssetLoaderCompatibilityResult>();
|
|
145
|
-
for (const result of results) {
|
|
146
|
-
indexed.set(result.assetId, {
|
|
147
|
-
loader: result.loader,
|
|
148
|
-
status: result.status,
|
|
149
|
-
diagnostics: result.diagnostics
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return indexed;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function externalResultKey(assetId: string, loader: "threejs" | "babylonjs"): string {
|
|
156
|
-
return `${assetId}:${loader}`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function externalLoaderScaffold(loader: "threejs" | "babylonjs", asset: GLTFCorpusAssetReport): AssetLoaderCompatibilityResult {
|
|
160
|
-
return {
|
|
161
|
-
loader,
|
|
162
|
-
status: "not-run",
|
|
163
|
-
diagnostics: [
|
|
164
|
-
diagnostic(
|
|
165
|
-
`ASSET_${loader.toUpperCase()}_LOADER_NOT_RUN`,
|
|
166
|
-
"External loader compatibility has not been executed in this bounded slice.",
|
|
167
|
-
`Run ${asset.id} through a pinned ${loader} GLTFLoader version and record pass, warn, or expected-fail evidence before claiming ${loader} parity.`
|
|
168
|
-
)
|
|
169
|
-
]
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function blenderExportScaffold(asset: GLTFCorpusAssetReport, fixturesPresent: boolean): AssetLoaderCompatibilityResult {
|
|
174
|
-
if (fixturesPresent) {
|
|
175
|
-
return {
|
|
176
|
-
loader: "blender-export",
|
|
177
|
-
status: "not-run",
|
|
178
|
-
diagnostics: [
|
|
179
|
-
diagnostic(
|
|
180
|
-
"ASSET_BLENDER_EXPORT_VALIDATION_PENDING",
|
|
181
|
-
"Blender export fixtures were detected, but no export validation runner is wired in this slice.",
|
|
182
|
-
`Run ${asset.id} or the matching fixture through the Blender exporter validation command and record the exported glTF diagnostics.`
|
|
183
|
-
)
|
|
184
|
-
]
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
loader: "blender-export",
|
|
189
|
-
status: "not-run",
|
|
190
|
-
diagnostics: [
|
|
191
|
-
diagnostic(
|
|
192
|
-
"ASSET_BLENDER_EXPORT_FIXTURES_MISSING",
|
|
193
|
-
"No Blender-export fixtures are present in the asset test corpus.",
|
|
194
|
-
"Add checked-in exported glTF/GLB fixtures before marking Blender-export validation complete."
|
|
195
|
-
)
|
|
196
|
-
]
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function summarize(
|
|
201
|
-
assets: readonly AssetCompatibilityReportAsset[],
|
|
202
|
-
loader: AssetCompatibilityLoaderName
|
|
203
|
-
): Record<AssetCompatibilityStatus, number> {
|
|
204
|
-
const counts: Record<AssetCompatibilityStatus, number> = {
|
|
205
|
-
pass: 0,
|
|
206
|
-
warn: 0,
|
|
207
|
-
"expected-fail": 0,
|
|
208
|
-
"not-run": 0
|
|
209
|
-
};
|
|
210
|
-
for (const asset of assets) {
|
|
211
|
-
const result = asset.loaders.find((entry) => entry.loader === loader);
|
|
212
|
-
counts[result?.status ?? "not-run"] += 1;
|
|
213
|
-
}
|
|
214
|
-
return counts;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function diagnostic(code: string, message: string, nextAction: string): AssetDiagnostic {
|
|
218
|
-
return {
|
|
219
|
-
code,
|
|
220
|
-
severity: "info",
|
|
221
|
-
message,
|
|
222
|
-
nextAction
|
|
223
|
-
};
|
|
224
|
-
}
|