{"_id":"@comfyorg/fbx-exporter-three","_rev":"5-8b34f7ad7c5c78e443b5fbb29d27ea75","name":"@comfyorg/fbx-exporter-three","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@comfyorg/fbx-exporter-three","version":"1.0.1","keywords":["fbx","fbx-exporter","three","three.js","3d","exporter","blender","unity","unreal","skinning","skeleton","animation","morph-targets","blendshape","comfyui"],"author":{"name":"Terry Jia","email":"terry@comfy.org"},"license":"MIT","_id":"@comfyorg/fbx-exporter-three@1.0.1","maintainers":[{"name":"terryjia","email":"terry@comfy.org"},{"name":"benceruleanlu-comfyorg","email":"ben@comfy.org"},{"name":"cbyrne","email":"cbyrne@comfy.org"}],"homepage":"https://comfy.org","bugs":{"url":"https://github.com/Comfy-Org/fbx-exporter-three/issues"},"dist":{"shasum":"e4da8facdfdbfef16b7bbcb6cb4584a6007b155c","tarball":"https://registry.npmjs.org/@comfyorg/fbx-exporter-three/-/fbx-exporter-three-1.0.1.tgz","fileCount":123,"integrity":"sha512-fQ1zBsgmmwfio6iEi91hRiFCr946yEgqR2DGh/UMismaLyUohiKGOJL/OnJQnW3+yne/PXxVoYgcortyumsO5w==","signatures":[{"sig":"MEQCIBFu4WE6yE3oIlOXz9fdqmH+4ZYt1QirsZ5QVLyl9gX4AiB7xc4qFGuhbmlj1+PukufV5MI7M+A3+CUXa/oMgKE8fg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comfyorg%2ffbx-exporter-three@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":423836},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"f51c8cf36c5fd43318511487a6adbb1e67cb3d0f","scripts":{"test":"node --import tsx test/m1-binary-layer.test.mjs && node --import tsx test/m1-fbxloader-binary.test.mjs && node --import tsx test/m1-deep-audit.test.mjs && node --import tsx test/m2-integration.test.mjs && node --import tsx test/m2-deep-audit.test.mjs && node --import tsx test/m3-mesh-roundtrip.test.mjs && node --import tsx test/m3-edge-cases.test.mjs && node --import tsx test/m3-deep-audit.test.mjs && node --import tsx test/m3-multiuv.test.mjs && node --import tsx test/m5-skinning.test.mjs && node --import tsx test/m5-deep-audit.test.mjs && node --import tsx test/m5-bindpose-recovery.test.mjs && node --import tsx test/m6-animation.test.mjs && node --import tsx test/m6-deep-audit.test.mjs && node --import tsx test/m7-morph.test.mjs && node --import tsx test/m7-deep-audit.test.mjs && node --import tsx test/m8-light-camera.test.mjs && node --import tsx test/m8-deep-audit.test.mjs && node --import tsx test/m9-texture.test.mjs && node --import tsx test/m9-deep-audit.test.mjs && node --import tsx test/m4-axis-units.test.mjs && node --import tsx test/m4-deep-audit.test.mjs && node --import tsx test/m4-light-camera-direction.test.mjs && node --import tsx test/m9-slot-roundtrip.test.mjs && node --import tsx test/options.test.mjs","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build && npm test"},"_npmUser":{"name":"terryjia","email":"terry@comfy.org"},"repository":{"url":"git+https://github.com/Comfy-Org/fbx-exporter-three.git","type":"git"},"_npmVersion":"10.8.2","description":"FBX exporter for three.js scenes — binary FBX writer with full skinning, animation, morph targets, lights, cameras and texture embedding.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"fflate":"^0.8.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.0","three":"^0.184.0","typescript":"^6.0.3","@types/node":"^25.8.0","@types/three":"^0.184.1"},"peerDependencies":{"three":">=0.160.0","@types/three":">=0.160.0"},"peerDependenciesMeta":{"@types/three":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/fbx-exporter-three_1.0.1_1779151204745_0.14436309242759227","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"_id":"@comfyorg/fbx-exporter-three@1.0.2","bugs":{"url":"https://github.com/Comfy-Org/fbx-exporter-three/issues"},"dist":{"shasum":"e801220414fb10488c58ddc6babf1faf84dcf9cb","tarball":"https://registry.npmjs.org/@comfyorg/fbx-exporter-three/-/fbx-exporter-three-1.0.2.tgz","fileCount":123,"integrity":"sha512-s2XOKXGB1IzNtyLbz7Ved5P8DKqAHUC97mQXCrf+08AKF+9ADCdcErFwZpjdMX+LLXu5lm51MbUCpMizFCJXCQ==","signatures":[{"sig":"MEYCIQCoe4lY2KG9ZQgv0h1gew4QD7Ow8UOGgSwwoHqPopC2nwIhAMjukwhRofu3zE7j/FpoRUrQqk8MeDE+igg+ejCi/s5t","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDeqvxqe3qnekNbnHxyICPoD/vaLpv6UN8SGaRN5awP2QIgcjRx4dBE4vBc0l6yFdLeQzawoOHc6Qyq1hPnJv/L7Jg="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comfyorg%2ffbx-exporter-three@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":426730},"main":"./dist/index.js","name":"@comfyorg/fbx-exporter-three","type":"module","types":"./dist/index.d.ts","author":{"name":"Terry Jia","email":"terry@comfy.org"},"engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"0a81a4b188605ed14c992a37f2873f7a4c965b68","license":"MIT","scripts":{"test":"node --import tsx test/m1-binary-layer.test.mjs && node --import tsx test/m1-fbxloader-binary.test.mjs && node --import tsx test/m1-deep-audit.test.mjs && node --import tsx test/m2-integration.test.mjs && node --import tsx test/m2-deep-audit.test.mjs && node --import tsx test/m3-mesh-roundtrip.test.mjs && node --import tsx test/m3-edge-cases.test.mjs && node --import tsx test/m3-deep-audit.test.mjs && node --import tsx test/m3-multiuv.test.mjs && node --import tsx test/m5-skinning.test.mjs && node --import tsx test/m5-deep-audit.test.mjs && node --import tsx test/m5-bindpose-recovery.test.mjs && node --import tsx test/m6-animation.test.mjs && node --import tsx test/m6-deep-audit.test.mjs && node --import tsx test/m7-morph.test.mjs && node --import tsx test/m7-deep-audit.test.mjs && node --import tsx test/m8-light-camera.test.mjs && node --import tsx test/m8-deep-audit.test.mjs && node --import tsx test/m9-texture.test.mjs && node --import tsx test/m9-deep-audit.test.mjs && node --import tsx test/m4-axis-units.test.mjs && node --import tsx test/m4-deep-audit.test.mjs && node --import tsx test/m4-light-camera-direction.test.mjs && node --import tsx test/m9-slot-roundtrip.test.mjs && node --import tsx test/options.test.mjs","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run build && npm test"},"version":"1.0.2","_npmUser":{"name":"terryjia","email":"terry@comfy.org"},"homepage":"https://comfy.org","keywords":["fbx","fbx-exporter","three","three.js","3d","exporter","blender","unity","unreal","skinning","skeleton","animation","morph-targets","blendshape","comfyui"],"repository":{"url":"git+https://github.com/Comfy-Org/fbx-exporter-three.git","type":"git"},"_npmVersion":"10.8.2","description":"FBX exporter for three.js scenes — binary FBX writer with full skinning, animation, morph targets, lights, cameras and texture embedding.","directories":{},"maintainers":[{"name":"terryjia","email":"terry@comfy.org"},{"name":"wei-hai-comfy","email":"wei@comfy.org"},{"name":"benceruleanlu-comfyorg","email":"ben@comfy.org"},{"name":"skishore23","email":"kishore@comfy.org"},{"name":"mattmillerai","email":"matt@miller-media.com"},{"name":"cbyrne","email":"cbyrne@comfy.org"}],"_nodeVersion":"20.20.2","dependencies":{"fflate":"^0.8.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.0","three":"^0.184.0","typescript":"^6.0.3","@types/node":"^25.8.0","@types/three":"^0.184.1"},"peerDependencies":{"three":">=0.160.0","@types/three":">=0.160.0"},"peerDependenciesMeta":{"@types/three":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fbx-exporter-three_1.0.2_1790124345181_0.1452516442402363"}}},"time":{"created":"2026-05-19T00:40:04.681Z","modified":"2026-09-23T00:45:45.728Z","1.0.1":"2026-05-19T00:40:04.901Z","1.0.2":"2026-09-23T00:45:45.269Z"},"bugs":{"url":"https://github.com/Comfy-Org/fbx-exporter-three/issues"},"author":{"name":"Terry Jia","email":"terry@comfy.org"},"license":"MIT","homepage":"https://comfy.org","keywords":["fbx","fbx-exporter","three","three.js","3d","exporter","blender","unity","unreal","skinning","skeleton","animation","morph-targets","blendshape","comfyui"],"repository":{"url":"git+https://github.com/Comfy-Org/fbx-exporter-three.git","type":"git"},"description":"FBX exporter for three.js scenes — binary FBX writer with full skinning, animation, morph targets, lights, cameras and texture embedding.","maintainers":[{"name":"terryjia","email":"terry@comfy.org"},{"name":"wei-hai-comfy","email":"wei@comfy.org"},{"name":"benceruleanlu-comfyorg","email":"ben@comfy.org"},{"name":"skishore23","email":"kishore@comfy.org"},{"name":"mattmillerai","email":"matt@miller-media.com"},{"name":"cbyrne","email":"cbyrne@comfy.org"}],"readme":"# @comfyorg/fbx-exporter-three\n\nFBX binary exporter for three.js scenes — Unity / Unreal / Maya / Blender / three.js's own `FBXLoader` all consume the output cleanly.\n\n- **Self-contained**: TypeScript source, ships ESM `.js` + `.d.ts`. Only runtime dep is `fflate` (zlib).\n- **One-shot or async**: `parseSync` when textures are `DataTexture`s or absent; `parseAsync` when textures need canvas-based encoding (`HTMLImageElement` / `ImageBitmap` / render targets).\n- **Tool presets**: `threejs`, `unity`, `unreal`, `blender`, `maya` — picks the right axis convention + unit scale for the target.\n- **Covers**: meshes, multi-UV layers, materials, textures (PNG-embedded), skinning, animation (TRS + morph), morph targets, lights, cameras.\n\n## Install\n\n```bash\nnpm install @comfyorg/fbx-exporter-three\n```\n\nPeer deps: `three >= 0.160` (required), `@types/three >= 0.160` (optional, only if you're using TypeScript).\n\n## TypeScript\n\nSource is written in TypeScript; published `dist/` ships ESM `.js` + `.d.ts`. Public types include `FBXExporter`, `FBXExportOptions`, `FBXPreset`, `FBXAxis`, `FBXExporterPlugin`.\n\n## Quickstart\n\n```js\nimport * as THREE from 'three';\nimport { FBXExporter } from '@comfyorg/fbx-exporter-three';\nimport { writeFileSync } from 'node:fs';\n\nconst scene = new THREE.Scene();\nscene.add(new THREE.Mesh(\n  new THREE.BoxGeometry(1, 1, 1),\n  new THREE.MeshStandardMaterial({ color: 0x6688ff }),\n));\n\nconst exporter = new FBXExporter();\nconst bytes = exporter.parseSync(scene);\n\nwriteFileSync('cube.fbx', bytes);\n```\n\nThe result is a binary FBX (`Kaydara FBX Binary` magic) you can drop straight into Unity, Unreal, Blender, Maya, or load back with `FBXLoader`.\n\n## API\n\n### `class FBXExporter`\n\n```js\nconst exporter = new FBXExporter();\n```\n\n#### `parseSync(input, options?) → Uint8Array`\n\nBuild an FBX in one call. Use this for static scenes, scenes that only have `DataTexture`s, or when you've already encoded all your textures.\n\n| Option | Type | Default | Notes |\n|---|---|---|---|\n| `preset` | `'threejs' \\| 'unity' \\| 'unreal' \\| 'blender' \\| 'maya'` | `'threejs'` | Picks axis + unit defaults. |\n| `axisUp` | `'X' \\| 'Y' \\| 'Z' \\| '-X' \\| '-Y' \\| '-Z'` | preset | Overrides preset. |\n| `axisForward` | same | preset | Overrides preset. |\n| `unitScale` | `number` | preset | Written to `GlobalSettings.UnitScaleFactor`. |\n| `bakeSpaceTransform` | `boolean` | `false` | Pre-multiplies axis matrix into geometry. See limitation note below. |\n| `version` | `number` | `7400` | FBX version (7400 / 7500 supported). |\n| `fps` | `number` | `24` | Animation frame rate. |\n| `embedTextures` | `boolean` | `true` | When `false`, textures are referenced by file path instead of embedded. |\n| `animations` | `AnimationClip[]` | auto | If omitted, we collect from `input.animations` (the convention used by `GLTFLoader` / `FBXLoader`). |\n| `includeAnimations` | `boolean` | `true` | Set `false` to export a static T-pose only (skips AnimStack/Curve nodes entirely). |\n| `onlyVisible` | `boolean` | `false` | Skip Object3Ds whose `.visible` is `false`. |\n| `objectFilter` | `(o: Object3D) => boolean` | — | Generic predicate — return `false` to skip an object. Composes with `onlyVisible`. |\n| `customProperties` | `boolean` | `false` | Emit each Object3D's `userData` as user-defined Properties70 entries. Maps booleans → `p_bool`, integers → `p_integer`, floats → `p_double`, strings → `p_string`, length-3 number arrays → `p_vector_3d`; other JS values get JSON-stringified into a `p_string`. |\n| `creator` | `string` | `@comfyorg/fbx-exporter-three - <ver>` | Custom string written to FBX `Creator` metadata. |\n\n#### `parseAsync(input, options?) → Promise<Uint8Array>`\n\nSame as `parseSync` but awaits async texture encoding paths — `HTMLImageElement`, `ImageBitmap`, canvas/render-target textures all need `canvas.toBlob()` which is async. Use this whenever your materials reference textures loaded via `TextureLoader` (any environment with a canvas API).\n\n```js\nconst bytes = await new FBXExporter().parseAsync(scene);\n```\n\n#### `register(callback)` / `unregister(callback)`\n\nPlugin hook in the style of three.js's `GLTFExporter`. The callback receives `sceneData` (the collected scene plan) after collection and texture encoding, before serialization:\n\n```js\nexporter.register((sceneData) => {\n  // e.g. swap pre-encoded PNG bytes into texture entries\n  for (const [tex, entry] of sceneData.textures.textures) {\n    if (tex.userData.preEncodedPng) entry.imageBytes = tex.userData.preEncodedPng;\n  }\n});\n```\n\n### Convenience exports\n\n```js\nimport {\n  FBXExporter,\n  PRESETS,            // tool preset table\n  resolvePreset,      // merge user options onto a preset\n  encodeRGBA8PNG,     // pure-JS PNG encoder used internally\n} from '@comfyorg/fbx-exporter-three';\n```\n\n## Feature coverage\n\n| three.js input | FBX output |\n|---|---|\n| `Mesh` + `BufferGeometry` (positions / normals / vertex colors) | `Geometry` + `LayerElementNormal` / `LayerElementColor` / `LayerElementMaterial` |\n| Multiple UV sets — `attributes.uv` / `uv1` / `uv2` / `uv3` (used for lightmap / AO) | Multiple `LayerElementUV` nodes with distinct `TypedIndex` + per-set `Layer` entries |\n| `MeshStandardMaterial` / `MeshPhongMaterial` / `MeshBasicMaterial` | `Material` with `FbxSurfacePhong` shading model |\n| `Texture` (DataTexture, HTMLImage, canvas) | `Texture` + `Video` with embedded PNG bytes |\n| `Group`, `Object3D` | `Model` (Null) |\n| `SkinnedMesh` + `Skeleton` + `Bone` | `Deformer` (Skin) + `Deformer` (Cluster) per bone + `Pose` (BindPose) + `Model` (LimbNode) per bone |\n| `AnimationClip` (position / rotation / scale tracks) | `AnimationStack` + `AnimationLayer` + `AnimationCurveNode` + `AnimationCurve` |\n| Morph targets (`morphAttributes.position`) | `Geometry` (Shape) per morph + `Deformer` (BlendShape) + `Deformer` (BlendShapeChannel) |\n| Morph animation (compound `morphTargetInfluences` track) | Per-channel `AnimationCurveNode` driving `DeformPercent` |\n| `PointLight` / `DirectionalLight` / `SpotLight` | `NodeAttribute` (Light) — `light.target.position` round-trips via `LookAtProperty` |\n| `PerspectiveCamera` / `OrthographicCamera` | `NodeAttribute` (Camera) |\n| Axis / unit conversion | `GlobalSettings.UpAxis` / `FrontAxis` / `UnitScaleFactor` |\n\n## Tool presets\n\n| Preset | UpAxis | FrontAxis | UnitScale | Bake | Use when… |\n|---|---|---|---|---|---|\n| `threejs` (default) | Y | Z | 1 | off | Round-tripping through `FBXLoader`. |\n| `unity` | Y | Z | 1 | off | Importing into Unity (it respects file axes). |\n| `unreal` | Z | X | 1 | off | Importing into Unreal Editor. Unreal's importer handles axis conversion when GlobalSettings declares it. |\n| `blender` | Y | Z | 100 | off | Files for Blender. UnitScale=100 because Blender stores meters but FBX canonical unit is centimeters. |\n| `maya` | Y | Z | 100 | off | Same as Blender. |\n\n```js\n// Use a preset:\nexporter.parseSync(scene, { preset: 'unreal' });\n\n// Or set axes directly:\nexporter.parseSync(scene, { axisUp: 'Z', axisForward: 'X' });\n\n// Mix: preset defaults + selective override:\nexporter.parseSync(scene, { preset: 'unity', unitScale: 100 });\n```\n\n## Examples\n\n### Skinned + animated character\n\n```js\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';\nimport { FBXExporter } from '@comfyorg/fbx-exporter-three';\n\nconst gltf = await new GLTFLoader().loadAsync('character.glb');\nconst scene = gltf.scene;\nscene.animations = gltf.animations;  // wire so the exporter finds them\n\nconst bytes = await new FBXExporter().parseAsync(scene);\n```\n\n### Textures from disk (Node)\n\n```js\nconst tex = new THREE.DataTexture(rgbaPixels, w, h, THREE.RGBAFormat);\ntex.needsUpdate = true;\nmaterial.map = tex;\n\nconst bytes = new FBXExporter().parseSync(scene);  // embeds PNG-encoded RGBA\n```\n\n### Saving to file\n\n```js\nimport { writeFileSync } from 'node:fs';\nwriteFileSync('out.fbx', bytes);\n```\n\n### Saving in the browser\n\n```js\nconst blob = new Blob([bytes], { type: 'application/octet-stream' });\nconst url = URL.createObjectURL(blob);\nconst a = document.createElement('a');\na.href = url;\na.download = 'out.fbx';\na.click();\nURL.revokeObjectURL(url);\n```\n\n## Known limitations\n\n- **`bakeSpaceTransform: true`** only pre-multiplies the axis matrix into **vertex positions + normals**. Object `Lcl` transforms, cluster matrices, animation curves, and light/camera transforms are NOT baked. The flag is only safe for single-mesh-at-origin scenes; leave it `false` otherwise — modern importers (Unity, Unreal, Blender, Maya) honour `GlobalSettings.UpAxis` so axis conversion happens on import.\n- **`light.target.position`** for `DirectionalLight` / `SpotLight` round-trips via a synthetic `LookAtProperty` Model + OP connection.\n- **Output is always binary FBX** (`Kaydara FBX Binary` magic). ASCII FBX is not implemented.\n\n## How it works (brief)\n\n```\n┌───────────────────┐   ┌──────────────┐   ┌──────────────┐\n│ three.js Scene    │ → │ SceneCollect │ → │ FBXElem tree │ → encodeBinaryFBX → Uint8Array\n└───────────────────┘   └──────────────┘   └──────────────┘\n                              │                    │\n                  builds UIDs, dedupes      one writer per FBX\n                  geometries / mats /       record kind (Geometry,\n                  textures, plans OO/OP     Material, Skin Deformer,\n                  connection graph         AnimStack, …)\n```\n\n## Testing\n\n```bash\nnpm test\n```\n\n446 unit tests covering the binary layer, scene collection, every Object kind (Mesh / Material / Texture / Skin / Animation / Morph / Light / Camera), axis/unit conversion, the public-options surface.\n\nReal-asset E2E tests (`test/e2e-*.test.mjs`) aren't part of `npm test` — they read large binary inputs from `test/_assets/` which is gitignored. Drop your own assets in that folder and run them individually:\n\n```bash\nnode --import tsx test/e2e-1-obj.test.mjs           # raw .obj round-trip\nnode --import tsx test/e2e-2-xbot.test.mjs          # Mixamo skinned + animated character\nnode --import tsx test/e2e-3-testTexture.test.mjs   # OBJ + MTL + PNG textures\nnode --import tsx test/e2e-4-threejs-models.test.mjs  # 7 three.js example glTFs\n```\n\n`test/m5-bindpose-recovery.test.mjs` IS in `npm test` but skips gracefully when `test/_assets/Xbot.glb` isn't there — drop in any skinned glTF to enable it.\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n\nPublished under [@comfyorg](https://www.npmjs.com/org/comfyorg).\n","readmeFilename":"README.md"}