【OpenMesh】存储自定义属性

OpenMesh的专有OM格式允许存储和恢复自定义属性,以及标准的属性。
我们必须使用指定的自定义属性,像下面的代码显示的一样。
OpenMesh::VPropHandleT<float> vprop_float;
mesh.add_property(vprop_float, "vprop_float");
我们给顶点注册一个浮点属性,名为"vprop_float"。属性的名字需要符合一些规定,如果我们想要长久的使用。
  1. 最长256字符
  2. 前缀:v:,h:,e:,f:和m:
如果我们坚持这个规则我们都很好。此外,我们必须考虑,名称处理是区分大小写的。
为了实现自定义属性足够持久,我们必须在属性中设定持久标记。
mesh.property(vprop_float).set_persistent(true);
现在我们可以使用IO::mesh_write()写网格数据到文件中。在文件中,自定义属性添加在标准属性后面,包括名字和二进制大小。这两条信息会在文件读取的时候被再次处理。为了成功的保存自定义数据,网格必须用相同的名字注册属性名字(区分大小写)。另外,当读取数据,读取属性的字节数必须符合文件提供的字节数。如果OM读取器没有发现一个合适的属性名字,它将会跳过这个属性。如果字节数不匹配,恢复工作将会停止以及IO::read_mesh()会返回真。如果数据不能够恢复,因为合适的恢复方法不可用,std::logic_error()异常会被抛出。
当我们知道了行为,我们需要知道哪一种数据能够被恢复?没有更多的操作,只用使用名字属性和设定持久符号,我们可以存储下面的类型
  • bool,作为bit位存储
  • 其他的基础数据类型 long double,(unsigned)long和size_t
  • std::string 每个最高65535字节
  • OpenMesh::Vec[1,2,3,4,6][c,uc,s,us,i,ui,f,d]
延伸一些我们调用这些基础类型。很明显我们不能存储非基础的类型,这些是
  • point
  • structs/classes
  • 甚至更复杂的数据结构,比如容器的容器。
然而这里有方法存储自定义类型(我们不能存储std::string)。我们用一些简单的自定义数据来开始。比如,我们有个MyData结构
struct MyData
{
int ival;
double dval;
bool bval;
OpenMesh::Vec4f vec4fval;
};
这里我们保存int,bool,double,Vec4f,都是基础数据类型。然后我们需要在名字空间中的Open::IO模板数据结构OpenMesh::IO::binary<>
template <> struct binary<MyData>
注意:不要使用long double, (unsigned) long 和 size_t作为基础数据,因为32/64位架构之间的不兼容。
我们必须实现下列的静态成员和函数:
static const bool is_streamable = true;
static size_t size_of(void) 
static size_t size_of(const value_type&) 
static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
static size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
标志is_streamable必须设置为true。否则其他的数据根本不能存储。

size_of方法

自定义数据的大小可以是静态的,这意味着我们在编译时知道大小,或它的大小是动态的,这意味着我大小是在运行时已知的,我们必须提供这两个size_of()方法。
第一个声明为静态情况下,而第二个为动态情况。虽然静态情况下更简单,它不够直接。我们不能简单地使用sizeof()来确定数据的大小,因为它将返回在内存中的字节数(可能的32位对齐)。相反我们需要二进制大小,因此我们必须增加单一元素的结构。
return sizeof(int)+sizeof(double)+sizeof(bool)+sizeof(OpenMesh::Vec4f); 
实际上我们需要单一元素的向量的和,但在这种情况下我们确切知道结果的(4个float类型使16字节,这是32位对齐因此sizeof()返回想要的大小)。但是需要记住,当编写自定义二进制支持的时候,这是个潜在的错误。
第二个声明适用于动态情况下,自定义数据包含指针或引用。这个静态成员必须正确计数数据,通过解析指针/引用,如果这个数据也必须存储。在动态stetting,静态变量不能返回的大小,因此它必须返回IO::UnknownSize。
在这种情况下,动态变量仅仅返回通过调用静态变量大小,对于这两种情况大小是相同的。

存储和恢复

