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
暂无评论内容