40个版本 (18个稳定版)

4.0.2 2024年3月27日
4.0.0 2023年4月29日
3.2.5 2023年4月4日
3.2.4 2023年2月3日
0.0.9 2015年6月26日

#3游戏开发

Download history 4162/week @ 2024-05-01 4143/week @ 2024-05-08 4984/week @ 2024-05-15 4179/week @ 2024-05-22 5237/week @ 2024-05-29 3960/week @ 2024-06-05 4498/week @ 2024-06-12 4304/week @ 2024-06-19 4305/week @ 2024-06-26 3727/week @ 2024-07-03 4881/week @ 2024-07-10 5279/week @ 2024-07-17 4567/week @ 2024-07-24 4301/week @ 2024-07-31 6689/week @ 2024-08-07 5681/week @ 2024-08-14

21,992 每月下载量
用于 72 库(直接使用46个)

MIT 许可证

115KB
2K SLoC

tobj – Tiny OBJ Loader

Crate Build Status

受Syoyo的出色tinyobjloader启发。旨在成为加载OBJ文件的一个简单且轻量级的解决方案。

仅返回两个包含加载模型和材质的Vec

三角化

网格可以在运行时三角化,也可以保持原样。

仅支持可以简单转换为三角扇形的面。任意多边形可能不会按预期行为。最佳解决方案是在建模软件中将网格转换为仅由三角形组成。

可选 - 法线、纹理坐标和顶点颜色

假设所有网格至少具有位置,但法线、纹理坐标和顶点颜色是可选的。

如果没有找到法线、纹理坐标或顶点颜色,则相应的Vec将为空。

平面数据

值以浮点数的形式存储在平面Vec中。

例如,Meshpositions成员将包含[x, y, z, x, y, z, ...],您可以使用它来执行任何操作。

索引

索引也会被加载,并且可能会重用网格中已经存在的顶点,这些数据存储在 indices 成员中。

Mesh 包含每个顶点每个面的法线或纹理坐标时,可以通过 single_index 标志将位置重复以实现每个顶点每个面的效果。这可能会改变拓扑结构(即使它们的顶点在空间中仍然共享位置,面也可能变得不连接)。

默认情况下,会创建法线和纹理坐标的单独索引。这也保证了当后者被指定为每个顶点每个面时,Mesh 的拓扑结构不会改变。

材质

也支持标准 MTL 属性。任何未识别的参数都将存储在包含未识别参数及其值的键值对的 HashMap 中。

特性

  • ahash – 默认启用。在读取文件和合并顶点时使用 AHashMap 进行哈希。要禁用并使用较慢的 HashMap,请在 Cargo.toml 中取消设置默认特性。

    [dependencies.tobj]
    default-features = false
    
  • merging – 在导入期间添加了对在断开连接的面之间合并相同顶点位置的支持。

    警告:此功能使用 const generics,因此至少需要一个 beta 工具链才能构建。

  • reordering – 添加了对重新排列法线和纹理坐标索引的支持。

  • async – 添加了对从缓冲区异步加载 obj 文件以及异步材质加载器的支持。在不支持阻塞 IO 的环境中很有用(例如 WebAssembly)。

文档

可以在 这里 找到 Rust 文档。

安装

crate 添加为 Cargo.toml 中的依赖项,然后您就设置好了!

示例

打印网格示例(下面也有)从命令行加载 OBJ 文件,并打印出关于其面、顶点和材质的一些信息。

fn main() {
    let obj_file = std::env::args()
        .skip(1)
        .next()
        .expect("A .obj file to print is required");

    let (models, materials) =
        tobj::load_obj(
            &obj_file,
            &tobj::LoadOptions::default()
        )
        .expect("Failed to OBJ load file");

    // Note: If you don't mind missing the materials, you can generate a default.
    let materials = materials.expect("Failed to load MTL file");

    println!("Number of models          = {}", models.len());
    println!("Number of materials       = {}", materials.len());

    for (i, m) in models.iter().enumerate() {
        let mesh = &m.mesh;
        println!("");
        println!("model[{}].name             = \'{}\'", i, m.name);
        println!("model[{}].mesh.material_id = {:?}", i, mesh.material_id);

        println!(
            "model[{}].face_count       = {}",
            i,
            mesh.face_arities.len()
        );

        let mut next_face = 0;
        for face in 0..mesh.face_arities.len() {
            let end = next_face + mesh.face_arities[face] as usize;

            let face_indices = &mesh.indices[next_face..end];
            println!(" face[{}].indices          = {:?}", face, face_indices);

            if !mesh.texcoord_indices.is_empty() {
                let texcoord_face_indices = &mesh.texcoord_indices[next_face..end];
                println!(
                    " face[{}].texcoord_indices = {:?}",
                    face, texcoord_face_indices
                );
            }
            if !mesh.normal_indices.is_empty() {
                let normal_face_indices = &mesh.normal_indices[next_face..end];
                println!(
                    " face[{}].normal_indices   = {:?}",
                    face, normal_face_indices
                );
            }

            next_face = end;
        }

        // Normals and texture coordinates are also loaded, but not printed in
        // this example.
        println!(
            "model[{}].positions        = {}",
            i,
            mesh.positions.len() / 3
        );
        assert!(mesh.positions.len() % 3 == 0);

        for vtx in 0..mesh.positions.len() / 3 {
            println!(
                "              position[{}] = ({}, {}, {})",
                vtx,
                mesh.positions[3 * vtx],
                mesh.positions[3 * vtx + 1],
                mesh.positions[3 * vtx + 2]
            );
        }
    }

    for (i, m) in materials.iter().enumerate() {
        println!("material[{}].name = \'{}\'", i, m.name);
        println!(
            "    material.Ka = ({}, {}, {})",
            m.ambient[0], m.ambient[1], m.ambient[2]
        );
        println!(
            "    material.Kd = ({}, {}, {})",
            m.diffuse[0], m.diffuse[1], m.diffuse[2]
        );
        println!(
            "    material.Ks = ({}, {}, {})",
            m.specular[0], m.specular[1], m.specular[2]
        );
        println!("    material.Ns = {}", m.shininess);
        println!("    material.d = {}", m.dissolve);
        println!("    material.map_Ka = {}", m.ambient_texture);
        println!("    material.map_Kd = {}", m.diffuse_texture);
        println!("    material.map_Ks = {}", m.specular_texture);
        println!("    material.map_Ns = {}", m.shininess_texture);
        println!("    material.map_Bump = {}", m.normal_texture);
        println!("    material.map_d = {}", m.dissolve_texture);

        for (k, v) in &m.unknown_param {
            println!("    material.{} = {}", k, v);
        }
    }
}

依赖项

~0.6–1MB
~14K SLoC