#pragma once #include #include #include "vde/foundation/math_types.h" namespace vde::foundation { /** * @brief OBJ 文件解析结果数据结构 * * 存储从 Wavefront OBJ 文件读取的完整几何数据, * 包括顶点位置、纹理坐标、法线、面信息和材质分组。 * * 内部索引均为 0-based,与文件中的 1-based 索引不同。 * * @ingroup foundation */ struct ObjMeshData { /// 顶点位置数组 std::vector vertices; /// 纹理坐标数组(可能为空) std::vector texcoords; /// 顶点法线数组(可能为空) std::vector normals; /** * @brief 每个面的顶点索引列表(0-based) * * 无论文件中面的格式如何(纯顶点、"v/vt"、"v/vt/vn"), * 此字段始终被填充。faces[i] 存储第 i 个面的所有顶点索引。 */ std::vector> faces; /** * @brief 每个面的纹理坐标索引列表(0-based) * * 与 faces 数组长度相同;若无纹理数据则为空。 */ std::vector> face_texcoords; /** * @brief 每个面的法线索引列表(0-based) * * 与 faces 数组长度相同;若无法线数据则为空。 */ std::vector> face_normals; /** * @brief 每个面对应的材质名称 * * face_materials[i] 给出第 i 个面使用的材质名。 * 材质名来源于 usemtl 指令,在定义后所有后续面使用该材质。 */ std::vector face_materials; /// 是否有纹理坐标数据 [[nodiscard]] bool has_texcoords() const { return !texcoords.empty(); } }; /** * @brief 读取 Wavefront OBJ 文件 * * 解析 ASCII OBJ 文件,支持以下 OBJ 特性: * - v(顶点)、vt(纹理坐标)、vn(法线) * - f(面):支持三角面和任意多边形面 * - usemtl(材质名称)、g(组名)、o(对象名) * - # 注释行 * * @param filepath OBJ 文件路径 * @return 解析后的 ObjMeshData * * @throws std::runtime_error 若文件无法打开或格式错误 * * @note 不解析 MTL 材质文件,仅记录 usemtl 指令中的材质名称 * @note 面索引在内部转换为 0-based;若文件中使用相对索引(负值),自动转换为正向 * * @code{.cpp} * auto data = read_obj("model.obj"); * std::cout << "Vertices: " << data.vertices.size() << "\n"; * std::cout << "Faces: " << data.faces.size() << "\n"; * @endcode * * @see write_obj, ObjMeshData * @ingroup foundation */ ObjMeshData read_obj(const std::string& filepath); /** * @brief 写入 Wavefront OBJ 文件 * * 将 ObjMeshData 以 ASCII OBJ 格式写出,包含顶点、纹理坐标、 * 法线和面信息。材质名称通过注释嵌入。 * * @param filepath 输出文件路径 * @param data 要写出的网格数据 * * @note 输出的面索引为 1-based(OBJ 标准) * * @code{.cpp} * ObjMeshData data; * // ... 填充 data ... * write_obj("output.obj", data); * @endcode * * @see read_obj * @ingroup foundation */ void write_obj(const std::string& filepath, const ObjMeshData& data); } // namespace vde::foundation