195 lines
22 KiB
HTML
195 lines
22 KiB
HTML
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Tiny OBJ Loader"><title>tobj - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-42caa33d.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="tobj" data-themes="" data-resource-suffix="" data-rustdoc-version="1.84.0 (9fc6b4312 2025-01-07)" data-channel="1.84.0" data-search-js="search-92e6798f.js" data-settings-js="settings-0f613d39.js" ><script src="../static.files/storage-59e33391.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-5f194d8c.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="mobile-topbar"><button class="sidebar-menu-toggle" title="show sidebar"></button></nav><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../tobj/index.html">tobj</a><span class="version">4.0.2</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#tiny-obj-loader" title="Tiny OBJ Loader">Tiny OBJ Loader</a><ul><li><a href="#triangulation" title="Triangulation">Triangulation</a></li><li><a href="#optional--normals--texture-coordinates" title="Optional – Normals & Texture Coordinates">Optional – Normals & Texture Coordinates</a></li><li><a href="#flat-data" title="Flat Data">Flat Data</a></li><li><a href="#indices" title="Indices">Indices</a></li><li><a href="#materials" title="Materials">Materials</a></li><li><a href="#example" title="Example">Example</a></li><li><a href="#rendering-examples" title="Rendering Examples">Rendering Examples</a></li><li><a href="#features" title="Features">Features</a></li></ul></li></ul><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li><li><a href="#constants" title="Constants">Constants</a></li><li><a href="#functions" title="Functions">Functions</a></li><li><a href="#types" title="Type Aliases">Type Aliases</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer"></div><main><div class="width-limiter"><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1>Crate <span>tobj</span><button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/tobj/lib.rs.html#1-2208">Source</a> </span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><h2 id="tiny-obj-loader"><a class="doc-anchor" href="#tiny-obj-loader">§</a>Tiny OBJ Loader</h2>
|
||
<p>A tiny OBJ loader, inspired by Syoyo’s excellent <a href="https://github.com/syoyo/tinyobjloader"><code>tinyobjloader</code></a>.
|
||
Aims to be a simple and lightweight option for loading <code>OBJ</code> files.</p>
|
||
<p>Just returns two <code>Vec</code>s containing loaded models and materials.</p>
|
||
<h3 id="triangulation"><a class="doc-anchor" href="#triangulation">§</a>Triangulation</h3>
|
||
<p>Meshes can be triangulated on the fly or left as-is.</p>
|
||
<p>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.</p>
|
||
<h3 id="optional--normals--texture-coordinates"><a class="doc-anchor" href="#optional--normals--texture-coordinates">§</a>Optional – Normals & Texture Coordinates</h3>
|
||
<p>It is assumed that all meshes will at least have positions, but normals and
|
||
texture coordinates are optional.</p>
|
||
<p>If no normals or texture coordinates are found then the corresponding
|
||
<a href="struct.Mesh.html#structfield.normals" title="field tobj::Mesh::normals"><code>Vec</code></a>s for the <a href="struct.Mesh.html" title="struct tobj::Mesh"><code>Mesh</code></a> will be empty.</p>
|
||
<h3 id="flat-data"><a class="doc-anchor" href="#flat-data">§</a>Flat Data</h3>
|
||
<p>Values are stored packed as <a href="https://doc.rust-lang.org/1.84.0/std/primitive.f32.html" title="primitive f32"><code>f32</code></a>s (or <a href="https://doc.rust-lang.org/1.84.0/std/primitive.f64.html" title="primitive f64"><code>f64</code></a>s with the use_f64 feature)
|
||
in flat <code>Vec</code>s.</p>
|
||
<p>For example, the <code>positions</code> member of a <code>Mesh</code> will contain <code>[x, y, z, x, y, z, ...]</code> which you can then use however you like.</p>
|
||
<h3 id="indices"><a class="doc-anchor" href="#indices">§</a>Indices</h3>
|
||
<p>Indices are also loaded and may re-use vertices already existing in the
|
||
mesh, this data is stored in the <a href="struct.Mesh.html#structfield.indices" title="field tobj::Mesh::indices"><code>indices</code></a> member.</p>
|
||
<p>When a <code>Mesh</code> contains <em>per vertex per face</em> normals or texture coordinates,
|
||
positions can be duplicated to be <em>per vertex per face</em> too via the
|
||
<a href="struct.LoadOptions.html#structfield.single_index" title="field tobj::LoadOptions::single_index"><code>single_index</code></a> flag. This potentially changes
|
||
the topology (faces may become disconnected even though their vertices still
|
||
share a position in space).</p>
|
||
<p>By default separate indices for normals and texture coordinates are created.
|
||
This also guarantees that the topology of the <code>Mesh</code> does <em>not</em> change when
|
||
either of the latter are specified <em>per vertex per face</em>.</p>
|
||
<h3 id="materials"><a class="doc-anchor" href="#materials">§</a>Materials</h3>
|
||
<p>Standard <code>MTL</code> attributes are supported too. Any unrecognized parameters
|
||
will be stored in a <code>HashMap</code> containing the key-value pairs of the
|
||
unrecognized parameter and its value.</p>
|
||
<h3 id="example"><a class="doc-anchor" href="#example">§</a>Example</h3>
|
||
<p>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 <code>print_model_info</code> and <code>print_material_info</code>
|
||
combined together, see them for a version that also prints out normals and
|
||
texture coordinates if the model has them.</p>
|
||
<p>The <a href="struct.LoadOptions.html" title="struct tobj::LoadOptions"><code>LoadOptions</code></a> used are typical for the case when the mesh is going to
|
||
be sent to a realtime rendering context (game engine, GPU etc.).</p>
|
||
|
||
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>tobj;
|
||
|
||
<span class="kw">let </span>cornell_box = tobj::load_obj(<span class="string">"obj/cornell_box.obj"</span>, <span class="kw-2">&</span>tobj::GPU_LOAD_OPTIONS);
|
||
<span class="macro">assert!</span>(cornell_box.is_ok());
|
||
|
||
<span class="kw">let </span>(models, materials) = cornell_box.expect(<span class="string">"Failed to load OBJ file"</span>);
|
||
|
||
<span class="comment">// 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.
|
||
</span><span class="kw">let </span>materials = materials.expect(<span class="string">"Failed to load MTL file"</span>);
|
||
|
||
<span class="macro">println!</span>(<span class="string">"# of models: {}"</span>, models.len());
|
||
<span class="macro">println!</span>(<span class="string">"# of materials: {}"</span>, materials.len());
|
||
|
||
<span class="kw">for </span>(i, m) <span class="kw">in </span>models.iter().enumerate() {
|
||
<span class="kw">let </span>mesh = <span class="kw-2">&</span>m.mesh;
|
||
|
||
<span class="macro">println!</span>(<span class="string">"model[{}].name = \'{}\'"</span>, i, m.name);
|
||
<span class="macro">println!</span>(<span class="string">"model[{}].mesh.material_id = {:?}"</span>, i, mesh.material_id);
|
||
|
||
<span class="macro">println!</span>(
|
||
<span class="string">"Size of model[{}].face_arities: {}"</span>,
|
||
i,
|
||
mesh.face_arities.len()
|
||
);
|
||
|
||
<span class="kw">let </span><span class="kw-2">mut </span>next_face = <span class="number">0</span>;
|
||
<span class="kw">for </span>f <span class="kw">in </span><span class="number">0</span>..mesh.face_arities.len() {
|
||
<span class="kw">let </span>end = next_face + mesh.face_arities[f] <span class="kw">as </span>usize;
|
||
<span class="kw">let </span>face_indices: Vec<<span class="kw">_</span>> = mesh.indices[next_face..end].iter().collect();
|
||
<span class="macro">println!</span>(<span class="string">" face[{}] = {:?}"</span>, f, face_indices);
|
||
next_face = end;
|
||
}
|
||
|
||
<span class="comment">// Normals and texture coordinates are also loaded, but not printed in this example
|
||
</span><span class="macro">println!</span>(<span class="string">"model[{}].vertices: {}"</span>, i, mesh.positions.len() / <span class="number">3</span>);
|
||
|
||
<span class="macro">assert!</span>(mesh.positions.len() % <span class="number">3 </span>== <span class="number">0</span>);
|
||
<span class="kw">for </span>v <span class="kw">in </span><span class="number">0</span>..mesh.positions.len() / <span class="number">3 </span>{
|
||
<span class="macro">println!</span>(
|
||
<span class="string">" v[{}] = ({}, {}, {})"</span>,
|
||
v,
|
||
mesh.positions[<span class="number">3 </span>* v],
|
||
mesh.positions[<span class="number">3 </span>* v + <span class="number">1</span>],
|
||
mesh.positions[<span class="number">3 </span>* v + <span class="number">2</span>]
|
||
);
|
||
}
|
||
}
|
||
|
||
<span class="kw">for </span>(i, m) <span class="kw">in </span>materials.iter().enumerate() {
|
||
<span class="macro">println!</span>(<span class="string">"material[{}].name = \'{}\'"</span>, i, m.name);
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(ambient) = m.ambient {
|
||
<span class="macro">println!</span>(
|
||
<span class="string">" material.Ka = ({}, {}, {})"</span>,
|
||
ambient[<span class="number">0</span>], ambient[<span class="number">1</span>], ambient[<span class="number">2</span>]
|
||
);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(diffuse) = m.diffuse {
|
||
<span class="macro">println!</span>(
|
||
<span class="string">" material.Kd = ({}, {}, {})"</span>,
|
||
diffuse[<span class="number">0</span>], diffuse[<span class="number">1</span>], diffuse[<span class="number">2</span>]
|
||
);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(specular) = m.specular {
|
||
<span class="macro">println!</span>(
|
||
<span class="string">" material.Ks = ({}, {}, {})"</span>,
|
||
specular[<span class="number">0</span>], specular[<span class="number">1</span>], specular[<span class="number">2</span>]
|
||
);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(shininess) = m.shininess {
|
||
<span class="macro">println!</span>(<span class="string">" material.Ns = {}"</span>, shininess);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(dissolve) = m.dissolve {
|
||
<span class="macro">println!</span>(<span class="string">" material.d = {}"</span>, dissolve);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(ambient_texture) = <span class="kw-2">&</span>m.ambient_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_Ka = {}"</span>, ambient_texture);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(diffuse_texture) = <span class="kw-2">&</span>m.diffuse_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_Kd = {}"</span>, diffuse_texture);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(specular_texture) = <span class="kw-2">&</span>m.specular_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_Ks = {}"</span>, specular_texture);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(shininess_texture) = <span class="kw-2">&</span>m.shininess_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_Ns = {}"</span>, shininess_texture);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(normal_texture) = <span class="kw-2">&</span>m.normal_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_Bump = {}"</span>, normal_texture);
|
||
}
|
||
<span class="kw">if let </span><span class="prelude-val">Some</span>(dissolve_texture) = <span class="kw-2">&</span>m.dissolve_texture {
|
||
<span class="macro">println!</span>(<span class="string">" material.map_d = {}"</span>, dissolve_texture);
|
||
}
|
||
|
||
<span class="kw">for </span>(k, v) <span class="kw">in </span><span class="kw-2">&</span>m.unknown_param {
|
||
<span class="macro">println!</span>(<span class="string">" material.{} = {}"</span>, k, v);
|
||
}
|
||
}</code></pre></div>
|
||
<h3 id="rendering-examples"><a class="doc-anchor" href="#rendering-examples">§</a>Rendering Examples</h3>
|
||
<p>For an example of integration with <a href="https://github.com/tomaka/glium">glium</a>
|
||
to make a simple OBJ viewer, check out <a href="https://github.com/Twinklebear/tobj_viewer"><code>tobj viewer</code></a>.
|
||
Some more sample images can be found in <a href="http://imgur.com/a/xsg6v">this gallery</a>.</p>
|
||
<p>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 <a href="http://graphics.cs.williams.edu/data/meshes.xml">Morgan McGuire’s</a>
|
||
meshes page and was originally built by kescha. Future work will focus on
|
||
improving performance and memory usage.</p>
|
||
<p><img src="http://i.imgur.com/wImyNG4.png" alt="Rungholt"
|
||
style="display:block; max-width:100%; height:auto"></p>
|
||
<p>For an example of integration within a ray tracer, check out tray_rust’s
|
||
<a href="https://github.com/Twinklebear/tray_rust/blob/master/src/geometry/mesh.rs">mesh module</a>.
|
||
The Stanford Buddha and Dragon from the
|
||
<a href="http://graphics.stanford.edu/data/3Dscanrep/">Stanford 3D Scanning Repository</a>
|
||
both load quite quickly. The Rust logo model was made by <a href="http://blenderartists.org/forum/showthread.php?362836-Rust-language-3D-logo">Nylithius on BlenderArtists</a>.
|
||
The materials used are from the <a href="http://www.merl.com/brdf/">MERL BRDF Database</a>.</p>
|
||
<p><img src="http://i.imgur.com/E1ylrZW.png" alt="Rust logo with friends"
|
||
style="display:block; max-width:100%; height:auto"></p>
|
||
<h3 id="features"><a class="doc-anchor" href="#features">§</a>Features</h3>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://crates.io/crates/ahash"><code>ahash</code></a> – On by default. Use <a href="https://docs.rs/ahash/latest/ahash/struct.AHashMap.html"><code>AHashMap</code></a>
|
||
for hashing when reading files and merging vertices. To disable and use
|
||
the slower <a href="https://doc.rust-lang.org/1.84.0/std/collections/hash/map/struct.HashMap.html" title="struct std::collections::hash::map::HashMap"><code>HashMap</code></a> instead, unset default
|
||
features in <code>Cargo.toml</code>:</p>
|
||
<div class="example-wrap"><pre class="language-toml"><code>[dependencies.tobj]
|
||
default-features = false</code></pre></div></li>
|
||
<li>
|
||
<p><a href="LoadOptions::merge_identical_points"><code>merging</code></a> – Adds support for
|
||
merging identical vertex positions on disconnected faces during import.</p>
|
||
<p><strong>Warning:</strong> this feature uses <em>const generics</em> and thus requires at
|
||
least a <code>beta</code> toolchain to build.</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="LoadOptions::reorder_data"><code>reordering</code></a> – Adds support for reordering
|
||
the normal- and texture coordinate indices.</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="load_obj_buf_async"><code>async</code></a> – 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).</p>
|
||
</li>
|
||
<li>
|
||
<p>[‘use_f64’] - Uses double-precision (f64) instead of single-precision
|
||
(f32) floating point types</p>
|
||
</li>
|
||
</ul>
|
||
</div></details><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="struct" href="struct.LoadOptions.html" title="struct tobj::LoadOptions">Load<wbr>Options</a></div><div class="desc docblock-short">Options for processing the mesh during loading.</div></li><li><div class="item-name"><a class="struct" href="struct.Material.html" title="struct tobj::Material">Material</a></div><div class="desc docblock-short">A material that may be referenced by one or more <a href="struct.Mesh.html" title="struct tobj::Mesh"><code>Mesh</code></a>es.</div></li><li><div class="item-name"><a class="struct" href="struct.Mesh.html" title="struct tobj::Mesh">Mesh</a></div><div class="desc docblock-short">A mesh made up of triangles loaded from some <code>OBJ</code> file.</div></li><li><div class="item-name"><a class="struct" href="struct.Model.html" title="struct tobj::Model">Model</a></div><div class="desc docblock-short">A named model within the file.</div></li></ul><h2 id="enums" class="section-header">Enums<a href="#enums" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="enum" href="enum.LoadError.html" title="enum tobj::LoadError">Load<wbr>Error</a></div><div class="desc docblock-short">Possible errors that may occur while loading <code>OBJ</code> and <code>MTL</code> files.</div></li></ul><h2 id="constants" class="section-header">Constants<a href="#constants" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="constant" href="constant.GPU_LOAD_OPTIONS.html" title="constant tobj::GPU_LOAD_OPTIONS">GPU_<wbr>LOAD_<wbr>OPTIONS</a></div><div class="desc docblock-short">Typical <a href="struct.LoadOptions.html" title="struct tobj::LoadOptions"><code>LoadOptions</code></a> for using meshes in a GPU/relatime context.</div></li><li><div class="item-name"><a class="constant" href="constant.OFFLINE_RENDERING_LOAD_OPTIONS.html" title="constant tobj::OFFLINE_RENDERING_LOAD_OPTIONS">OFFLINE_<wbr>RENDERING_<wbr>LOAD_<wbr>OPTIONS</a></div><div class="desc docblock-short">Typical <a href="struct.LoadOptions.html" title="struct tobj::LoadOptions"><code>LoadOptions</code></a> for using meshes with an offline rendeder.</div></li></ul><h2 id="functions" class="section-header">Functions<a href="#functions" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="fn" href="fn.load_mtl.html" title="fn tobj::load_mtl">load_<wbr>mtl</a></div><div class="desc docblock-short">Load the materials defined in a <code>MTL</code> file.</div></li><li><div class="item-name"><a class="fn" href="fn.load_mtl_buf.html" title="fn tobj::load_mtl_buf">load_<wbr>mtl_<wbr>buf</a></div><div class="desc docblock-short">Load the various materials in a <code>MTL</code> buffer.</div></li><li><div class="item-name"><a class="fn" href="fn.load_obj.html" title="fn tobj::load_obj">load_<wbr>obj</a></div><div class="desc docblock-short">Load the various objects specified in the <code>OBJ</code> file and any associated
|
||
<code>MTL</code> file.</div></li><li><div class="item-name"><a class="fn" href="fn.load_obj_buf.html" title="fn tobj::load_obj_buf">load_<wbr>obj_<wbr>buf</a></div><div class="desc docblock-short">Load the various meshes in an <code>OBJ</code> buffer.</div></li></ul><h2 id="types" class="section-header">Type Aliases<a href="#types" class="anchor">§</a></h2><ul class="item-table"><li><div class="item-name"><a class="type" href="type.LoadResult.html" title="type tobj::LoadResult">Load<wbr>Result</a></div><div class="desc docblock-short">A <a href="https://doc.rust-lang.org/1.84.0/core/result/enum.Result.html" title="enum core::result::Result"><code>Result</code></a> containing all the models loaded from the file and any
|
||
materials from referenced material libraries. Or an error that occured while
|
||
loading.</div></li><li><div class="item-name"><a class="type" href="type.MTLLoadResult.html" title="type tobj::MTLLoadResult">MTLLoad<wbr>Result</a></div><div class="desc docblock-short">A <a href="https://doc.rust-lang.org/1.84.0/core/result/enum.Result.html" title="enum core::result::Result"><code>Result</code></a> containing all the materials loaded from the file and a map of
|
||
<code>MTL</code> name to index. Or an error that occured while loading.</div></li></ul></section></div></main></body></html> |