Repository navigation
Geometry and format support
Import accepts OBJ (.obj) and VRML (.wrl, .vrml) and writes glTF 2.0 JSON with an external binary buffer. The supported VRML scene subset is listed below. For glTF inputs accepted by inspect, see the README.
OBJ objects become named meshes/nodes. Each material used by an object is one primitive, and faces with and without UVs in the same material are two. Every primitive owns its buffer views; no vertex buffer is shared between meshes. Empty objects are omitted. Many exporters write groups (g) and no objects, so until a file's first o each g starts an object named after the group; groups inside objects are not represented. A repeated o or g name starts a new object rather than reopening the earlier one, as in obj2gltf, which also maps groups to meshes.
Valid source normals are preserved and normalized. Zero-length source normals are repaired with generated smooth normals and reported. Missing normals default to smooth shading: triangle normals are accumulated with area weights by source position index in a disk-backed spool, then normalized. Smoothing groups only switch flat shading, as in three.js: faces after s off or s 0 get their own face normal, are not shared with other faces, and do not weigh on their neighbours' smooth normals. A group number or a bare s smooths faces together again; different numbers are not kept apart, as in three.js and Blender, so no hard edges appear between groups. Files without s, such as most scans, stay smooth (Blender would shade them flat). VRML crease angles do not disable the smooth default. Smoothing crosses material and UV boundaries for shared position indices; separate position indices are not welded. Opposing faces whose accumulated normals cancel fall back to their polygon normal; such corners are never shared, since each carries its own face's normal. Faces mixing present and missing UVs/normals are rejected.
OBJ and MTL keywords match whatever their case, as in obj2gltf and three.js. A trailing \ continues any OBJ statement on the next line, as in Blender. Comments are stripped first, so a comment line never continues. Unsupported OBJ records warn and are omitted. A face with fewer than three corners is really a line or a point, so it is omitted the same way and counted on stderr, after its indices are checked.
Triangulation never aborts an import (src/triangulate.rs, one function per rule, each crediting its source):
- A corner at the same position as the previous one is dropped, so
f 1 2 3 3is a triangle. This follows earcut's point filtering. - A face's plane is that of its Newell normal. When that normal vanishes, as with a bowtie whose two halves cancel, the plane comes from its largest fan triangle.
- Ears are found with earcut's test, the library obj2gltf uses: only reflex or flat corners can block an ear, and a corner at the position of the ear's first corner does not. Keyhole polygons, a hole joined to the outline by a doubled edge, therefore triangulate exactly.
- When no ear exists, the face is self-intersecting or otherwise not simple. A convex or flat corner is clipped anyway, following Blender's polygon filling and Held's FIST, so the face is covered rather than dropped. These faces are counted in a warning.
- Faces and triangles of zero area are skipped, with a count on stderr.
Per-vertex colors written after the position (v x y z r g b, as exported by RealityCapture, Metashape or MeshLab) become a float COLOR_0 attribute. They are read as sRGB display values in [0,1] and converted to linear, as glTF requires; values outside that range are clamped. Positions without a color in a file that has some are white. glTF multiplies COLOR_0 into the base color, which would tint a map_Kd texture a second time. Textured faces therefore keep their colors as _COLOR instead, in the same linear float format: glTF reserves a leading underscore for application-specific attributes, which renderers ignore, so the data survives without changing the rendering. A warning counts those faces. Untextured faces of the same file, including faces without UVs whose material has a texture, use COLOR_0. --no-color drops vertex colors altogether.
Vertex components with no glTF equivalent are omitted with one warning per kind instead of failing the import: a color alpha (v x y z r g b a), a rational weight other than 1 (v x y z w, which is not applied as a divisor), any other extra v component, and texture coordinates beyond u v.
VRML coverage is IndexedFaceSet geometry with inline Coordinate, TextureCoordinate, Normal, Material and ImageTexture nodes. Index arrays may arrive in any order. Non-identity transforms, USE references, non-IndexedFaceSet primitive geometry, conditional nodes, ccw FALSE and normalPerVertex FALSE are rejected rather than silently producing misplaced geometry. VRML per-vertex colors warn and are omitted; texture transforms, generated UVs and animation are not implemented. Non-default fields beyond this subset are not a complete VRML scene conversion.