使用mesh属性和特征
本例中,我们将演示如何修改位置、法线、颜色、纹理坐标等标准属性的数据类型。前一节中,我们已经学习了通过调用合适的request方法使用标准属性。与自定义属性不同,用户可以指定数据类型,并传递给句柄给mesh,而标准属性中,数据类型通过所谓的特征(traits)结构定义。通过特征结构traits,我们可以自定义并扩展mesh的数据结构,实现这一目的,我们需要修改两个重要的特性:
1.修改位置、法线、颜色及纹理坐标的数据类型
2.扩展mesh实体的节点、表面、边缘及半边
开始吧。
每一个自定义特征,都应该继承自缺省特征。
struct MyTraits: OpenMesh::DefaultTraits
正如前面提到的,我们可以把诸如MyMesh::Point, MyMesh::Normal, MyMesh::Color, 及 MyMesh::TexCoord等这些标准属性的基本类型都改掉。改成OpenMesh提供的向量(vector)类,甚至用另外一个库的其他类型都可以。这里,我们简单地将位置和法线的数据类型,从缺省的OpenMesh::Vec3f(在 OpenMesh::DefaultTraits中定义)类型修改为OpenMesh::Vec3d类型。
typedef OpenMesh::Vec3dPoint;
typedef OpenMesh::Vec3dNormal;
(常规的,位置和法线最好是一样的类型,否则,我们就不得不进行大量的,依赖于vector类的,类型转换)
注意这些设置将覆盖其traits父类中的对应元素!既然我们一般都从DefaultTraits类中派生我们自己的traits,那么,现在就看看DefaultTraits是什么样:
实际上,OpenMesh::DefaultTraits仅仅是一个空的结构。它仅定义了位置、法线、纹理坐标和颜色的类型,还有一个我们需要显式调用的属性。
//HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );
PrevHalfedge 属性有所不同,它不控制一个属性,但他仍然对mesh数据有巨大影响,它在halfedge结构中添加额外的信息,但其对下列因素的影响是双倍的:
1.快速访问前一个半边
2.增加内存开销
使用这个特性,取决于我们的具体需求。一种情况是前一个半边需要经常访问的,如频繁调用add_face()函数,使用这个属性将使其执行时间将会显著下降。但如果不是必要的,我们一般不用它,这样可以降低内存开销。
//HalfedgeAttributes( OpenMesh::Attributes::None );
Then we need 8bytes less per edge, which can be quite a lot as one can derive from the Eulerformula ( ), that for aregular triangle meshes with genus the number ofedges is approximatelythree times the number of vertices : .(译者注:不太懂这段说什么,不翻译了)
完整代码如下:
#include
#include
//--------------------
#include
#include
#include
#ifndefDOXY_IGNORE_THIS
// Define mypersonal traits
struct MyTraits: OpenMesh::DefaultTraits
{
// Let Point andNormal be a vector of doubles
typedef OpenMesh::Vec3dPoint;
typedef OpenMesh::Vec3dNormal;
// Alreadydefined in OpenMesh::DefaultTraits
//HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );
// Uncomment nextline to disable attribute PrevHalfedge
//HalfedgeAttributes( OpenMesh::Attributes::None );
//
// or
//
//HalfedgeAttributes( 0 );
};
#endif
// Define my meshwith the new traits!
typedef OpenMesh::TriMesh_ArrayKernelT
//------------------------------------------------------------------ main ----
int main(int argc, char **argv)
{
MyMesh mesh;
if (argc!=2)
{
std::cerr <<"Usage:"<< argv[0] << " \n";
return 1;
}
// Just make surethat point element type is double
if ( typeid( OpenMesh::vector_traits
!= typeid(double) )
{
std::cerr <<"Ouch!ERROR! Data type is wrong!\n";
return 1;
}
// Make sure thatnormal element type is double
if ( typeid( OpenMesh::vector_traits
!= typeid(double) )
{
std::cerr <<"Ouch!ERROR! Data type is wrong!\n";
return 1;
}
// Add vertexnormals as default property (ref. previous tutorial)
mesh.request_vertex_normals();
// Add facenormals as default property
mesh.request_face_normals();
// load a mesh
OpenMesh::IO::Optionsopt;
if ( ! OpenMesh::IO::read_mesh(mesh,argv[1],opt))
{
std::cerr <<"Errorloading mesh from file " << argv[1] << std::endl;
return 1;
}
// If the filedid not provide vertex normals, then calculate them
if ( !opt.check( OpenMesh::IO::Options::VertexNormal) &&
mesh.has_face_normals()&& mesh.has_vertex_normals() )
{
// let the meshupdate the normals
mesh.update_normals();
}
// move allvertices one unit length along it's normal direction
for(MyMesh::VertexIter v_it = mesh.vertices_begin();
v_it !=mesh.vertices_end(); ++v_it)
{
std::cout <<"Vertex#"<< *v_it << ": " << mesh.point( *v_it );
mesh.set_point(*v_it, mesh.point(*v_it)+mesh.normal(*v_it) );
std::cout <<" moved to"<< mesh.point( *v_it ) << std::endl;
}
return 0;
}