@flighthq/scene3d-formats 0.2.1-next.840.857425c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/awd2Inflate.d.ts +4 -0
- package/dist/awd2Inflate.d.ts.map +1 -0
- package/dist/awd2Inflate.js +249 -0
- package/dist/awd2Inflate.js.map +1 -0
- package/dist/awd2Parse.d.ts +7 -0
- package/dist/awd2Parse.d.ts.map +1 -0
- package/dist/awd2Parse.js +1533 -0
- package/dist/awd2Parse.js.map +1 -0
- package/dist/awd2Schema.d.ts +44 -0
- package/dist/awd2Schema.d.ts.map +1 -0
- package/dist/awd2Schema.js +67 -0
- package/dist/awd2Schema.js.map +1 -0
- package/dist/gltfParse.d.ts +9 -0
- package/dist/gltfParse.d.ts.map +1 -0
- package/dist/gltfParse.js +1456 -0
- package/dist/gltfParse.js.map +1 -0
- package/dist/gltfPunctualLights.d.ts +3 -0
- package/dist/gltfPunctualLights.d.ts.map +1 -0
- package/dist/gltfPunctualLights.js +103 -0
- package/dist/gltfPunctualLights.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/md2Parse.d.ts +5 -0
- package/dist/md2Parse.d.ts.map +1 -0
- package/dist/md2Parse.js +408 -0
- package/dist/md2Parse.js.map +1 -0
- package/dist/md2Schema.d.ts +12 -0
- package/dist/md2Schema.d.ts.map +1 -0
- package/dist/md2Schema.js +193 -0
- package/dist/md2Schema.js.map +1 -0
- package/dist/md5AnimParse.d.ts +3 -0
- package/dist/md5AnimParse.d.ts.map +1 -0
- package/dist/md5AnimParse.js +385 -0
- package/dist/md5AnimParse.js.map +1 -0
- package/dist/md5Parse.d.ts +6 -0
- package/dist/md5Parse.d.ts.map +1 -0
- package/dist/md5Parse.js +635 -0
- package/dist/md5Parse.js.map +1 -0
- package/dist/mtlParse.d.ts +3 -0
- package/dist/mtlParse.d.ts.map +1 -0
- package/dist/mtlParse.js +256 -0
- package/dist/mtlParse.js.map +1 -0
- package/dist/objParse.d.ts +5 -0
- package/dist/objParse.d.ts.map +1 -0
- package/dist/objParse.js +464 -0
- package/dist/objParse.js.map +1 -0
- package/dist/sceneSkeleton.d.ts +3 -0
- package/dist/sceneSkeleton.d.ts.map +1 -0
- package/dist/sceneSkeleton.js +34 -0
- package/dist/sceneSkeleton.js.map +1 -0
- package/dist/shared.d.ts +18 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +143 -0
- package/dist/shared.js.map +1 -0
- package/dist/threeDsParse.d.ts +4 -0
- package/dist/threeDsParse.d.ts.map +1 -0
- package/dist/threeDsParse.js +718 -0
- package/dist/threeDsParse.js.map +1 -0
- package/package.json +55 -0
- package/src/awd2Inflate.test.ts +118 -0
- package/src/awd2Parse.test.ts +2431 -0
- package/src/gltfParse.test.ts +2339 -0
- package/src/gltfPunctualLights.test.ts +184 -0
- package/src/gltfTreeShaking.test.ts +45 -0
- package/src/md2Parse.test.ts +1033 -0
- package/src/md5AnimParse.test.ts +673 -0
- package/src/md5Parse.test.ts +1094 -0
- package/src/mtlParse.test.ts +244 -0
- package/src/objParse.test.ts +690 -0
- package/src/sceneSkeleton.test.ts +35 -0
- package/src/shared.test.ts +210 -0
- package/src/threeDsParse.test.ts +1089 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type { ImportDiagnostic } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { parseObjMaterialLibrary } from './mtlParse';
|
|
4
|
+
|
|
5
|
+
// Asserts EXACTLY ONE crumb of `kind` was recorded (guards the count) and returns it so a test can lock
|
|
6
|
+
// the full contract — severity, true origin, and detail — for that emitted diagnostic.
|
|
7
|
+
function expectOneCrumb(diagnostics: readonly ImportDiagnostic[], kind: string): ImportDiagnostic {
|
|
8
|
+
const matches = diagnostics.filter((d) => d.kind === kind);
|
|
9
|
+
expect(matches).toHaveLength(1);
|
|
10
|
+
return matches[0];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('parseObjMaterialLibrary', () => {
|
|
14
|
+
it('parses a single material with all property types', () => {
|
|
15
|
+
const mtl = [
|
|
16
|
+
'newmtl TestMat',
|
|
17
|
+
'Ka 0.1 0.2 0.3',
|
|
18
|
+
'Kd 0.4 0.5 0.6',
|
|
19
|
+
'Ks 0.7 0.8 0.9',
|
|
20
|
+
'Ns 100.0',
|
|
21
|
+
'd 0.75',
|
|
22
|
+
'illum 2',
|
|
23
|
+
'map_Kd diffuse.png',
|
|
24
|
+
'map_Ka ambient.png',
|
|
25
|
+
'map_Ks specular.png',
|
|
26
|
+
'map_Bump normal.png',
|
|
27
|
+
].join('\n');
|
|
28
|
+
|
|
29
|
+
const lib = parseObjMaterialLibrary(mtl);
|
|
30
|
+
expect(lib.materials.size).toBe(1);
|
|
31
|
+
|
|
32
|
+
const mat = lib.materials.get('TestMat')!;
|
|
33
|
+
expect(mat.name).toBe('TestMat');
|
|
34
|
+
expect(mat.ambient).toEqual([0.1, 0.2, 0.3]);
|
|
35
|
+
expect(mat.diffuse).toEqual([0.4, 0.5, 0.6]);
|
|
36
|
+
expect(mat.specular).toEqual([0.7, 0.8, 0.9]);
|
|
37
|
+
expect(mat.specularExponent).toBe(100);
|
|
38
|
+
expect(mat.dissolve).toBe(0.75);
|
|
39
|
+
expect(mat.illumination).toBe(2);
|
|
40
|
+
expect(mat.mapDiffuse).toBe('diffuse.png');
|
|
41
|
+
expect(mat.mapAmbient).toBe('ambient.png');
|
|
42
|
+
expect(mat.mapSpecular).toBe('specular.png');
|
|
43
|
+
expect(mat.mapBump).toBe('normal.png');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('parses multiple materials', () => {
|
|
47
|
+
const mtl = ['newmtl MatA', 'Kd 1 0 0', '', 'newmtl MatB', 'Kd 0 1 0'].join('\n');
|
|
48
|
+
|
|
49
|
+
const lib = parseObjMaterialLibrary(mtl);
|
|
50
|
+
expect(lib.materials.size).toBe(2);
|
|
51
|
+
expect(lib.materials.get('MatA')!.diffuse).toEqual([1, 0, 0]);
|
|
52
|
+
expect(lib.materials.get('MatB')!.diffuse).toEqual([0, 1, 0]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('treats Tr as inverse dissolve', () => {
|
|
56
|
+
const mtl = 'newmtl M\nTr 0.3\n';
|
|
57
|
+
const lib = parseObjMaterialLibrary(mtl);
|
|
58
|
+
expect(lib.materials.get('M')!.dissolve).toBeCloseTo(0.7);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('accepts bump as an alias for map_Bump', () => {
|
|
62
|
+
const mtl = 'newmtl M\nbump normal_map.tga\n';
|
|
63
|
+
const lib = parseObjMaterialLibrary(mtl);
|
|
64
|
+
expect(lib.materials.get('M')!.mapBump).toBe('normal_map.tga');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns an empty library for empty input', () => {
|
|
68
|
+
const lib = parseObjMaterialLibrary('');
|
|
69
|
+
expect(lib.materials.size).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('returns an empty library for comment-only input', () => {
|
|
73
|
+
const lib = parseObjMaterialLibrary('# This is a comment\n# Another comment\n');
|
|
74
|
+
expect(lib.materials.size).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('skips unrecognized directives without recording a diagnostic', () => {
|
|
78
|
+
const mtl = 'newmtl M\nKd 1 1 1\nunknown_directive value\n';
|
|
79
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
80
|
+
const lib = parseObjMaterialLibrary(mtl, diagnostics);
|
|
81
|
+
expect(lib.materials.size).toBe(1);
|
|
82
|
+
expect(diagnostics).toHaveLength(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each([
|
|
86
|
+
{ directive: 'Kd', mtl: 'newmtl M\nKd abc def ghi\n', reason: 'non-numeric' },
|
|
87
|
+
{ directive: 'Ka', mtl: 'newmtl M\nKa 1 2\n', reason: 'too-few-components' },
|
|
88
|
+
])(
|
|
89
|
+
'records color-malformed (Recover, parseObjMaterialLibrary) — $directive $reason',
|
|
90
|
+
({ directive, mtl, reason }) => {
|
|
91
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
92
|
+
parseObjMaterialLibrary(mtl, diagnostics);
|
|
93
|
+
expect(diagnostics).toHaveLength(1);
|
|
94
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.color-malformed');
|
|
95
|
+
expect(crumb.severity).toBe('Recover');
|
|
96
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
97
|
+
expect(crumb.detail?.firstDirective).toBe(directive);
|
|
98
|
+
expect(crumb.detail?.firstLine).toBe(2);
|
|
99
|
+
expect(crumb.detail?.reason).toBe(reason);
|
|
100
|
+
expect(crumb.detail?.count).toBe(1);
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
it.each([{ directive: 'Ns' }, { directive: 'd' }, { directive: 'Tr' }, { directive: 'illum' }])(
|
|
105
|
+
'records invalid-value (Recover, parseObjMaterialLibrary) on a non-numeric $directive value',
|
|
106
|
+
({ directive }) => {
|
|
107
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
108
|
+
parseObjMaterialLibrary(`newmtl M\n${directive} abc\n`, diagnostics);
|
|
109
|
+
expect(diagnostics).toHaveLength(1);
|
|
110
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.invalid-value');
|
|
111
|
+
expect(crumb.severity).toBe('Recover');
|
|
112
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
113
|
+
expect(crumb.detail?.directive).toBe(directive);
|
|
114
|
+
expect(crumb.detail?.firstLine).toBe(2);
|
|
115
|
+
expect(crumb.detail?.count).toBe(1);
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
it('records directive-before-material (Drop, parseObjMaterialLibrary) when a directive precedes any newmtl', () => {
|
|
120
|
+
const mtl = 'Kd 1 0 0\n';
|
|
121
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
122
|
+
parseObjMaterialLibrary(mtl, diagnostics);
|
|
123
|
+
expect(diagnostics).toHaveLength(1);
|
|
124
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.directive-before-material');
|
|
125
|
+
expect(crumb.severity).toBe('Drop');
|
|
126
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
127
|
+
expect(crumb.detail?.firstDirective).toBe('Kd');
|
|
128
|
+
expect(crumb.detail?.firstLine).toBe(1);
|
|
129
|
+
expect(crumb.detail?.count).toBe(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('records newmtl-no-name (Drop, parseObjMaterialLibrary) on a newmtl with no name', () => {
|
|
133
|
+
const mtl = 'newmtl\n';
|
|
134
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
135
|
+
parseObjMaterialLibrary(mtl, diagnostics);
|
|
136
|
+
expect(diagnostics).toHaveLength(1);
|
|
137
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.newmtl-no-name');
|
|
138
|
+
expect(crumb.severity).toBe('Drop');
|
|
139
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
140
|
+
expect(crumb.detail?.firstLine).toBe(1);
|
|
141
|
+
expect(crumb.detail?.count).toBe(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('aggregates repeated MTL failures into ONE crumb per (kind, discriminator) with the total count', () => {
|
|
145
|
+
// Two invalid Ns lines aggregate to a single invalid-value:Ns crumb (count 2, first line kept); a
|
|
146
|
+
// separate invalid Tr line is a distinct directive, so it is its own crumb — never emitted per line.
|
|
147
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
148
|
+
parseObjMaterialLibrary('newmtl M\nNs a\nNs b\nTr c\n', diagnostics);
|
|
149
|
+
const nsCrumbs = diagnostics.filter((d) => d.kind === 'mtl.invalid-value' && d.detail?.directive === 'Ns');
|
|
150
|
+
expect(nsCrumbs).toHaveLength(1);
|
|
151
|
+
expect(nsCrumbs[0].detail?.count).toBe(2);
|
|
152
|
+
expect(nsCrumbs[0].detail?.firstLine).toBe(2);
|
|
153
|
+
const trCrumbs = diagnostics.filter((d) => d.kind === 'mtl.invalid-value' && d.detail?.directive === 'Tr');
|
|
154
|
+
expect(trCrumbs).toHaveLength(1);
|
|
155
|
+
expect(trCrumbs[0].detail?.count).toBe(1);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// BARE recognized directives (no value) after a material are implicit malformed no-ops the old code
|
|
159
|
+
// skipped silently. Probe every one: the announced value/filename still records a drop.
|
|
160
|
+
it.each([{ directive: 'Ka' }, { directive: 'Kd' }, { directive: 'Ks' }])(
|
|
161
|
+
'records color-malformed (Recover, parseObjMaterialLibrary) for a bare $directive after a material',
|
|
162
|
+
({ directive }) => {
|
|
163
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
164
|
+
parseObjMaterialLibrary(`newmtl M\n${directive}\n`, diagnostics);
|
|
165
|
+
expect(diagnostics).toHaveLength(1);
|
|
166
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.color-malformed');
|
|
167
|
+
expect(crumb.severity).toBe('Recover');
|
|
168
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
169
|
+
expect(crumb.detail?.firstDirective).toBe(directive);
|
|
170
|
+
expect(crumb.detail?.reason).toBe('too-few-components');
|
|
171
|
+
expect(crumb.detail?.firstLine).toBe(2);
|
|
172
|
+
expect(crumb.detail?.count).toBe(1);
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
it.each([{ directive: 'Ns' }, { directive: 'd' }, { directive: 'Tr' }, { directive: 'illum' }])(
|
|
177
|
+
'records invalid-value (Recover, parseObjMaterialLibrary) for a bare $directive after a material',
|
|
178
|
+
({ directive }) => {
|
|
179
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
180
|
+
parseObjMaterialLibrary(`newmtl M\n${directive}\n`, diagnostics);
|
|
181
|
+
expect(diagnostics).toHaveLength(1);
|
|
182
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.invalid-value');
|
|
183
|
+
expect(crumb.severity).toBe('Recover');
|
|
184
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
185
|
+
expect(crumb.detail?.directive).toBe(directive);
|
|
186
|
+
expect(crumb.detail?.firstLine).toBe(2);
|
|
187
|
+
expect(crumb.detail?.count).toBe(1);
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
it.each([
|
|
192
|
+
{ directive: 'map_Kd' },
|
|
193
|
+
{ directive: 'map_Ka' },
|
|
194
|
+
{ directive: 'map_Ks' },
|
|
195
|
+
{ directive: 'map_Bump' },
|
|
196
|
+
{ directive: 'bump' },
|
|
197
|
+
])(
|
|
198
|
+
'records map-no-filename (Drop, parseObjMaterialLibrary) for a bare $directive after a material',
|
|
199
|
+
({ directive }) => {
|
|
200
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
201
|
+
parseObjMaterialLibrary(`newmtl M\n${directive}\n`, diagnostics);
|
|
202
|
+
expect(diagnostics).toHaveLength(1);
|
|
203
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.map-no-filename');
|
|
204
|
+
expect(crumb.severity).toBe('Drop');
|
|
205
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
206
|
+
expect(crumb.detail?.directive).toBe(directive);
|
|
207
|
+
expect(crumb.detail?.firstLine).toBe(2);
|
|
208
|
+
expect(crumb.detail?.count).toBe(1);
|
|
209
|
+
},
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
it('records directive-before-material (Drop) for a BARE directive before any newmtl', () => {
|
|
213
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
214
|
+
parseObjMaterialLibrary('Kd\n', diagnostics);
|
|
215
|
+
expect(diagnostics).toHaveLength(1);
|
|
216
|
+
const crumb = expectOneCrumb(diagnostics, 'mtl.directive-before-material');
|
|
217
|
+
expect(crumb.severity).toBe('Drop');
|
|
218
|
+
expect(crumb.origin).toBe('parseObjMaterialLibrary');
|
|
219
|
+
expect(crumb.detail?.firstDirective).toBe('Kd');
|
|
220
|
+
expect(crumb.detail?.count).toBe(1);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('records no diagnostics for a well-formed MTL even with a collector engaged', () => {
|
|
224
|
+
const diagnostics: ImportDiagnostic[] = [];
|
|
225
|
+
parseObjMaterialLibrary('newmtl M\nKd 1 0 0\nNs 32\nmap_Kd tex.png\n', diagnostics);
|
|
226
|
+
expect(diagnostics).toHaveLength(0);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('provides defaults for unset properties', () => {
|
|
230
|
+
const mtl = 'newmtl Default\n';
|
|
231
|
+
const lib = parseObjMaterialLibrary(mtl);
|
|
232
|
+
const mat = lib.materials.get('Default')!;
|
|
233
|
+
expect(mat.ambient).toEqual([0, 0, 0]);
|
|
234
|
+
expect(mat.diffuse).toEqual([0.8, 0.8, 0.8]);
|
|
235
|
+
expect(mat.specular).toEqual([0, 0, 0]);
|
|
236
|
+
expect(mat.specularExponent).toBe(0);
|
|
237
|
+
expect(mat.dissolve).toBe(1);
|
|
238
|
+
expect(mat.illumination).toBe(2);
|
|
239
|
+
expect(mat.mapDiffuse).toBeNull();
|
|
240
|
+
expect(mat.mapAmbient).toBeNull();
|
|
241
|
+
expect(mat.mapSpecular).toBeNull();
|
|
242
|
+
expect(mat.mapBump).toBeNull();
|
|
243
|
+
});
|
|
244
|
+
});
|