@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,35 @@
|
|
|
1
|
+
import { createMeshGeometry } from '@flighthq/mesh';
|
|
2
|
+
import { addNodeChild } from '@flighthq/node';
|
|
3
|
+
import { createMesh, createScene3D, createNode3D } from '@flighthq/scene3d';
|
|
4
|
+
import { createSkeleton3D } from '@flighthq/skeleton3d';
|
|
5
|
+
import type { Node3D } from '@flighthq/types';
|
|
6
|
+
|
|
7
|
+
import { findScene3DSkeletonJoints } from './sceneSkeleton';
|
|
8
|
+
import { CANONICAL_LAYOUT } from './shared';
|
|
9
|
+
|
|
10
|
+
describe('findScene3DSkeletonJoints', () => {
|
|
11
|
+
it('returns the joints of the first skinned mesh found in the scene', () => {
|
|
12
|
+
const scene = createScene3D();
|
|
13
|
+
const joint0 = createNode3D();
|
|
14
|
+
const joint1 = createNode3D();
|
|
15
|
+
const skeleton = createSkeleton3D([joint0, joint1], new Float32Array(32), null);
|
|
16
|
+
const geometry = createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: new Float32Array(0) });
|
|
17
|
+
const mesh = createMesh(geometry, []);
|
|
18
|
+
mesh.skin = { skeleton, skeletonRoot: null };
|
|
19
|
+
addNodeChild(scene.root, mesh as unknown as Node3D);
|
|
20
|
+
|
|
21
|
+
expect(findScene3DSkeletonJoints(scene.root)).toBe(skeleton.joints);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns null when the scene has no skinned mesh', () => {
|
|
25
|
+
const scene = createScene3D();
|
|
26
|
+
addNodeChild(
|
|
27
|
+
scene.root,
|
|
28
|
+
createMesh(
|
|
29
|
+
createMeshGeometry({ layout: CANONICAL_LAYOUT, vertices: new Float32Array(0) }),
|
|
30
|
+
[],
|
|
31
|
+
) as unknown as Node3D,
|
|
32
|
+
);
|
|
33
|
+
expect(findScene3DSkeletonJoints(scene.root)).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { EmbeddedImageResourceReference, ExternalImageResourceReference } from '@flighthq/types';
|
|
2
|
+
import { ResourceResolutionState } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildEmbeddedImageResourceReference,
|
|
6
|
+
buildExternalImageResourceReference,
|
|
7
|
+
convertPositionsZUpToYUp,
|
|
8
|
+
convertQuaternionsZUpToYUp,
|
|
9
|
+
convertTransformLhToRh,
|
|
10
|
+
createEmbeddedTextureRef,
|
|
11
|
+
createExternalTextureRef,
|
|
12
|
+
negateVec3Z,
|
|
13
|
+
packSkinInfluences,
|
|
14
|
+
reverseTriangleWinding,
|
|
15
|
+
} from './shared';
|
|
16
|
+
|
|
17
|
+
describe('buildEmbeddedImageResourceReference', () => {
|
|
18
|
+
it('builds an unresolved embedded image identity without a Texture wrapper', () => {
|
|
19
|
+
const bytes = new Uint8Array([1, 2, 3]);
|
|
20
|
+
const ref = buildEmbeddedImageResourceReference(bytes, 'image/png');
|
|
21
|
+
expect(ref.bytes).toBe(bytes);
|
|
22
|
+
expect(ref.mimeType).toBe('image/png');
|
|
23
|
+
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('buildExternalImageResourceReference', () => {
|
|
28
|
+
it('builds an unresolved external image identity with its base path', () => {
|
|
29
|
+
const ref = buildExternalImageResourceReference('leaf.png', '/models');
|
|
30
|
+
expect(ref.basePath).toBe('/models');
|
|
31
|
+
expect(ref.uri).toBe('leaf.png');
|
|
32
|
+
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('convertPositionsZUpToYUp', () => {
|
|
37
|
+
it('converts (x, y, z) to (x, z, -y) for packed vec3 data', () => {
|
|
38
|
+
const values = [1, 2, 3, 4, 5, 6];
|
|
39
|
+
convertPositionsZUpToYUp(values);
|
|
40
|
+
expect(values).toEqual([1, 3, -2, 4, 6, -5]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('handles interleaved data with stride and offset', () => {
|
|
44
|
+
// Interleaved: [px, py, pz, nx, ny, nz] per vertex, stride=6, offset=0 for positions
|
|
45
|
+
const values = [1, 2, 3, 10, 20, 30, 4, 5, 6, 40, 50, 60];
|
|
46
|
+
convertPositionsZUpToYUp(values, 6, 0);
|
|
47
|
+
expect(values).toEqual([1, 3, -2, 10, 20, 30, 4, 6, -5, 40, 50, 60]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('avoids producing negative zero', () => {
|
|
51
|
+
const values = [1, 0, 3];
|
|
52
|
+
convertPositionsZUpToYUp(values);
|
|
53
|
+
expect(Object.is(values[2], -0)).toBe(false);
|
|
54
|
+
expect(values[2]).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('handles an empty array', () => {
|
|
58
|
+
const values: number[] = [];
|
|
59
|
+
convertPositionsZUpToYUp(values);
|
|
60
|
+
expect(values).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('convertQuaternionsZUpToYUp', () => {
|
|
65
|
+
it('converts (qx, qy, qz, qw) to (qx, qz, -qy, qw) for packed quaternion data', () => {
|
|
66
|
+
const values = [0.5, 0.5, 0.5, -0.5];
|
|
67
|
+
convertQuaternionsZUpToYUp(values);
|
|
68
|
+
expect(values).toEqual([0.5, 0.5, -0.5, -0.5]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('handles multiple quaternions', () => {
|
|
72
|
+
const values = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
73
|
+
convertQuaternionsZUpToYUp(values);
|
|
74
|
+
expect(values).toEqual([1, 3, -2, 4, 5, 7, -6, 8]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('avoids producing negative zero', () => {
|
|
78
|
+
const values = [0, 0, 1, 1];
|
|
79
|
+
convertQuaternionsZUpToYUp(values);
|
|
80
|
+
expect(Object.is(values[2], -0)).toBe(false);
|
|
81
|
+
expect(values[2]).toBe(0);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('convertTransformLhToRh', () => {
|
|
86
|
+
it('negates S·M·S indices (2, 5, 6, 7, 11) of a 4×3 column-major transform', () => {
|
|
87
|
+
const transform = new Float64Array([1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 20, 30]);
|
|
88
|
+
convertTransformLhToRh(transform);
|
|
89
|
+
// Identity rotation stays identity (negated zeros); only tz flips.
|
|
90
|
+
expect(transform[9]).toBe(10);
|
|
91
|
+
expect(transform[10]).toBe(20);
|
|
92
|
+
expect(transform[11]).toBe(-30);
|
|
93
|
+
expect(transform[0]).toBe(1);
|
|
94
|
+
expect(transform[4]).toBe(1);
|
|
95
|
+
expect(transform[8]).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('negates the correct off-diagonal and translation elements', () => {
|
|
99
|
+
const transform = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
|
100
|
+
convertTransformLhToRh(transform);
|
|
101
|
+
expect(transform[0]).toBe(1);
|
|
102
|
+
expect(transform[1]).toBe(2);
|
|
103
|
+
expect(transform[2]).toBe(-3);
|
|
104
|
+
expect(transform[3]).toBe(4);
|
|
105
|
+
expect(transform[4]).toBe(5);
|
|
106
|
+
expect(transform[5]).toBe(-6);
|
|
107
|
+
expect(transform[6]).toBe(-7);
|
|
108
|
+
expect(transform[7]).toBe(-8);
|
|
109
|
+
expect(transform[8]).toBe(9);
|
|
110
|
+
expect(transform[9]).toBe(10);
|
|
111
|
+
expect(transform[10]).toBe(11);
|
|
112
|
+
expect(transform[11]).toBe(-12);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('createEmbeddedTextureRef', () => {
|
|
117
|
+
it('wraps encoded bytes as an Unresolved Embedded resource ref without decoding them', () => {
|
|
118
|
+
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
119
|
+
const texture = createEmbeddedTextureRef(bytes, 'image/png');
|
|
120
|
+
expect(texture.image).toBeNull();
|
|
121
|
+
const ref = texture.resource as EmbeddedImageResourceReference;
|
|
122
|
+
expect(ref.kind).toBe('Embedded');
|
|
123
|
+
expect(ref.bytes).toBe(bytes);
|
|
124
|
+
expect(ref.mimeType).toBe('image/png');
|
|
125
|
+
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('accepts a null mimeType for the resolver to sniff', () => {
|
|
129
|
+
const ref = createEmbeddedTextureRef(new Uint8Array([0]), null).resource as EmbeddedImageResourceReference;
|
|
130
|
+
expect(ref.mimeType).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('createExternalTextureRef', () => {
|
|
135
|
+
it('wraps a filename as an Unresolved External resource ref without loading it', () => {
|
|
136
|
+
const texture = createExternalTextureRef('models/hero.png');
|
|
137
|
+
expect(texture.image).toBeNull();
|
|
138
|
+
const ref = texture.resource as ExternalImageResourceReference;
|
|
139
|
+
expect(ref.kind).toBe('External');
|
|
140
|
+
expect(ref.uri).toBe('models/hero.png');
|
|
141
|
+
expect(ref.state).toBe(ResourceResolutionState.Unresolved);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('negateVec3Z', () => {
|
|
146
|
+
it('negates the Z component of every xyz triple', () => {
|
|
147
|
+
const values = [1, 2, 3, 4, 5, 6];
|
|
148
|
+
negateVec3Z(values);
|
|
149
|
+
expect(values).toEqual([1, 2, -3, 4, 5, -6]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('avoids producing negative zero', () => {
|
|
153
|
+
const values = [1, 2, 0];
|
|
154
|
+
negateVec3Z(values);
|
|
155
|
+
expect(Object.is(values[2], -0)).toBe(false);
|
|
156
|
+
expect(values[2]).toBe(0);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('packSkinInfluences', () => {
|
|
161
|
+
it('keeps the four highest-weight influences and renormalizes them to sum 1', () => {
|
|
162
|
+
const joints: number[] = [];
|
|
163
|
+
const weights: number[] = [];
|
|
164
|
+
packSkinInfluences(
|
|
165
|
+
[
|
|
166
|
+
{ jointIndex: 5, weight: 0.1 },
|
|
167
|
+
{ jointIndex: 2, weight: 0.4 },
|
|
168
|
+
{ jointIndex: 9, weight: 0.05 },
|
|
169
|
+
{ jointIndex: 1, weight: 0.3 },
|
|
170
|
+
{ jointIndex: 7, weight: 0.15 },
|
|
171
|
+
],
|
|
172
|
+
joints,
|
|
173
|
+
weights,
|
|
174
|
+
);
|
|
175
|
+
// Top four by weight: joints 2 (0.4), 1 (0.3), 7 (0.15), 5 (0.1); joint 9 (0.05) dropped.
|
|
176
|
+
expect(joints).toEqual([2, 1, 7, 5]);
|
|
177
|
+
expect(weights[0] + weights[1] + weights[2] + weights[3]).toBeCloseTo(1);
|
|
178
|
+
expect(weights[0]).toBeCloseTo(0.4 / 0.95);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('zero-fills unused slots for fewer than four influences', () => {
|
|
182
|
+
const joints: number[] = [];
|
|
183
|
+
const weights: number[] = [];
|
|
184
|
+
packSkinInfluences([{ jointIndex: 3, weight: 0.5 }], joints, weights);
|
|
185
|
+
expect(joints).toEqual([3, 0, 0, 0]);
|
|
186
|
+
expect(weights).toEqual([1, 0, 0, 0]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('leaves all weights zero for a vertex with no influence', () => {
|
|
190
|
+
const joints: number[] = [];
|
|
191
|
+
const weights: number[] = [];
|
|
192
|
+
packSkinInfluences([], joints, weights);
|
|
193
|
+
expect(joints).toEqual([0, 0, 0, 0]);
|
|
194
|
+
expect(weights).toEqual([0, 0, 0, 0]);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('reverseTriangleWinding', () => {
|
|
199
|
+
it('swaps second and third index of each triangle', () => {
|
|
200
|
+
const indices = [0, 1, 2, 3, 4, 5];
|
|
201
|
+
reverseTriangleWinding(indices);
|
|
202
|
+
expect(indices).toEqual([0, 2, 1, 3, 5, 4]);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('handles a single triangle', () => {
|
|
206
|
+
const indices = [10, 20, 30];
|
|
207
|
+
reverseTriangleWinding(indices);
|
|
208
|
+
expect(indices).toEqual([10, 30, 20]);
|
|
209
|
+
});
|
|
210
|
+
});
|