@forgeax/engine-gltf 0.1.27 → 0.1.29
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 +13 -11
- package/dist/bridge.d.ts +7 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/cli-gltf.d.ts.map +1 -1
- package/dist/cli-gltf.mjs +19 -17
- package/dist/cli-gltf.mjs.map +1 -1
- package/dist/gltf-importer.d.ts.map +1 -1
- package/dist/importer-entry.mjs +47 -21
- package/dist/importer-entry.mjs.map +1 -1
- package/dist/index.mjs +47 -21
- package/dist/index.mjs.map +1 -1
- package/dist/node-file-entry.mjs +9 -7
- package/dist/node-file-entry.mjs.map +1 -1
- package/dist/parse-gltf.d.ts.map +1 -1
- package/dist/parse-skin.d.ts +8 -16
- package/dist/parse-skin.d.ts.map +1 -1
- package/package.json +13 -16
- package/src/__tests__/bridge.unit.test.ts +70 -51
- package/src/__tests__/cli-gltf.integration.test.ts +1 -1
- package/src/__tests__/gltf.unit.test.ts +96 -16
- package/src/__tests__/morph-import.integration.test.ts +1 -1
- package/src/bridge.ts +34 -10
- package/src/cli-gltf.ts +16 -18
- package/src/errors.ts +3 -3
- package/src/gltf-importer.ts +29 -11
- package/src/parse-gltf.ts +5 -0
- package/src/parse-skin.ts +14 -41
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ reflection, Pack refs, Catalog/load, and runtime sampling.
|
|
|
51
51
|
> structured `material-tangent-required` error. The importer persists generated
|
|
52
52
|
> tangents in `MeshAsset.attributes.tangent` and never writes an identity guess.
|
|
53
53
|
|
|
54
|
-
> Runtime glTF 2.0 importer (Tier-C subset). Pure-function pipeline `parseGlb` / `parseGltf` / `toAssetPack`
|
|
54
|
+
> Runtime glTF 2.0 importer (Tier-C subset). Pure-function pipeline `parseGlb` / `parseGltf` / `toAssetPack` is selected by the unified `forgeax asset import` producer, which writes `<source>.meta.json` (external-asset-package; dispatch on top-level `importer: 'gltf'`); runtime spawn happens via the existing `loadByGuid<SceneAsset>` plus `world.instantiateScene` 4-step recipe (no `loadGltf(url)` parallel API).
|
|
55
55
|
|
|
56
56
|
> [!IMPORTANT]
|
|
57
57
|
> `toAssetPack` and `reimportReuseMeta` return `Result` values. The producer derives semantic `sourceKey` values before GUID reuse; duplicate or ambiguous identities return `duplicate-source-key` / `ambiguous-source-key` and the CLI leaves the previous sidecar untouched. `sourceIndex` is a locator only, never a generated identity.
|
|
@@ -228,7 +228,7 @@ When `ImporterRegistry` + `runImport` consumes a glTF source, malformed base64 i
|
|
|
228
228
|
> Two submodules: `parse-skin.ts` (skin index dedupe via UUIDv5 + IBM decoding + jointPath derivation) + `parse-animation.ts` (LINEAR/STEP samplers, CUBICSPLINE/morph fail-fast). Called inside `parseGltfWithBin` -> `toAssetPack` which extends sub-asset output from 3 kinds (mesh/material/scene) to 6 (+ skeleton + skin + animation-clip).
|
|
229
229
|
|
|
230
230
|
- **Limitations**: CUBICSPLINE interpolation not supported; morph weight animation not supported; jointPath resolution uses leaf-name first-match (same-name sibling is warn-only).
|
|
231
|
-
- **
|
|
231
|
+
- **Animated skin bounds** are producer-authored facts. The primary source is `skins[].extras.forgeax.conservativeAnimatedBounds`; `importSettings.conservativeAnimatedBounds[sourceIndex]` is accepted only when source extras do not provide bounds. The importer publishes the six-float bounds on `SkeletonAsset`. If both sources are absent, bounds remain absent and Render keeps the skin on the `cpu-deformation` lane; no BindPose AABB is derived as an animation-bound fallback.
|
|
232
232
|
|
|
233
233
|
## 4-step runtime recipe (apps/hello/gltf, M5)
|
|
234
234
|
|
|
@@ -248,19 +248,21 @@ const root = engine.assets.instantiate(sceneResult.value, world);
|
|
|
248
248
|
// equivalent to world.instantiateScene(handle).
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
##
|
|
251
|
+
## Unified asset producer
|
|
252
252
|
|
|
253
|
-
The
|
|
253
|
+
The glTF producer is selected by the unified `asset import` command. DevKit
|
|
254
|
+
loads this package's producer only when a `.gltf` or `.glb` source is passed;
|
|
255
|
+
the package no longer publishes an independent executable.
|
|
254
256
|
|
|
255
257
|
| Subcommand | Description | Exit code |
|
|
256
258
|
|:--|:--|:--|
|
|
257
|
-
| `forgeax
|
|
258
|
-
| `forgeax
|
|
259
|
+
| `forgeax asset import <path> --root <project>` | Parse `.gltf` / `.glb`; write sidecar `<source>.meta.json` (top-level `importer: 'gltf'`) next to source; UUIDv7 GUIDs assigned per sub-asset in document order | 0 success / 1 `GltfError` |
|
|
260
|
+
| `forgeax asset import <path> --dry-run --root <project>` | Validate the producer path without writing a sidecar | 0 if valid / 1 if missing or invalid |
|
|
259
261
|
|
|
260
262
|
```bash
|
|
261
|
-
#
|
|
262
|
-
forgeax
|
|
263
|
-
forgeax
|
|
263
|
+
# Unified invocation
|
|
264
|
+
forgeax asset import apps/hello/gltf/assets/box.glb --root ./game --json
|
|
265
|
+
forgeax asset import apps/hello/gltf/assets/ --dry-run --root ./game --json
|
|
264
266
|
```
|
|
265
267
|
|
|
266
268
|
### End-to-end source-to-Pack route
|
|
@@ -270,14 +272,14 @@ Pack/DDC publication path. Keep these steps together when adding a glTF LOD:
|
|
|
270
272
|
|
|
271
273
|
```mermaid
|
|
272
274
|
flowchart LR
|
|
273
|
-
source["source .gltf/.glb"] --> cli["
|
|
275
|
+
source["source .gltf/.glb"] --> cli["forgeax asset import"]
|
|
274
276
|
cli --> meta["source.meta.json"]
|
|
275
277
|
meta --> registry["ImporterRegistry + gltfImporter"]
|
|
276
278
|
registry --> runner["runImport"]
|
|
277
279
|
runner --> pack["Pack/DDC + Catalog"]
|
|
278
280
|
```
|
|
279
281
|
|
|
280
|
-
1. Run `forgeax
|
|
282
|
+
1. Run `forgeax asset import <source> --root <project>` to create or refresh the
|
|
281
283
|
adjacent `<source>.meta.json` and its stable sub-asset GUIDs.
|
|
282
284
|
2. Register `gltfImporter` with the build-time `ImporterRegistry` and pass the
|
|
283
285
|
sidecar to `runImport`; this validates the declared LOD GUID closure and
|
package/dist/bridge.d.ts
CHANGED
|
@@ -53,6 +53,13 @@ export interface GltfBridgeContext {
|
|
|
53
53
|
* matching SkinAsset.jointPaths against the spawn subtree to fill Skin.joints[].
|
|
54
54
|
*/
|
|
55
55
|
readonly skeletonGuidBySkinIndex?: ReadonlyMap<number, string>;
|
|
56
|
+
/**
|
|
57
|
+
* glTF node index -> a live renderer-owned instance collection identity.
|
|
58
|
+
* The bridge is intentionally pure and cannot mint collection ids or copy
|
|
59
|
+
* matrix bytes into ECS. Runtime consumers create collections through their
|
|
60
|
+
* Renderer first, then provide this map when lowering the SceneAsset.
|
|
61
|
+
*/
|
|
62
|
+
readonly instanceCollectionIdsByNodeIndex?: ReadonlyMap<number, number>;
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
65
|
* Convert a parsed GltfDoc into a SceneAsset POD. Caller supplies registry
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EAEN,aAAa,EACb,aAAa,EAEb,SAAS,EAIT,MAAM,EACN,UAAU,
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EAEN,aAAa,EACb,aAAa,EAEb,SAAS,EAIT,MAAM,EACN,UAAU,EAGX,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAA4B,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,UAAU,EACV,UAAU,EAGX,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,SAAS,GAAE;IACT,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD,GACL,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CA0V9B;AAED,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjF;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/D;;;;;OAKG;IACH,QAAQ,CAAC,gCAAgC,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzE;AA+DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,GAAG,UAAU,CAgPpF;AAED,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC;AAEtC,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChF,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAuKD,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAsC7B;AAED,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,UAAU,EAChB,KAAK,SAAc,GAClB,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAoD7B;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,EAAE,qBAAqB,GAAG,aAAa,CA2H/F"}
|
package/dist/cli-gltf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-gltf.d.ts","sourceRoot":"","sources":["../src/cli-gltf.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli-gltf.d.ts","sourceRoot":"","sources":["../src/cli-gltf.ts"],"names":[],"mappings":";AA8BA,UAAU,QAAQ;IAChB,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAkCD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAe/E"}
|
package/dist/cli-gltf.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { realpath, stat, readFile, writeFile, readdir } from 'fs/promises';
|
|
|
3
3
|
import { resolve, dirname, join } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { parseArgs } from 'util';
|
|
6
|
-
import { err, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ok, ImportError, IMPORT_ERROR_HINTS } from '@forgeax/engine-types';
|
|
6
|
+
import { err, reconcileMeshMaterialSlotTopology, MESH_MATERIAL_SLOT_SOURCE_OVERRIDE_PAYLOAD_SCHEMA, ok, ImportError, IMPORT_ERROR_HINTS, parseConservativeAnimatedBounds } from '@forgeax/engine-types';
|
|
7
7
|
import { vec3, quat, mat4 } from '@forgeax/engine-math';
|
|
8
8
|
import { deriveDefaultLodScreenCoverages, reconcileMeshLodMeta } from '@forgeax/engine-import';
|
|
9
9
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
@@ -14,7 +14,7 @@ var GLTF_MESHOPT_FILTERS = ["NONE", "OCTAHEDRAL", "QUATERNION", "EXPONENTIAL"];
|
|
|
14
14
|
var gltfErrorPolicy = {
|
|
15
15
|
"gltf-malformed-header": {
|
|
16
16
|
expected: "GLB 12-byte header (magic 0x46546C67 + version=2 + length) plus mandatory JSON chunk",
|
|
17
|
-
hint: "verify .glb is not truncated; rerun: forgeax
|
|
17
|
+
hint: "verify .glb is not truncated; rerun: forgeax asset import <path> --root <project> --json"
|
|
18
18
|
},
|
|
19
19
|
"gltf-version-unsupported": {
|
|
20
20
|
expected: 'asset.version === "2.0"',
|
|
@@ -50,7 +50,7 @@ var gltfErrorPolicy = {
|
|
|
50
50
|
},
|
|
51
51
|
"gltf-meta-missing": {
|
|
52
52
|
expected: "sidecar <source>.meta.json (importer: 'gltf') present in same directory",
|
|
53
|
-
hint: "run: forgeax
|
|
53
|
+
hint: "run: forgeax asset import <path> --root <project> --json"
|
|
54
54
|
},
|
|
55
55
|
"gltf-instancing-count-mismatch": {
|
|
56
56
|
expected: "all instance attribute accessors share the same count",
|
|
@@ -78,7 +78,7 @@ var gltfErrorPolicy = {
|
|
|
78
78
|
},
|
|
79
79
|
"gltf-image-extract-failed": {
|
|
80
80
|
expected: "image bytes extractable from bufferView / data-URI / external URI without corruption",
|
|
81
|
-
hint: "verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax
|
|
81
|
+
hint: "verify the bufferView byte range / data: URI base64 / external URI sibling file is intact next to the .gltf source; rerun: forgeax asset import <path> --root <project> --json"
|
|
82
82
|
},
|
|
83
83
|
"gltf-skin-attr-asymmetric": {
|
|
84
84
|
expected: "mesh primitive declares JOINTS_0 and WEIGHTS_0 symmetrically (both present or both absent)",
|
|
@@ -1320,8 +1320,6 @@ function parseGlbChunks(buffer, filePath) {
|
|
|
1320
1320
|
...binChunk === void 0 ? {} : { binChunk }
|
|
1321
1321
|
});
|
|
1322
1322
|
}
|
|
1323
|
-
|
|
1324
|
-
// src/parse-skin.ts
|
|
1325
1323
|
var MAX_JOINTS = 256;
|
|
1326
1324
|
var SKIN_ACCESSOR_TYPES = ["MAT4"];
|
|
1327
1325
|
function identityMat4() {
|
|
@@ -1402,10 +1400,14 @@ function parseSkin(skinsJson, nodesJson, accessors, bufferViews, buffers) {
|
|
|
1402
1400
|
if (!pathResult.ok) return err(pathResult.error);
|
|
1403
1401
|
jointPaths.push(pathResult.value.join("/"));
|
|
1404
1402
|
}
|
|
1403
|
+
const bounds = parseConservativeAnimatedBounds(
|
|
1404
|
+
skin.extras?.forgeax?.conservativeAnimatedBounds
|
|
1405
|
+
);
|
|
1405
1406
|
records.push({
|
|
1406
1407
|
jointCount: joints.length,
|
|
1407
1408
|
inverseBindMatrices: ibm,
|
|
1408
|
-
jointPaths
|
|
1409
|
+
jointPaths,
|
|
1410
|
+
...bounds === void 0 ? {} : { bounds }
|
|
1409
1411
|
});
|
|
1410
1412
|
}
|
|
1411
1413
|
return ok(records);
|
|
@@ -2612,14 +2614,14 @@ function emitError(ctx, err3) {
|
|
|
2612
2614
|
}
|
|
2613
2615
|
function helpBody() {
|
|
2614
2616
|
return [
|
|
2615
|
-
"forgeax
|
|
2617
|
+
"forgeax asset import \u2014 glTF / GLB sidecar importer (internal producer)",
|
|
2616
2618
|
"",
|
|
2617
2619
|
"Usage:",
|
|
2618
|
-
" forgeax
|
|
2619
|
-
" forgeax
|
|
2620
|
+
" forgeax asset import <path.gltf|path.glb> --root <project>",
|
|
2621
|
+
" forgeax asset import <dir> --dry-run --root <project>",
|
|
2620
2622
|
"",
|
|
2621
|
-
"
|
|
2622
|
-
"
|
|
2623
|
+
"produces texture, mesh, material, and scene sub-asset entries in a sibling",
|
|
2624
|
+
"<source>.meta.json sidecar.",
|
|
2623
2625
|
""
|
|
2624
2626
|
].join("\n");
|
|
2625
2627
|
}
|
|
@@ -2633,7 +2635,7 @@ async function runCliGltf(rest, ctx) {
|
|
|
2633
2635
|
return emitError(ctx, {
|
|
2634
2636
|
code: "unknown-subcommand",
|
|
2635
2637
|
expected: "subcommand 'import'",
|
|
2636
|
-
hint: "run 'forgeax
|
|
2638
|
+
hint: "run 'forgeax help asset import' for usage",
|
|
2637
2639
|
detail: { subcommand: sub }
|
|
2638
2640
|
});
|
|
2639
2641
|
}
|
|
@@ -2655,8 +2657,8 @@ async function runImport(rest, ctx) {
|
|
|
2655
2657
|
const message = e instanceof Error ? e.message : String(e);
|
|
2656
2658
|
return emitError(ctx, {
|
|
2657
2659
|
code: "cli-parse-error",
|
|
2658
|
-
expected: "forgeax
|
|
2659
|
-
hint: "run 'forgeax
|
|
2660
|
+
expected: "forgeax asset import [--dry-run] <path> --root <project>",
|
|
2661
|
+
hint: "run 'forgeax help asset import' for usage",
|
|
2660
2662
|
detail: { message }
|
|
2661
2663
|
});
|
|
2662
2664
|
}
|
|
@@ -2664,7 +2666,7 @@ async function runImport(rest, ctx) {
|
|
|
2664
2666
|
if (target === void 0) {
|
|
2665
2667
|
return emitError(ctx, {
|
|
2666
2668
|
code: "cli-parse-error",
|
|
2667
|
-
expected: check ? "forgeax
|
|
2669
|
+
expected: check ? "forgeax asset import <dir> --dry-run --root <project>" : "forgeax asset import <path.gltf|path.glb> --root <project>",
|
|
2668
2670
|
hint: "pass a positional <gltf-or-glb> argument; with --check pass a directory"
|
|
2669
2671
|
});
|
|
2670
2672
|
}
|
|
@@ -2730,7 +2732,7 @@ async function runCheck(target, ctx) {
|
|
|
2730
2732
|
const message = e instanceof Error ? e.message : String(e);
|
|
2731
2733
|
return emitError(ctx, {
|
|
2732
2734
|
code: "cli-parse-error",
|
|
2733
|
-
expected: "forgeax
|
|
2735
|
+
expected: "forgeax asset import <dir> --dry-run --root <project>",
|
|
2734
2736
|
hint: "pass a directory that exists and is readable",
|
|
2735
2737
|
detail: { path: target, message }
|
|
2736
2738
|
});
|