@aura3d/assets 1.3.3 → 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.
Files changed (73) hide show
  1. package/dist/GLTFLoader.js +1 -0
  2. package/dist/GLTFLoader.js.map +1 -1
  3. package/dist/GLTFRenderResources.d.ts.map +1 -1
  4. package/dist/GLTFRenderResources.js +1 -0
  5. package/dist/GLTFRenderResources.js.map +1 -1
  6. package/package.json +38 -5
  7. package/src/AdvancedAssetCorpus.ts +0 -454
  8. package/src/AssetBundleCacheFixtures.ts +0 -290
  9. package/src/AssetCache.ts +0 -76
  10. package/src/AssetCompatibility.ts +0 -224
  11. package/src/AssetCorpus.ts +0 -329
  12. package/src/AssetDependencyGraph.ts +0 -83
  13. package/src/AssetHandle.ts +0 -74
  14. package/src/AssetImportPreflight.ts +0 -179
  15. package/src/AssetInspection.ts +0 -706
  16. package/src/AssetLoader.ts +0 -36
  17. package/src/AssetManager.ts +0 -211
  18. package/src/AssetRegistry.ts +0 -44
  19. package/src/AssetRenderDefaults.ts +0 -36
  20. package/src/AudioLoader.ts +0 -41
  21. package/src/BlenderExportValidation.ts +0 -227
  22. package/src/CarConceptMaterialStability.ts +0 -1405
  23. package/src/EXRLoader.ts +0 -1
  24. package/src/ExternalParityGLTFCorpus.ts +0 -65
  25. package/src/GLTFAnimationRuntime.ts +0 -1416
  26. package/src/GLTFAutoFit.ts +0 -133
  27. package/src/GLTFCompressionDecoders.ts +0 -190
  28. package/src/GLTFExtensionSupport.ts +0 -138
  29. package/src/GLTFLoader.ts +0 -4059
  30. package/src/GLTFRenderResources.ts +0 -2269
  31. package/src/HDRLoader.ts +0 -1
  32. package/src/ImageLoader.ts +0 -52
  33. package/src/ImportPipeline.ts +0 -148
  34. package/src/KTX2BasisTextureTranscoder.ts +0 -225
  35. package/src/LoadContext.ts +0 -60
  36. package/src/MaterialLoader.ts +0 -166
  37. package/src/MeshOptimization.ts +0 -93
  38. package/src/OBJLoader.ts +0 -551
  39. package/src/SceneAnalysisFixtures.ts +0 -438
  40. package/src/SceneLoader.ts +0 -62
  41. package/src/ShaderLoader.ts +0 -30
  42. package/src/TextureLoader.ts +0 -30
  43. package/src/TexturePipeline.ts +0 -96
  44. package/src/TextureStreaming.ts +0 -20
  45. package/src/WorkerAssetJobs.ts +0 -56
  46. package/src/advanced-gallery/index.ts +0 -18
  47. package/src/asset-corpus/GLTFSceneLoader.ts +0 -9
  48. package/src/asset-corpus/ProductionAssetCorpus.ts +0 -230
  49. package/src/asset-corpus/ProductionAssetPipeline.ts +0 -5
  50. package/src/asset-corpus/ProductionGLTFRenderPipeline.ts +0 -182
  51. package/src/asset-corpus/ProductionHDRTextureLoader.ts +0 -2
  52. package/src/asset-corpus/ProductionKTX2TextureLoader.ts +0 -2
  53. package/src/asset-corpus/ProductionTextureLoader.ts +0 -2
  54. package/src/asset-corpus/index.ts +0 -29
  55. package/src/browser-index.ts +0 -229
  56. package/src/createRenderableScene.ts +0 -128
  57. package/src/index.ts +0 -325
  58. package/src/loadRenderableAsset.ts +0 -67
  59. package/src/loaders/CubeTextureLoader.ts +0 -8
  60. package/src/loaders/EXRLoader.ts +0 -10
  61. package/src/loaders/HDRLoader.ts +0 -7
  62. package/src/loaders/KTX2Loader.ts +0 -7
  63. package/src/loaders/LoaderDiagnostics.ts +0 -36
  64. package/src/loaders/MTLLoader.ts +0 -20
  65. package/src/loaders/OBJLoader.ts +0 -24
  66. package/src/loaders/TextureLoader.ts +0 -14
  67. package/src/loaders/ThreeCompatGLTFLoader.ts +0 -19
  68. package/src/loaders/index.ts +0 -14
  69. package/src/threejs-compatibility/ThreeCompatAssetProvenance.ts +0 -51
  70. package/src/threejs-compatibility/ThreeCompatAssetRegistry.ts +0 -61
  71. package/src/threejs-example-parity/index.ts +0 -262
  72. package/tests/assets.test.ts +0 -393
  73. package/tsconfig.json +0 -7
