@forgeax/engine-import 0.1.19 → 0.1.20
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/.tsbuildinfo +1 -1
- package/dist/__tests__/scriptable-pack-texture.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-texture.unit.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +39 -17
- package/dist/index.mjs.map +1 -1
- package/dist/scriptable-pack-output-producers.d.ts +1 -0
- package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/scriptable-pack-product.contract.test.ts +4 -5
- package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +4 -5
- package/src/__tests__/scriptable-pack-texture.unit.test.ts +96 -0
- package/src/index.ts +1 -0
- package/src/scriptable-pack-output-producers.ts +54 -16
|
@@ -2,6 +2,7 @@ import type { ScriptablePackSceneComponent } from '@forgeax/engine-pack/source';
|
|
|
2
2
|
import { type AssetOutputProducer, AssetOutputProducerRegistry } from './scriptable-pack.js';
|
|
3
3
|
export declare const materialAssetOutputProducer: AssetOutputProducer;
|
|
4
4
|
export declare const meshAssetOutputProducer: AssetOutputProducer;
|
|
5
|
+
export declare const textureAssetOutputProducer: AssetOutputProducer;
|
|
5
6
|
export declare function createSceneAssetOutputProducer(sceneComponents?: readonly ScriptablePackSceneComponent[]): AssetOutputProducer;
|
|
6
7
|
export declare function createStandardAssetOutputProducerRegistry(sceneComponents?: readonly ScriptablePackSceneComponent[]): AssetOutputProducerRegistry;
|
|
7
8
|
//# sourceMappingURL=scriptable-pack-output-producers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scriptable-pack-output-producers.d.ts","sourceRoot":"","sources":["../src/scriptable-pack-output-producers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"scriptable-pack-output-producers.d.ts","sourceRoot":"","sources":["../src/scriptable-pack-output-producers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAkChF,OAAO,EAEL,KAAK,mBAAmB,EACxB,2BAA2B,EAE5B,MAAM,sBAAsB,CAAC;AAuZ9B,eAAO,MAAM,2BAA2B,qBAIvC,CAAC;AACF,eAAO,MAAM,uBAAuB,qBAA2D,CAAC;AAChG,eAAO,MAAM,0BAA0B,qBAItC,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,eAAe,GAAE,SAAS,4BAA4B,EAAO,GAC5D,mBAAmB,CAKrB;AAED,wBAAgB,yCAAyC,CACvD,eAAe,GAAE,SAAS,4BAA4B,EAAO,GAC5D,2BAA2B,CAuB7B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-import",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"LICENSE"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@forgeax/engine-ddc": "0.1.
|
|
31
|
-
"@forgeax/engine-ecs": "0.1.
|
|
32
|
-
"@forgeax/engine-geometry": "0.1.
|
|
33
|
-
"@forgeax/engine-pack": "0.1.
|
|
34
|
-
"@forgeax/engine-scene": "0.1.
|
|
35
|
-
"@forgeax/engine-types": "0.1.
|
|
30
|
+
"@forgeax/engine-ddc": "0.1.20",
|
|
31
|
+
"@forgeax/engine-ecs": "0.1.20",
|
|
32
|
+
"@forgeax/engine-geometry": "0.1.20",
|
|
33
|
+
"@forgeax/engine-pack": "0.1.20",
|
|
34
|
+
"@forgeax/engine-scene": "0.1.20",
|
|
35
|
+
"@forgeax/engine-types": "0.1.20",
|
|
36
36
|
"@noble/hashes": "^2.2.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -21,12 +21,11 @@ describe('ScriptablePack terminal product contract', () => {
|
|
|
21
21
|
ok({
|
|
22
22
|
hero: {
|
|
23
23
|
kind: 'texture',
|
|
24
|
-
width: 1,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
data: new Uint8Array(),
|
|
24
|
+
shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
|
|
25
|
+
format: 'rgba8unorm-srgb',
|
|
26
|
+
data: new Uint8Array(4),
|
|
28
27
|
colorSpace: 'srgb',
|
|
29
|
-
|
|
28
|
+
mips: { kind: 'none' },
|
|
30
29
|
},
|
|
31
30
|
}),
|
|
32
31
|
},
|
|
@@ -89,12 +89,11 @@ const matrixAssets = [
|
|
|
89
89
|
{ kind: 'scene', entities: [] } satisfies SceneAsset,
|
|
90
90
|
{
|
|
91
91
|
kind: 'texture',
|
|
92
|
-
width: 1,
|
|
93
|
-
|
|
94
|
-
format: 'rgba8unorm',
|
|
92
|
+
shape: { viewDimension: '2d', extent: { width: 1, height: 1 } },
|
|
93
|
+
format: 'rgba8unorm-srgb',
|
|
95
94
|
data: new Uint8Array([255, 255, 255, 255]),
|
|
96
95
|
colorSpace: 'srgb',
|
|
97
|
-
|
|
96
|
+
mips: { kind: 'none' },
|
|
98
97
|
} satisfies TextureAsset,
|
|
99
98
|
{
|
|
100
99
|
kind: 'equirect',
|
|
@@ -179,7 +178,7 @@ describe('standard ScriptablePack output producers', () => {
|
|
|
179
178
|
scene: 'scene-pack/3',
|
|
180
179
|
skeleton: 'ordinary-pod/1',
|
|
181
180
|
skin: 'ordinary-pod/1',
|
|
182
|
-
texture: '
|
|
181
|
+
texture: 'texture-pack/1',
|
|
183
182
|
tileset: 'ordinary-pod/1',
|
|
184
183
|
video: 'ordinary-pod/1',
|
|
185
184
|
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
2
|
+
import type { AssetGuid as AssetGuidType, TextureAsset } from '@forgeax/engine-types';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { textureAssetOutputProducer } from '../scriptable-pack-output-producers.js';
|
|
5
|
+
|
|
6
|
+
const GUID = '019ffa97-3000-7000-8000-000000000201';
|
|
7
|
+
const SOURCE_KEY = 'density/volume';
|
|
8
|
+
|
|
9
|
+
function guid(value: string): AssetGuidType {
|
|
10
|
+
const parsed = AssetGuid.parse(value);
|
|
11
|
+
if (!parsed.ok) throw parsed.error;
|
|
12
|
+
return parsed.value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function texture(shape: TextureAsset['shape'], data: Uint8Array): TextureAsset {
|
|
16
|
+
return {
|
|
17
|
+
kind: 'texture',
|
|
18
|
+
shape,
|
|
19
|
+
format: 'r8unorm',
|
|
20
|
+
colorSpace: 'linear',
|
|
21
|
+
mips: { kind: 'none' },
|
|
22
|
+
data,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('ScriptablePack texture output producer', () => {
|
|
27
|
+
it.each([
|
|
28
|
+
{
|
|
29
|
+
label: 'array',
|
|
30
|
+
shape: { viewDimension: '2d-array' as const, extent: { width: 8, height: 4, layers: 3 } },
|
|
31
|
+
bytes: 8 * 4 * 3,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: 'volume',
|
|
35
|
+
shape: { viewDimension: '3d' as const, extent: { width: 4, height: 4, depth: 4 } },
|
|
36
|
+
bytes: 4 * 4 * 4,
|
|
37
|
+
},
|
|
38
|
+
])('matches source producer facts for a $label logical asset', async ({ shape, bytes }) => {
|
|
39
|
+
const payload = texture(shape, new Uint8Array(bytes).fill(23));
|
|
40
|
+
const result = await textureAssetOutputProducer.produce({
|
|
41
|
+
guid: GUID,
|
|
42
|
+
sourceKey: SOURCE_KEY,
|
|
43
|
+
asset: payload,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(result.ok).toBe(true);
|
|
47
|
+
if (!result.ok) return;
|
|
48
|
+
expect(result.value.payload).toEqual(payload);
|
|
49
|
+
expect(result.value.artifacts.body).toMatchObject({
|
|
50
|
+
mediaType: 'application/x-forgeax-r8',
|
|
51
|
+
assetCodec: { name: 'r8unorm', version: '1' },
|
|
52
|
+
});
|
|
53
|
+
expect(result.value.artifacts.body?.bytes).toEqual(payload.data);
|
|
54
|
+
expect(result.value.refs).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('keeps one publication GUID and rejects slice or layer identity fabrication', async () => {
|
|
58
|
+
const payload = texture(
|
|
59
|
+
{ viewDimension: '3d', extent: { width: 2, height: 2, depth: 2 } },
|
|
60
|
+
new Uint8Array(8),
|
|
61
|
+
);
|
|
62
|
+
const result = await textureAssetOutputProducer.produce({
|
|
63
|
+
guid: AssetGuid.format(guid(GUID)),
|
|
64
|
+
sourceKey: SOURCE_KEY,
|
|
65
|
+
asset: payload,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(result.ok).toBe(true);
|
|
69
|
+
if (!result.ok) return;
|
|
70
|
+
expect((result.value.payload as TextureAsset).shape).toEqual(payload.shape);
|
|
71
|
+
expect(result.value.artifacts.body?.bytes.byteLength).toBe(8);
|
|
72
|
+
expect(result.value.refs).toEqual([]);
|
|
73
|
+
expect(result.value).not.toHaveProperty('sliceGuids');
|
|
74
|
+
expect(result.value).not.toHaveProperty('layerGuids');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('fails fast when the payload data is not a canonical byte stream', async () => {
|
|
78
|
+
const payload = texture(
|
|
79
|
+
{ viewDimension: '3d', extent: { width: 2, height: 2, depth: 2 } },
|
|
80
|
+
new Uint8Array(7),
|
|
81
|
+
);
|
|
82
|
+
const result = await textureAssetOutputProducer.produce({
|
|
83
|
+
guid: GUID,
|
|
84
|
+
sourceKey: SOURCE_KEY,
|
|
85
|
+
asset: payload,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(result.ok).toBe(false);
|
|
89
|
+
if (!result.ok) {
|
|
90
|
+
expect(result.error.code).toBe('import-internal-error');
|
|
91
|
+
expect(result.error.detail).toMatchObject({
|
|
92
|
+
reason: expect.stringContaining('texture data is not canonical'),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -126,6 +126,7 @@ export {
|
|
|
126
126
|
createStandardAssetOutputProducerRegistry,
|
|
127
127
|
materialAssetOutputProducer,
|
|
128
128
|
meshAssetOutputProducer,
|
|
129
|
+
textureAssetOutputProducer,
|
|
129
130
|
} from './scriptable-pack-output-producers.js';
|
|
130
131
|
export {
|
|
131
132
|
createScriptablePackStagedAssetSnapshotSource,
|
|
@@ -23,7 +23,13 @@ import type {
|
|
|
23
23
|
TilesetAsset,
|
|
24
24
|
VideoAsset,
|
|
25
25
|
} from '@forgeax/engine-types';
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
deriveTextureLayout,
|
|
28
|
+
err,
|
|
29
|
+
ImportError,
|
|
30
|
+
MATERIAL_TEXTURE_SLOTS,
|
|
31
|
+
ok,
|
|
32
|
+
} from '@forgeax/engine-types';
|
|
27
33
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
28
34
|
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
29
35
|
import { packMeshBinV4 } from './mesh-bin.js';
|
|
@@ -246,23 +252,50 @@ function bytes(value: Uint8Array | Uint8ClampedArray): Uint8Array {
|
|
|
246
252
|
return Uint8Array.from(value);
|
|
247
253
|
}
|
|
248
254
|
|
|
255
|
+
function textureProduct(input: AssetOutputInput): AssetOutputProduct {
|
|
256
|
+
if (input.asset.kind !== 'texture') throw new TypeError('expected TextureAsset');
|
|
257
|
+
const texture = input.asset as TextureAsset;
|
|
258
|
+
const layout = deriveTextureLayout({
|
|
259
|
+
shape: texture.shape,
|
|
260
|
+
format: texture.format,
|
|
261
|
+
mips: texture.mips,
|
|
262
|
+
actualByteLength: texture.data.byteLength,
|
|
263
|
+
order: 'mip-major,image-major,row-major',
|
|
264
|
+
});
|
|
265
|
+
if (!layout.ok) {
|
|
266
|
+
const expectedBytes =
|
|
267
|
+
layout.error.code === 'texture-packing-invalid'
|
|
268
|
+
? layout.error.detail.expectedBytes
|
|
269
|
+
: texture.data.byteLength;
|
|
270
|
+
const actualBytes =
|
|
271
|
+
layout.error.code === 'texture-packing-invalid'
|
|
272
|
+
? layout.error.detail.actualBytes
|
|
273
|
+
: texture.data.byteLength;
|
|
274
|
+
throw new TypeError(
|
|
275
|
+
`texture data is not canonical: expected ${expectedBytes} bytes, got ${actualBytes}`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
payload: texture,
|
|
280
|
+
refs: [],
|
|
281
|
+
artifacts: {
|
|
282
|
+
body: {
|
|
283
|
+
mediaType:
|
|
284
|
+
texture.format === 'r8unorm'
|
|
285
|
+
? 'application/x-forgeax-r8'
|
|
286
|
+
: `application/x-forgeax-${texture.format}`,
|
|
287
|
+
assetCodec: { name: texture.format, version: '1' },
|
|
288
|
+
bytes: bytes(texture.data),
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
249
294
|
function ordinaryPodProduct(input: AssetOutputInput): AssetOutputProduct {
|
|
250
295
|
const asset = input.asset;
|
|
251
296
|
switch (asset.kind) {
|
|
252
|
-
case 'texture':
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
payload: texture,
|
|
256
|
-
refs: [],
|
|
257
|
-
artifacts: {
|
|
258
|
-
body: {
|
|
259
|
-
mediaType: 'image/raw',
|
|
260
|
-
assetCodec: { name: 'raw-image', version: '1' },
|
|
261
|
-
bytes: bytes(texture.data),
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
};
|
|
265
|
-
}
|
|
297
|
+
case 'texture':
|
|
298
|
+
return textureProduct(input);
|
|
266
299
|
case 'equirect': {
|
|
267
300
|
const equirect = asset as EquirectAsset;
|
|
268
301
|
return {
|
|
@@ -418,6 +451,11 @@ export const materialAssetOutputProducer = createSafeProducer(
|
|
|
418
451
|
materialProduct,
|
|
419
452
|
);
|
|
420
453
|
export const meshAssetOutputProducer = createSafeProducer('mesh', 'mesh-binary/4', meshProduct);
|
|
454
|
+
export const textureAssetOutputProducer = createSafeProducer(
|
|
455
|
+
'texture',
|
|
456
|
+
'texture-pack/1',
|
|
457
|
+
textureProduct,
|
|
458
|
+
);
|
|
421
459
|
|
|
422
460
|
export function createSceneAssetOutputProducer(
|
|
423
461
|
sceneComponents: readonly ScriptablePackSceneComponent[] = [],
|
|
@@ -434,9 +472,9 @@ export function createStandardAssetOutputProducerRegistry(
|
|
|
434
472
|
const registry = new AssetOutputProducerRegistry();
|
|
435
473
|
registry.register(materialAssetOutputProducer);
|
|
436
474
|
registry.register(meshAssetOutputProducer);
|
|
475
|
+
registry.register(textureAssetOutputProducer);
|
|
437
476
|
registry.register(createSceneAssetOutputProducer(sceneComponents));
|
|
438
477
|
for (const kind of [
|
|
439
|
-
'texture',
|
|
440
478
|
'equirect',
|
|
441
479
|
'sampler',
|
|
442
480
|
'font',
|