npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@forgeax/engine-fbx

v0.2.2

Published

FBX importer for the forgeax engine via ufbx compiled to WebAssembly. Single parser for both browser and Node; no Autodesk FBX SDK or native addon. Emits the engine FBX POD schema (meshes, nodes, materials, skeletons, skins, clips).

Downloads

4,774

Readme

@forgeax/engine-fbx

FBX importer for the forgeax engine. A single ufbx-based parser compiled to WebAssembly via Emscripten -- works in both the browser and Node.js, no Autodesk FBX SDK, no native addon. Emits the engine FBX POD JSON schema (meshes, nodes, materials, skeletons, skins, clips), consumed by the shared parse-*.ts / to-asset-pack.ts bridge layer.

Evidence and recovery

The FBX producer writes source meta and a producer-owned CookReceipt; the catalog only supplies the GUID locator (packageUrl and optional cookReceiptUrl). AssetEvidence joins those records with Pack v2 package and artifact verification so browser/runtime code does not guess whether a cook completed.

Read notCooked, ready/current, ready/stale, or unknown literally. Package and artifact evidence must remain notChecked, passed, or failed. If the probe reports stale or failed, repair the source/import settings or recook and rerun lookup/verify --guid --project --catalog --json; retain the structured error hint for the next action.

FBX bytes -> ufbx (wasm) -> JSON POD -> parse-*.ts -> meta.json

AI user consumption path

The importer key 'fbx' is unchanged from the SDK era -- zero migration for existing .fbx.meta.json sidecars. The engine resolves the importer automatically via loadByGuid:

// 1) Point the asset registry at the pack index
assets.configurePackIndex('/pack-index.json');

// 2) loadByGuid dispatches on meta.importer: 'fbx' automatically
const res = await assets.loadByGuid<SceneAsset>(guid);
if (!res.ok) {
  console.error(res.error.code, res.error.hint);
  return;
}
const scene = res.value; // SceneAsset -- ready for instantiate()

Under the hood, vite-plugin-pack calls fbxImporter.import(ctx) which initializes the ufbx WASM module on first use, parses the FBX bytes, and returns ImportedAsset[] (mesh, material, scene, skeleton, skin, animation-clip, texture). The importer registration is a one-liner if you need it explicitly:

import { fbxImporter } from '@forgeax/engine-fbx';
import { ImporterRegistry } from '@forgeax/engine-import';
const importers = new ImporterRegistry();
importers.register(fbxImporter);

Unified asset producer

The project CLI selects this producer automatically for .fbx sources. It creates the sibling sidecar and reuses existing GUIDs by producer sourceKey or (kind, sourceIndex) when a legacy sidecar has no matching key:

forgeax asset import assets/character.fbx --root ./game --json
forgeax asset import assets/character.fbx --dry-run --root ./game --json

The sidecar is then consumed by the same ImporterRegistry and runImport path as every other external source. An existing non-FBX sidecar is a structured conflict; the CLI never replaces another producer's identity.

7 sub-asset POD types

The FBX importer produces 7 sub-asset kinds. Types are defined in @forgeax/engine-types (SSOT) -- see the Asset union and the per-kind POD interfaces. This section is a discovery index; do not copy-paste member lists from here.

| POD type | Description | Source anchor | |:--|:--|:--| | MeshPod | Vertices, indices, attributes, submeshes | @forgeax/engine-types MeshPod | | MaterialPod | PBR parameters (StingrayPBS / Phong / Lambert / fallback) | @forgeax/engine-types MaterialPod | | ScenePod | Entity hierarchy + mounts | @forgeax/engine-types ScenePod | | TexturePod | External file path | @forgeax/engine-types TexturePod | | SkeletonPod | Joint count + inverse bind matrices | @forgeax/engine-types SkeletonPod | | SkinPod | Skeleton GUID + joint paths | @forgeax/engine-types SkinPod | | AnimationClipPod | Duration + channels + samplers | @forgeax/engine-types AnimationClipPod |

Material mapping

Three branches, one output (passes[0].shader = 'forgeax::default-standard-pbr'). Priority: StingrayPBS > Phong > Lambert > fallback.

| Branch | Detection | Mapping | |:--|:--|:--| | StingrayPBS | kind === 'stingray-pbs' in the bridge JSON POD | Channels copied directly: baseColor, metallic, roughness, normal, occlusion | | Phong | kind === 'phong' | baseColor = diffuse, metallic = 0, roughness = 1 - sqrt(shininess / 100) (Family A) | | Lambert | kind === 'lambert' | baseColor = diffuse, metallic = 0, roughness = 0.5 (no specular) | | Fallback | No recognized material type | baseColor = [0.5, 0.5, 0.5] (grey), metallic = 0, roughness = 0.5 |

The Phong-to-PBR roughness formula is Family A: roughness = 1 - sqrt(shininess / maxGloss) with maxGloss = 100. Implementation SSOT: src/parse-material.ts lines 32-35 (phongRoughness function). Industry survey (5 engines/tools) and formula rationale: KB 2026-06-15-fbx-phong-roughness-conversion.md (Family A vs Family B comparison, max_gloss convention).

Error codes

Errors are structured: every error object carries .code, .expected, .hint, and .detail. AI users switch on .code for exhaustive handling.

FbxErrorCode (this package, closed union): source SSOT is src/errors.ts. Do not copy-paste the member list; inspect the source for the current union.

ImportErrorCode (in @forgeax/engine-types): source SSOT is packages/types/src/index.ts; it owns the runtime importer-dispatch union.

