Expand description
§Tiny OBJ Loader
A tiny OBJ loader, inspired by Syoyo’s excellent tinyobjloader.
Aims to be a simple and lightweight option for loading OBJ files.
Just returns two Vecs containing loaded models and materials.
§Triangulation
Meshes can be triangulated on the fly or left as-is.
Only polygons that are trivially convertible to triangle fans are supported. Arbitrary polygons may not behave as expected. The best solution would be to convert your mesh to solely consist of triangles in your modeling software.
§Optional – Normals & Texture Coordinates
It is assumed that all meshes will at least have positions, but normals and texture coordinates are optional.
If no normals or texture coordinates are found then the corresponding
Vecs for the Mesh will be empty.
§Flat Data
Values are stored packed as f32s (or f64s with the use_f64 feature)
in flat Vecs.
For example, the positions member of a Mesh will contain [x, y, z, x, y, z, ...] which you can then use however you like.
§Indices
Indices are also loaded and may re-use vertices already existing in the
mesh, this data is stored in the indices member.
When a Mesh contains per vertex per face normals or texture coordinates,
positions can be duplicated to be per vertex per face too via the
single_index flag. This potentially changes
the topology (faces may become disconnected even though their vertices still
share a position in space).
By default separate indices for normals and texture coordinates are created.
This also guarantees that the topology of the Mesh does not change when
either of the latter are specified per vertex per face.
§Materials
Standard MTL attributes are supported too. Any unrecognized parameters
will be stored in a HashMap containing the key-value pairs of the
unrecognized parameter and its value.
§Example
In this simple example we load the classic Cornell Box model that only
defines positions and print out its attributes. This example is a slightly
trimmed down version of print_model_info and print_material_info
combined together, see them for a version that also prints out normals and
texture coordinates if the model has them.
The LoadOptions used are typical for the case when the mesh is going to
be sent to a realtime rendering context (game engine, GPU etc.).
use tobj;
let cornell_box = tobj::load_obj("obj/cornell_box.obj", &tobj::GPU_LOAD_OPTIONS);
assert!(cornell_box.is_ok());
let (models, materials) = cornell_box.expect("Failed to load OBJ file");
// Materials might report a separate loading error if the MTL file wasn't found.
// If you don't need the materials, you can generate a default here and use that
// instead.
let materials = materials.expect("Failed to load MTL file");
println!("# of models: {}", models.len());
println!("# of materials: {}", materials.len());
for (i, m) in models.iter().enumerate() {
let mesh = &m.mesh;
println!("model[{}].name = \'{}\'", i, m.name);
println!("model[{}].mesh.material_id = {:?}", i, mesh.material_id);
println!(
"Size of model[{}].face_arities: {}",
i,
mesh.face_arities.len()
);
let mut next_face = 0;
for f in 0..mesh.face_arities.len() {
let end = next_face + mesh.face_arities[f] as usize;
let face_indices: Vec<_> = mesh.indices[next_face..end].iter().collect();
println!(" face[{}] = {:?}", f, face_indices);
next_face = end;
}
// Normals and texture coordinates are also loaded, but not printed in this example
println!("model[{}].vertices: {}", i, mesh.positions.len() / 3);
assert!(mesh.positions.len() % 3 == 0);
for v in 0..mesh.positions.len() / 3 {
println!(
" v[{}] = ({}, {}, {})",
v,
mesh.positions[3 * v],
mesh.positions[3 * v + 1],
mesh.positions[3 * v + 2]
);
}
}
for (i, m) in materials.iter().enumerate() {
println!("material[{}].name = \'{}\'", i, m.name);
if let Some(ambient) = m.ambient {
println!(
" material.Ka = ({}, {}, {})",
ambient[0], ambient[1], ambient[2]
);
}
if let Some(diffuse) = m.diffuse {
println!(
" material.Kd = ({}, {}, {})",
diffuse[0], diffuse[1], diffuse[2]
);
}
if let Some(specular) = m.specular {
println!(
" material.Ks = ({}, {}, {})",
specular[0], specular[1], specular[2]
);
}
if let Some(shininess) = m.shininess {
println!(" material.Ns = {}", shininess);
}
if let Some(dissolve) = m.dissolve {
println!(" material.d = {}", dissolve);
}
if let Some(ambient_texture) = &m.ambient_texture {
println!(" material.map_Ka = {}", ambient_texture);
}
if let Some(diffuse_texture) = &m.diffuse_texture {
println!(" material.map_Kd = {}", diffuse_texture);
}
if let Some(specular_texture) = &m.specular_texture {
println!(" material.map_Ks = {}", specular_texture);
}
if let Some(shininess_texture) = &m.shininess_texture {
println!(" material.map_Ns = {}", shininess_texture);
}
if let Some(normal_texture) = &m.normal_texture {
println!(" material.map_Bump = {}", normal_texture);
}
if let Some(dissolve_texture) = &m.dissolve_texture {
println!(" material.map_d = {}", dissolve_texture);
}
for (k, v) in &m.unknown_param {
println!(" material.{} = {}", k, v);
}
}§Rendering Examples
For an example of integration with glium
to make a simple OBJ viewer, check out tobj viewer.
Some more sample images can be found in this gallery.
The Rungholt model shown below is reasonably large (6.7M triangles, 12.3M vertices) and is loaded in ~7.47s using a peak of ~1.1GB of memory on a Windows 10 machine with an i7-4790k and 16GB of 1600Mhz DDR3 RAM with tobj 0.1.1 on rustc 1.6.0. The model can be found on Morgan McGuire’s meshes page and was originally built by kescha. Future work will focus on improving performance and memory usage.

