@forgeax/engine-fbx 0.1.3 → 0.1.6
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 +9 -17
- package/dist/.tsbuildinfo +1 -1
- package/dist/fbx-importer.d.ts +1 -2
- package/dist/fbx-importer.d.ts.map +1 -1
- package/dist/index.mjs +206 -8253
- package/dist/index.mjs.map +1 -1
- package/dist/parse-material.d.ts +1 -7
- package/dist/parse-material.d.ts.map +1 -1
- package/dist/parse-texture.d.ts +1 -5
- package/dist/parse-texture.d.ts.map +1 -1
- package/dist/to-asset-pack.d.ts +1 -8
- package/dist/to-asset-pack.d.ts.map +1 -1
- package/package.json +11 -12
- package/pkg/fbx-wasm.wasm +0 -0
- package/src/__tests__/blendshape-import.integration.test.ts +12 -18
- package/src/__tests__/fbx-importer.test.ts +1 -165
- package/src/__tests__/index.test.ts +1 -0
- package/src/fbx-importer.ts +10 -236
- package/src/native/bridge.c +0 -118
- package/src/parse-material.ts +1 -49
- package/src/parse-texture.ts +3 -17
- package/src/to-asset-pack.ts +20 -91
- package/dist/__tests__/asset-runtime-fixture.d.ts +0 -9
- package/dist/__tests__/asset-runtime-fixture.d.ts.map +0 -1
- package/src/__tests__/asset-runtime-fixture.ts +0 -251
package/src/native/bridge.c
CHANGED
|
@@ -395,118 +395,6 @@ static void write_nodes(Buf *b, ufbx_scene *scene) {
|
|
|
395
395
|
|
|
396
396
|
/* ── Materials writing ─────────────────────────────────────────────── */
|
|
397
397
|
|
|
398
|
-
static int texture_index(ufbx_scene *scene, const ufbx_texture *texture) {
|
|
399
|
-
if (!texture) return -1;
|
|
400
|
-
for (size_t i = 0; i < scene->textures.count; i++) {
|
|
401
|
-
if (scene->textures.data[i] == texture) return (int)i;
|
|
402
|
-
}
|
|
403
|
-
return -1;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
static int map_texture_index(ufbx_scene *scene, const ufbx_material_map *map) {
|
|
407
|
-
if (!map || !map->texture_enabled || !map->texture) return -1;
|
|
408
|
-
return texture_index(scene, map->texture);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
static void write_texture_binding(
|
|
412
|
-
Buf *b,
|
|
413
|
-
ufbx_scene *scene,
|
|
414
|
-
const char *slot,
|
|
415
|
-
const ufbx_material_map *map,
|
|
416
|
-
int *first
|
|
417
|
-
) {
|
|
418
|
-
int index = map_texture_index(scene, map);
|
|
419
|
-
if (index < 0) return;
|
|
420
|
-
if (!*first) buf_char(b, ',');
|
|
421
|
-
*first = 0;
|
|
422
|
-
buf_str(b, "{\"slot\":");
|
|
423
|
-
buf_quoted(b, slot);
|
|
424
|
-
buf_str(b, ",\"textureIndex\":");
|
|
425
|
-
buf_int(b, index);
|
|
426
|
-
buf_char(b, '}');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
static void write_texture_bindings(Buf *b, ufbx_scene *scene, ufbx_material *mat) {
|
|
430
|
-
const ufbx_material_map *base_color =
|
|
431
|
-
map_texture_index(scene, &mat->pbr.base_color) >= 0 ? &mat->pbr.base_color : &mat->fbx.diffuse_color;
|
|
432
|
-
const ufbx_material_map *normal =
|
|
433
|
-
map_texture_index(scene, &mat->pbr.normal_map) >= 0 ? &mat->pbr.normal_map : &mat->fbx.normal_map;
|
|
434
|
-
const ufbx_material_map *specular =
|
|
435
|
-
map_texture_index(scene, &mat->pbr.specular_color) >= 0 ? &mat->pbr.specular_color : &mat->fbx.specular_color;
|
|
436
|
-
const ufbx_material_map *emissive =
|
|
437
|
-
map_texture_index(scene, &mat->pbr.emission_color) >= 0 ? &mat->pbr.emission_color : &mat->fbx.emission_color;
|
|
438
|
-
const ufbx_material_map *occlusion = &mat->pbr.ambient_occlusion;
|
|
439
|
-
const ufbx_material_map *metalness = &mat->pbr.metalness;
|
|
440
|
-
const ufbx_material_map *roughness = &mat->pbr.roughness;
|
|
441
|
-
int has_any =
|
|
442
|
-
map_texture_index(scene, base_color) >= 0 ||
|
|
443
|
-
map_texture_index(scene, normal) >= 0 ||
|
|
444
|
-
map_texture_index(scene, specular) >= 0 ||
|
|
445
|
-
map_texture_index(scene, emissive) >= 0 ||
|
|
446
|
-
map_texture_index(scene, occlusion) >= 0 ||
|
|
447
|
-
(map_texture_index(scene, metalness) >= 0 && map_texture_index(scene, roughness) >= 0 &&
|
|
448
|
-
map_texture_index(scene, metalness) == map_texture_index(scene, roughness));
|
|
449
|
-
if (!has_any) return;
|
|
450
|
-
|
|
451
|
-
buf_str(b, ",\"textureBindings\":[");
|
|
452
|
-
int first = 1;
|
|
453
|
-
write_texture_binding(b, scene, "baseColorTexture", base_color, &first);
|
|
454
|
-
write_texture_binding(b, scene, "normalTexture", normal, &first);
|
|
455
|
-
write_texture_binding(b, scene, "specularTintTexture", specular, &first);
|
|
456
|
-
write_texture_binding(b, scene, "emissiveTexture", emissive, &first);
|
|
457
|
-
write_texture_binding(b, scene, "occlusionTexture", occlusion, &first);
|
|
458
|
-
if (map_texture_index(scene, metalness) >= 0 &&
|
|
459
|
-
map_texture_index(scene, metalness) == map_texture_index(scene, roughness)) {
|
|
460
|
-
write_texture_binding(b, scene, "metallicRoughnessTexture", metalness, &first);
|
|
461
|
-
}
|
|
462
|
-
buf_char(b, ']');
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
static const char *texture_type_name(ufbx_texture_type type) {
|
|
466
|
-
switch (type) {
|
|
467
|
-
case UFBX_TEXTURE_FILE: return "file";
|
|
468
|
-
case UFBX_TEXTURE_LAYERED: return "layered";
|
|
469
|
-
case UFBX_TEXTURE_PROCEDURAL: return "procedural";
|
|
470
|
-
case UFBX_TEXTURE_SHADER: return "shader";
|
|
471
|
-
default: return "unknown";
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
static void write_textures(Buf *b, ufbx_scene *scene) {
|
|
476
|
-
if (scene->textures.count == 0) return;
|
|
477
|
-
buf_str(b, ",\"textures\":[");
|
|
478
|
-
for (size_t i = 0; i < scene->textures.count; i++) {
|
|
479
|
-
ufbx_texture *texture = scene->textures.data[i];
|
|
480
|
-
if (i > 0) buf_char(b, ',');
|
|
481
|
-
buf_str(b, "{\"name\":");
|
|
482
|
-
buf_quoted(b, texture->name.data ? texture->name.data : "");
|
|
483
|
-
buf_str(b, ",\"type\":");
|
|
484
|
-
buf_quoted(b, texture_type_name(texture->type));
|
|
485
|
-
buf_str(b, ",\"filePath\":");
|
|
486
|
-
if (texture->relative_filename.data && texture->relative_filename.data[0] != '\0') {
|
|
487
|
-
buf_quoted(b, texture->relative_filename.data);
|
|
488
|
-
} else if (texture->filename.data && texture->filename.data[0] != '\0') {
|
|
489
|
-
buf_quoted(b, texture->filename.data);
|
|
490
|
-
} else {
|
|
491
|
-
buf_quoted(b, "");
|
|
492
|
-
}
|
|
493
|
-
buf_str(b, ",\"relativeFilePath\":");
|
|
494
|
-
buf_quoted(b, texture->relative_filename.data ? texture->relative_filename.data : "");
|
|
495
|
-
buf_str(b, ",\"absoluteFilePath\":");
|
|
496
|
-
buf_quoted(b, texture->absolute_filename.data ? texture->absolute_filename.data : "");
|
|
497
|
-
buf_str(b, ",\"sourceIndex\":");
|
|
498
|
-
buf_size(b, i);
|
|
499
|
-
buf_str(b, ",\"embeddedBytes\":[");
|
|
500
|
-
const unsigned char *content = (const unsigned char *)texture->content.data;
|
|
501
|
-
for (size_t byte = 0; content && byte < texture->content.size; byte++) {
|
|
502
|
-
if (byte > 0) buf_char(b, ',');
|
|
503
|
-
buf_int(b, content[byte]);
|
|
504
|
-
}
|
|
505
|
-
buf_str(b, "]}");
|
|
506
|
-
}
|
|
507
|
-
buf_char(b, ']');
|
|
508
|
-
}
|
|
509
|
-
|
|
510
398
|
static void write_materials(Buf *b, ufbx_scene *scene) {
|
|
511
399
|
if (scene->materials.count == 0) return;
|
|
512
400
|
|
|
@@ -596,8 +484,6 @@ static void write_materials(Buf *b, ufbx_scene *scene) {
|
|
|
596
484
|
buf_str(b, "\"kind\":\"fallback\"");
|
|
597
485
|
}
|
|
598
486
|
|
|
599
|
-
write_texture_bindings(b, scene, mat);
|
|
600
|
-
|
|
601
487
|
buf_str(b, ",\"sourceIndex\":");
|
|
602
488
|
buf_size(b, i);
|
|
603
489
|
|
|
@@ -1024,10 +910,6 @@ void parseFbxWasm(const void *data, size_t size) {
|
|
|
1024
910
|
/* Nodes */
|
|
1025
911
|
write_nodes(&b, scene);
|
|
1026
912
|
|
|
1027
|
-
/* Textures must be emitted before materials so the JSON remains easy to
|
|
1028
|
-
inspect; material bindings refer to these stable scene texture indices. */
|
|
1029
|
-
write_textures(&b, scene);
|
|
1030
|
-
|
|
1031
913
|
/* Materials */
|
|
1032
914
|
write_materials(&b, scene);
|
|
1033
915
|
|
package/src/parse-material.ts
CHANGED
|
@@ -7,13 +7,7 @@
|
|
|
7
7
|
// 3. Lambert → same formula as Phong (max_gloss=100 default)
|
|
8
8
|
// 4. Fallback → default grey PBR: baseColor=[0.5,0.5,0.5], metal=0, rough=0.5
|
|
9
9
|
|
|
10
|
-
import type { MaterialPod
|
|
11
|
-
|
|
12
|
-
export interface FbxRawTextureBinding {
|
|
13
|
-
readonly slot: MaterialTextureBindingPod['slot'];
|
|
14
|
-
readonly textureIndex: number;
|
|
15
|
-
readonly texCoord?: number;
|
|
16
|
-
}
|
|
10
|
+
import type { MaterialPod } from '@forgeax/engine-types';
|
|
17
11
|
|
|
18
12
|
/** JSON shape emitted by binding.cc WriteMaterials for a single material. */
|
|
19
13
|
export interface FbxRawMaterial {
|
|
@@ -23,7 +17,6 @@ export interface FbxRawMaterial {
|
|
|
23
17
|
readonly diffuse?: readonly [number, number, number];
|
|
24
18
|
readonly shininess?: number;
|
|
25
19
|
readonly specular?: readonly [number, number, number];
|
|
26
|
-
readonly textureBindings?: readonly FbxRawTextureBinding[];
|
|
27
20
|
readonly stingrayProps?: {
|
|
28
21
|
readonly baseColor?: readonly [number, number, number];
|
|
29
22
|
readonly metallic?: number;
|
|
@@ -50,43 +43,6 @@ function phongRoughness(shininess: number, maxGloss = 100): number {
|
|
|
50
43
|
export function parseMaterial(raw: FbxRawMaterial, sourceIndex: number): MaterialPod {
|
|
51
44
|
void sourceIndex; // reserved for future cross-referencing
|
|
52
45
|
|
|
53
|
-
const textureBindings = raw.textureBindings?.map((binding) => ({
|
|
54
|
-
slot: binding.slot,
|
|
55
|
-
textureIndex: binding.textureIndex,
|
|
56
|
-
...(binding.texCoord === undefined ? {} : { texCoord: binding.texCoord }),
|
|
57
|
-
}));
|
|
58
|
-
const textureFields: Pick<
|
|
59
|
-
MaterialPod,
|
|
60
|
-
| 'textureBindings'
|
|
61
|
-
| 'baseColorTextureIndex'
|
|
62
|
-
| 'metallicRoughnessTextureIndex'
|
|
63
|
-
| 'normalTextureIndex'
|
|
64
|
-
| 'occlusionTextureIndex'
|
|
65
|
-
| 'emissiveTextureIndex'
|
|
66
|
-
| 'specularTintTextureIndex'
|
|
67
|
-
> =
|
|
68
|
-
textureBindings === undefined || textureBindings.length === 0
|
|
69
|
-
? {}
|
|
70
|
-
: {
|
|
71
|
-
textureBindings,
|
|
72
|
-
...Object.fromEntries(
|
|
73
|
-
textureBindings.map((binding) => [
|
|
74
|
-
binding.slot === 'baseColorTexture'
|
|
75
|
-
? 'baseColorTextureIndex'
|
|
76
|
-
: binding.slot === 'metallicRoughnessTexture'
|
|
77
|
-
? 'metallicRoughnessTextureIndex'
|
|
78
|
-
: binding.slot === 'normalTexture'
|
|
79
|
-
? 'normalTextureIndex'
|
|
80
|
-
: binding.slot === 'occlusionTexture'
|
|
81
|
-
? 'occlusionTextureIndex'
|
|
82
|
-
: binding.slot === 'emissiveTexture'
|
|
83
|
-
? 'emissiveTextureIndex'
|
|
84
|
-
: 'specularTintTextureIndex',
|
|
85
|
-
binding.textureIndex,
|
|
86
|
-
]),
|
|
87
|
-
),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
46
|
switch (raw.kind) {
|
|
91
47
|
case 'stingray-pbs': {
|
|
92
48
|
const sp = raw.stingrayProps ?? {};
|
|
@@ -100,7 +56,6 @@ export function parseMaterial(raw: FbxRawMaterial, sourceIndex: number): Materia
|
|
|
100
56
|
],
|
|
101
57
|
metallicFactor: sp.metallic ?? 0,
|
|
102
58
|
roughnessFactor: sp.roughness ?? 0.5,
|
|
103
|
-
...textureFields,
|
|
104
59
|
};
|
|
105
60
|
}
|
|
106
61
|
|
|
@@ -112,7 +67,6 @@ export function parseMaterial(raw: FbxRawMaterial, sourceIndex: number): Materia
|
|
|
112
67
|
baseColorFactor: [d[0] ?? 0.5, d[1] ?? 0.5, d[2] ?? 0.5, 1],
|
|
113
68
|
metallicFactor: 0,
|
|
114
69
|
roughnessFactor: phongRoughness(gloss),
|
|
115
|
-
...textureFields,
|
|
116
70
|
};
|
|
117
71
|
}
|
|
118
72
|
|
|
@@ -123,7 +77,6 @@ export function parseMaterial(raw: FbxRawMaterial, sourceIndex: number): Materia
|
|
|
123
77
|
baseColorFactor: [d[0] ?? 0.5, d[1] ?? 0.5, d[2] ?? 0.5, 1],
|
|
124
78
|
metallicFactor: 0,
|
|
125
79
|
roughnessFactor: 0.5, // lambert has no specular → default roughness
|
|
126
|
-
...textureFields,
|
|
127
80
|
};
|
|
128
81
|
}
|
|
129
82
|
|
|
@@ -133,7 +86,6 @@ export function parseMaterial(raw: FbxRawMaterial, sourceIndex: number): Materia
|
|
|
133
86
|
baseColorFactor: [0.5, 0.5, 0.5, 1],
|
|
134
87
|
metallicFactor: 0,
|
|
135
88
|
roughnessFactor: 0.5,
|
|
136
|
-
...textureFields,
|
|
137
89
|
};
|
|
138
90
|
}
|
|
139
91
|
}
|
package/src/parse-texture.ts
CHANGED
|
@@ -4,11 +4,7 @@ import type { TexturePod } from '@forgeax/engine-types';
|
|
|
4
4
|
|
|
5
5
|
export interface FbxRawTexture {
|
|
6
6
|
readonly name?: string;
|
|
7
|
-
readonly filePath
|
|
8
|
-
readonly relativeFilePath?: string;
|
|
9
|
-
readonly absoluteFilePath?: string;
|
|
10
|
-
readonly embeddedBytes?: readonly number[];
|
|
11
|
-
readonly type?: 'file' | 'layered' | 'procedural' | 'shader' | 'unknown';
|
|
7
|
+
readonly filePath: string;
|
|
12
8
|
readonly sourceIndex: number;
|
|
13
9
|
}
|
|
14
10
|
|
|
@@ -16,21 +12,11 @@ export interface FbxRawTextures {
|
|
|
16
12
|
readonly textures?: readonly FbxRawTexture[];
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
function normalizePath(path: string): string {
|
|
20
|
-
return path.replaceAll('\\', '/');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
export function parseTextures(raw: FbxRawTextures): TexturePod[] {
|
|
24
16
|
const textures = raw.textures ?? [];
|
|
25
17
|
return textures.map((t) => ({
|
|
26
|
-
name: t.name ?? t.filePath
|
|
27
|
-
filePath:
|
|
28
|
-
...(t.relativeFilePath === undefined
|
|
29
|
-
? {}
|
|
30
|
-
: { relativeFilePath: normalizePath(t.relativeFilePath) }),
|
|
31
|
-
...(t.absoluteFilePath === undefined ? {} : { absoluteFilePath: t.absoluteFilePath }),
|
|
32
|
-
...(t.embeddedBytes === undefined ? {} : { embeddedBytes: Uint8Array.from(t.embeddedBytes) }),
|
|
33
|
-
...(t.type === undefined ? {} : { type: t.type }),
|
|
18
|
+
name: t.name ?? t.filePath,
|
|
19
|
+
filePath: t.filePath,
|
|
34
20
|
sourceIndex: t.sourceIndex,
|
|
35
21
|
}));
|
|
36
22
|
}
|
package/src/to-asset-pack.ts
CHANGED
|
@@ -7,18 +7,15 @@
|
|
|
7
7
|
// The import-runner then validates produced == declared and rejects mismatches.
|
|
8
8
|
|
|
9
9
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
10
|
-
import { packMeshBinV4 } from '@forgeax/engine-import';
|
|
10
|
+
import { packMeshBinV4 } from '@forgeax/engine-import/mesh-bin';
|
|
11
11
|
import { box3 } from '@forgeax/engine-math';
|
|
12
12
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
13
13
|
import type {
|
|
14
14
|
AnimationClipPod,
|
|
15
|
-
AssetCodec,
|
|
16
15
|
AssetRef,
|
|
17
16
|
ImportedAsset,
|
|
18
17
|
MaterialAsset,
|
|
19
18
|
MaterialPod,
|
|
20
|
-
MaterialTextureBindingPod,
|
|
21
|
-
MaterialTextureValue,
|
|
22
19
|
MeshAsset,
|
|
23
20
|
MeshMaterialSlot,
|
|
24
21
|
MeshMaterialSlotTopologyEntry,
|
|
@@ -28,7 +25,6 @@ import type {
|
|
|
28
25
|
SkeletonPod,
|
|
29
26
|
SkinPod,
|
|
30
27
|
SourceOverrideMap,
|
|
31
|
-
TextureAsset,
|
|
32
28
|
TexturePod,
|
|
33
29
|
} from '@forgeax/engine-types';
|
|
34
30
|
import {
|
|
@@ -346,61 +342,11 @@ export function buildMeshAsset(
|
|
|
346
342
|
};
|
|
347
343
|
}
|
|
348
344
|
|
|
349
|
-
|
|
350
|
-
readonly texture: TextureAsset;
|
|
351
|
-
readonly bytes: Uint8Array;
|
|
352
|
-
readonly mediaType?: string;
|
|
353
|
-
readonly assetCodec?: AssetCodec;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function materialBindings(pod: MaterialPod): readonly MaterialTextureBindingPod[] {
|
|
357
|
-
if (pod.textureBindings !== undefined) return pod.textureBindings;
|
|
358
|
-
const bindings: MaterialTextureBindingPod[] = [];
|
|
359
|
-
const legacy: readonly [MaterialTextureBindingPod['slot'], number | undefined][] = [
|
|
360
|
-
['baseColorTexture', pod.baseColorTextureIndex],
|
|
361
|
-
['metallicRoughnessTexture', pod.metallicRoughnessTextureIndex],
|
|
362
|
-
['normalTexture', pod.normalTextureIndex],
|
|
363
|
-
['specularTintTexture', pod.specularTintTextureIndex],
|
|
364
|
-
['emissiveTexture', pod.emissiveTextureIndex],
|
|
365
|
-
['occlusionTexture', pod.occlusionTextureIndex],
|
|
366
|
-
];
|
|
367
|
-
for (const [slot, textureIndex] of legacy) {
|
|
368
|
-
if (textureIndex !== undefined) bindings.push({ slot, textureIndex });
|
|
369
|
-
}
|
|
370
|
-
return bindings;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function buildMaterialAsset(
|
|
374
|
-
pod: MaterialPod,
|
|
375
|
-
guid: string,
|
|
376
|
-
skinned = false,
|
|
377
|
-
textureGuidByIndex: ReadonlyMap<number, string> = new Map(),
|
|
378
|
-
): ImportedAsset {
|
|
345
|
+
function buildMaterialAsset(pod: MaterialPod, guid: string, skinned = false): ImportedAsset {
|
|
379
346
|
// A material consumed by a skinned mesh must select the pbr-skin shader so the
|
|
380
347
|
// runtime PSO chain (LayoutKind 'pbr-skin' + 18-float vertex layout + joint
|
|
381
348
|
// palette) is exercised; the render-system fail-fasts otherwise (mirror of
|
|
382
349
|
// gltfImporter's `skinned` routing).
|
|
383
|
-
const values: Record<string, NonNullable<MaterialAsset['values']>[string]> = {
|
|
384
|
-
baseColor: pod.baseColorFactor as readonly [number, number, number, number],
|
|
385
|
-
metallic: pod.metallicFactor,
|
|
386
|
-
roughness: pod.roughnessFactor,
|
|
387
|
-
};
|
|
388
|
-
const refs: AssetRef[] = [];
|
|
389
|
-
for (const binding of materialBindings(pod)) {
|
|
390
|
-
const textureGuid = textureGuidByIndex.get(binding.textureIndex);
|
|
391
|
-
if (textureGuid === undefined) continue;
|
|
392
|
-
const textureValue: MaterialTextureValue = {
|
|
393
|
-
// Runtime material loading interprets texture handles as indexes into
|
|
394
|
-
// this asset's refs[]; the GUID is carried by the corresponding edge.
|
|
395
|
-
texture: refs.length as unknown as MaterialTextureValue['texture'],
|
|
396
|
-
...(binding.texCoord === undefined ? {} : { coordinates: { set: binding.texCoord } }),
|
|
397
|
-
};
|
|
398
|
-
values[binding.slot] = textureValue;
|
|
399
|
-
refs.push({
|
|
400
|
-
guid: textureGuid,
|
|
401
|
-
sourceField: { componentName: '<material>', fieldName: binding.slot },
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
350
|
const mat: MaterialAsset = {
|
|
405
351
|
kind: 'material',
|
|
406
352
|
colorSpace: 'linear',
|
|
@@ -411,14 +357,18 @@ function buildMaterialAsset(
|
|
|
411
357
|
renderState: { tags: { LightMode: 'Forward' }, queue: 2000 },
|
|
412
358
|
},
|
|
413
359
|
],
|
|
414
|
-
values
|
|
360
|
+
values: {
|
|
361
|
+
baseColor: pod.baseColorFactor as readonly [number, number, number, number],
|
|
362
|
+
metallic: pod.metallicFactor,
|
|
363
|
+
roughness: pod.roughnessFactor,
|
|
364
|
+
},
|
|
415
365
|
};
|
|
416
366
|
return {
|
|
417
367
|
guid,
|
|
418
368
|
kind: 'material',
|
|
419
369
|
...(pod.name !== undefined ? { name: pod.name } : {}),
|
|
420
370
|
payload: mat,
|
|
421
|
-
refs,
|
|
371
|
+
refs: [],
|
|
422
372
|
artifacts: {},
|
|
423
373
|
};
|
|
424
374
|
}
|
|
@@ -434,7 +384,7 @@ interface SceneBuildContext {
|
|
|
434
384
|
readonly refs: readonly string[];
|
|
435
385
|
/**
|
|
436
386
|
* Skin GUIDs (inline strings) injected into the SceneAsset payload and the
|
|
437
|
-
* scene envelope's refs[] so the
|
|
387
|
+
* scene envelope's refs[] so the runtime recursive loadByGuid walk pulls each
|
|
438
388
|
* SkinAsset on the browser-async pack-fetch path. Mirrors gltf-importer:
|
|
439
389
|
* stored as GUID strings (the on-disk round-trip + parseScenePayload's
|
|
440
390
|
* resolveSkinGuids accept both shapes).
|
|
@@ -528,26 +478,18 @@ function buildSceneAsset(pod: ScenePod, guid: string, ctx: SceneBuildContext): I
|
|
|
528
478
|
};
|
|
529
479
|
}
|
|
530
480
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
481
|
+
// M3: TextureAsset requires decoded pixel data — deferred to M4.
|
|
482
|
+
// TexturePod.filePath is preserved for diagnostics during M3.
|
|
483
|
+
function buildTextureNote(_pod: TexturePod, _guid: string): ImportedAsset {
|
|
484
|
+
// Produce a minimal placeholder; real texture import (decode + upload)
|
|
485
|
+
// lands with M4 material parsing.
|
|
536
486
|
return {
|
|
537
|
-
guid,
|
|
487
|
+
guid: _guid,
|
|
538
488
|
kind: 'texture',
|
|
539
|
-
...(
|
|
540
|
-
payload:
|
|
489
|
+
...(_pod.name !== undefined ? { name: _pod.name } : {}),
|
|
490
|
+
payload: {} as never,
|
|
541
491
|
refs: [],
|
|
542
|
-
artifacts: {
|
|
543
|
-
body: {
|
|
544
|
-
mediaType: decoded.mediaType?.startsWith('image/ktx2')
|
|
545
|
-
? decoded.mediaType
|
|
546
|
-
: 'application/x-forgeax-rgba8',
|
|
547
|
-
...(decoded.assetCodec === undefined ? {} : { assetCodec: decoded.assetCodec }),
|
|
548
|
-
bytes: decoded.bytes,
|
|
549
|
-
},
|
|
550
|
-
},
|
|
492
|
+
artifacts: {},
|
|
551
493
|
};
|
|
552
494
|
}
|
|
553
495
|
|
|
@@ -561,14 +503,9 @@ export function toAssetPack(params: {
|
|
|
561
503
|
readonly animationClips: readonly AnimationClipPod[];
|
|
562
504
|
readonly subAssets: readonly SubAsset[];
|
|
563
505
|
readonly sourceOverrides?: SourceOverrideMap;
|
|
564
|
-
readonly decodedTextures?: ReadonlyMap<number, FbxDecodedTexture>;
|
|
565
506
|
}): readonly ImportedAsset[] {
|
|
566
507
|
const assets: ImportedAsset[] = [];
|
|
567
508
|
const guidOf = makeGuidResolver(params.subAssets);
|
|
568
|
-
const textureGuidByIndex = new Map<number, string>();
|
|
569
|
-
for (const texture of params.subAssets) {
|
|
570
|
-
if (texture.kind === 'texture') textureGuidByIndex.set(texture.sourceIndex, texture.guid);
|
|
571
|
-
}
|
|
572
509
|
const materialGuidByIndex = new Map<number, string>();
|
|
573
510
|
const materialNameByIndex = new Map<number, string>();
|
|
574
511
|
const materialSourceKeyByIndex = new Map<number, string>();
|
|
@@ -642,20 +579,12 @@ export function toAssetPack(params: {
|
|
|
642
579
|
if (!mat) continue;
|
|
643
580
|
const guid = guidOf('material', i);
|
|
644
581
|
// Single-mesh fixtures: any material is consumed by the skinned mesh.
|
|
645
|
-
if (guid !== undefined)
|
|
646
|
-
assets.push(buildMaterialAsset(mat, guid, hasSkin, textureGuidByIndex));
|
|
647
|
-
}
|
|
582
|
+
if (guid !== undefined) assets.push(buildMaterialAsset(mat, guid, hasSkin));
|
|
648
583
|
}
|
|
649
584
|
|
|
650
585
|
for (const tex of params.textures) {
|
|
651
586
|
const guid = guidOf('texture', tex.sourceIndex);
|
|
652
|
-
if (guid !== undefined)
|
|
653
|
-
const decoded = params.decodedTextures?.get(tex.sourceIndex);
|
|
654
|
-
if (decoded === undefined) {
|
|
655
|
-
throw new Error(`fbx texture ${tex.sourceIndex} was declared but not decoded`);
|
|
656
|
-
}
|
|
657
|
-
assets.push(buildTextureAsset(tex, guid, decoded));
|
|
658
|
-
}
|
|
587
|
+
if (guid !== undefined) assets.push(buildTextureNote(tex, guid));
|
|
659
588
|
}
|
|
660
589
|
|
|
661
590
|
// Scene refs[] ordering (mirror of gltfImporter): only direct scene
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
2
|
-
import { type DdcPack } from '@forgeax/engine-import';
|
|
3
|
-
import type { AssetDecoder, MeshAsset } from '@forgeax/engine-types';
|
|
4
|
-
type FixtureAsset = DdcPack['assets'][number];
|
|
5
|
-
export declare const normalizedMeshAssetDecoder: AssetDecoder<MeshAsset>;
|
|
6
|
-
export declare function installMeshDecoder(assets: AssetRegistry): import("@forgeax/engine-types").AssetDecoderLease;
|
|
7
|
-
export declare function createAssetRuntimeFixture(sourceAssets: readonly FixtureAsset[]): AssetRegistry;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=asset-runtime-fixture.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"asset-runtime-fixture.d.ts","sourceRoot":"","sources":["../../src/__tests__/asset-runtime-fixture.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,KAAK,OAAO,EAAoB,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EACV,YAAY,EAEZ,SAAS,EAIV,MAAM,uBAAuB,CAAC;AAwB/B,KAAK,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AA4F9C,eAAO,MAAM,0BAA0B,EAAE,YAAY,CAAC,SAAS,CAqB9D,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,qDAEvD;AAwCD,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,SAAS,YAAY,EAAE,GAAG,aAAa,CAsD9F"}
|