@forgeax/engine-gltf 0.1.6 → 0.1.19
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 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/transmission-pack-carrier.integration.test.d.ts +2 -0
- package/dist/__tests__/transmission-pack-carrier.integration.test.d.ts.map +1 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/check-extensions.d.ts +3 -3
- package/dist/check-extensions.d.ts.map +1 -1
- package/dist/cli-gltf.mjs +161 -69
- package/dist/cli-gltf.mjs.map +1 -1
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/gltf-importer.d.ts.map +1 -1
- package/dist/importer-entry.mjs +177 -72
- package/dist/importer-entry.mjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +177 -72
- package/dist/index.mjs.map +1 -1
- package/dist/material/parse-material.d.ts +118 -0
- package/dist/material/parse-material.d.ts.map +1 -0
- package/dist/node-file-entry.mjs +161 -69
- package/dist/node-file-entry.mjs.map +1 -1
- package/dist/parse-gltf.d.ts +17 -103
- package/dist/parse-gltf.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/__tests__/bridge-material-values.unit.test.ts +46 -0
- package/src/__tests__/gltf-error-derived-views.test-d.ts +4 -0
- package/src/__tests__/gltf-error-policy-owner.unit.test.ts +26 -178
- package/src/__tests__/gltf.unit.test.ts +28 -4
- package/src/__tests__/material-pack-refs.integration.test.ts +12 -0
- package/src/__tests__/parse-gltf.unit.test.ts +121 -0
- package/src/__tests__/transmission-pack-carrier.integration.test.ts +82 -0
- package/src/bridge.ts +12 -1
- package/src/check-extensions.ts +14 -9
- package/src/errors.ts +12 -0
- package/src/gltf-importer.ts +6 -0
- package/src/index.ts +1 -0
- package/src/material/parse-material.ts +342 -0
- package/src/parse-gltf.ts +50 -277
package/dist/parse-gltf.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ImportError } from '@forgeax/engine-types';
|
|
2
2
|
import { COMPONENT_TYPE } from './accessor/decode-accessor.js';
|
|
3
3
|
import { type GltfError, type Result } from './errors.js';
|
|
4
|
+
import { type GltfImageIr, type GltfMaterialIr, type GltfSamplerIr, type GltfTextureIr } from './material/parse-material.js';
|
|
4
5
|
import { type GltfBufferViewDecodeCapability } from './meshopt-decode.js';
|
|
5
6
|
import { type GltfAnimationClipRecord } from './parse-animation.js';
|
|
6
7
|
import { type GltfSkeletonRecord } from './parse-skin.js';
|
|
7
8
|
import { type GltfMetaJson, type GltfSubAssetEntry } from './reimport-reuse-meta.js';
|
|
8
9
|
import type { GltfSourceKeyError } from './source-key.js';
|
|
9
10
|
import { type DecomposedTransform } from './transform.js';
|
|
11
|
+
export type { GltfImageIr, GltfMaterialIr, GltfMaterialJson, GltfNormalTextureInfoIr, GltfOcclusionTextureInfoIr, GltfSamplerIr, GltfTextureInfoIr, GltfTextureIr, GltfTextureTransformIr, NormalTextureInfoJson, OcclusionTextureInfoJson, TextureInfoJson, } from './material/parse-material.js';
|
|
10
12
|
export interface MeshPrimitiveJson {
|
|
11
13
|
readonly attributes?: Record<string, number>;
|
|
12
14
|
readonly indices?: number;
|
|
@@ -24,26 +26,26 @@ export interface GltfMeshIr {
|
|
|
24
26
|
readonly positions: Float32Array;
|
|
25
27
|
readonly normals?: Float32Array;
|
|
26
28
|
readonly texcoord0?: Float32Array;
|
|
27
|
-
/** TEXCOORD_1 per-vertex UV set 1
|
|
29
|
+
/** TEXCOORD_1 per-vertex UV set 1. */
|
|
28
30
|
readonly texcoord1?: Float32Array;
|
|
29
|
-
/** TEXCOORD_2 per-vertex UV set 2
|
|
31
|
+
/** TEXCOORD_2 per-vertex UV set 2. */
|
|
30
32
|
readonly texcoord2?: Float32Array;
|
|
31
|
-
/** TEXCOORD_3 per-vertex UV set 3
|
|
33
|
+
/** TEXCOORD_3 per-vertex UV set 3. */
|
|
32
34
|
readonly texcoord3?: Float32Array;
|
|
33
|
-
/** TEXCOORD_4 per-vertex UV set 4
|
|
35
|
+
/** TEXCOORD_4 per-vertex UV set 4. */
|
|
34
36
|
readonly texcoord4?: Float32Array;
|
|
35
|
-
/** TEXCOORD_5 per-vertex UV set 5
|
|
37
|
+
/** TEXCOORD_5 per-vertex UV set 5. */
|
|
36
38
|
readonly texcoord5?: Float32Array;
|
|
37
|
-
/** TEXCOORD_6 per-vertex UV set 6
|
|
39
|
+
/** TEXCOORD_6 per-vertex UV set 6. */
|
|
38
40
|
readonly texcoord6?: Float32Array;
|
|
39
|
-
/** TEXCOORD_7 per-vertex UV set 7
|
|
41
|
+
/** TEXCOORD_7 per-vertex UV set 7. */
|
|
40
42
|
readonly texcoord7?: Float32Array;
|
|
41
43
|
readonly tangents?: Float32Array;
|
|
42
|
-
/** COLOR_0 importer carrier
|
|
44
|
+
/** COLOR_0 importer carrier. */
|
|
43
45
|
readonly colors0?: Float32Array;
|
|
44
|
-
/** JOINTS_0 per-vertex joint indices
|
|
46
|
+
/** JOINTS_0 per-vertex joint indices. */
|
|
45
47
|
readonly joints0?: Uint16Array;
|
|
46
|
-
/** WEIGHTS_0 per-vertex skin weights
|
|
48
|
+
/** WEIGHTS_0 per-vertex skin weights. */
|
|
47
49
|
readonly weights0?: Float32Array;
|
|
48
50
|
readonly morphTargets?: readonly {
|
|
49
51
|
readonly position?: Float32Array;
|
|
@@ -51,100 +53,16 @@ export interface GltfMeshIr {
|
|
|
51
53
|
readonly tangent?: Float32Array;
|
|
52
54
|
}[];
|
|
53
55
|
readonly morphWeights?: Float32Array;
|
|
54
|
-
/**
|
|
55
|
-
* Optional per-glTF-spec: when omitted, primitive declares non-indexed
|
|
56
|
-
* geometry (vertex buffer is consumed in vertex order, every 3 verts =
|
|
57
|
-
* 1 triangle for triangle-list). bridge.ts handles undefined by routing
|
|
58
|
-
* MeshAsset.indices to undefined and submesh.indexCount=0 (vertexCount
|
|
59
|
-
* carries the draw count for `pass.draw(vertexCount)`).
|
|
60
|
-
* bug-20260612 hello-skin visual layered gate: prior shape coerced
|
|
61
|
-
* undefined into Uint16Array(0) and tripped MeshAsset.indices !==
|
|
62
|
-
* undefined branches downstream into drawIndexed(0).
|
|
63
|
-
* U16 (incl. widened U8) or U32 — width preserved from the source accessor;
|
|
64
|
-
* bridge.ts narrows U32 to U16 when the merged maxIndex fits.
|
|
65
|
-
*/
|
|
56
|
+
/** Undefined means non-indexed; otherwise source width is preserved. */
|
|
66
57
|
readonly indices?: Uint16Array | Uint32Array;
|
|
67
58
|
readonly materialIndex: number | null;
|
|
68
|
-
/**
|
|
69
|
-
* Owning glTF mesh index in the original document (`gltf.meshes[meshIndex]`).
|
|
70
|
-
* After OOS-7 flattening, multiple GltfMeshIr entries may share the same
|
|
71
|
-
* meshIndex (one per primitive). Bridge layer uses this to filter material
|
|
72
|
-
* collection per node, fixing the verify-r1 charter-defect where multi
|
|
73
|
-
* glTF-mesh documents would silently misalign materials across nodes.
|
|
74
|
-
*/
|
|
59
|
+
/** Owning glTF mesh index; multiple IR rows may share it after flattening. */
|
|
75
60
|
readonly meshIndex: number;
|
|
76
61
|
}
|
|
77
|
-
export interface GltfTextureIr {
|
|
78
|
-
readonly sampler?: number;
|
|
79
|
-
readonly source: number;
|
|
80
|
-
readonly name?: string;
|
|
81
|
-
}
|
|
82
|
-
export interface GltfTextureTransformIr {
|
|
83
|
-
readonly offset?: readonly [number, number];
|
|
84
|
-
readonly rotation?: number;
|
|
85
|
-
readonly scale?: readonly [number, number];
|
|
86
|
-
}
|
|
87
|
-
export interface GltfTextureInfoIr {
|
|
88
|
-
readonly texture: number;
|
|
89
|
-
readonly sampler?: number;
|
|
90
|
-
readonly texCoord?: number;
|
|
91
|
-
readonly transform?: GltfTextureTransformIr;
|
|
92
|
-
}
|
|
93
|
-
export interface GltfNormalTextureInfoIr extends GltfTextureInfoIr {
|
|
94
|
-
readonly scale?: number;
|
|
95
|
-
}
|
|
96
|
-
export interface GltfOcclusionTextureInfoIr extends GltfTextureInfoIr {
|
|
97
|
-
readonly strength?: number;
|
|
98
|
-
}
|
|
99
|
-
export interface GltfImageIr {
|
|
100
|
-
readonly uri?: string;
|
|
101
|
-
readonly mimeType?: string;
|
|
102
|
-
readonly bufferView?: number;
|
|
103
|
-
readonly name?: string;
|
|
104
|
-
}
|
|
105
|
-
export interface GltfSamplerIr {
|
|
106
|
-
readonly magFilter?: number;
|
|
107
|
-
readonly minFilter?: number;
|
|
108
|
-
readonly wrapS: number;
|
|
109
|
-
readonly wrapT: number;
|
|
110
|
-
readonly name?: string;
|
|
111
|
-
}
|
|
112
|
-
export interface GltfMaterialIr {
|
|
113
|
-
readonly name?: string;
|
|
114
|
-
readonly baseColorFactor: readonly [number, number, number, number];
|
|
115
|
-
/** glTF emissiveFactor; omitted means the glTF default [0, 0, 0]. */
|
|
116
|
-
readonly emissiveFactor?: readonly [number, number, number];
|
|
117
|
-
readonly baseColorTexture?: GltfTextureInfoIr | number;
|
|
118
|
-
/** Texture binding for the emissive map. */
|
|
119
|
-
readonly emissiveTexture?: GltfTextureInfoIr | number;
|
|
120
|
-
readonly metallicFactor: number;
|
|
121
|
-
readonly roughnessFactor: number;
|
|
122
|
-
readonly metallicRoughnessTexture?: GltfTextureInfoIr | number;
|
|
123
|
-
readonly normalTexture?: GltfNormalTextureInfoIr | number;
|
|
124
|
-
readonly occlusionTexture?: GltfOcclusionTextureInfoIr | number;
|
|
125
|
-
/**
|
|
126
|
-
* glTF `alphaMode` (`'OPAQUE'` | `'MASK'` | `'BLEND'`). Absent -> `'OPAQUE'`
|
|
127
|
-
* (glTF spec default). Drives the bridge's transparent / alpha-test routing.
|
|
128
|
-
*/
|
|
129
|
-
readonly alphaMode?: 'OPAQUE' | 'MASK' | 'BLEND';
|
|
130
|
-
/** glTF `alphaCutoff` (MASK-mode threshold; glTF spec default 0.5). */
|
|
131
|
-
readonly alphaCutoff?: number;
|
|
132
|
-
/**
|
|
133
|
-
* glTF `doubleSided`. Absent -> false (the glTF spec default). The bridge
|
|
134
|
-
* maps this to a no-cull material pass so reverse-facing glass and cards
|
|
135
|
-
* remain visible without changing their authored winding.
|
|
136
|
-
*/
|
|
137
|
-
readonly doubleSided?: boolean;
|
|
138
|
-
/**
|
|
139
|
-
* baseColorTexture UV-set index (glTF `baseColorTexture.texCoord`). Absent
|
|
140
|
-
* -> 0. Selects which interleaved UV set the base-color sampler reads.
|
|
141
|
-
*/
|
|
142
|
-
readonly baseColorTexCoord?: number;
|
|
143
|
-
}
|
|
144
62
|
export interface NodeInstancingIr {
|
|
145
|
-
/** Number of instances.
|
|
63
|
+
/** Number of instances. */
|
|
146
64
|
readonly count: number;
|
|
147
|
-
/**
|
|
65
|
+
/** Column-major transforms packed into one Float32Array. */
|
|
148
66
|
readonly transforms: Float32Array;
|
|
149
67
|
}
|
|
150
68
|
export type GltfPunctualLightType = 'directional' | 'point' | 'spot';
|
|
@@ -162,16 +80,12 @@ export interface GltfNodeIr {
|
|
|
162
80
|
readonly name?: string;
|
|
163
81
|
readonly transform: DecomposedTransform;
|
|
164
82
|
readonly meshIndex: number | null;
|
|
165
|
-
/** Node-authored morph weights override mesh defaults
|
|
83
|
+
/** Node-authored morph weights override mesh defaults. */
|
|
166
84
|
readonly morphWeights?: Float32Array;
|
|
167
|
-
/** Valid when node carries `skin` reference. Null when no skin. */
|
|
168
85
|
readonly skinIndex: number | null;
|
|
169
86
|
readonly children: readonly number[];
|
|
170
|
-
/** Present when node carries EXT_mesh_gpu_instancing (feat-20260518). */
|
|
171
87
|
readonly instancing?: NodeInstancingIr;
|
|
172
|
-
/** glTF camera index. Null when the node does not reference a camera. */
|
|
173
88
|
readonly camera: number | null;
|
|
174
|
-
/** KHR_lights_punctual light index. Null when the node has no light. */
|
|
175
89
|
readonly lightIndex?: number | null;
|
|
176
90
|
}
|
|
177
91
|
export interface GltfSceneIr {
|
package/dist/parse-gltf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-gltf.d.ts","sourceRoot":"","sources":["../src/parse-gltf.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse-gltf.d.ts","sourceRoot":"","sources":["../src/parse-gltf.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-gltf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -38,21 +38,21 @@
|
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@forgeax/engine-animation": "0.1.
|
|
42
|
-
"@forgeax/engine-geometry": "0.1.
|
|
43
|
-
"@forgeax/engine-import": "0.1.
|
|
44
|
-
"@forgeax/engine-math": "0.1.
|
|
45
|
-
"@forgeax/engine-pack": "0.1.
|
|
46
|
-
"@forgeax/engine-scene": "0.1.
|
|
47
|
-
"@forgeax/engine-types": "0.1.
|
|
41
|
+
"@forgeax/engine-animation": "0.1.19",
|
|
42
|
+
"@forgeax/engine-geometry": "0.1.19",
|
|
43
|
+
"@forgeax/engine-import": "0.1.19",
|
|
44
|
+
"@forgeax/engine-math": "0.1.19",
|
|
45
|
+
"@forgeax/engine-pack": "0.1.19",
|
|
46
|
+
"@forgeax/engine-scene": "0.1.19",
|
|
47
|
+
"@forgeax/engine-types": "0.1.19",
|
|
48
48
|
"@webgpu/types": "^0.1.71",
|
|
49
49
|
"meshoptimizer": "^0.20.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@forgeax/engine-assets-runtime": "0.1.
|
|
53
|
-
"@forgeax/engine-picking": "0.1.
|
|
54
|
-
"@forgeax/engine-render": "0.1.
|
|
55
|
-
"@forgeax/engine-runtime": "0.1.
|
|
52
|
+
"@forgeax/engine-assets-runtime": "0.1.19",
|
|
53
|
+
"@forgeax/engine-picking": "0.1.19",
|
|
54
|
+
"@forgeax/engine-render": "0.1.19",
|
|
55
|
+
"@forgeax/engine-runtime": "0.1.19",
|
|
56
56
|
"@types/node": "^20.14.0"
|
|
57
57
|
},
|
|
58
58
|
"forgeax": {
|
|
@@ -103,4 +103,50 @@ describe('glTF material bridge values', () => {
|
|
|
103
103
|
expect(material.values?.baseColorTexture).toEqual({ texture: 100 });
|
|
104
104
|
expect(material.values?.baseColorTexture).not.toHaveProperty('coordinates');
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
it('projects transmission and volume texture references without flattening coordinates', () => {
|
|
108
|
+
const material = toMaterialAsset(
|
|
109
|
+
{
|
|
110
|
+
baseColorFactor: [1, 1, 1, 1],
|
|
111
|
+
metallicFactor: 0,
|
|
112
|
+
roughnessFactor: 0.5,
|
|
113
|
+
transmissionFactor: 0.8,
|
|
114
|
+
transmissionTexture: { texture: 5, sampler: 15, texCoord: 2 },
|
|
115
|
+
ior: 1.45,
|
|
116
|
+
thicknessFactor: 0.2,
|
|
117
|
+
thicknessTexture: {
|
|
118
|
+
texture: 6,
|
|
119
|
+
sampler: 16,
|
|
120
|
+
texCoord: 3,
|
|
121
|
+
transform: { offset: [0.1, 0.2] },
|
|
122
|
+
},
|
|
123
|
+
attenuationColor: [0.5, 0.6, 0.7],
|
|
124
|
+
attenuationDistance: 2,
|
|
125
|
+
} as unknown as GltfMaterialIr,
|
|
126
|
+
{
|
|
127
|
+
textureHandles: new Map([
|
|
128
|
+
[5, 105],
|
|
129
|
+
[6, 106],
|
|
130
|
+
]) as unknown as ReadonlyMap<number, Handle<'TextureAsset', 'shared'>>,
|
|
131
|
+
samplerHandles: new Map([
|
|
132
|
+
[15, 205],
|
|
133
|
+
[16, 206],
|
|
134
|
+
]) as unknown as ReadonlyMap<number, Handle<'SamplerAsset', 'shared'>>,
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
expect(material.values).toMatchObject({
|
|
139
|
+
transmission: 0.8,
|
|
140
|
+
ior: 1.45,
|
|
141
|
+
thickness: 0.2,
|
|
142
|
+
attenuationColor: [0.5, 0.6, 0.7],
|
|
143
|
+
attenuationDistance: 2,
|
|
144
|
+
transmissionTexture: { texture: 105, sampler: 205, coordinates: { set: 2 } },
|
|
145
|
+
thicknessTexture: {
|
|
146
|
+
texture: 106,
|
|
147
|
+
sampler: 206,
|
|
148
|
+
coordinates: { set: 3, transform: { offset: [0.1, 0.2] } },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
});
|
|
106
152
|
});
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type GltfImageMimeUnsupportedDetail,
|
|
15
15
|
type GltfInstancingCountMismatchDetail,
|
|
16
16
|
type GltfMalformedHeaderDetail,
|
|
17
|
+
type GltfMaterialTransmissionInvalidDetail,
|
|
17
18
|
type GltfMeshBridgeInvalidDetail,
|
|
18
19
|
type GltfMeshoptDecodeFailedDetail,
|
|
19
20
|
type GltfMeshoptDecoderRequiredDetail,
|
|
@@ -51,6 +52,7 @@ type ExpectedDetails = {
|
|
|
51
52
|
readonly 'gltf-color-accessor-unsupported': GltfColorAccessorUnsupportedDetail;
|
|
52
53
|
readonly 'gltf-color-accessor-malformed': GltfColorAccessorMalformedDetail;
|
|
53
54
|
readonly 'gltf-mesh-bridge-invalid': GltfMeshBridgeInvalidDetail;
|
|
55
|
+
readonly 'gltf-material-transmission-invalid': GltfMaterialTransmissionInvalidDetail;
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
type ExpectedCodes = keyof ExpectedDetails;
|
|
@@ -109,6 +111,8 @@ function exhaustive(error: GltfError): string {
|
|
|
109
111
|
return `${error.detail.semantic}:${error.detail.accessorIndex}:${error.detail.reason}`;
|
|
110
112
|
case 'gltf-mesh-bridge-invalid':
|
|
111
113
|
return error.detail.reason;
|
|
114
|
+
case 'gltf-material-transmission-invalid':
|
|
115
|
+
return `${error.detail.extension}:${error.detail.field}:${error.detail.reason}`;
|
|
112
116
|
}
|
|
113
117
|
return error;
|
|
114
118
|
}
|
|
@@ -1,185 +1,33 @@
|
|
|
1
|
-
import { describe, expect,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'all instance attribute accessors share the same count',
|
|
20
|
-
'image/mimeType is image/jpeg or image/png',
|
|
21
|
-
'skin.joints.length <= MAX_JOINTS (256)',
|
|
22
|
-
'animation sampler interpolation is LINEAR or STEP',
|
|
23
|
-
'animation channel target path is one of translation, rotation, scale, or weights',
|
|
24
|
-
'every joint node has a non-empty name and belongs to an acyclic hierarchy',
|
|
25
|
-
'image bytes extractable from bufferView / data-URI / external URI without corruption',
|
|
26
|
-
'mesh primitive declares JOINTS_0 and WEIGHTS_0 symmetrically (both present or both absent)',
|
|
27
|
-
'every animation channel resolves to one uniquely named scene node and stable target ID',
|
|
28
|
-
'a required or compressed-only EXT_meshopt_compression bufferView has a ready decoder capability',
|
|
29
|
-
'the EXT_meshopt_compression declaration and decoder output are structurally valid',
|
|
30
|
-
'morph target and default-weight arrays are dense, bounded, and match the base vertex count',
|
|
31
|
-
'COLOR_0 dense accessor uses VEC3/VEC4 FLOAT or normalized UBYTE/USHORT',
|
|
32
|
-
'COLOR_0 accessor is non-empty, finite, in range, and fully addressable',
|
|
33
|
-
'a non-empty merged mesh with consistent morph and COLOR_0 cardinality',
|
|
34
|
-
] as const;
|
|
35
|
-
|
|
36
|
-
const HINTS_IN_POLICY_ORDER = [
|
|
37
|
-
'verify .glb is not truncated; rerun: forgeax-engine-remote-gltf import <path>',
|
|
38
|
-
'asset.version must be "2.0"; v1 or v3 not supported',
|
|
39
|
-
'rebuild .gltf with valid bufferViews; check accessor index; ensure accessor.byteOffset + EFFECTIVE_STRIDE * (count - 1) + element_size <= bufferView.byteLength',
|
|
40
|
-
'see feat-future-gltf-extensions-allowlist; remove this extension or wait for the allowlist to expand',
|
|
41
|
-
'sparse: see feat-future-gltf-sparse-accessor; morph: see feat-future-gltf-morph; interleaved: see feat-future-gltf-mesh-multi-section',
|
|
42
|
-
'check sidecar meta.json + textures/ directory + vite-plugin-pack /__pack/lookup route',
|
|
43
|
-
'run: forgeax-engine-remote-gltf import <path>',
|
|
44
|
-
'EXT_mesh_gpu_instancing requires TRANSLATION/ROTATION/SCALE accessors to share count; see https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/EXT_mesh_gpu_instancing/README.md#extending-nodes-with-instance-attributes',
|
|
45
|
-
'convert to JPG/PNG via external tool; only image/jpeg and image/png are supported',
|
|
46
|
-
'reduce joint count below MAX_JOINTS (256) or see OOS-skin-max-joints',
|
|
47
|
-
'see OOS-skin-cubicspline; convert CUBICSPLINE to LINEAR/STEP in DCC tool',
|
|
48
|
-
'animation target path must be translation, rotation, scale, or weights',
|
|
49
|
-
'ensure every joint node has a non-empty name and the node hierarchy is acyclic',
|
|
50
|
-
'verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax-engine-remote-gltf import <path>',
|
|
51
|
-
'glTF spec requires JOINTS_0 and WEIGHTS_0 to appear together for each skinned primitive; add the missing attribute or remove the present one in the DCC tool',
|
|
52
|
-
'name every node in the animated hierarchy and ensure each animated full path is unique',
|
|
53
|
-
'provide the build-only EXT_meshopt_compression decoder or author a valid core fallback bufferView',
|
|
54
|
-
'the meshopt decoder accepts the declared compressed range and produces the declared byte count',
|
|
55
|
-
're-export dense morph targets with at most eight targets/attributes and matching vertex/default-weight lengths',
|
|
56
|
-
're-export COLOR_0 with a supported type/component/normalized combination; morph and sparse COLOR_0 remain deferred',
|
|
57
|
-
'repair the COLOR_0 accessor count, reference, range, or buffer bounds, then re-import the glTF source',
|
|
58
|
-
'repair the source primitive and re-import; inspect detail.reason and its typed facts',
|
|
59
|
-
] as const;
|
|
60
|
-
|
|
61
|
-
const policyErrors: readonly GltfError[] = [
|
|
62
|
-
gltfErr('gltf-malformed-header', { filePath: 'model.glb', byteOffset: 0 }),
|
|
63
|
-
gltfErr('gltf-version-unsupported', { filePath: 'model.gltf', actualVersion: '1.0' }),
|
|
64
|
-
gltfErr('gltf-buffer-out-of-bounds', {
|
|
65
|
-
accessor: 0,
|
|
66
|
-
byteOffset: 0,
|
|
67
|
-
byteLength: 4,
|
|
68
|
-
bufferIndex: 0,
|
|
69
|
-
}),
|
|
70
|
-
gltfErr('gltf-extension-unsupported', {
|
|
71
|
-
extension: 'KHR_x',
|
|
72
|
-
source: 'extensionsRequired',
|
|
73
|
-
}),
|
|
74
|
-
gltfErr('gltf-accessor-type-mismatch', { accessorIndex: 0, reason: 'sparse' }),
|
|
75
|
-
gltfErr('gltf-texture-load-failed', { uri: 'textures/test.png' }),
|
|
76
|
-
gltfErr('gltf-meta-missing', {
|
|
77
|
-
filePath: 'model.gltf',
|
|
78
|
-
expectedMetaPath: 'model.gltf.meta.json',
|
|
79
|
-
}),
|
|
80
|
-
gltfErr('gltf-instancing-count-mismatch', {
|
|
81
|
-
nodeIndex: 0,
|
|
82
|
-
accessor: 'TRANSLATION',
|
|
83
|
-
expectedCount: 2,
|
|
84
|
-
actualCount: 3,
|
|
85
|
-
}),
|
|
86
|
-
gltfErr('gltf-image-mime-unsupported', { mimeType: 'image/bmp' }),
|
|
87
|
-
gltfErr('gltf-skin-joint-count-exceeded', { skinIndex: 0, jointCount: 300, maxJoints: 256 }),
|
|
88
|
-
gltfErr('gltf-animation-cubicspline-unsupported', { animationIndex: 0, samplerIndex: 1 }),
|
|
89
|
-
gltfErr('gltf-morph-unsupported', { animationIndex: 0, channelIndex: 2, nodeIndex: 3 }),
|
|
90
|
-
gltfErr('gltf-skin-joint-name-missing', {
|
|
91
|
-
reason: 'name-missing',
|
|
92
|
-
skinIndex: 0,
|
|
93
|
-
jointPathIndex: 4,
|
|
94
|
-
nodeIndex: 5,
|
|
95
|
-
}),
|
|
96
|
-
gltfErr('gltf-image-extract-failed', { imageIndex: 0, source: 'bufferView', reason: 'sample' }),
|
|
97
|
-
gltfErr('gltf-skin-attr-asymmetric', {
|
|
98
|
-
meshIndex: 0,
|
|
99
|
-
primitiveIndex: 0,
|
|
100
|
-
hasJoints: true,
|
|
101
|
-
hasWeights: false,
|
|
102
|
-
}),
|
|
103
|
-
gltfErr('gltf-animation-target-invalid', {
|
|
104
|
-
reason: 'name-missing',
|
|
105
|
-
animationIndex: 0,
|
|
106
|
-
channelIndex: 0,
|
|
107
|
-
nodeIndex: 0,
|
|
108
|
-
}),
|
|
109
|
-
gltfErr('gltf-meshopt-decoder-required', {
|
|
110
|
-
bufferView: 0,
|
|
111
|
-
actual: 'required',
|
|
112
|
-
hasCoreFallback: false,
|
|
113
|
-
}),
|
|
114
|
-
gltfErr('gltf-meshopt-decode-failed', {
|
|
115
|
-
bufferView: 1,
|
|
116
|
-
actual: 'decoded byte count mismatch',
|
|
117
|
-
mode: 'ATTRIBUTES',
|
|
118
|
-
filter: 'NONE',
|
|
119
|
-
}),
|
|
120
|
-
gltfErr('gltf-morph-invalid', {
|
|
121
|
-
meshIndex: 0,
|
|
122
|
-
primitiveIndex: 0,
|
|
123
|
-
reason: 'target-count-exceeded',
|
|
124
|
-
targetCount: 9,
|
|
125
|
-
attributeCount: 1,
|
|
126
|
-
vertexCount: 3,
|
|
127
|
-
}),
|
|
128
|
-
gltfErr('gltf-color-accessor-unsupported', {
|
|
129
|
-
semantic: 'COLOR_0',
|
|
130
|
-
accessorIndex: 0,
|
|
131
|
-
reason: 'component',
|
|
132
|
-
}),
|
|
133
|
-
gltfErr('gltf-color-accessor-malformed', {
|
|
134
|
-
semantic: 'COLOR_0',
|
|
135
|
-
accessorIndex: 0,
|
|
136
|
-
reason: 'bounds',
|
|
137
|
-
}),
|
|
138
|
-
gltfErr('gltf-mesh-bridge-invalid', { reason: 'empty-input', primitiveCount: 0 }),
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
describe('glTF error policy owner', () => {
|
|
142
|
-
it('projects one exact twenty-two-code policy surface with stable own-key order', () => {
|
|
143
|
-
const codes = policyErrors.map(({ code }) => code);
|
|
144
|
-
|
|
145
|
-
expect(codes).toHaveLength(22);
|
|
146
|
-
expect(new Set(codes).size).toBe(22);
|
|
147
|
-
expect(Object.keys(GLTF_ERROR_HINTS)).toEqual(codes);
|
|
148
|
-
expect(Object.getOwnPropertyNames(GLTF_ERROR_HINTS)).toEqual(codes);
|
|
149
|
-
expect(Object.values(GLTF_ERROR_HINTS)).toEqual(HINTS_IN_POLICY_ORDER);
|
|
150
|
-
|
|
151
|
-
for (const code of codes) {
|
|
152
|
-
expect(Object.prototype.propertyIsEnumerable.call(GLTF_ERROR_HINTS, code)).toBe(true);
|
|
153
|
-
}
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { checkExtensions } from '../check-extensions.js';
|
|
3
|
+
import { gltfErr } from '../errors.js';
|
|
4
|
+
|
|
5
|
+
describe('glTF transmission error policy', () => {
|
|
6
|
+
it('keeps invalid material facts in a structured closed detail', () => {
|
|
7
|
+
const error = gltfErr('gltf-material-transmission-invalid', {
|
|
8
|
+
extension: 'KHR_materials_ior',
|
|
9
|
+
field: 'ior',
|
|
10
|
+
reason: 'range',
|
|
11
|
+
actual: 0,
|
|
12
|
+
});
|
|
13
|
+
expect(error).toMatchObject({
|
|
14
|
+
code: 'gltf-material-transmission-invalid',
|
|
15
|
+
detail: { extension: 'KHR_materials_ior', field: 'ior', reason: 'range', actual: 0 },
|
|
16
|
+
expected: expect.any(String),
|
|
17
|
+
hint: expect.any(String),
|
|
18
|
+
});
|
|
154
19
|
});
|
|
155
20
|
|
|
156
|
-
it('
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
expect(error.hint).toBe(HINTS_IN_POLICY_ORDER[index]);
|
|
160
|
-
expect(GLTF_ERROR_HINTS[error.code]).toBe(HINTS_IN_POLICY_ORDER[index]);
|
|
161
|
-
expect(Object.keys(error)).toEqual(['code', 'expected', 'hint', 'detail']);
|
|
162
|
-
}
|
|
21
|
+
it('admits supported KHR transmission extensionsRequired declarations', () => {
|
|
22
|
+
const result = checkExtensions({ extensionsRequired: ['KHR_materials_transmission'] });
|
|
23
|
+
expect(result).toEqual({ ok: true, value: { unsupportedUsed: [] } });
|
|
163
24
|
});
|
|
164
25
|
|
|
165
|
-
it('
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
actualVersion: '1.0',
|
|
171
|
-
});
|
|
172
|
-
expectTypeOf(versionError.detail).toEqualTypeOf<GltfVersionUnsupportedDetail>();
|
|
173
|
-
expect(versionError.detail.actualVersion).toBe('1.0');
|
|
174
|
-
|
|
175
|
-
const skinError = gltfErr('gltf-skin-attr-asymmetric', {
|
|
176
|
-
meshIndex: 2,
|
|
177
|
-
primitiveIndex: 1,
|
|
178
|
-
hasJoints: true,
|
|
179
|
-
hasWeights: false,
|
|
26
|
+
it('keeps unknown extensionsRequired declarations rejected', () => {
|
|
27
|
+
const result = checkExtensions({ extensionsRequired: ['KHR_materials_unlit'] });
|
|
28
|
+
expect(result).toMatchObject({
|
|
29
|
+
ok: false,
|
|
30
|
+
error: { code: 'gltf-extension-unsupported' },
|
|
180
31
|
});
|
|
181
|
-
expectTypeOf(skinError.detail).toEqualTypeOf<GltfSkinAttrAsymmetricDetail>();
|
|
182
|
-
expect(skinError.detail.meshIndex).toBe(2);
|
|
183
|
-
expect(skinError.detail.hasWeights).toBe(false);
|
|
184
32
|
});
|
|
185
33
|
});
|
|
@@ -278,6 +278,7 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
278
278
|
'gltf-version-unsupported',
|
|
279
279
|
'gltf-buffer-out-of-bounds',
|
|
280
280
|
'gltf-extension-unsupported',
|
|
281
|
+
'gltf-material-transmission-invalid',
|
|
281
282
|
'gltf-accessor-type-mismatch',
|
|
282
283
|
'gltf-texture-load-failed',
|
|
283
284
|
'gltf-meta-missing',
|
|
@@ -308,6 +309,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
308
309
|
return 'oob';
|
|
309
310
|
case 'gltf-extension-unsupported':
|
|
310
311
|
return 'ext';
|
|
312
|
+
case 'gltf-material-transmission-invalid':
|
|
313
|
+
return 'material-transmission-invalid';
|
|
311
314
|
case 'gltf-accessor-type-mismatch':
|
|
312
315
|
return 'accessor';
|
|
313
316
|
case 'gltf-texture-load-failed':
|
|
@@ -357,6 +360,13 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
357
360
|
return gltfErr(code, { accessor: 0, byteOffset: 0, byteLength: 0, bufferIndex: 0 });
|
|
358
361
|
case 'gltf-extension-unsupported':
|
|
359
362
|
return gltfErr(code, { extension: 'KHR_x', source: 'extensionsRequired' });
|
|
363
|
+
case 'gltf-material-transmission-invalid':
|
|
364
|
+
return gltfErr(code, {
|
|
365
|
+
extension: 'KHR_materials_transmission',
|
|
366
|
+
field: 'transmissionFactor',
|
|
367
|
+
reason: 'range',
|
|
368
|
+
actual: 2,
|
|
369
|
+
});
|
|
360
370
|
case 'gltf-accessor-type-mismatch':
|
|
361
371
|
return gltfErr(code, { accessorIndex: 0, reason: 'sparse' });
|
|
362
372
|
case 'gltf-texture-load-failed':
|
|
@@ -446,8 +456,8 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
446
456
|
});
|
|
447
457
|
|
|
448
458
|
describe('GltfErrorCode roster', () => {
|
|
449
|
-
it('GLTF_ERROR_HINTS exposes exactly
|
|
450
|
-
expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(
|
|
459
|
+
it('GLTF_ERROR_HINTS exposes exactly 23 keys', () => {
|
|
460
|
+
expect(Object.keys(GLTF_ERROR_HINTS).length).toBe(23);
|
|
451
461
|
});
|
|
452
462
|
|
|
453
463
|
it.each(ALL_CODES)('hint for %s is a non-empty string', (code) => {
|
|
@@ -2016,7 +2026,7 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
2016
2026
|
expect(EXTENSION_ALLOWLIST).toEqual(['EXT_mesh_gpu_instancing', 'EXT_meshopt_compression']);
|
|
2017
2027
|
});
|
|
2018
2028
|
|
|
2019
|
-
it('(a) rejects extensionsRequired entries
|
|
2029
|
+
it('(a) rejects unsupported extensionsRequired entries', () => {
|
|
2020
2030
|
const result = checkExtensions({
|
|
2021
2031
|
extensionsRequired: ['KHR_materials_pbrSpecularGlossiness'],
|
|
2022
2032
|
});
|
|
@@ -2029,7 +2039,9 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
2029
2039
|
});
|
|
2030
2040
|
|
|
2031
2041
|
it('(b) accepts extensionsUsed (not required) into diagnostics list with no stderr', () => {
|
|
2032
|
-
const result = checkExtensions({
|
|
2042
|
+
const result = checkExtensions({
|
|
2043
|
+
extensionsUsed: ['KHR_materials_pbrSpecularGlossiness', 'KHR_materials_transmission'],
|
|
2044
|
+
});
|
|
2033
2045
|
expect(result.ok).toBe(true);
|
|
2034
2046
|
if (!result.ok) return;
|
|
2035
2047
|
expect(result.value.unsupportedUsed).toEqual(['KHR_materials_pbrSpecularGlossiness']);
|
|
@@ -2073,6 +2085,18 @@ function unwrapReimport(result: ReturnType<typeof reimportReuseMeta>) {
|
|
|
2073
2085
|
expect(stderrSpy).not.toHaveBeenCalled();
|
|
2074
2086
|
});
|
|
2075
2087
|
|
|
2088
|
+
it.each([
|
|
2089
|
+
'KHR_materials_transmission',
|
|
2090
|
+
'KHR_materials_ior',
|
|
2091
|
+
'KHR_materials_volume',
|
|
2092
|
+
])('%s in extensionsRequired routes ok (supported)', (extension) => {
|
|
2093
|
+
const result = checkExtensions({ extensionsRequired: [extension] });
|
|
2094
|
+
expect(result.ok).toBe(true);
|
|
2095
|
+
if (!result.ok) return;
|
|
2096
|
+
expect(result.value.unsupportedUsed).toEqual([]);
|
|
2097
|
+
expect(stderrSpy).not.toHaveBeenCalled();
|
|
2098
|
+
});
|
|
2099
|
+
|
|
2076
2100
|
it('EXT_mesh_gpu_instancing in extensionsUsed (not required) routes ok with no warn', () => {
|
|
2077
2101
|
const result = checkExtensions({ extensionsUsed: ['EXT_mesh_gpu_instancing'] });
|
|
2078
2102
|
expect(result.ok).toBe(true);
|