Modules | C++
I/O
File I/O operations for reading and writing mesh data.
The I/O module provides functions for reading and writing mesh data in STL format (both ASCII and binary) and OBJ format (ASCII).
Include the module with:
#include <trueform/io.hpp>
Reading
STL Files
Read an STL file and return triangular polygons:
#include <trueform/io.hpp>
// Read STL file (Index defaults to int)
auto polygons = tf::read_stl("model.stl");
// Returns: tf::polygons_buffer<int, float, 3, 3>
// Access polygon data
auto faces = polygons.faces(); // (N, 3) connectivity
auto points = polygons.points(); // (M, 3) vertex positions
// For large meshes (> 2 billion vertices), specify int64_t
auto large_polygons = tf::read_stl<int64_t>("large_model.stl");
Features:
- Automatically detects binary vs ASCII format
- Deduplicates vertices during loading via
tf::clean::polygon_soup - Returns 3D triangular polygons with float coordinates
OBJ Files
Read an OBJ file and return polygons:
#include <trueform/io.hpp>
// Read with dynamic ngon (Index defaults to int)
auto dynamic_mesh = tf::read_obj("model.obj");
// dynamic_mesh: tf::polygons_buffer<int, float, 3, tf::dynamic_size>
// Read triangular mesh
auto triangles = tf::read_obj<3>("model.obj");
// triangles: tf::polygons_buffer<int, float, 3, 3>
// Read quad mesh
auto quads = tf::read_obj<4>("quad_model.obj");
// quads: tf::polygons_buffer<int, float, 3, 4>
// Access polygon data
auto faces = triangles.faces(); // (N, 3) connectivity
auto points = triangles.points(); // (M, 3) vertex positions
// For large meshes (> 2 billion vertices), specify int64_t
auto large_triangles = tf::read_obj<int64_t, 3>("large_model.obj");
// large_triangles: tf::polygons_buffer<int64_t, float, 3, 3>
Features:
- Reads ASCII OBJ format
- Converts 1-based OBJ indices to 0-based
- Only reads vertex positions (ignores normals and texture coordinates)
- Returns 3D polygons with float coordinates
Complete mode
For full-attribute payloads (positions, normals, texture coordinates, groups, objects), pass the tf::complete tag. The reader deduplicates unique (v, vt, vn) triplets so all per-vertex buffers are aligned [0, n_pts):
#include <trueform/io.hpp>
// Returns tf::obj_file<int>
auto f = tf::read_obj("model.obj", tf::complete);
auto points = f.polygons.points(); // (M, 3)
auto faces = f.polygons.faces(); // dynamic-size
auto normals = f.normals; // (M, 3) or empty
auto textures = f.textures; // (M, 2) or empty
// Per-face label arrays + name lists.
auto &face_groups = f.face_groups; // [n_faces] or empty
auto &group_names = f.group_names; // [n_groups]
auto &face_objects = f.face_objects; // [n_faces] or empty
auto &object_names = f.object_names; // [n_objects]
// For large meshes (> 2 billion vertices), specify int64_t
auto large = tf::read_obj<int64_t>("large.obj", tf::complete);
// large: tf::obj_file<int64_t>
Layout:
points,normals,texturesare aligned[0, n_pts)— the same vertex id indexes into all three.- A position with two distinct normals or texture coordinates in the file becomes two distinct output vertices (texture seams, sharp edges).
face_groups[i]/face_objects[i]index intogroup_names/object_names.group_*andobject_*arrays are empty when the file has nog/odirectives.
Behavior:
- All-or-nothing per attribute: the first
fline locks the format mode (v,v/vt,v//vn,v/vt/vn); inconsistent face refs return an emptyobj_file. - Multi-name
g a b clines: only the first name is kept. - Faces emitted before the first
g/oget an implicit"default"label at index 0. mtllib,usemtl,s,#comments, and unknown directives are silently skipped.- Negative (relative) indices are not supported — files using
f -3 -2 -1orf 1/-1/...return an emptyobj_file.
Writing
STL Files
Write polygons to binary STL format:
#include <trueform/io.hpp>
// Write polygons to file (.stl extension auto-appended if missing)
auto polygons = tf::read_stl("input.stl");
bool success = tf::write_stl(polygons, "output.stl");
Requirements:
- Must be 3D triangular polygons
- Normals are written if available, otherwise zero normals are used
With transformations:
#include <trueform/core.hpp>
// Create transformation matrix
auto frame = tf::random_frame<float, 3>();
tf::write_stl(polygons | tf::tag(frame), "translated.stl");
// Or inline
tf::write_stl(polygons | tf::tag(transform), "translated.stl");
Performance: Files < 500MB use parallel buffered writing, files ≥ 500MB use sequential streaming.
OBJ Files
Write polygons to ASCII OBJ format:
#include <trueform/io.hpp>
// Write dynamic mesh (any polygon sizes)
auto mesh = tf::read_obj("input.obj");
bool success = tf::write_obj(mesh, "output.obj");
// Write triangular mesh (.obj extension auto-appended if missing)
auto triangles = tf::read_obj<3>("input.obj");
tf::write_obj(triangles, "output.obj");
// Write quad mesh
auto quads = tf::read_obj<4>("quad_input.obj");
tf::write_obj(quads, "quad_output.obj");
With transformations:
#include <trueform/core.hpp>
// Create transformation matrix
auto frame = tf::random_frame<float, 3>();
tf::write_obj(triangles | tf::tag(frame), "translated.obj");
For Python bindings, see the Python I/O documentation.
