OpenMesh基本用法

OpenMesh 是一个轻量级,易上手的Mesh网处理库(尤其相对于CGAL这种重型库),支持多边形网格,总之能胜任大多数轻量级任务。

整理一下它的基本用法:

准备工作

1
2
3
4
5
6
7
8
9
10
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>

struct MyTraits : public OpenMesh::DefaultTraits
{
typedef OpenMesh::Vec3d Point; // use double-values points
typedef OpenMesh::Vec3d Normal; // use double-values normal
};
typedef OpenMesh::PolyMesh_ArrayKernelT<MyTraits> MyMesh;

注意:

  • 我这里用的是PolyMesh_ArrayKernel,如果你的数据是三角网的话,可以用TriMesh_ArrayKernelT
  • TriMesh是特殊的PolyMesh,所以PolyMesh Kernel 可以读三角网,但是用不了某些TriMesh特有的函数,TriMesh Kernel 读不了多边形网
  • 我更改了OpenMesh的DefaultTrait,将 point 和 normal 的数据类型改成了 double,默认是 float
  • PropertyManager.hh 是用来增加属性的,后面会用到

Mesh读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Read Mesh
OpenMesh::IO::read_mesh(mesh, path);

// Write Mesh
OpenMesh::IO::write_mesh(mesh, path);

// Read with face color
mesh.request_face_colors();
OpenMesh::IO::Options ropt;
ropt += OpenMesh::IO::Options::FaceColor;
OpenMesh::IO::read_mesh(mesh, path, ropt);

// Write with face color
OpenMesh::IO::Options wopt;
wopt += OpenMesh::IO::Options::FaceColor;
OpenMesh::IO::write_mesh(mesh, path, wopt);

Mesh属性

  • 标准属性

    例如 face color、face normal、vertex normal、halfedge texcoords2D等等,在使用标准属性之前一定要先申请:

    1
    2
    3
    4
    mesh.request_face_colors();
    mesh.request_face_normals();
    mesh.request_vertex_normals();
    mesh.request_halfedge_texcoords2D();

    详见 standard properties

  • 自定义属性

    例如对每个面增加一个Plane_3的属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Make property
    auto f_plane = OpenMesh::getOrMakeProperty<OpenMesh::FaceHandle, Plane_3>(mesh, "f_plane");

    // Assign property
    f_plane[f_h] = plane;

    // Get property
    auto f_plane = OM::getProperty<OM::FaceHandle, WSHCGAL::Plane_3>(mesh, "f_plane");

    // Visit property
    Plane_3 plane = f_plane[f_h];

    详见 Using (custom) properties

Mesh数据结构访问

网上很多例子都没有考虑OpenMesh支持的C++11特性,下面介绍一下一些基本用法,一定要学会举一反三:

  • 遍历Mesh中所有的面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    for (const auto& f_h : mesh.faces())
    {
    std::cout << "face id = " << f_h.idx() << std::endl;
    std::cout << "face valence = " << mesh.valence(f_h) << std::endl;
    std::cout << "face normal = " << mesh.normal(f_h) << std::endl;
    std::cout << "face color = " << mesh.color(f_h) << std::endl;
    std::cout << "face centroid = " << mesh.calc_face_centroid(f_h) << std::endl;
    // std::cout << "face area = " << mesh.calc_face_area(f_h) << std::endl;
    }

    注意:

    1. 这里的 auto 类型其实是 OpenMesh::SmartFaceHandle。handle 是什么?只可意会: )
    2. 计算 face area 为什么注释掉了,因为这个函数是TriMesh特有的函数。为什么PolyMesh不给算面积?可能大多人认为计算面积很简单,其实还真是,不管是凸还是非凸多边形,都可以用这种算法计算:计算任意多边形面积,不知道OpenMesh为什么不实现一下
  • 举一反三 - > 遍历Mesh中所有的点、边、半边

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    for (const auto& v_h : mesh.vertices())
    {
    std::cout << "vertex id = " << v_h.idx() << std::endl;
    std::cout << "vertex normal = " << mesh.normal(v_h) << std::endl;
    }

    for (const auto& e_h : mesh.edges())
    {
    std::cout << "edge id = " << e_h.idx() << std::endl;
    std::cout << "edge length = " << mesh.calc_edge_length(e_h) << std::endl;
    }

    for (const auto& h_h : mesh.halfedges())
    {
    std::cout << "halfedge id = " << h_h.idx() << std::endl;
    std::cout << "halfedge length = " << mesh.calc_edge_length(h_h) << std::endl;
    }
  • 遍历某个面的所有顶点

    1
    2
    3
    4
    for (const auto& v_h : mesh.fv_range(f_h))
    {
    std::cout << "vertex id = " << v_h.idx() << std::endl;
    }
  • 举一反三 - > 遍历某个顶点周围的所有面、遍历面内的所有边和半边

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    for (const auto& f_h : mesh.vf_range(v_h))
    {
    std::cout << "face id = " << f_h.idx() << std::endl;
    }

    for (const auto& e_h : mesh.fe_range(f_h))
    {
    std::cout << "edge id = " << e_h.idx() << std::endl;
    }

    for (const auto& h_h : mesh.fh_range(f_h))
    {
    std::cout << "halfedge id = " << h_h.idx() << std::endl;
    }

完整示例

创建一个立方体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>

// use DefaultTraits
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;

int main()
{
MyMesh mesh;
// generate vertices
MyMesh::VertexHandle vhandle[8];
vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
vhandle[1] = mesh.add_vertex(MyMesh::Point(1, -1, 1));
vhandle[2] = mesh.add_vertex(MyMesh::Point(1, 1, 1));
vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
vhandle[5] = mesh.add_vertex(MyMesh::Point(1, -1, -1));
vhandle[6] = mesh.add_vertex(MyMesh::Point(1, 1, -1));
vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1));

// generate (quadrilateral) faces
std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);

face_vhandles.clear();
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[4]);
face_vhandles.push_back(vhandle[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[6]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[7]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);

if (!OpenMesh::IO::write_mesh(mesh, "output.off"))
{
std::cerr << "Cannot write mesh to file 'output.off'" << std::endl;
return 1;
}
return 0;
}