@forgeax/engine-pack 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +429 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-path.test.d.ts +2 -0
- package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts +2 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cli.unit.test.d.ts +2 -0
- package/dist/__tests__/cli.unit.test.d.ts.map +1 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-asset-config.test.d.ts +2 -0
- package/dist/__tests__/load-asset-config.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts +2 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts.map +1 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts +2 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts.map +1 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts +2 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts +2 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
- package/dist/__tests__/pack.unit.test.d.ts +2 -0
- package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/runtime.browser.test.d.ts +2 -0
- package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology.unit.test.d.ts +2 -0
- package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
- package/dist/artifact-path.d.ts +9 -0
- package/dist/artifact-path.d.ts.map +1 -0
- package/dist/artifact-path.mjs +53 -0
- package/dist/artifact-path.mjs.map +1 -0
- package/dist/atlas/run-atlas.d.ts +8 -0
- package/dist/atlas/run-atlas.d.ts.map +1 -0
- package/dist/atlas/shelf-pack.d.ts +55 -0
- package/dist/atlas/shelf-pack.d.ts.map +1 -0
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.mjs +3 -0
- package/dist/bridge.mjs.map +1 -0
- package/dist/build.d.ts +19 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +10203 -0
- package/dist/build.mjs.map +1 -0
- package/dist/builtin.d.ts +44 -0
- package/dist/builtin.d.ts.map +1 -0
- package/dist/builtin.mjs +141 -0
- package/dist/builtin.mjs.map +1 -0
- package/dist/catalog-builder.d.ts +43 -0
- package/dist/catalog-builder.d.ts.map +1 -0
- package/dist/catalog-delta.d.ts +11 -0
- package/dist/catalog-delta.d.ts.map +1 -0
- package/dist/catalog-projection.d.ts +91 -0
- package/dist/catalog-projection.d.ts.map +1 -0
- package/dist/cli-asset.d.ts +22 -0
- package/dist/cli-asset.d.ts.map +1 -0
- package/dist/cli-asset.mjs +9697 -0
- package/dist/cli-asset.mjs.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.mjs +29 -0
- package/dist/config.mjs.map +1 -0
- package/dist/deriveAssetName.d.ts +15 -0
- package/dist/deriveAssetName.d.ts.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +19 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/evidence/material-cook.d.ts +92 -0
- package/dist/evidence/material-cook.d.ts.map +1 -0
- package/dist/evidence/offline-evidence.d.ts +25 -0
- package/dist/evidence/offline-evidence.d.ts.map +1 -0
- package/dist/evidence/source-inventory.d.ts +29 -0
- package/dist/evidence/source-inventory.d.ts.map +1 -0
- package/dist/guid.d.ts +45 -0
- package/dist/guid.d.ts.map +1 -0
- package/dist/guid.mjs +94 -0
- package/dist/guid.mjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8612 -0
- package/dist/index.mjs.map +1 -0
- package/dist/material/artifact-writer.d.ts +14 -0
- package/dist/material/artifact-writer.d.ts.map +1 -0
- package/dist/material-cook.d.ts +2 -0
- package/dist/material-cook.d.ts.map +1 -0
- package/dist/material-cook.mjs +248 -0
- package/dist/material-cook.mjs.map +1 -0
- package/dist/mesh-bin-contract.d.ts +36 -0
- package/dist/mesh-bin-contract.d.ts.map +1 -0
- package/dist/mesh-bin-contract.mjs +96 -0
- package/dist/mesh-bin-contract.mjs.map +1 -0
- package/dist/name.mjs +22 -0
- package/dist/name.mjs.map +1 -0
- package/dist/native-cooker-registry.d.ts +47 -0
- package/dist/native-cooker-registry.d.ts.map +1 -0
- package/dist/native-cooker.d.ts +3 -0
- package/dist/native-cooker.d.ts.map +1 -0
- package/dist/native-cooker.mjs +142 -0
- package/dist/native-cooker.mjs.map +1 -0
- package/dist/package-finalizer.d.ts +121 -0
- package/dist/package-finalizer.d.ts.map +1 -0
- package/dist/producer-contract.d.ts +19 -0
- package/dist/producer-contract.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts +4 -0
- package/dist/resolve-asset-source.d.ts.map +1 -0
- package/dist/resolve-asset-source.mjs +109 -0
- package/dist/resolve-asset-source.mjs.map +1 -0
- package/dist/runtime-projection.d.ts +14 -0
- package/dist/runtime-projection.d.ts.map +1 -0
- package/dist/runtime-publication.d.ts +44 -0
- package/dist/runtime-publication.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +7261 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/scanner.d.ts +113 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.mjs +8819 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/schema-compiled.d.ts +12 -0
- package/dist/schema-compiled.d.ts.map +1 -0
- package/dist/schema.mjs +7369 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scriptable-pack-node.d.ts +62 -0
- package/dist/scriptable-pack-node.d.ts.map +1 -0
- package/dist/scriptable-pack-node.mjs +1674 -0
- package/dist/scriptable-pack-node.mjs.map +1 -0
- package/dist/scriptable-pack-worker.d.ts +2 -0
- package/dist/scriptable-pack-worker.d.ts.map +1 -0
- package/dist/scriptable-pack-worker.mjs +224 -0
- package/dist/scriptable-pack-worker.mjs.map +1 -0
- package/dist/scriptable-pack.d.ts +558 -0
- package/dist/scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack.mjs +672 -0
- package/dist/scriptable-pack.mjs.map +1 -0
- package/dist/topology.d.ts +10 -0
- package/dist/topology.d.ts.map +1 -0
- package/package.json +154 -0
- package/schema/meta.schema.json +173 -0
- package/schema/pack.schema.json +232 -0
- package/src/__tests__/ai-recovery.unit.test.ts +114 -0
- package/src/__tests__/artifact-path.test.ts +41 -0
- package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
- package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
- package/src/__tests__/asset-evidence-schema.test.ts +86 -0
- package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
- package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
- package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
- package/src/__tests__/cli.unit.test.ts +448 -0
- package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
- package/src/__tests__/guid-collision.unit.test.ts +112 -0
- package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
- package/src/__tests__/load-asset-config.test.ts +121 -0
- package/src/__tests__/material-cook-dependencies.unit.test.ts +112 -0
- package/src/__tests__/material-cook-receipt.unit.test.ts +148 -0
- package/src/__tests__/material-cook-schema.unit.test.ts +152 -0
- package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +22 -0
- package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
- package/src/__tests__/meta-schema-optional.test.ts +116 -0
- package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
- package/src/__tests__/native-cooker-registry.test.ts +64 -0
- package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
- package/src/__tests__/offline-evidence.integration.test.ts +106 -0
- package/src/__tests__/owner-chain.integration.test.ts +78 -0
- package/src/__tests__/pack-v2-fixture-migration.test.ts +87 -0
- package/src/__tests__/pack-v2-schema.test.ts +142 -0
- package/src/__tests__/pack.unit.test.ts +2218 -0
- package/src/__tests__/package-finalizer.contract.test.ts +105 -0
- package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
- package/src/__tests__/resolve-asset-source.test.ts +121 -0
- package/src/__tests__/runtime-projection.unit.test.ts +87 -0
- package/src/__tests__/runtime.browser.test.ts +15 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
- package/src/__tests__/scanner-inventory.test.ts +76 -0
- package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
- package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
- package/src/__tests__/scriptable-pack.test-d.ts +131 -0
- package/src/__tests__/scriptable-pack.unit.test.ts +789 -0
- package/src/__tests__/topology-actions.unit.test.ts +94 -0
- package/src/__tests__/topology.unit.test.ts +85 -0
- package/src/artifact-path.ts +69 -0
- package/src/atlas/run-atlas.ts +358 -0
- package/src/atlas/shelf-pack.ts +172 -0
- package/src/atlas/upng-ambient.d.ts +13 -0
- package/src/bridge.ts +2 -0
- package/src/build.ts +139 -0
- package/src/builtin.ts +117 -0
- package/src/catalog-builder.ts +313 -0
- package/src/catalog-delta.ts +189 -0
- package/src/catalog-projection.ts +344 -0
- package/src/cli-asset.ts +586 -0
- package/src/config.ts +36 -0
- package/src/deriveAssetName.ts +42 -0
- package/src/errors.ts +47 -0
- package/src/evidence/material-cook.ts +411 -0
- package/src/evidence/offline-evidence.ts +90 -0
- package/src/evidence/source-inventory.ts +193 -0
- package/src/guid.ts +99 -0
- package/src/index.ts +108 -0
- package/src/material/artifact-writer.ts +26 -0
- package/src/material-cook.ts +15 -0
- package/src/mesh-bin-contract.ts +150 -0
- package/src/native-cooker-registry.ts +235 -0
- package/src/native-cooker.ts +2 -0
- package/src/package-finalizer.ts +394 -0
- package/src/producer-contract.ts +277 -0
- package/src/resolve-asset-source.ts +88 -0
- package/src/runtime-projection.ts +100 -0
- package/src/runtime-publication.ts +190 -0
- package/src/runtime.ts +23 -0
- package/src/scanner.ts +715 -0
- package/src/schema/material-cook.schema.json +119 -0
- package/src/schema-compiled.ts +238 -0
- package/src/scriptable-pack-node.ts +1394 -0
- package/src/scriptable-pack-worker.ts +308 -0
- package/src/scriptable-pack.ts +1006 -0
- package/src/topology.ts +157 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Pure-function shelf packer for the `forgeax-engine-remote-asset atlas`
|
|
2
|
+
// subcommand (feat-20260521-sprite-atlas-animation M5' T-31). Maps a list
|
|
3
|
+
// of decoded RGBA sprites to a non-overlapping atlas region map +
|
|
4
|
+
// atlasWidth / atlasHeight envelope.
|
|
5
|
+
//
|
|
6
|
+
// Why pure-fn (plan-strategy section 2 D-4):
|
|
7
|
+
// - sidecar (`<name>.atlas.meta.json`) addresses regions by source name,
|
|
8
|
+
// so the on-disk contract is stable under algorithm swaps. Future feats
|
|
9
|
+
// can replace shelfPack with MaxRects / rectpack2D without breaking
|
|
10
|
+
// downstream consumers (charter P5 producer/consumer split).
|
|
11
|
+
// - the caller (runAtlas) owns disk IO + decode; this file owns only the
|
|
12
|
+
// geometric layout decision so unit tests do not need filesystem state.
|
|
13
|
+
//
|
|
14
|
+
// Algorithm (research F-1 Godot chart_pack shelf precedent, ~80 LOC MVP):
|
|
15
|
+
// 1. Validate: empty input -> 'atlas-empty-input'; any single image
|
|
16
|
+
// width/height > maxAtlasSize -> 'atlas-size-exceeded' (caller cannot
|
|
17
|
+
// recover by repacking, so we fail-fast before the inner loop).
|
|
18
|
+
// 2. Sort by height descending (standard shelf optimisation -- tall
|
|
19
|
+
// sprites on the bottom shelves keep gaps small); break ties by name
|
|
20
|
+
// so layouts are deterministic across runs (helps reproducible builds
|
|
21
|
+
// + cross-platform CI parity).
|
|
22
|
+
// 3. Pick a target shelf width by `nextPow2(ceil(sqrt(totalArea)))` so
|
|
23
|
+
// the first shelf width is roughly square; clamp to maxAtlasSize and
|
|
24
|
+
// widen if any single image exceeds the initial guess.
|
|
25
|
+
// 4. Walk left-to-right; start a new shelf when the cursor would overflow
|
|
26
|
+
// the target width. New shelves stack downward; bail out with
|
|
27
|
+
// 'atlas-size-exceeded' if a placement would push past maxAtlasSize.
|
|
28
|
+
// 5. atlasHeight = nextPow2(usedHeight) clamped to maxAtlasSize so the
|
|
29
|
+
// output dimensions are GPU-friendly (most sprite atlases live happily
|
|
30
|
+
// inside power-of-two limits even when WebGPU lifts the requirement).
|
|
31
|
+
|
|
32
|
+
import type { ImageErrorCode } from '@forgeax/engine-types';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Decoded image input for shelfPack. `pixels` length must equal
|
|
36
|
+
* `width * height * 4` (RGBA8); the field is carried so the caller can
|
|
37
|
+
* blit into the atlas buffer without re-decoding inside this module.
|
|
38
|
+
*/
|
|
39
|
+
export interface AtlasImageInput {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly width: number;
|
|
42
|
+
readonly height: number;
|
|
43
|
+
readonly pixels: Uint8Array;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* One placed sprite inside the packed atlas. `(x, y)` is the top-left
|
|
48
|
+
* corner in pixel coordinates; `(w, h)` mirrors the source dimensions.
|
|
49
|
+
*/
|
|
50
|
+
export interface AtlasRegion {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly x: number;
|
|
53
|
+
readonly y: number;
|
|
54
|
+
readonly w: number;
|
|
55
|
+
readonly h: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ShelfPackResult {
|
|
59
|
+
readonly atlasWidth: number;
|
|
60
|
+
readonly atlasHeight: number;
|
|
61
|
+
readonly regions: ReadonlyArray<AtlasRegion>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Detail shape used by both `atlas-empty-input` and `atlas-size-exceeded`
|
|
66
|
+
* branches. Fields are optional because the two codes carry different
|
|
67
|
+
* subsets; the caller projects each branch onto the matching
|
|
68
|
+
* `ImageErrorDetail` variant before emitting the structured error.
|
|
69
|
+
*/
|
|
70
|
+
export interface ShelfPackError {
|
|
71
|
+
readonly code: Extract<ImageErrorCode, 'atlas-empty-input' | 'atlas-size-exceeded'>;
|
|
72
|
+
readonly detail: {
|
|
73
|
+
readonly name?: string;
|
|
74
|
+
readonly width?: number;
|
|
75
|
+
readonly height?: number;
|
|
76
|
+
readonly maxAtlasSize?: number;
|
|
77
|
+
readonly receivedCount?: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ShelfPackOutcome =
|
|
82
|
+
| { readonly ok: true; readonly value: ShelfPackResult }
|
|
83
|
+
| { readonly ok: false; readonly error: ShelfPackError };
|
|
84
|
+
|
|
85
|
+
function sizeExceeded(
|
|
86
|
+
image: AtlasImageInput,
|
|
87
|
+
maxAtlasSize: number,
|
|
88
|
+
): { readonly ok: false; readonly error: ShelfPackError } {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
error: {
|
|
92
|
+
code: 'atlas-size-exceeded',
|
|
93
|
+
detail: {
|
|
94
|
+
name: image.name,
|
|
95
|
+
width: image.width,
|
|
96
|
+
height: image.height,
|
|
97
|
+
maxAtlasSize,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function nextPow2(n: number): number {
|
|
104
|
+
if (n <= 1) return 1;
|
|
105
|
+
let p = 1;
|
|
106
|
+
while (p < n) p <<= 1;
|
|
107
|
+
return p;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function shelfPack(
|
|
111
|
+
images: ReadonlyArray<AtlasImageInput>,
|
|
112
|
+
opts: { readonly maxAtlasSize: number },
|
|
113
|
+
): ShelfPackOutcome {
|
|
114
|
+
const { maxAtlasSize } = opts;
|
|
115
|
+
|
|
116
|
+
if (images.length === 0) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
error: { code: 'atlas-empty-input', detail: { receivedCount: 0 } },
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const img of images) {
|
|
124
|
+
if (img.width > maxAtlasSize || img.height > maxAtlasSize) {
|
|
125
|
+
return sizeExceeded(img, maxAtlasSize);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const sorted = [...images].sort((a, b) => {
|
|
130
|
+
if (b.height !== a.height) return b.height - a.height;
|
|
131
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
let totalArea = 0;
|
|
135
|
+
let maxImageWidth = 0;
|
|
136
|
+
for (const img of sorted) {
|
|
137
|
+
totalArea += img.width * img.height;
|
|
138
|
+
if (img.width > maxImageWidth) maxImageWidth = img.width;
|
|
139
|
+
}
|
|
140
|
+
let targetWidth = nextPow2(Math.ceil(Math.sqrt(totalArea)));
|
|
141
|
+
if (maxImageWidth > targetWidth) targetWidth = nextPow2(maxImageWidth);
|
|
142
|
+
if (targetWidth > maxAtlasSize) targetWidth = maxAtlasSize;
|
|
143
|
+
|
|
144
|
+
const regions: AtlasRegion[] = [];
|
|
145
|
+
let cursorX = 0;
|
|
146
|
+
let cursorY = 0;
|
|
147
|
+
const firstHeight = sorted[0]?.height ?? 0;
|
|
148
|
+
let currentShelfHeight = firstHeight;
|
|
149
|
+
let usedWidth = 0;
|
|
150
|
+
|
|
151
|
+
for (const img of sorted) {
|
|
152
|
+
if (cursorX + img.width > targetWidth && cursorX !== 0) {
|
|
153
|
+
cursorY += currentShelfHeight;
|
|
154
|
+
cursorX = 0;
|
|
155
|
+
currentShelfHeight = img.height;
|
|
156
|
+
}
|
|
157
|
+
if (cursorY + img.height > maxAtlasSize) {
|
|
158
|
+
return sizeExceeded(img, maxAtlasSize);
|
|
159
|
+
}
|
|
160
|
+
regions.push({ name: img.name, x: cursorX, y: cursorY, w: img.width, h: img.height });
|
|
161
|
+
cursorX += img.width;
|
|
162
|
+
if (cursorX > usedWidth) usedWidth = cursorX;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const usedHeight = cursorY + currentShelfHeight;
|
|
166
|
+
let atlasWidth = nextPow2(usedWidth);
|
|
167
|
+
if (atlasWidth > maxAtlasSize) atlasWidth = maxAtlasSize;
|
|
168
|
+
let atlasHeight = nextPow2(usedHeight);
|
|
169
|
+
if (atlasHeight > maxAtlasSize) atlasHeight = maxAtlasSize;
|
|
170
|
+
|
|
171
|
+
return { ok: true, value: { atlasWidth, atlasHeight, regions } };
|
|
172
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Ambient declaration for `upng-js` (PNG decoder/encoder) used by the
|
|
2
|
+
// `forgeax-engine-remote-asset atlas` subcommand (run-atlas.ts) and by
|
|
3
|
+
// its hermetic CLI integration tests under `__tests__/`. The package
|
|
4
|
+
// ships without bundled `.d.ts`; consumer sites cast through local
|
|
5
|
+
// interfaces to match the subset they use.
|
|
6
|
+
//
|
|
7
|
+
// Mirrors `packages/image/src/image-decoders.d.ts` so the `import('upng-js')`
|
|
8
|
+
// site in run-atlas.ts type-resolves under tsc -b without an `any` escape
|
|
9
|
+
// hatch leak. The pack package keeps a private declaration here rather
|
|
10
|
+
// than reaching into engine-image (`@forgeax/engine-image` would create a
|
|
11
|
+
// circular dep: image already depends on pack via @forgeax/engine-pack).
|
|
12
|
+
|
|
13
|
+
declare module 'upng-js';
|
package/src/bridge.ts
ADDED
package/src/build.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// @forgeax/engine-pack/build
|
|
2
|
+
// Node-only catalog, source scanning, package finalization, and build evidence.
|
|
3
|
+
|
|
4
|
+
export type {
|
|
5
|
+
AnimationClip,
|
|
6
|
+
AnimationGraph,
|
|
7
|
+
Asset,
|
|
8
|
+
AudioClipAsset,
|
|
9
|
+
EquirectAsset,
|
|
10
|
+
FontAsset,
|
|
11
|
+
MaterialAsset,
|
|
12
|
+
MeshAsset,
|
|
13
|
+
PackV2,
|
|
14
|
+
PackV2Error,
|
|
15
|
+
ParticleEffectAsset,
|
|
16
|
+
RenderPipelineAsset,
|
|
17
|
+
SamplerAsset,
|
|
18
|
+
SceneAsset,
|
|
19
|
+
SkeletonAsset,
|
|
20
|
+
SkinAsset,
|
|
21
|
+
TextureAsset,
|
|
22
|
+
TilesetAsset,
|
|
23
|
+
VideoAsset,
|
|
24
|
+
} from '@forgeax/engine-types';
|
|
25
|
+
export type { ArtifactPathContext } from './artifact-path.js';
|
|
26
|
+
export { validateArtifactPath } from './artifact-path.js';
|
|
27
|
+
export {
|
|
28
|
+
buildCatalogProjection,
|
|
29
|
+
type CatalogAuthority,
|
|
30
|
+
type CatalogBuildError,
|
|
31
|
+
type CatalogBuildErrorCode,
|
|
32
|
+
type CatalogBuildProjectionOptions,
|
|
33
|
+
type CatalogBuildResult,
|
|
34
|
+
type CatalogImporterPolicy,
|
|
35
|
+
type CatalogProducerVisibility,
|
|
36
|
+
metaPathForGuid,
|
|
37
|
+
} from './catalog-builder.js';
|
|
38
|
+
export { calculateCatalogDelta } from './catalog-delta.js';
|
|
39
|
+
export {
|
|
40
|
+
type CatalogOutputDeclaration,
|
|
41
|
+
type CatalogProducerMeta,
|
|
42
|
+
type CookedPackageProjection,
|
|
43
|
+
catalogProjectionFor,
|
|
44
|
+
currentProjectionFor,
|
|
45
|
+
findReservedAssetKindConflict,
|
|
46
|
+
type PackageCatalogInput,
|
|
47
|
+
projectCookedPackageEntry,
|
|
48
|
+
projectExternalCatalogEntries,
|
|
49
|
+
projectPackageCatalog,
|
|
50
|
+
projectRuntimeCatalogRow,
|
|
51
|
+
type RuntimeCatalogRowInput,
|
|
52
|
+
} from './catalog-projection.js';
|
|
53
|
+
export { type LoadAssetConfigResult, loadAssetConfig } from './config.js';
|
|
54
|
+
export {
|
|
55
|
+
type CookedMaterialRecord,
|
|
56
|
+
collectMaterialCookRefs,
|
|
57
|
+
createMaterialArtifactDigest,
|
|
58
|
+
type MaterialCookArtifact,
|
|
59
|
+
type MaterialCookIdentityExpectation,
|
|
60
|
+
type MaterialCookReceipt,
|
|
61
|
+
type MaterialCookRecordError,
|
|
62
|
+
type MaterialCookRefs,
|
|
63
|
+
projectCookedMaterialRecord,
|
|
64
|
+
serializeCookedMaterialRecord,
|
|
65
|
+
serializeMaterialCookReceipt,
|
|
66
|
+
validateCookedMaterialRecord,
|
|
67
|
+
validateMaterialCookReceipt,
|
|
68
|
+
} from './evidence/material-cook.js';
|
|
69
|
+
export { buildOfflineAssetEvidence, packageVerification } from './evidence/offline-evidence.js';
|
|
70
|
+
export {
|
|
71
|
+
decodeMeshBinHeader,
|
|
72
|
+
MESH_BIN_DIGEST_BYTES,
|
|
73
|
+
MESH_BIN_HEADER_V4_BYTES,
|
|
74
|
+
MESH_BIN_PROJECTION_VERSION,
|
|
75
|
+
MESH_BIN_VERSION,
|
|
76
|
+
type MeshBinContractError,
|
|
77
|
+
type MeshBinHeaderResult,
|
|
78
|
+
type MeshBinHeaderV4,
|
|
79
|
+
writeMeshBinHeader,
|
|
80
|
+
} from './mesh-bin-contract.js';
|
|
81
|
+
export {
|
|
82
|
+
type AuthoredPackAssetInput,
|
|
83
|
+
type AuthoredPackInput,
|
|
84
|
+
type FinalizedPackageProduct,
|
|
85
|
+
finalizePackageProduct,
|
|
86
|
+
finalizePackageTransportSource,
|
|
87
|
+
type PackageArtifactBody,
|
|
88
|
+
type PackageFinalizePolicy,
|
|
89
|
+
type PackageFinalizeResult,
|
|
90
|
+
type PackageFinalizerError,
|
|
91
|
+
type PackageProduct,
|
|
92
|
+
type PackageProductAsset,
|
|
93
|
+
packageTransportRevision,
|
|
94
|
+
upgradeLegacyAuthoredPack,
|
|
95
|
+
} from './package-finalizer.js';
|
|
96
|
+
export { validateProducerContract, validateProducerOutputs } from './producer-contract.js';
|
|
97
|
+
export { resolveAssetSource } from './resolve-asset-source.js';
|
|
98
|
+
export { parsePackV2, validateMeta, validatePack, validatePackV2 } from './runtime.js';
|
|
99
|
+
export {
|
|
100
|
+
createRuntimePackPublication,
|
|
101
|
+
type RuntimePackAssetInput,
|
|
102
|
+
type RuntimePackEnvelope,
|
|
103
|
+
type RuntimePackInput,
|
|
104
|
+
type RuntimePackPublication,
|
|
105
|
+
type RuntimePackPublicationInput,
|
|
106
|
+
} from './runtime-publication.js';
|
|
107
|
+
export {
|
|
108
|
+
type InventoryDeclaration,
|
|
109
|
+
type ScanInventory,
|
|
110
|
+
type ScanOptions,
|
|
111
|
+
type ScriptablePackInventoryDeclaration,
|
|
112
|
+
type ScriptablePackScanOptions,
|
|
113
|
+
STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS,
|
|
114
|
+
scanInventory,
|
|
115
|
+
} from './scanner.js';
|
|
116
|
+
export {
|
|
117
|
+
type AssetReader,
|
|
118
|
+
isScriptablePackAssetKind,
|
|
119
|
+
projectScriptablePackMeta,
|
|
120
|
+
projectScriptablePackSceneComponents,
|
|
121
|
+
SCRIPTABLE_PACK_ASSET_KINDS,
|
|
122
|
+
SCRIPTABLE_PACK_CAPABILITY_MANIFEST,
|
|
123
|
+
type ScriptablePackAssetDeclaration,
|
|
124
|
+
type ScriptablePackAssetDeclarations,
|
|
125
|
+
type ScriptablePackAssetFor,
|
|
126
|
+
type ScriptablePackAssetKind,
|
|
127
|
+
type ScriptablePackDefinition,
|
|
128
|
+
type ScriptablePackError,
|
|
129
|
+
type ScriptablePackExternalAssets,
|
|
130
|
+
type ScriptablePackMetaJson,
|
|
131
|
+
type ScriptablePackOutputs,
|
|
132
|
+
type ScriptablePackPublicationEnvelope,
|
|
133
|
+
type ScriptablePackReadError,
|
|
134
|
+
type ScriptablePackSceneComponent,
|
|
135
|
+
type ScriptablePackSceneComponentInput,
|
|
136
|
+
type ScriptablePackSourceClosureEntry,
|
|
137
|
+
validateScriptablePackDefinition,
|
|
138
|
+
} from './scriptable-pack.js';
|
|
139
|
+
export { calculateTopologyDiff, diffTopology } from './topology.js';
|
package/src/builtin.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { AssetGuid } from './guid.js';
|
|
2
|
+
import { AssetGuid as AssetGuidNS } from './guid.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ForgeaX engine namespace UUID for UUIDv5 derivation.
|
|
6
|
+
* Value: 9a09805a-7623-482e-b322-9fc3591f2a38
|
|
7
|
+
* This is a custom namespace; it is intentionally NOT the RFC 4122 X.500 namespace.
|
|
8
|
+
*/
|
|
9
|
+
const _nsResult = AssetGuidNS.parse('9a09805a-7623-482e-b322-9fc3591f2a38');
|
|
10
|
+
if (!_nsResult.ok) throw new Error('builtin: FORGEAX_NAMESPACE is not a valid UUID');
|
|
11
|
+
export const FORGEAX_NAMESPACE: AssetGuid = _nsResult.value;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Derive a stable UUIDv5 AssetGuid from a name within the ForgeaX namespace.
|
|
15
|
+
* Uses SHA-1 via Node.js `node:crypto` or Web Crypto API (RFC 4122 §4.3).
|
|
16
|
+
* Deterministic: same name always produces the same bytes.
|
|
17
|
+
*/
|
|
18
|
+
export async function deriveBuiltin(name: string): Promise<AssetGuid> {
|
|
19
|
+
const nsBytes = FORGEAX_NAMESPACE;
|
|
20
|
+
const nameBytes = new TextEncoder().encode(name);
|
|
21
|
+
|
|
22
|
+
// Concatenate namespace bytes + name bytes for SHA-1 digest
|
|
23
|
+
const concat = new Uint8Array(16 + nameBytes.length);
|
|
24
|
+
concat.set(nsBytes, 0);
|
|
25
|
+
concat.set(nameBytes, 16);
|
|
26
|
+
|
|
27
|
+
let hashBytes: Uint8Array;
|
|
28
|
+
|
|
29
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto?.subtle !== undefined) {
|
|
30
|
+
// Browser / Web Crypto path
|
|
31
|
+
const buffer = await globalThis.crypto.subtle.digest('SHA-1', concat);
|
|
32
|
+
hashBytes = new Uint8Array(buffer);
|
|
33
|
+
} else {
|
|
34
|
+
// Node.js path
|
|
35
|
+
const { createHash } = await import('node:crypto');
|
|
36
|
+
const hash = createHash('sha1');
|
|
37
|
+
hash.update(concat);
|
|
38
|
+
hashBytes = hash.digest();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// RFC 4122 §4.3: assemble UUIDv5 from first 16 bytes of SHA-1 digest
|
|
42
|
+
const uuidBytes = new Uint8Array(16);
|
|
43
|
+
uuidBytes.set(hashBytes.subarray(0, 16));
|
|
44
|
+
|
|
45
|
+
// Set version bits: byte 6, high nibble = 0x5 (version 5)
|
|
46
|
+
// biome-ignore lint/style/noNonNullAssertion: uuidBytes is 16 bytes
|
|
47
|
+
uuidBytes[6] = (uuidBytes[6]! & 0x0f) | 0x50;
|
|
48
|
+
// Set variant bits: byte 8, high 2 bits = 0b10
|
|
49
|
+
// biome-ignore lint/style/noNonNullAssertion: uuidBytes is 16 bytes
|
|
50
|
+
uuidBytes[8] = (uuidBytes[8]! & 0x3f) | 0x80;
|
|
51
|
+
|
|
52
|
+
return uuidBytes as AssetGuid;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function formatSync(bytes: Uint8Array): string {
|
|
56
|
+
return AssetGuidNS.format(bytes as AssetGuid);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function deriveSync(name: string): Promise<string> {
|
|
60
|
+
return formatSync(await deriveBuiltin(name));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Pre-computed compile-time constant for the HANDLE_CUBE builtin GUID.
|
|
65
|
+
* Derived from: deriveBuiltin('HANDLE_CUBE') under FORGEAX_NAMESPACE.
|
|
66
|
+
*/
|
|
67
|
+
export const BUILTIN_HANDLE_CUBE = await deriveSync('HANDLE_CUBE');
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pre-computed compile-time constant for the HANDLE_TRIANGLE builtin GUID.
|
|
71
|
+
* Derived from: deriveBuiltin('HANDLE_TRIANGLE') under FORGEAX_NAMESPACE.
|
|
72
|
+
*/
|
|
73
|
+
export const BUILTIN_HANDLE_TRIANGLE = await deriveSync('HANDLE_TRIANGLE');
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Pre-computed compile-time constant for the HANDLE_QUAD builtin GUID.
|
|
77
|
+
* Derived from: deriveBuiltin('HANDLE_QUAD') under FORGEAX_NAMESPACE.
|
|
78
|
+
*/
|
|
79
|
+
export const BUILTIN_HANDLE_QUAD = await deriveSync('HANDLE_QUAD');
|
|
80
|
+
|
|
81
|
+
/** Pre-computed UUIDv5 for the Engine-owned sphere mesh descriptor. */
|
|
82
|
+
export const BUILTIN_HANDLE_SPHERE = await deriveSync('HANDLE_SPHERE');
|
|
83
|
+
|
|
84
|
+
/** Pre-computed UUIDv5 for the Engine-owned nine-slice quad descriptor. */
|
|
85
|
+
export const BUILTIN_HANDLE_NINESLICE_QUAD = await deriveSync('HANDLE_NINESLICE_QUAD');
|
|
86
|
+
|
|
87
|
+
/** Pre-computed UUIDv5 for the Engine-owned cylinder mesh descriptor. */
|
|
88
|
+
export const BUILTIN_HANDLE_CYLINDER = await deriveSync('HANDLE_CYLINDER');
|
|
89
|
+
|
|
90
|
+
export type BuiltinMeshGeometry =
|
|
91
|
+
| 'procedural-cube'
|
|
92
|
+
| 'procedural-triangle'
|
|
93
|
+
| 'procedural-quad'
|
|
94
|
+
| 'procedural-sphere'
|
|
95
|
+
| 'procedural-nine-slice-quad'
|
|
96
|
+
| 'procedural-cylinder';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The complete Engine-owned procedural mesh catalog.
|
|
100
|
+
*
|
|
101
|
+
* These are Pack descriptors, not runtime payloads: the Geometry decoder
|
|
102
|
+
* expands each `geometry` token into its validated MeshAsset. DevKit uses this
|
|
103
|
+
* list to materialize only the descriptors a standalone project does not
|
|
104
|
+
* already author, keeping the shipped game self-contained without duplicate
|
|
105
|
+
* GUID declarations.
|
|
106
|
+
*/
|
|
107
|
+
export const BUILTIN_MESH_ASSETS: ReadonlyArray<{
|
|
108
|
+
readonly guid: string;
|
|
109
|
+
readonly geometry: BuiltinMeshGeometry;
|
|
110
|
+
}> = Object.freeze([
|
|
111
|
+
{ guid: BUILTIN_HANDLE_CUBE, geometry: 'procedural-cube' },
|
|
112
|
+
{ guid: BUILTIN_HANDLE_TRIANGLE, geometry: 'procedural-triangle' },
|
|
113
|
+
{ guid: BUILTIN_HANDLE_QUAD, geometry: 'procedural-quad' },
|
|
114
|
+
{ guid: BUILTIN_HANDLE_SPHERE, geometry: 'procedural-sphere' },
|
|
115
|
+
{ guid: BUILTIN_HANDLE_NINESLICE_QUAD, geometry: 'procedural-nine-slice-quad' },
|
|
116
|
+
{ guid: BUILTIN_HANDLE_CYLINDER, geometry: 'procedural-cylinder' },
|
|
117
|
+
]);
|