动态情况下的静态情况,我们必须考虑如何存储数据的策略。一种选择是存储数据的长度,然后存储数据本身。例如类型std::string是这种方式实现的。(我们首先存储长度在16bit(= > max.length65536),之后是字符。所以size_of()返回2byte再加上实际的长度。)因为MyData包含基础数据,我们可以实现必要方法存储和恢复数据,通过通过简单地将数据分解成基本类型使用预定义的存储/恢复方法:
static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{ 
size_t bytes;
bytes = IO::store( _os, _v.ival, _swap );
bytes += IO::store( _os, _v.dval, _swap );
bytes += IO::store( _os, _v.bval, _swap );
bytes += IO::store( _os, _v.vec4fval, _swap );
return _os.good() ? bytes : 0;
}
static size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
{ 
size_t bytes;
bytes = IO::restore( _is, _v.ival, _swap );
bytes += IO::restore( _is, _v.dval, _swap );
bytes += IO::restore( _is, _v.bval, _swap );
bytes += IO::restore( _is, _v.vec4fval, _swap );
return _is.good() ? bytes : 0;
}
这是非常重要的,存储/恢复方法正确地计算写入/读取字节和返回值。在返回错误的时候这两个函数必须返回0。
下面给出一个复杂的例子
typedef std::map< std::string, unsigned int > MyMap;
OpenMesh::MPropHandleT<MyMap> mprop_map;
在这种情况下,数据包含一个容器,包含一个从字符串到数字的映射。如果想要存储这些东西,我们需要想出一个方法解决映射顺序存储的问题。首先我们在映射中存储元素数。之后,因为我们就遍历整个映射并存储所有的key/value对。整个过程等同于size_of(),store()和restore()方法。比如size_of()方法像这样
static size_t size_of(void) { return UnknownSize; }
static size_t size_of(const value_type& _v) 
{ 
if (_v.empty())
return sizeof(unsigned int);

value_type::const_iterator it = _v.begin();
unsigned int N = _v.size();
size_t bytes = IO::size_of(N);
for(;it!=_v.end(); ++it)
{
bytes += IO::size_of( it->first );
bytes += IO::size_of( it->second );
}
return bytes;
}
实现store()和restore()的方法也依照这个模型。
给出的程序提供下列步骤
  1. 创建网格和生成立方体
  2. 添加几个自定义属性
  3. 填充测试数据
  4. 属性持久化
  5. 保存网格到文件中,名为"persistent-check.om"
  6. 清除网格
  7. 恢复网格
  8. 用测试数据检查内容一致性
