OpenMesh的进阶用法

OpenMesh的进阶用法

  OpenMesh的中文教程已经发布很久, OpenMesh作为半边数据结构的封装库, 在CG编程中还是比较好用的, 但是实际上只用入门教程中的那种使用方法/流程, 感觉还是比较繁琐, 尤其是对于稍微大一点的程序, 不仅容易使程序结构混乱, 而且还大大降低了编程效率, 所以本文记录和介绍一种OpenMesh的进阶用法.

  先看入门教程中涉及的程序流程:

// 先定义一个MyMesh类
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
...
// 定义个MyMesh类型的变量
MyMesh mesh;
...
// 当要用到元素循环器/迭代器时, 代码通常是这样的
for (MyMesh::FaceIter f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it)
{  
    for (MyMesh::FaceVertexIter fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) 
    {
        // do something with fv_it
    }
}
...
// 访问一个点位置数据或法向
auto pos = mesh.point(vh); // vh是某个顶点的句柄
auto pos = mesh.normal(vh); // vh是某个顶点的句柄
...
// 访问一个函数, 也要通过.操作
bool flag = mesh.is_boundary(fh);// vh是某个面的句柄

  可以看到, 这些过程都很繁琐, 而且程序长了也不好组织, 接下来就想到使用C++中的面向对象方法, 定义个自定义的派生类, 基类就是我们定义的这个MyMesh(这里重命名为BaseMesh)类.

  上面所述是方向, 举一个简单的源码例子, 看看其中的注释应该就清楚了.

CMesh头文件Mesh.h

#pragma once
//! 包含OpenMesh库的头文件
#include <OpenMesh\\Core\\IO\\MeshIO.hh>
#include <OpenMesh\\Core\\Mesh\\PolyMesh_ArrayKernelT.hh>

//! 常用类的简短定义
typedef OpenMesh::Vec3f vec3;

//! BaseMesh的traits定义
struct Traits :public OpenMesh::DefaultTraits {
public:
    VertexTraits{
        Point cog; // center of gravity重心
    };
};

//! BaseMesh的定义
typedef OpenMesh::PolyMesh_ArrayKernelT<Traits> BaseMesh;


class CMesh : public BaseMesh // 派生类继承自BaseMesh
{
public:
    //! 构造函数, 析构函数
    CMesh(); // 默认构造函数
    CMesh(const char *filename); // 自定义构造函数, 给定文件名, 读入数据
    ~CMesh();

    //! 接口
public:
    void doSmoothing(); // 进行一次网格平滑操作

    //! 私有方法
private:
    void computeCog(); // 计算网格中每个点的重心, 供doSmoothing()调用
};

CMesh实现源文件Mesh.cpp

#include "Mesh.h"



CMesh::CMesh()
{
}

CMesh::CMesh(const char * filename)
{
    // 没错, 这样也行!!! (不考虑异常处理)
    OpenMesh::IO::read_mesh(*this, filename);
}


CMesh::~CMesh()
{
}

void CMesh::doSmoothing()
{
    //! 计算所有点的重心
    computeCog(); 
    //! 将所有的点, 移动到其重心上
    VertexIter v_it, v_itend = vertices_end(); /* 访问迭代器, 不需要再通过MyMesh::VertexIter的方式
                                                  访问函数, 也不需要再通过mesh.vertices_end()的方式了 */
    for (v_it = vertices_begin(); v_it != v_itend; ++v_it) {
        if(!is_boundary(*v_it)) // 边界上的点无需移动, 访问函数的方式更简单了
            set_point(*v_it, data(*v_it).cog); /* 入门教程中的对应语句应该是下面这样, 这里简单多了
                                              mesh.set_point(*v_it, mesh.data(*v_it).cog); */
    }
}

void CMesh::computeCog()
{
    VertexIter v_it, v_itend = vertices_end();  
    VertexVertexIter vv_it; // 而不是MyMesh::VertexVertexIter的繁琐方式
    vec3 cog; // 临时记录重心
    int valance; // 统计当前点的相邻点数
    for (v_it = vertices_begin(); v_it != v_itend; ++v_it) {
        if (!is_boundary(*v_it)) { // 边界上的点不动, 所以就不用计算了
            cog = vec3(0.0);
            valance = 0;
            for (vv_it = vv_iter(*v_it); vv_it.is_valid(); ++vv_it) {
                cog += point(*vv_it);
                valance++;
            }
            cog /= float(valance);
            data(*v_it).cog = cog;
        }
    }
}

主函数(引用)文件man.cpp

#include <iostream>
#include "Mesh.h" // 包含派生类定义头文件
using namespace std;

int main() 
{
    CMesh mesh("toy.obj");
    cout << "读入模型信息:" << endl;
    cout << "  顶点数:  " << mesh.n_vertices() << endl;
    cout << "  边  数:  " << mesh.n_edges() << endl;
    cout << "  面片数:  " << mesh.n_faces() << endl;
    cout << "平滑操作:" << endl;
    mesh.doSmoothing(); // 进行一次平滑
    cout << "  平滑操作完成" << endl;
    return EXIT_SUCCESS;
}

程序执行输出结果:

读入模型信息:
  顶点数:  74
  边  数:  133
  面片数:  60
平滑操作:
  平滑操作完成
请按任意键继续. . .

  上面只是一个示例程序, 但是个人觉得, 通过这种方式, 程序真的清爽了很多. 上面的输出结果也只能证明程序读入文件是成功了, 而且doSmoothing()函数也运行了, 但是平滑操作是否正确, 就没有进行检查和进一步验证了, 但是主要的几行代码和注释已经能够涵盖OpenMesh进阶用法的核心.

© 版权声明
THE END
喜欢就支持一下吧
点赞420 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容