网格文件读写
本文主要介绍用OpenMesh对网格文件进行读写的操作。
相应的函数定义在名称空间OpenMesh::MeshIO中。
IOManager快速入门
文件读写的代码框架如下:
#include <OpenMesh/Core/IO/MeshIO.hh>
MyMesh mesh;
if (!OpenMesh::IO::read_mesh(mesh, "some input file"))
{
std::cerr << "read error\\n";
exit(1);
}
// do something with your mesh ...
if (!OpenMesh::IO::write_mesh(mesh, "some output file"))
{
std::cerr << "write error\\n";
exit(1);
}
注意
1. 如果通过静态链接使用OpenMesh,需要在应用程序中定义OM_STATIC_BUILD,以保证文件读写能够正确初始化;
2. IOManager根据文件的扩展名决定使用不同的reader/writer。
OpenMesh中的文件读写原理
通常,网格的reader和writer程序,都是根据其所支持的数据结构对相应的不同格式的网格文件进行读写。这种方式有一点不好,那就是无法对其不支持的其他数据结构或者文件格式进行读写操作的话,需要重复代码(不太理解)。
而OpenMesh中的IOManager提供了一个从特定数据到任意数据结构的接口,功能定义在reader/writer 和 importer/exporter模块中。如下图所示:
在读文件的过程中,任意格式的特定数据首先通过一个reader模块进行解释,然后数据通过特定的接口传到一个importer模块得到特定的数据结构。数据的写过程也是类似的。IOManager控制着整个过程,Reader/Writer模块对用户是不可见的,然后Importer/Exporter模块需要显式给定,因为需要关联到特定的数据结构。
下面一节将可以看出,这种特定数据和数据结构的完全分离,使得保证现有代码和在两个模块中扩展功能都变得尤其简单。
怎样扩展IOManager
扩展对新的文件格式的支持
要扩展对新的文件格式的支持,涉及到增加一个reader和writer模块。其中Reader模块为一个继承自OpenMesh::IO::BaseReader的类,部分经常需要定义的接口显示如下:
class BaseReader
{
public:
virtual std::string get_description() const = 0;
virtual std::string get_extensions() const = 0;
virtual std::string get_magic() const { return std::string(""); }
virtual bool read(std::istream& _is, BaseImporter& _bi) const = 0;
virtual bool read(const std::string& _filename, BaseImporter& _bi) const = 0;
...
};
根据文件扩展名和头文件信息,IOManager选择使用特定的Reader模块。Reader模块解析的格式和信息会通过继承自OpenMesh::IO::BaseImporter的类,传递到目标数据结构中。
Writer模块继承自OpenMesh::IO::BaseWriter,和reader模块的工作方式相同。
扩展对新的数据结构的支持
正如我们前面所见,Inporter模块从Reader模块中获得信息,Reader模块通过特定的接口传递信息:
class BaseImporter
{
public:
virtual void add_vertex (const OpenMesh::Vec3f&) {};
virtual void add_normal (const OpenMesh::Vec3f&) {};
virtual void add_texture (const OpenMesh::Vec2f&) {};
virtual void add_face (const FaceType&) {};
};
然后Impoter负责填充目标数据结构。从一个数据结构中导出信息比导入信息更加复杂一些,Writer模块需要能够遍历所有的向量、纹理坐标、面,因此一个Expoter需要提供这些迭代器接口:
class BaseExporter
{
public:
virtual void update() = 0;
virtual PVertexIter const_vertices_begin() = 0;
virtual PVertexIter const_vertices_end() = 0;
virtual PTexCoordIter const_texcoords_begin() = 0;
virtual PTexCoordIter const_texcoords_end() = 0;
virtual PIdxFaceIter const_idx_faces_begin() = 0;
virtual PIdxFaceIter const_idx_faces_end() = 0;
virtual PFaceIter const_set_faces_begin() = 0;
virtual PFaceIter const_set_faces_end() = 0;
virtual unsigned int n_faces() = 0;
virtual unsigned int n_vertices() = 0;
virtual unsigned int n_texcoords() = 0;
};
要让Expoter能够快速提取相关的信息,update()函数需要在函数BaseWriter::save()调用最开始调用,以保证其所提取的信息是最新的。
光有上面这些基础的介绍,要扩展IOManager是不太容易的,更多信息将在后续文章中给出。
暂无评论内容