For an example of integration within a ray tracer, check out tray_rust’s mesh module. The Stanford Buddha and Dragon from the Stanford 3D Scanning Repository both load quite quickly. The Rust logo model was made by Nylithius on BlenderArtists. The materials used are from the MERL BRDF Database.

§Features
-
ahash– On by default. UseAHashMapfor hashing when reading files and merging vertices. To disable and use the slowerHashMapinstead, unset default features inCargo.toml:[dependencies.tobj] default-features = false -
merging– Adds support for merging identical vertex positions on disconnected faces during import.Warning: this feature uses const generics and thus requires at least a
betatoolchain to build. -
reordering– Adds support for reordering the normal- and texture coordinate indices. -
async– Adds support for async loading of obj files from a buffer, with an async material loader. Useful in environments that do not support blocking IO (e.g. WebAssembly). -
futures- Adds support for async loading of objs and materials using futures AsyncRead traits. -
tokio- Adds support for async loading of objs and materials using tokio AsyncRead traits. -
[‘use_f64’] - Uses double-precision (f64) instead of single-precision (f32) floating point types
Modules§
- futures
- Optional module supporting async loading with
futurestraits. - tokio
- Optional module supporting async loading with
tokiotraits.
Structs§
- Load
Options - Options for processing the mesh during loading.
- Material
- A material that may be referenced by one or more
Meshes. - Mesh
- A mesh made up of triangles loaded from some
OBJfile. - Model
- A named model within the file.
Enums§
- Load
Error - Possible errors that may occur while loading
OBJandMTLfiles.
Constants§
- GPU_
LOAD_ OPTIONS - Typical
LoadOptionsfor using meshes in a GPU/relatime context. - OFFLINE_
RENDERING_ LOAD_ OPTIONS - Typical
LoadOptionsfor using meshes with an offline rendeder.
Functions§
- load_
mtl - Load the materials defined in a
MTLfile. - load_
mtl_ buf - Load the various materials in a
MTLbuffer. - load_
obj - Load the various objects specified in the
OBJfile and any associatedMTLfile. - load_
obj_ buf - Load the various meshes in an
OBJbuffer. - load_
obj_ buf_ async Deprecated - Load the various meshes in an
OBJbuffer.
Type Aliases§
- Load
Result - A
Resultcontaining all the models loaded from the file and any materials from referenced material libraries. Or an error that occured while loading. - MTLLoad
Result - A
Resultcontaining all the materials loaded from the file and a map ofMTLname to index. Or an error that occured while loading.