@@ -1,438 +0,0 @@
1
- import type { GLTFAsset, GLTFMaterialAsset, GLTFMeshAsset } from "./GLTFLoader";
2
-
3
- export interface GLTFSceneAnalysisOptions {
4
- readonly asset: GLTFAsset;
5
- readonly url?: string;
6
- readonly maskWidth?: number;
7
- readonly maskHeight?: number;
8
- readonly minCoverage?: number;
9
- readonly topCategories?: number;
10
- }
11
-
12
- export interface GLTFSemanticSegment {
13
- readonly label: string;
14
- readonly classIndex: number;
15
- readonly pixelCount: number;
16
- readonly coverage: number;
17
- readonly confidence: number;
18
- readonly source: "geometry" | "material" | "texture" | "animation" | "scene";
19
- }
20
-
21
- export type GLTFComputerVisionBoundingBox = readonly [number, number, number, number];
22
-
23
- export interface GLTFObjectDetectionEvidence {
24
- readonly label: string;
25
- readonly confidence: number;
26
- readonly bbox: GLTFComputerVisionBoundingBox;
27
- readonly classIndex: number;
28
- readonly meshName: string;
29
- readonly materialName: string;
30
- }
31
-
32
- export interface GLTFObjectTrackEvidence {
33
- readonly id: number;
34
- readonly label: string;
35
- readonly bbox: GLTFComputerVisionBoundingBox;
36
- readonly confidence: number;
37
- readonly age: number;
38
- readonly timeSinceUpdate: number;
39
- readonly velocity: GLTFComputerVisionBoundingBox;
40
- readonly historyLength: number;
41
- }
42
-
43
- export interface GLTFPoseKeypointEvidence {
44
- readonly name: string;
45
- readonly x: number;
46
- readonly y: number;
47
- readonly confidence: number;
48
- }
49
-
50
- export interface GLTFPoseEvidence {
51
- readonly label: string;
52
- readonly confidence: number;
53
- readonly bbox: GLTFComputerVisionBoundingBox;
54
- readonly keypoints: readonly GLTFPoseKeypointEvidence[];
55
- }
56
-
57
- export interface GLTFSceneAnalysisEvidence {
58
- readonly source: "origin-master-scene-analyzer-adapted";
59
- readonly analyzer: "deterministic-gltf-scene-analysis";
60
- readonly assetId: string;
61
- readonly mask: {
62
- readonly width: number;
63
- readonly height: number;
64
- readonly classCount: number;
65
- readonly hash: string;
66
- readonly dominantClassIndex: number;
67
- };
68
- readonly segments: readonly GLTFSemanticSegment[];
69
- readonly dominantCategories: readonly string[];
70
- readonly confidence: number;
71
- readonly objectDetections: readonly GLTFObjectDetectionEvidence[];
72
- readonly objectTracks: readonly GLTFObjectTrackEvidence[];
73
- readonly poses: readonly GLTFPoseEvidence[];
74
- readonly cvSystem: {
75
- readonly source: "gltf-metadata-not-camera";
76
- readonly classificationTelemetry: boolean;
77
- readonly detectionTelemetry: boolean;
78
- readonly trackingTelemetry: boolean;
79
- readonly poseTelemetry: boolean;
80
- readonly inferenceRuntime: "not-used";
81
- readonly modelLoading: "not-used";
82
- };
83
- readonly sceneComplexity: {
84
- readonly meshCount: number;
85
- readonly materialCount: number;
86
- readonly textureCount: number;
87
- readonly vertexCount: number;
88
- readonly indexCount: number;
89
- readonly animationCount: number;
90
- readonly skinCount: number;
91
- readonly morphTargetCount: number;
92
- readonly extensionCount: number;
93
- };
94
- readonly productionReadiness: {
95
- readonly semanticSegmentTelemetry: true;
96
- readonly deterministicMaskSummary: true;
97
- readonly deterministicObjectTelemetry: true;
98
- readonly deterministicTrackingTelemetry: true;
99
- readonly skeletalPoseTelemetry: boolean;
100
- readonly complexityBudgetTelemetry: true;
101
- readonly browserAssetViewerEvidence: true;
102
- };
103
- readonly blockedClaims: readonly string[];
104
- readonly claimBoundary: string;
105
- readonly hash: string;
106
- }
107
-
108
- type SegmentAccumulator = {
109
- readonly label: string;
110
- readonly classIndex: number;
111
- readonly source: GLTFSemanticSegment["source"];
112
- weight: number;
113
- confidenceSum: number;
114
- confidenceSamples: number;
115
- };
116
-
117
- export function createGLTFSceneAnalysisEvidence(options: GLTFSceneAnalysisOptions): GLTFSceneAnalysisEvidence {
118
- const maskWidth = normalizeDimension(options.maskWidth ?? 48, "maskWidth");
119
- const maskHeight = normalizeDimension(options.maskHeight ?? 32, "maskHeight");
120
- const minCoverage = Math.max(0, options.minCoverage ?? 0.5);
121
- const topCategories = Math.max(1, Math.trunc(options.topCategories ?? 5));
122
- const assetId = normalizeAssetId(options.asset.url || options.url || options.asset.meshes[0]?.name || "gltf-scene");
123
- const accumulators = collectSegments(options.asset);
124
- const totalWeight = Math.max(1, [...accumulators.values()].reduce((sum, segment) => sum + segment.weight, 0));
125
- const segments = [...accumulators.values()]
126
- .map((segment): GLTFSemanticSegment => ({
127
- label: segment.label,
128
- classIndex: segment.classIndex,
129
- pixelCount: Math.max(1, Math.round((segment.weight / totalWeight) * maskWidth * maskHeight)),
130
- coverage: Number(((segment.weight / totalWeight) * 100).toFixed(3)),
131
- confidence: Number((segment.confidenceSum / Math.max(1, segment.confidenceSamples)).toFixed(4)),
132
- source: segment.source
133
- }))
134
- .filter((segment) => segment.coverage >= minCoverage)
135
- .sort((left, right) => right.coverage - left.coverage || left.classIndex - right.classIndex);
136
- const mask = createMaskSummary(maskWidth, maskHeight, segments);
137
- const sceneComplexity = summarizeComplexity(options.asset);
138
- const objectDetections = createObjectDetections(options.asset);
139
- const objectTracks = createObjectTracks(objectDetections);
140
- const poses = createPoseEvidence(options.asset, objectDetections);
141
- const core = {
142
- assetId,
143
- mask,
144
- dominantCategories: segments.slice(0, topCategories).map((segment) => segment.label),
145
- objectDetections,
146
- objectTracks,
147
- poses,
148
- sceneComplexity
149
- };
150
- return {
151
- source: "origin-master-scene-analyzer-adapted",
152
- analyzer: "deterministic-gltf-scene-analysis",
153
- assetId,
154
- mask,
155
- segments,
156
- dominantCategories: core.dominantCategories,
157
- confidence: Number((segments.reduce((sum, segment) => sum + segment.confidence * segment.coverage, 0) / Math.max(1, segments.reduce((sum, segment) => sum + segment.coverage, 0))).toFixed(4)),
158
- objectDetections,
159
- objectTracks,
160
- poses,
161
- cvSystem: {
162
- source: "gltf-metadata-not-camera",
163
- classificationTelemetry: segments.length > 0,
164
- detectionTelemetry: objectDetections.length > 0,
165
- trackingTelemetry: objectTracks.length > 0,
166
- poseTelemetry: poses.length > 0,
167
- inferenceRuntime: "not-used",
168
- modelLoading: "not-used"
169
- },
170
- sceneComplexity,
171
- productionReadiness: {
172
- semanticSegmentTelemetry: true,
173
- deterministicMaskSummary: true,
174
- deterministicObjectTelemetry: true,
175
- deterministicTrackingTelemetry: true,
176
- skeletalPoseTelemetry: poses.length > 0,
177
- complexityBudgetTelemetry: true,
178
- browserAssetViewerEvidence: true
179
- },
180
- blockedClaims: [
181
- "neural semantic segmentation parity",
182
- "pixel-accurate object detection parity",
183
- "real camera/image computer-vision inference",
184
- "Unity Sentis/Barracuda runtime parity",
185
- "Unreal ML Deformer or computer-vision plugin parity"
186
- ],
187
- claimBoundary: "This evidence ports the old branch SceneAnalyzer/ObjectDetector/ObjectTracker/PoseEstimator concepts into deterministic glTF scene telemetry: semantic buckets, object boxes, tracks, and skeleton pose keypoints are inferred from loaded asset metadata. It does not run neural segmentation, camera inference, YOLO/SORT/Posenet models, or pixel-accurate object detection, and it does not claim Unity or Unreal ML/CV parity.",
188
- hash: stableHash(JSON.stringify(core))
189
- };
190
- }
191
-
192
- function collectSegments(asset: GLTFAsset): Map<string, SegmentAccumulator> {
193
- const segments = new Map<string, SegmentAccumulator>();
194
- for (const mesh of asset.meshes) {
195
- const material = asset.materials[mesh.materialIndex ?? -1];
196
- addSegment(segments, "mesh-geometry", 1, "geometry", Math.max(1, mesh.geometry.vertexCount / 128), 0.88);
197
- addSegment(segments, lodGeometryLabel(mesh), 2, "geometry", Math.max(1, mesh.geometry.indexCount / 192), 0.82);
198
- if (mesh.skinIndex !== undefined) addSegment(segments, "skinned-character-geometry", 3, "geometry", 2.5, 0.9);
199
- if (mesh.morphTargets.length > 0) addSegment(segments, "morph-target-geometry", 4, "geometry", mesh.morphTargets.length * 1.8, 0.9);
200
- if (mesh.materialVariants.length > 0) addSegment(segments, "variant-material-region", 5, "material", mesh.materialVariants.length * 1.4, 0.84);
201
- if (material) collectMaterialSegments(segments, material);
202
- }
203
- if (asset.textures.length > 0) addSegment(segments, "texture-backed-surface", 10, "texture", asset.textures.length * 1.6, 0.86);
204
- if (asset.images.length > 0) addSegment(segments, "image-resource-region", 11, "texture", asset.images.length * 1.1, 0.82);
205
- if (asset.animations.length > 0) addSegment(segments, "animated-scene-region", 12, "animation", asset.animations.length * 2.2, 0.87);
206
- if (asset.skins.length > 0) addSegment(segments, "skeleton-rig-region", 13, "animation", asset.skins.length * 2.4, 0.89);
207
- if (asset.cameras.length > 0) addSegment(segments, "camera-authored-scene", 14, "scene", asset.cameras.length, 0.8);
208
- if (asset.lights.length > 0) addSegment(segments, "punctual-light-scene", 15, "scene", asset.lights.length * 1.2, 0.82);
209
- if (segments.size === 0) addSegment(segments, "empty-gltf-scene", 0, "scene", 1, 0.25);
210
- return segments;
211
- }
212
-
213
- function collectMaterialSegments(segments: Map<string, SegmentAccumulator>, material: GLTFMaterialAsset): void {
214
- addSegment(segments, material.unlit ? "unlit-material" : "pbr-material", 20, "material", material.unlit ? 1.1 : 2.8, material.unlit ? 0.76 : 0.9);
215
- if (material.metallicFactor > 0.05) addSegment(segments, "metallic-surface", 21, "material", 1 + material.metallicFactor * 2, 0.86);
216
- if (material.roughnessFactor < 0.45) addSegment(segments, "glossy-surface", 22, "material", 1 + (0.45 - material.roughnessFactor) * 2, 0.82);
217
- if (material.normalTexture) addSegment(segments, "normal-mapped-surface", 23, "texture", 1.5, 0.86);
218
- if (material.baseColorTexture) addSegment(segments, "albedo-textured-surface", 24, "texture", 1.7, 0.86);
219
- if (material.metallicRoughnessTexture) addSegment(segments, "metallic-roughness-textured-surface", 25, "texture", 1.5, 0.84);
220
- if (material.emissiveTexture || material.emissiveStrength > 1 || material.emissiveFactor.some((value) => value > 0)) addSegment(segments, "emissive-surface", 26, "material", 1.2 + material.emissiveStrength * 0.2, 0.84);
221
- if (material.alphaMode !== "OPAQUE") addSegment(segments, "transparent-surface", 27, "material", material.alphaMode === "BLEND" ? 1.7 : 1.2, 0.83);
222
- if (material.doubleSided) addSegment(segments, "double-sided-surface", 28, "material", 1.1, 0.8);
223
- if (material.clearcoat) addSegment(segments, "clearcoat-surface", 29, "material", 1.5, 0.84);
224
- if (material.transmission || material.diffuseTransmission || material.volume) addSegment(segments, "transmissive-volume-surface", 30, "material", 1.8, 0.82);
225
- if (material.sheen) addSegment(segments, "sheen-fabric-surface", 31, "material", 1.4, 0.82);
226
- if (material.anisotropy) addSegment(segments, "anisotropic-surface", 32, "material", 1.4, 0.82);
227
- if (material.iridescence) addSegment(segments, "iridescent-surface", 33, "material", 1.4, 0.82);
228
- }
229
-
230
- function addSegment(
231
- segments: Map<string, SegmentAccumulator>,
232
- label: string,
233
- classIndex: number,
234
- source: GLTFSemanticSegment["source"],
235
- weight: number,
236
- confidence: number
237
- ): void {
238
- const existing = segments.get(label);
239
- if (existing) {
240
- existing.weight += Math.max(0, weight);
241
- existing.confidenceSum += confidence;
242
- existing.confidenceSamples += 1;
243
- return;
244
- }
245
- segments.set(label, {
246
- label,
247
- classIndex,
248
- source,
249
- weight: Math.max(0, weight),
250
- confidenceSum: confidence,
251
- confidenceSamples: 1
252
- });
253
- }
254
-
255
- function createMaskSummary(width: number, height: number, segments: readonly GLTFSemanticSegment[]): GLTFSceneAnalysisEvidence["mask"] {
256
- const totalPixels = width * height;
257
- const mask = new Uint8Array(totalPixels);
258
- let cursor = 0;
259
- for (const segment of segments) {
260
- const count = Math.min(totalPixels - cursor, Math.max(1, Math.round((segment.coverage / 100) * totalPixels)));
261
- mask.fill(segment.classIndex, cursor, cursor + count);
262
- cursor += count;
263
- if (cursor >= totalPixels) break;
264
- }
265
- return {
266
- width,
267
- height,
268
- classCount: new Set(segments.map((segment) => segment.classIndex)).size,
269
- hash: stableHash(String.fromCharCode(...mask)),
270
- dominantClassIndex: segments[0]?.classIndex ?? 0
271
- };
272
- }
273
-
274
- function summarizeComplexity(asset: GLTFAsset): GLTFSceneAnalysisEvidence["sceneComplexity"] {
275
- return {
276
- meshCount: asset.meshes.length,
277
- materialCount: asset.materials.length,
278
- textureCount: asset.textures.length,
279
- vertexCount: asset.meshes.reduce((sum, mesh) => sum + mesh.geometry.vertexCount, 0),
280
- indexCount: asset.meshes.reduce((sum, mesh) => sum + mesh.geometry.indexCount, 0),
281
- animationCount: asset.animations.length,
282
- skinCount: asset.skins.length,
283
- morphTargetCount: asset.meshes.reduce((sum, mesh) => sum + mesh.morphTargets.length, 0),
284
- extensionCount: asset.loaderDiagnostics.extensionsUsed.length
285
- };
286
- }
287
-
288
- function createObjectDetections(asset: GLTFAsset): readonly GLTFObjectDetectionEvidence[] {
289
- const bounds = globalBounds(asset.meshes);
290
- return asset.meshes
291
- .map((mesh, index): GLTFObjectDetectionEvidence => {
292
- const material = asset.materials[mesh.materialIndex ?? -1];
293
- const bbox = normalizedBoundsBox(mesh, bounds);
294
- const materialConfidence = material && !material.unlit ? 0.06 : 0;
295
- const geometryConfidence = Math.min(0.12, mesh.geometry.vertexCount / 50_000);
296
- return {
297
- label: mesh.name || material?.name || `mesh-${index}`,
298
- confidence: Number(Math.min(0.98, 0.74 + materialConfidence + geometryConfidence).toFixed(4)),
299
- bbox,
300
- classIndex: index + 1,
301
- meshName: mesh.name || `mesh-${index}`,
302
- materialName: material?.name ?? mesh.material
303
- };
304
- })
305
- .sort((left, right) => right.confidence - left.confidence)
306
- .slice(0, 24);
307
- }
308
-
309
- function createObjectTracks(detections: readonly GLTFObjectDetectionEvidence[]): readonly GLTFObjectTrackEvidence[] {
310
- return detections.map((detection, index) => {
311
- const velocityScale = (index + 1) * 0.001;
312
- return {
313
- id: index + 1,
314
- label: detection.label,
315
- bbox: detection.bbox,
316
- confidence: detection.confidence,
317
- age: 2,
318
- timeSinceUpdate: 0,
319
- velocity: [
320
- Number((velocityScale * 0.8).toFixed(4)),
321
- Number((velocityScale * -0.45).toFixed(4)),
322
- 0,
323
- 0
324
- ],
325
- historyLength: 2
326
- };
327
- });
328
- }
329
-
330
- function createPoseEvidence(asset: GLTFAsset, detections: readonly GLTFObjectDetectionEvidence[]): readonly GLTFPoseEvidence[] {
331
- if (asset.skins.length === 0) return [];
332
- const target = detections[0];
333
- const bbox = target?.bbox ?? [0.35, 0.18, 0.3, 0.64] as const;
334
- const [x, y, width, height] = bbox;
335
- const keypointNames = [
336
- "head",
337
- "neck",
338
- "left_shoulder",
339
- "right_shoulder",
340
- "left_elbow",
341
- "right_elbow",
342
- "left_wrist",
343
- "right_wrist",
344
- "spine",
345
- "left_hip",
346
- "right_hip",
347
- "left_knee",
348
- "right_knee",
349
- "left_ankle",
350
- "right_ankle"
351
- ];
352
- const offsets: readonly (readonly [number, number])[] = [
353
- [0.5, 0.08],
354
- [0.5, 0.2],
355
- [0.28, 0.25],
356
- [0.72, 0.25],
357
- [0.18, 0.43],
358
- [0.82, 0.43],
359
- [0.12, 0.62],
360
- [0.88, 0.62],
361
- [0.5, 0.46],
362
- [0.36, 0.62],
363
- [0.64, 0.62],
364
- [0.32, 0.78],
365
- [0.68, 0.78],
366
- [0.3, 0.96],
367
- [0.7, 0.96]
368
- ];
369
- const keypoints = keypointNames.map((name, index): GLTFPoseKeypointEvidence => ({
370
- name,
371
- x: Number((x + width * (offsets[index]?.[0] ?? 0.5)).toFixed(4)),
372
- y: Number((y + height * (offsets[index]?.[1] ?? 0.5)).toFixed(4)),
373
- confidence: Number((0.74 + Math.min(0.2, asset.skins.length * 0.04) - index * 0.003).toFixed(4))
374
- }));
375
- return [{
376
- label: asset.skins[0]?.name || "gltf-skeleton-pose",
377
- confidence: Number((keypoints.reduce((sum, keypoint) => sum + keypoint.confidence, 0) / keypoints.length).toFixed(4)),
378
- bbox,
379
- keypoints
380
- }];
381
- }
382
-
383
- function globalBounds(meshes: readonly GLTFMeshAsset[]): { readonly min: readonly [number, number, number]; readonly max: readonly [number, number, number] } {
384
- const min: [number, number, number] = [Infinity, Infinity, Infinity];
385
- const max: [number, number, number] = [-Infinity, -Infinity, -Infinity];
386
- for (const mesh of meshes) {
387
- for (let axis = 0; axis < 3; axis += 1) {
388
- min[axis] = Math.min(min[axis] ?? Infinity, mesh.geometry.bounds.min[axis] ?? 0);
389
- max[axis] = Math.max(max[axis] ?? -Infinity, mesh.geometry.bounds.max[axis] ?? 0);
390
- }
391
- }
392
- if (!min.every(Number.isFinite) || !max.every(Number.isFinite)) return { min: [-1, -1, -1], max: [1, 1, 1] };
393
- return { min, max };
394
- }
395
-
396
- function normalizedBoundsBox(mesh: GLTFMeshAsset, bounds: ReturnType<typeof globalBounds>): GLTFComputerVisionBoundingBox {
397
- const rangeX = Math.max(0.001, bounds.max[0] - bounds.min[0]);
398
- const rangeY = Math.max(0.001, bounds.max[1] - bounds.min[1]);
399
- const x = clamp01((mesh.geometry.bounds.min[0] - bounds.min[0]) / rangeX);
400
- const y = clamp01((bounds.max[1] - mesh.geometry.bounds.max[1]) / rangeY);
401
- const width = Math.max(0.02, clamp01((mesh.geometry.bounds.max[0] - mesh.geometry.bounds.min[0]) / rangeX));
402
- const height = Math.max(0.02, clamp01((mesh.geometry.bounds.max[1] - mesh.geometry.bounds.min[1]) / rangeY));
403
- return [
404
- Number(x.toFixed(4)),
405
- Number(y.toFixed(4)),
406
- Number(Math.min(1 - x, width).toFixed(4)),
407
- Number(Math.min(1 - y, height).toFixed(4))
408
- ];
409
- }
410
-
411
- function lodGeometryLabel(mesh: GLTFMeshAsset): string {
412
- const vertices = mesh.geometry.vertexCount;
413
- if (vertices >= 20_000) return "high-density-geometry";
414
- if (vertices >= 2_000) return "medium-density-geometry";
415
- return "low-density-geometry";
416
- }
417
-
418
- function clamp01(value: number): number {
419
- return Math.max(0, Math.min(1, value));
420
- }
421
-
422
- function normalizeDimension(value: number, name: string): number {
423
- if (!Number.isInteger(value) || value < 8 || value > 256) throw new Error(`${name} must be an integer from 8 to 256.`);
424
- return value;
425
- }
426
-
427
- function normalizeAssetId(id: string): string {
428
- return id.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "gltf-scene";
429
- }
430
-
431
- function stableHash(value: string): string {
432
- let hash = 2166136261;
433
- for (let index = 0; index < value.length; index += 1) {
434
- hash ^= value.charCodeAt(index);
435
- hash = Math.imul(hash, 16777619);
436
- }
437
- return (hash >>> 0).toString(16).padStart(8, "0");
438
- }
@@ -1,62 +0,0 @@
1
- import { AnimationClip, type SerializedAnimationClip } from "@aura3d/animation";
2
- import { Renderable, Scene } from "@aura3d/scene";
3
- import type { AssetLoadRequest, AssetLoader } from "./AssetLoader";
4
- import type { LoadContext } from "./LoadContext";
5
-
6
- export interface NativeSceneNodeDescriptor {
7
- readonly name?: string;
8
- readonly translation?: readonly [number, number, number];
9
- readonly rotation?: readonly [number, number, number, number];
10
- readonly scale?: readonly [number, number, number];
11
- readonly renderable?: {
12
- readonly geometry: string;
13
- readonly material: string;
14
- readonly morphWeights?: readonly number[];
15
- };
16
- readonly children?: readonly NativeSceneNodeDescriptor[];
17
- }
18
-
19
- export interface NativeSceneAsset {
20
- readonly url: string;
21
- readonly scene: Scene;
22
- readonly animations: readonly AnimationClip[];
23
- }
24
-
25
- export class SceneLoader implements AssetLoader<NativeSceneAsset> {
26
- readonly type = "scene";
27
-
28
- canLoad(request: AssetLoadRequest): boolean {
29
- return /\.scene\.json(?:\?.*)?$/i.test(request.url);
30
- }
31
-
32
- async load(request: AssetLoadRequest, context: LoadContext): Promise<NativeSceneAsset> {
33
- context.throwIfAborted(request.url);
34
- if (typeof fetch !== "function") {
35
- throw new Error("SceneLoader requires fetch");
36
- }
37
- const response = await fetch(request.url, { signal: request.signal });
38
- if (!response.ok) {
39
- throw new Error(`Scene request failed with ${response.status}`);
40
- }
41
- const descriptor = (await response.json()) as {
42
- readonly nodes?: readonly NativeSceneNodeDescriptor[];
43
- readonly animations?: readonly SerializedAnimationClip[];
44
- };
45
- const scene = new Scene();
46
- for (const node of descriptor.nodes ?? []) {
47
- scene.root.addChild(createNode(scene, node));
48
- }
49
- const animations = (descriptor.animations ?? []).map((animation) => AnimationClip.fromJSON(animation));
50
- return { url: request.url, scene, animations };
51
- }
52
- }
53
-
54
- function createNode(scene: Scene, descriptor: NativeSceneNodeDescriptor) {
55
- const node = scene.createNode(descriptor.name);
56
- if (descriptor.translation) node.transform.setPosition(descriptor.translation[0], descriptor.translation[1], descriptor.translation[2]);
57
- if (descriptor.rotation) node.transform.setRotation(descriptor.rotation[0], descriptor.rotation[1], descriptor.rotation[2], descriptor.rotation[3]);
58
- if (descriptor.scale) node.transform.setScale(descriptor.scale[0], descriptor.scale[1], descriptor.scale[2]);
59
- if (descriptor.renderable) scene.addRenderable(node, new Renderable(descriptor.renderable));
60
- for (const child of descriptor.children ?? []) node.addChild(createNode(scene, child));
61
- return node;
62
- }
@@ -1,30 +0,0 @@
1
- import type { AssetLoadRequest, AssetLoader } from "./AssetLoader";
2
- import type { LoadContext } from "./LoadContext";
3
-
4
- export interface ShaderSourceAsset {
5
- readonly url: string;
6
- readonly source: string;
7
- }
8
-
9
- export class ShaderLoader implements AssetLoader<ShaderSourceAsset> {
10
- readonly type = "shader";
11
-
12
- canLoad(request: AssetLoadRequest): boolean {
13
- return /\.(?:wgsl|glsl|vert|frag)(?:\?.*)?$/i.test(request.url);
14
- }
15
-
16
- async load(request: AssetLoadRequest, context: LoadContext): Promise<ShaderSourceAsset> {
17
- context.throwIfAborted(request.url);
18
-
19
- if (typeof fetch !== "function") {
20
- throw new Error("ShaderLoader requires fetch");
21
- }
22
-
23
- const response = await fetch(request.url, { signal: request.signal });
24
- if (!response.ok) {
25
- throw new Error(`Shader request failed with ${response.status}`);
26
- }
27
-
28
- return { url: request.url, source: await response.text() };
29
- }
30
- }
@@ -1,30 +0,0 @@
1
- import type { AssetLoadRequest, AssetLoader } from "./AssetLoader";
2
- import type { LoadContext } from "./LoadContext";
3
- import type { ImageAsset } from "./ImageLoader";
4
-
5
- export interface TextureDescriptorAsset {
6
- readonly url: string;
7
- readonly width: number;
8
- readonly height: number;
9
- readonly colorSpace: "srgb" | "linear";
10
- readonly source: ImageAsset["source"];
11
- }
12
-
13
- export class TextureLoader implements AssetLoader<TextureDescriptorAsset> {
14
- readonly type = "texture";
15
-
16
- canLoad(request: AssetLoadRequest): boolean {
17
- return request.type === this.type && (/\.(?:png|jpg|jpeg|webp|avif)(?:\?.*)?$/i.test(request.url) || request.url.startsWith("data:image/"));
18
- }
19
-
20
- async load(request: AssetLoadRequest, context: LoadContext): Promise<TextureDescriptorAsset> {
21
- const image = await context.loadDependency<ImageAsset>(request.url, "image");
22
- return {
23
- url: request.url,
24
- width: image.value.width,
25
- height: image.value.height,
26
- colorSpace: "srgb",
27
- source: image.value.source
28
- };
29
- }
30
- }
@@ -1,96 +0,0 @@
1
- import type { ImportStage } from "./ImportPipeline";
2
-
3
- export interface TextureMipGenerationInput {
4
- readonly width: number;
5
- readonly height: number;
6
- readonly data: Uint8Array | Uint8ClampedArray;
7
- readonly colorSpace?: "linear" | "srgb";
8
- }
9
-
10
- export interface TextureMipLevel {
11
- readonly width: number;
12
- readonly height: number;
13
- readonly data: Uint8Array;
14
- }
15
-
16
- export interface TextureMipGenerationResult {
17
- readonly width: number;
18
- readonly height: number;
19
- readonly colorSpace: "linear" | "srgb";
20
- readonly levels: readonly TextureMipLevel[];
21
- }
22
-
23
- export interface TextureMipGenerationStageOptions {
24
- readonly name?: string;
25
- }
26
-
27
- export function generateTextureMipChain(input: TextureMipGenerationInput): TextureMipGenerationResult {
28
- validateTextureMipInput(input);
29
-
30
- const levels: TextureMipLevel[] = [{
31
- width: input.width,
32
- height: input.height,
33
- data: new Uint8Array(input.data)
34
- }];
35
-
36
- while (levels[levels.length - 1]!.width > 1 || levels[levels.length - 1]!.height > 1) {
37
- levels.push(downsampleRGBA8(levels[levels.length - 1]!));
38
- }
39
-
40
- return {
41
- width: input.width,
42
- height: input.height,
43
- colorSpace: input.colorSpace ?? "srgb",
44
- levels
45
- };
46
- }
47
-
48
- export function createTextureMipGenerationStage(options: TextureMipGenerationStageOptions = {}): ImportStage<TextureMipGenerationInput, TextureMipGenerationResult> {
49
- return {
50
- name: options.name ?? "texture-mip-generation",
51
- run: (input) => generateTextureMipChain(input)
52
- };
53
- }
54
-
55
- function validateTextureMipInput(input: TextureMipGenerationInput): void {
56
- if (!Number.isInteger(input.width) || input.width <= 0) {
57
- throw new Error("Texture mip generation width must be a positive integer");
58
- }
59
- if (!Number.isInteger(input.height) || input.height <= 0) {
60
- throw new Error("Texture mip generation height must be a positive integer");
61
- }
62
- if (!(input.data instanceof Uint8Array) && !(input.data instanceof Uint8ClampedArray)) {
63
- throw new Error("Texture mip generation requires RGBA8 byte data");
64
- }
65
- if (input.data.byteLength !== input.width * input.height * 4) {
66
- throw new Error("Texture mip generation data must contain exactly width * height * 4 bytes");
67
- }
68
- if (input.colorSpace !== undefined && input.colorSpace !== "linear" && input.colorSpace !== "srgb") {
69
- throw new Error("Texture mip generation colorSpace must be linear or srgb");
70
- }
71
- }
72
-
73
- function downsampleRGBA8(source: TextureMipLevel): TextureMipLevel {
74
- const width = Math.max(1, Math.ceil(source.width / 2));
75
- const height = Math.max(1, Math.ceil(source.height / 2));
76
- const data = new Uint8Array(width * height * 4);
77
-
78
- for (let y = 0; y < height; y += 1) {
79
- for (let x = 0; x < width; x += 1) {
80
- const samples: number[] = [];
81
- for (let sy = y * 2; sy < Math.min(source.height, y * 2 + 2); sy += 1) {
82
- for (let sx = x * 2; sx < Math.min(source.width, x * 2 + 2); sx += 1) {
83
- samples.push((sy * source.width + sx) * 4);
84
- }
85
- }
86
- const output = (y * width + x) * 4;
87
- for (let component = 0; component < 4; component += 1) {
88
- let total = 0;
89
- for (const sample of samples) total += source.data[sample + component] ?? 0;
90
- data[output + component] = Math.round(total / samples.length);
91
- }
92
- }
93
- }
94
-
95
- return { width, height, data };
96
- }
@@ -1,20 +0,0 @@
1
- export interface TextureStreamingBudget {
2
- readonly maxTextureBytes: number;
3
- readonly loadedTextureBytes: number;
4
- }
5
-
6
- export interface TextureStreamingDecision {
7
- readonly canUpload: boolean;
8
- readonly remainingBytes: number;
9
- }
10
-
11
- export function evaluateTextureStreamingBudget(budget: TextureStreamingBudget): TextureStreamingDecision {
12
- if (!Number.isFinite(budget.maxTextureBytes) || budget.maxTextureBytes < 0) {
13
- throw new Error("Texture streaming maxTextureBytes must be finite and non-negative.");
14
- }
15
- if (!Number.isFinite(budget.loadedTextureBytes) || budget.loadedTextureBytes < 0) {
16
- throw new Error("Texture streaming loadedTextureBytes must be finite and non-negative.");
17
- }
18
- const remainingBytes = Math.max(0, budget.maxTextureBytes - budget.loadedTextureBytes);
19
- return { canUpload: remainingBytes > 0, remainingBytes };
20
- }