FBX to NWB Conversion - limztudio/NWBLot GitHub Wiki

FBX to NWB Conversion

nwb_fbx_to_nwb imports binary or ASCII FBX scenes (through the vendored ufbx loader) into NWB mesh metadata. It can write a standalone mesh, a self-contained asset bunch, a separately packaged model, or the skeleton and skin assets required by a skinned mesh. It also refreshes existing NWB mesh sources into the canonical stream layout.

The source-controlled command reference lives in utilities/fbx_to_nwb/README.md. This page summarizes the current main-branch workflow and output contract.

Build and run

From the repository root, build the importer without starting it (python3 on Linux):

python -m launcher fbx-to-nwb --build-only --config dbg

The utility owns utilities/fbx_to_nwb/launch.py. Its discovered shortcut selects the matching preset, configures when needed, and builds its dependencies; no manual CMake command is needed. The run examples pass --working-directory . before -- so relative input and output paths resolve from the repository root.

The executable target is nwb_fbx_to_nwb. Use the discovered fbx-to-nwb repository command so it builds the target as needed; arguments after -- are passed to the importer:

python -m launcher fbx-to-nwb --working-directory . -- --help
python -m launcher fbx-to-nwb --working-directory . -- assets/models/crate.fbx

The second command starts the interactive workflow. Automated pipelines should pass --yes, which accepts defaults for every omitted option and disables prompts:

python -m launcher fbx-to-nwb --working-directory . -- \
    assets/models/crate.fbx \
    --output assets/models/crate.nwb \
    --asset-type bunch \
    --normal-mode imported \
    --yes

When --output is omitted, the tool replaces the input extension with .nwb. It never overwrites an existing primary output unless --force is supplied or an interactive overwrite prompt is accepted.

Select source meshes

Inspect visible mesh instances before exporting a scene:

python -m launcher fbx-to-nwb --working-directory . -- assets/characters/hero.fbx --list-meshes

--mesh (or -m) defaults to all. It also accepts first, a zero-based instance index, a node name, or a mesh name. Matching is case-insensitive: exact matches win, while partial matches export every matching instance. Hidden mesh nodes are excluded unless --include-hidden is present.

For a single static mesh from the first instance:

python -m launcher fbx-to-nwb --working-directory . -- \
    assets/props/crate.fbx \
    --output assets/meshes/crate.nwb \
    --asset-type mesh \
    --mesh first \
    --yes

Selected instances are merged into one exported mesh. Polygon faces are triangulated and degenerate triangles are discarded. The default --triangle-area-length-squared-epsilon is 1e-20; raise it to discard additional small triangles.

Space and vertex attributes

By default, the importer converts to NWB space: +X right, +Y up, +Z forward, with one unit equal to one metre. Use --preserve-space to retain source axes and units. Node transforms are baked into the mesh unless --local is passed, and --scale applies an additional positive uniform scale.

Normal handling is explicit:

Mode Result
imported (default) Uses the imported per-corner normal stream.
smooth Rebuilds smooth normals shared by identical output positions.
regenerate Rebuilds one face normal per triangle.

NWB meshes always have tangents. Complete source tangents are retained only with imported normals; otherwise the tool regenerates them from uv0. Missing or degenerate UVs use a safe fallback tangent frame. uv0 defaults to zero when absent. Vertex colors are imported unless --ignore-colors requests the --default-color R,G,B,A value (default 1,1,1,1). --flip-winding swaps the second and third vertex of every output triangle.

Output layouts

--asset-type determines the output contract:

Type Files written Intended use
bunch (default) One .nwb containing a mesh and model, plus skeleton/skin data when needed. Self-contained import.
mesh One mesh .nwb. Standalone mesh payload; no skin or skeleton output.
model One model .nwb with package-asset references. Referenced mesh/skin/skeleton assets already exist.
skeleton One skeleton .nwb. Skinned source only.
skin One skin .nwb with mesh and skeleton references. Skinned source only; referenced assets are not written.

For model, skin, and separate-package output, asset references are derived from the output path. For example, assets/characters/hero.nwb produces a default virtual base of project/characters/hero; --virtual-root engine uses engine/... instead. A path outside an assets directory falls back to its stem under the selected virtual root.

--separate-assets is valid only with --asset-type bunch. It writes a model to the requested output and its child assets alongside it:

assets/characters/hero.nwb
assets/characters/hero/mesh.nwb
assets/characters/hero/skeleton.nwb  # skinned sources only
assets/characters/hero/skin.nwb      # skinned sources only

bunch, model, skeleton, and skin selections cannot mix static and skinned meshes. The skeleton and skin forms require a skinned selection.

The emitted model metadata uses the explicit named object maps described in Mesh Model and Skinning: skeletons, static_meshes, and skinned_meshes. Static objects declare their mesh. Skinned objects declare a Skin reference and local skeleton-object name; their mesh comes from the Skin and is not repeated in the model.

Refresh an NWB mesh source

Passing a .nwb file automatically enters refresh mode; --refresh-nwb can force it. Refresh validates each supported mesh declaration, canonicalizes and deduplicates its streams (including associated skin influences), then rewrites only changed lists while preserving the rest of the document.

Create a canonical copy:

python -m launcher fbx-to-nwb --working-directory . -- \
    assets/meshes/crate.nwb \
    --output assets/meshes/crate_canonical.nwb \
    --yes

Replace an input in place only with an explicit overwrite acknowledgement:

python -m launcher fbx-to-nwb --working-directory . -- \
    assets/meshes/crate.nwb \
    --yes --force

--list-meshes is available only for FBX input. Refresh rejects unsupported mesh fields rather than silently dropping them. See Mesh Model and Skinning for the runtime mesh, model, skeleton, and skin contracts that consume this authored metadata.

Command-line reference

Option Description
input FBX input path, or an NWB mesh path for refresh mode.
-o, --output PATH Primary output .nwb path.
--asset-type TYPE bunch, mesh, model, skeleton, or skin.
--virtual-root ROOT Virtual root for generated package references; defaults to project.
-m, --mesh SELECTOR all, first, an index, a node name, or a mesh name.
--normal-mode MODE imported, smooth, or regenerate.
--scale VALUE Positive finite uniform scale after import.
--triangle-area-length-squared-epsilon VALUE Non-negative threshold for discarding degenerate triangles.
--default-color R,G,B,A Fallback color, four finite values.
--preserve-space Do not convert source axes or units to NWB space.
--include-hidden Include hidden FBX mesh nodes.
--local Do not bake node transforms into the mesh.
--ignore-colors Use the default color even when source vertex colors exist.
--flip-winding Reverse every output triangle winding.
--separate-assets Write a bunch as a model plus child .nwb files.
--refresh-nwb Treat the input as an NWB mesh asset to canonicalize.
--list-meshes Print importable FBX mesh instances and exit.
--force Allow replacement of an existing primary output.
-y, --yes Accept defaults for omitted import options and disable prompts.
-h, --help Show the generated command help.

For the exact source-managed documentation and future option changes, use utilities/fbx_to_nwb/README.md on main.