【OpenMesh】使用网格的属性和特征

例子主要展示如何改变位置,法向量,颜色和纹理的数据类型。
在之前的指南中我们学习使用标准属性,通过调用适合的请求方法。不像自定义属性,用户通过传递数据类型到句柄来指定数据类型(比如,MyMesh::FPropHandleT< int>),标准属性的数据类型定义为网格特征。我们可以和特征一起定制和扩展网格数据结构。我们通过两方面做到这一点。

  • 改变位置(Position),法向量(Normal),颜色(Color),和纹理坐标(Texture coordinate不知道翻译对了没)的数据类型。
  • 扩展网格实体,包括顶点,面,边和Halfedge.

我们开始吧。每一个定制特性应该继承自默认特性。

struct MyTraits : OpenMesh::DefaultTraits

之前提到的,我们可以为基本数据类型改变基础数据结构 MyMesh::Point, MyMesh::Normal, MyMesh::Color, and MyMesh::TexCoord。我们可以使用提供的向量类或者我们使用其他类库提供的类。这里我们简单的替换Position和Normal的默认类型OpenMesh::Vec3f为OpenMesh::Vec3d

typedef OpenMesh::Vec3d Point;

typedef OpenMesh::Vec3d Normal;

(通常,Point和Normal向量最好用相同的标量类型,比如在这里使用double型。不然我们将要必须考虑向量类的实现。)
注意,这些设置覆盖父类的特征!正如我们通常继承DefaultTraits,让我们仔细看看。
实际上,OpenMesh::DefaultTraits仅仅是一个没有内容的类。它只是为Point,Normal,TexCoord和Color以及一个属性定义了类型,我们一直隐式地使用它们:

// HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );

属性PrevHalfedge是不同的,因为它没有控制属性。然而,它对网格类型的最终结果有个很大的影响,因为它在Halfedge结构中添加了额外的信息。影响有两点:
快速地访问前一个Halfedge
添加内存消耗(居然不是一个优点……)
使用这个特点取决于我们的需要。一种情况是我们需要访问前一个Halfedge非常便利,这是网格的成员变量函数add_face().当前一个Halfedge可用的时候,成员函数的执行时间迅速下降。通常我们希望有这个信息。但是为了节约内存,我们可以轻松的移除这个特性

// HalfedgeAttributes( OpenMesh::Attributes::None );

然后我们需要少于8Byte的空间存储一条边,这就节省很多了,通过欧拉方程可以知道V-E+F=2(1-g),对于一个三角形网格,g=0,边的数目几乎是三倍的顶点数,既E=3V。
完整的代码:

#include <iostream>

#include <typeinfo>

// --------------------

#include <OpenMesh/Core/IO/MeshIO.hh>

#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>

#include <OpenMesh/Core/Geometry/VectorT.hh>

#ifndef DOXY_IGNORE_THIS

// Define my personal traits

struct MyTraits : OpenMesh::DefaultTraits

{

// Let Point and Normal be a vector of doubles

typedef OpenMesh::Vec3d Point;

typedef OpenMesh::Vec3d Normal;

// Already defined in OpenMesh::DefaultTraits

// HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );



// Uncomment next line to disable attribute PrevHalfedge

// HalfedgeAttributes( OpenMesh::Attributes::None );

//

// or

//

// HalfedgeAttributes( 0 );

};

#endif

// Define my mesh with the new traits!

typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh;

// ------------------------------------------------------------------ main ----

int main(int argc, char **argv)

{

MyMesh mesh;

if (argc!=2)

{

std::cerr << "Usage: " << argv[0] << " <input>\n";

return 1;

}

// Just make sure that point element type is double

if ( typeid( OpenMesh::vector_traits<MyMesh::Point>::value_type ) 

!= typeid(double) )

{

std::cerr << "Ouch! ERROR! Data type is wrong!\n";

return 1;

}

// Make sure that normal element type is double

if ( typeid( OpenMesh::vector_traits<MyMesh::Normal>::value_type ) 

!= typeid(double) )

{

std::cerr << "Ouch! ERROR! Data type is wrong!\n";

return 1;

}

// Add vertex normals as default property (ref. previous tutorial)

mesh.request_vertex_normals();

// Add face normals as default property

mesh.request_face_normals();

// load a mesh

OpenMesh::IO::Options opt;

if ( ! OpenMesh::IO::read_mesh(mesh,argv[1], opt))

{

std::cerr << "Error loading mesh from file " << argv[1] << std::endl;

return 1;

}

// If the file did not provide vertex normals, then calculate them

if ( !opt.check( OpenMesh::IO::Options::VertexNormal ) &&

mesh.has_face_normals() && mesh.has_vertex_normals() )

{

// let the mesh update the normals

mesh.update_normals();

}

// move all vertices 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;

}



你可能感兴趣的:(open)