Contributor toolchain

The pkg/ directory is not committed to git (zero-binary invariant). A workspace pnpm install or bun install runs a package-local, best-effort postinstall hook that fetches the matching bundle when it is absent. The hook is idempotent and non-fatal: it skips a complete pkg/, and an unavailable release leaves installation successful with the explicit recovery command below. Set FORGEAX_SKIP_FBX_WASM_FETCH=1 to opt out.

To provision a fresh checkout explicitly, fetch the pre-built WASM bundle from GitHub Releases instead of compiling locally:

pnpm -F @forgeax/engine-fbx fetch-wasm

This runs scripts/fetch-wasm.mjs, which:

  1. Resolves the GitHub repo from git remote get-url origin (SSH or HTTPS).
  2. Computes the content key = SHA256(bridge.c + fetch-ufbx.mjs + build-wasm.mjs) truncated to 8 hex chars (SSOT: scripts/content-key.mjs).
  3. Looks for a matching asset fbx-wasm-v0.23.0-{sha8}.tar.gz under the wasm-artifacts release tag.
  4. Downloads and extracts it into pkg/. emcc emits a pair — pkg/fbx-wasm.wasm + its self-loading pkg/fbx-wasm.mjs glue — and the runtime imports the .mjs glue, so the release ships the whole pkg/ as one tarball (mirrors @forgeax/engine-wgpu-wasm and @forgeax/engine-codec), not a lone .wasm.

The content key guarantees you get the bundle that matches your exact source -- no accidental mismatch. The shared downloader tries Node fetch first; when it cannot complete the TLS/network handshake, it falls back to authenticated gh api/gh release download, then curl (curl.exe on Windows). All paths remain pinned to this repository, the wasm-artifacts tag, and the exact content-keyed asset name.

If the asset is not found (e.g. modified bridge.c that was never published), the script prints a structured error with a hint to compile locally. If the repo is private, set GITHUB_TOKEN/GH_TOKEN or run gh auth login; public repos work anonymously (no token needed).

When no pre-built release is available, compile locally with Emscripten:

# Prerequisite: install emsdk and activate it
# https://emscripten.org/docs/getting_started/downloads.html

pnpm -F @forgeax/engine-fbx build:wasm

build:wasm runs two scripts in order:

  1. scripts/fetch-ufbx.mjs -- downloads ufbx.c + ufbx.h (v0.23.0) from the official ufbx repo. Idempotent: skips if already present.
  2. scripts/build-wasm.mjs -- invokes emcc to compile ufbx.c + bridge.c into pkg/fbx-wasm.mjs + pkg/fbx-wasm.wasm.

Both ufbx.c/.h and pkg/ are in .gitignore; CI provides emsdk via emscripten-core/setup-emsdk and rebuilds from a bare checkout on every run.

Fetch-wasm error codes

| Code | Meaning | Self-help | |:--|:--|:--| | E1_NETWORK | Node fetch and available native transports failed, or an unexpected HTTP error occurred | Check the TLS/proxy diagnosis and retry with gh auth login, then use pnpm -F @forgeax/engine-fbx build:wasm (local emcc) | | E2_ASSET_NOT_FOUND | Release tag or asset not found | pnpm -F @forgeax/engine-fbx build:wasm, or push to main to trigger CI release | | E3_ORIGIN_UNSUPPORTED_HOST | git remote get-url origin returned a non-GitHub host | Check git remote -v; set origin to a GitHub remote | | E3_ORIGIN_PARSE_FAILED | Cannot parse the origin URL into owner/repo | Expected [email protected]:OWNER/REPO.git or https://github.com/OWNER/REPO.git | | E3_NO_ORIGIN | No origin remote configured | git remote add origin <url> or build:wasm | | E4_HASH_MISMATCH | content key does not match any published asset | pnpm -F @forgeax/engine-fbx build:wasm | | E5_AUTH_FAILED | Private repo requires authentication (401/403) | Set GITHUB_TOKEN environment variable, or build:wasm |

Content-keyed idempotency

The CI main-push release step packs pkg/ into fbx-wasm-v0.23.0-{sha8}.tar.gz and publishes it under the wasm-artifacts release tag. The publish step checks for an existing asset with the same name before uploading -- identical source content never produces a duplicate release. The hash is computed from the source at build time via scripts/content-key.mjs (Derive, Don't Duplicate -- no stored hash file).

License

MIT

FbxLODGroup contract

The ufbx bridge emits native child order and optional native threshold/mode diagnostics. The first child is the root MeshAsset (LOD0); remaining children are ordinary mesh GUID references projected into MeshAsset.lods[] and root refs[]. Native distance or percentage thresholds are diagnostic facts only; the runtime selector uses the shared absolute screen-coverage contract.

flowchart LR
  A["FbxLODGroup child order"] --> B["sourceKey and GUID lookup"]
  B --> C["shared coverage defaults or sidecar values"]
  C --> D["MeshAsset lods plus refs closure"]

Forced eShow and eHide modes fail as fbx-lod-display-mode-unsupported with expected, hint, and the native displayMode detail. Repair the FbxLODGroup in the DCC source, then recook; no partial Pack publication is allowed.

Animated skin bounds

After applying source/sidecar bounds, the FBX importer derives missing bounds from control-point influences, inverse bind matrices, node hierarchy, morph deltas and the same resampled clips it publishes. Unique full joint paths or unambiguous source joint names must resolve. The shared animation enclosure contract defines coverage and fail-closed cases. No per-frame import or bind-pose guess is needed to enter the GPU skin lane.