因为这个例子有点比平时长,源文件是在几个文件中。主程序在persistence.cc,立方体生成器在generate_cube.hh,stats.hh提供简单的工具显示网格和属性信息,文件fill_props.hh提供测试数据,和introman.hh/.cc文件被fill_props.hh使用。其他的文件参照OpenMesh/Doc/Tutorial/09-persistence/。
#include <iostream>
#include <string>
#include <map>
// -------------------- OpenMesh
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
// -------------------- little helper
#include "generate_cube.hh"
#include "stats.hh"
#include "fill_props.hh"
// ----------------------------------------------------------------------------
// Set to 1 to use an PolyMesh type.
#define UsePolyMesh 1
// ----------------------------------------------------------------------------
using namespace OpenMesh;
// ----------------------------------------------------------------------------
typedef TriMesh_ArrayKernelT<> TriMesh;
typedef PolyMesh_ArrayKernelT<> PolyMesh;
#if UsePolyMesh
typedef PolyMesh Mesh;
#else
typedef TriMesh Mesh;
#endif
// ----------------------------------------------------------------------------
#ifndef DOXY_IGNORE_THIS
struct MyData
{
int ival;
double dval;
bool bval;
OpenMesh::Vec4f vec4fval;
MyData()
: ival(0), dval(0.0), bval(false)
{ }
MyData( const MyData& _cpy )
: ival(_cpy.ival), dval(_cpy.dval), bval(_cpy.bval), 
vec4fval(_cpy.vec4fval)
{ }
// ---------- assignment
MyData& operator = (const MyData& _rhs) 
{ 
ival = _rhs.ival; 
dval = _rhs.dval;
bval = _rhs.bval;
vec4fval = _rhs.vec4fval;
return *this;
}
MyData& operator = (int _rhs) { ival = _rhs; return *this; }
MyData& operator = (double _rhs) { dval = _rhs; return *this; }
MyData& operator = (bool _rhs) { bval = _rhs; return *this; }
MyData& operator = (const OpenMesh::Vec4f& _rhs) 
{ vec4fval = _rhs; return *this; }
// ---------- comparison
bool operator == (const MyData& _rhs) const
{ 
return ival == _rhs.ival
&& dval == _rhs.dval
&& bval == _rhs.bval
&& vec4fval == _rhs.vec4fval;
}
bool operator != (const MyData& _rhs) const { return !(*this == _rhs); }
};
#endif
// ----------------------------------------------------------------------------
typedef std::map< std::string, unsigned int > MyMap;
// ----------------------------------------------------------------------------
#ifndef DOXY_IGNORE_THIS
namespace OpenMesh {
namespace IO {
// support persistence for struct MyData
template <> struct binary<MyData>
{
typedef MyData value_type;
static const bool is_streamable = true;
// return binary size of the value
static size_t size_of(void) 
{ 
return sizeof(int)+sizeof(double)+sizeof(bool)+sizeof(OpenMesh::Vec4f); 
}
static size_t size_of(const value_type&) 
{ 
return size_of(); 
}
static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{ 
size_t bytes;
bytes = IO::store( _os, _v.ival, _swap );
bytes += IO::store( _os, _v.dval, _swap );
bytes += IO::store( _os, _v.bval, _swap );
bytes += IO::store( _os, _v.vec4fval, _swap );
return _os.good() ? bytes : 0;
}
static size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
{ 
size_t bytes;
bytes = IO::restore( _is, _v.ival, _swap );
bytes += IO::restore( _is, _v.dval, _swap );
bytes += IO::restore( _is, _v.bval, _swap );
bytes += IO::restore( _is, _v.vec4fval, _swap );
return _is.good() ? bytes : 0;
}
};
template <> struct binary< MyMap >
{
typedef MyMap value_type;
static const bool is_streamable = true;
// return generic binary size of self, if known
static size_t size_of(void) { return UnknownSize; }
// return binary size of the value
static size_t size_of(const value_type& _v) 
{ 
if (_v.empty())
return sizeof(unsigned int);

value_type::const_iterator it = _v.begin();
unsigned int N = _v.size();
size_t bytes = IO::size_of(N);
for(;it!=_v.end(); ++it)
{
bytes += IO::size_of( it->first );
bytes += IO::size_of( it->second );
}
return bytes;
}
static 
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{ 
size_t bytes = 0;
unsigned int N = _v.size();
value_type::const_iterator it = _v.begin();
bytes += IO::store( _os, N, _swap );
for (; it != _v.end() && _os.good(); ++it)
{
bytes += IO::store( _os, it->first, _swap );
bytes += IO::store( _os, it->second, _swap );
}
return _os.good() ? bytes : 0;
}
static 
size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
{ 
size_t bytes = 0;
unsigned int N = 0;
_v.clear();
bytes += IO::restore( _is, N, _swap );
value_type::iterator it = _v.begin();
std::string key;
size_t val;
for (size_t i=0; i<N && _is.good(); ++i)
{ 
bytes += IO::restore( _is, key, _swap );
bytes += IO::restore( _is, val, _swap );
_v[key] = val;
}
return _is.good() ? bytes : 0;
}
};
}
}
#endif
// ----------------------------------------------------------------------------
int main(void)
{
//
Mesh mesh;

// generate a geometry
generate_cube<Mesh>(mesh);
// should display 8 vertices, 18/12 edges, 12/6 faces (Tri/Poly)
mesh_stats(mesh);
// print out information about properties
mesh_property_stats(mesh);
std::cout << "Define some custom properties..\n";
OpenMesh::VPropHandleT<float> vprop_float;
OpenMesh::EPropHandleT<bool> eprop_bool;
OpenMesh::FPropHandleT<std::string> fprop_string;
OpenMesh::HPropHandleT<MyData> hprop_mydata;
OpenMesh::MPropHandleT<MyMap> mprop_map;

std::cout << ".. and registrate them at the mesh object.\n";
mesh.add_property(vprop_float, "vprop_float");
mesh.add_property(eprop_bool, "eprop_bool");
mesh.add_property(fprop_string, "fprop_string");
mesh.add_property(hprop_mydata, "hprop_mydata");
mesh.add_property(mprop_map, "mprop_map");
mesh_property_stats(mesh);
std::cout << "Now let's fill the props..\n";
fill_props(mesh, vprop_float);
fill_props(mesh, eprop_bool);
fill_props(mesh, fprop_string);
fill_props(mesh, hprop_mydata);
fill_props(mesh, mprop_map);
std::cout << "Check props..\n";
#define CHK_PROP( PH ) \
std::cout << " " << #PH << " " \
<< (fill_props(mesh, PH, true)?"ok\n":"error\n")
CHK_PROP(vprop_float);
CHK_PROP(eprop_bool);
CHK_PROP(fprop_string);
CHK_PROP(hprop_mydata);
CHK_PROP(mprop_map);
#undef CHK_PROP
std::cout << "Set persistent flag..\n";
#define SET_PERS( PH ) \
mesh.property(PH).set_persistent(true); \
std::cout << " " << #PH << " " \
<< (mesh.property(PH).persistent()?"ok\n":"failed!\n")
mesh.property(vprop_float).set_persistent(true);
std::cout << " vprop_float "
<< (mesh.property(vprop_float).persistent()?"ok\n":"failed!\n");

SET_PERS( eprop_bool );
SET_PERS( fprop_string );
SET_PERS( hprop_mydata );
mesh.mproperty(mprop_map).set_persistent(true);
std::cout << " mprop_map "
<< (mesh.mproperty(mprop_map).persistent()?"ok\n":"failed!\n");

std::cout << "Write mesh..";
if (IO::write_mesh( mesh, "persistence-check.om" ))
std::cout << " ok\n";
else
{
std::cout << " failed\n";
return 1;
}
std::cout << "Clear mesh\n";
mesh.clear();
mesh_stats(mesh, " ");

std::cout << "Read back mesh..";
try
{
if (IO::read_mesh( mesh, "persistence-check.om" ))
std::cout << " ok\n";
else
{
std::cout << " failed!\n";
return 1;
}
mesh_stats(mesh, " ");
}
catch( std::exception &x )
{
std::cerr << x.what() << std::endl;
return 1;
}
std::cout << "Check props..\n";
#define CHK_PROP( PH ) \
std::cout << " " << #PH << " " \
<< (fill_props(mesh, PH, true)?"ok\n":"error\n")
CHK_PROP(vprop_float);
CHK_PROP(eprop_bool);
CHK_PROP(fprop_string);
CHK_PROP(hprop_mydata);
CHK_PROP(mprop_map);
#undef CHK_PROP
return 0;
}
// end of file
// ============================================================================

你可能感兴趣的:(【OpenMesh】存储自定义属性)