How to convert FBX to OBJ
- Parse the FBX containerA binary or ASCII FBX is read by the three.js FBXLoader, which builds a Group of nested meshes, transforms, materials and animation data.
- Resolve world transformsMatrix chains from the Group root down to each mesh are combined so every vertex can be expressed in one common coordinate space.
- Flatten into a single meshVisible meshes are combined into a solitary geometry holding coordinates and normals alone; groups, materials, UV channels and node labels are all cast aside.
- Triangulate polygon facesQuads and n-gons carried in the FBX meshes are converted to triangles by the loader so each face can be written as one OBJ f record.
- Write the OBJ textOne v line per vertex, one vn line per normal and one f line per triangle are emitted, with no .mtl file, no vt lines and no group names.
What happens when FBX is converted to OBJ
What an FBX file actually holds
FBX is Autodesk's scene exchange container, available as a binary blob or an ASCII text variant. It is far more than a mesh: it stores a hierarchy of Model nodes, polygon geometry that may include quads and n-gons, material definitions, references to texture files or embedded image blobs, cameras, lights, skeletons built from limb nodes, skin deformers with per-vertex weights, and animation curves organised into takes. It also records file-level metadata such as the working unit and the up axis. All of this travels together in one file, which is why a scene round-trips through animation software with its structure intact.
How FBXLoader represents the scene
The three.js FBXLoader parses that document and returns a THREE.Group whose children form a nested tree. Mesh nodes sit at various depths, each carrying a local position, rotation and scale, and parent groups add further transforms on top. Geometry is converted into BufferGeometry objects, polygons are triangulated, materials are instantiated and referenced textures are resolved where possible. Skeletons, skins and animation clips are also attached to the returned structure. Nothing is yet in a single flat coordinate space at this point; the placement of a mesh is still the product of every matrix from the Group root down to that node.
Applying world matrices while flattening
To collapse the tree, the converter walks the Group and computes each mesh's world matrix, accumulating all ancestor transforms into one. Every vertex position is multiplied by that matrix, and normals are carried through by the rotation component so they remain perpendicular to the moved triangles. The transformed triangles from every visible mesh are appended to one BufferGeometry that holds only position and normal attributes. This baking is what lets a deeply nested part end up in the right place without any transform node surviving in the output. It also means there is exactly one coordinate frame for the whole result.
What flattening deliberately discards
Once geometry is merged into a single structure, everything that is not a position or a normal has nowhere to go. Bone hierarchies and skeleton objects are dropped, animation curves and takes are not evaluated or exported, skin weights are ignored, and the mesh is taken at its static rest pose. Material names, material definitions and every embedded or referenced texture are discarded. Node names, group names, cameras, lights and any custom properties vanish as well. UV coordinates are not carried into the merged geometry, so no texture coordinate attribute exists to be written out later.
The OBJ record set it produces
OBJ is a plain text format, so the exporter writes lines rather than binary records. Each unique vertex becomes a v line with three float coordinates; each normal becomes a vn line. Faces are written as f records that reference a vertex and a normal index using the v//vn form, for example f 1//1 2//2 3//3, because there is no texture coordinate to fill a middle slot. No .mtl file is generated, no usemtl or o or g group lines appear, and no vt lines exist. The file is one undifferentiated list of geometry.
Units, up axis and static pose
FBX stores a unit scale and an up-axis convention in its global settings, often centimetres with a particular axis pointing up. OBJ has no equivalent fields at all: it is a raw list of coordinates with no coordinate-system declaration. The converter writes the transformed numbers verbatim and does not convert centimetres to anything, nor rotate the model to a different up axis. Animation is not sampled either, so the geometry captured is the static rest pose of every mesh, not a deformed frame. Only visible, static surface geometry survives the trip.
FBX vs OBJ
| FBX source | OBJ output | |
|---|---|---|
| Container type | Binary or ASCII scene file | Plain text geometry list |
| Structure | Node hierarchy with transforms | Single flat unnamed mesh |
| Records | Nodes, connections and properties | v, vn and f lines |
| Materials | Material and texture references | None, no .mtl |
| UV coordinates | Present on many meshes | Absent, no vt lines |
| Animation | Curves, takes and skin weights | Not representable |
| World transforms | Per-node matrices | Baked into vertex positions |
| Units / up axis | Declared in global settings | No equivalent field |
| Cameras / lights / bones | Stored as scene nodes | Dropped entirely |
Supported formats
| Format | Read | Write | Notes |
|---|---|---|---|
| STEP (.step .stp) | Yes | – | ISO 10303 B-rep, AP203 / AP214 / AP242 |
| IGES (.iges .igs) | Yes | – | Entity-based surfaces, up to 5.3 |
| BREP (.brep) | Yes | – | OpenCascade native B-rep |
| STL (.stl) | Yes | Yes | Binary and ASCII triangles, no units |
| OBJ (.obj) | Yes | Yes | v / vn / f records; .mtl references ignored |
| PLY (.ply) | Yes | Yes | Binary position and normal arrays |
| GLB / glTF (.glb .gltf) | Yes | Yes | glTF 2.0, binary or JSON with data URI |
| 3MF (.3mf) | Yes | Yes | OPC/ZIP package, declares millimetres |
| FBX (.fbx) | Yes | – | Binary and ASCII, static mesh only |
| DAE (.dae) | Yes | – | Collada scene graph is flattened |
| 3DS (.3ds) | Yes | – | Legacy chunk-based 3D Studio format |
| AMF (.amf) | Yes | – | XML, ZIP-compressed packages supported |
| USDZ (.usdz) | Yes | Yes | ZIP containing a USD stage |
Known limitations and why a conversion can fail
No materials, textures or UV coordinates
An FBX can carry named materials, shader parameters, diffuse colours and texture images that are either referenced by path or embedded as binary blobs. The flattened mesh keeps only position and normal attributes, so every one of those is discarded. OBJ output here has no .mtl companion, no usemtl statements and no vt texture-coordinate lines, because no UV attribute survived the merge. A consumer opening the result sees untextured, uncoloured geometry with a single default appearance, regardless of how richly the source scene was shaded. Even a texture embedded as a binary blob inside the FBX is never extracted, since the flattening pass never creates the UV channel that would be needed to place it on a face.
The scene graph becomes one unnamed mesh
FBX organises geometry into a tree of named nodes with parent-child transforms, groups and instances. OBJ as written here has no object names, no g or o grouping lines and no hierarchy of any kind. Flattening concatenates every visible mesh into one undifferentiated vertex and face list, so the node names and the parent-child relationships are lost, and only connectivity derived from the triangles themselves remains. You cannot select an individual part by name afterwards, and the structure that made the scene editable as separate objects is unrecoverable from the text file. Instance links are resolved into ordinary triangles too, so two references to one source mesh become two copies in the flat list with no shared identity left behind.
Animation, bones and skinning are gone
FBX frequently contains skeletons built from limb nodes, skin deformers with per-vertex weights, and animation curves grouped into takes that drive those bones over time. None of that has any representation in a plain OBJ file. The exporter does not sample a frame, evaluate inverse kinematics or bake a deformed pose; it takes each mesh at its static rest configuration. Bones, weights, clips and node animations are all dropped during flattening. If the source relied on a skinned character, the result is that character frozen in its undeformed shape, with no rig. No bind matrices or bone names reach the OBJ either, and the rest pose is exactly what the FBXLoader left in its BufferGeometry before any animation clip was evaluated.
Unit and up-axis metadata has no target
FBX records a global unit scale and an up-axis convention, so a coordinate of 1.0 may mean one centimetre along a particular axis. OBJ defines no unit field and no axis convention, so those settings cannot be carried across. The converter does not rescale or rotate the geometry to compensate; it writes the transformed coordinates exactly as computed. A viewer that assumes a different unit or a different up axis may show the model at an unexpected size or orientation, and there is nothing in the file that can correct the assumption. The OBJ header offers only free-form comment space, and this exporter writes no comment recording the source unit or axis, so even a person reading the text gets no hint.
Text output and untouched surface quality
Because OBJ is human-readable text, a dense mesh produces a large file of v, vn and f lines with no binary compression. More importantly, no cleanup or re-tessellation occurs: flipped windings, non-manifold edges, degenerate triangles and self-intersections in the FBX geometry pass straight through. Normals are recomputed during flattening rather than preserved from any stored source, so custom or smoothed shading intent is lost. FBX reaches the converter as a mesh-based scene, so no boundary-surface tessellation is needed and the OpenCascade kernel does not run for this pair. Each f line uses the v//vn form, so the face list points at the recomputed normals rather than any authored normal layer that came with the FBX.
Frequently asked questions
Are materials and textures exported to OBJ?
No. The flattened mesh keeps only position and normal attributes, so material names, diffuse colours and embedded or referenced texture images are dropped. The OBJ output contains no .mtl file, no usemtl statements and no vt texture-coordinate lines, because no UV attribute survives the merge. Whatever surface appearance the FBX defined is replaced by a single default look chosen by the application that opens the file.
What happens to the FBX node hierarchy?
It is collapsed. The FBXLoader returns a Group of nested meshes, and the converter accumulates each mesh's world matrix from the root, applies it to the vertices, and appends the triangles to one geometry. Node names, groups, instances and parent-child transforms have no equivalent in the OBJ text written here, so the result is a single unnamed mesh with no o or g lines.
Is animation or skinning preserved?
No. Skeletons, limb nodes, per-vertex skin weights and animation curves grouped into takes are all discarded during flattening, and no frame is sampled or evaluated. The geometry is captured at each mesh's static rest pose, never a deformed one. A skinned character therefore exports frozen in its undeformed shape, with no rig and no clips. Any motion the FBX contained is simply absent from the OBJ.
Are FBX units converted for the OBJ?
No. FBX can declare a working unit and an up axis, but OBJ has no unit field and no coordinate-system declaration. The converter writes the transformed coordinate numbers verbatim and does not rescale centimetres into anything else or rotate the model to a different up axis. If a viewer assumes different settings, the model may appear at an unexpected size or orientation, with nothing in the file to correct it.
Does the OBJ include UV coordinates?
It does not. Although FBX meshes often carry UV sets, the flattening step keeps only position and normal attributes, so no texture-coordinate attribute reaches the exporter. As a result there are no vt lines, and each f record uses the v//vn form that leaves the texture slot empty. Without UVs, no texture from the source can be mapped onto the output geometry even if the image data were available.
Are cameras, lights and bones kept?
No. FBX stores cameras, lights, skeletons and their nodes alongside the meshes, but only visible mesh geometry survives this conversion. Anything that is not a triangle surface is dropped while the Group is flattened into a single mesh. The OBJ text consequently holds only v, vn and f records describing static surfaces, with no camera definitions, no light parameters and no bone or deformer nodes of any kind.
Are quads and n-gons preserved as such?
They are not. The three.js FBXLoader triangulates polygon faces while building its BufferGeometry objects, so by export time every face is already a triangle. Each OBJ f record therefore references exactly three vertex and normal pairs. If the original FBX used quads or higher-sided polygons, that structure is gone; the output is an all-triangle mesh regardless of how the source surfaces were originally subdivided.
What do the vn lines contain?
Each vn line holds one normal vector, and each f line references a normal index alongside its vertex index in the v//vn form. The normals come from the normal attribute computed while flattening the nested meshes, transformed by the rotation component of each world matrix. They are not custom normals copied from the FBX, so any smoothed or authored shading intent is replaced by the recomputed geometry normals.
Related converters
All converters
Last updated: