網格文件讀寫
本文主要介紹用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是不太容易的,更多信息將在後續文章中給出。