开源网格划分软件-OpenMesh 二次开发教程 (4)高级主题

本章深入探讨 OpenMesh 的高级功能,包括使用标准和自定义属性、自定义网格特性、网格扩展以及删除几何元素。这些功能使开发者能够实现复杂的网格处理任务,例如动态数据管理、自定义网格行为和拓扑修改。OpenMesh 是一个基于半边数据结构的 C++ 库,广泛应用于计算机图形学和几何处理。本章通过详细的说明和代码示例,帮助开发者掌握这些高级技术。

4.1 使用标准属性

OpenMesh 提供了多种标准属性(standard properties),这些属性可以附加到网格元素(如顶点、面、边和半边)上,用于存储常见数据,如颜色、法线、位置、状态和纹理坐标。除了位置属性(始终可用)外,其他属性需要通过特定的请求函数启用,并在不再需要时释放,以优化内存使用。

4.1.1 标准属性的类型和函数

以下表格总结了 OpenMesh 的标准属性及其相关函数:

属性顶点半边请求函数释放函数检查函数
颜色XXXrequest_vertex_colors(), request_face_colors(), request_edge_colors()release_vertex_colors(), release_face_colors(), release_edge_colors()has_vertex_colors(), has_face_colors(), has_edge_colors()
法线XXXrequest_vertex_normals(), request_face_normals(), request_halfedge_normals()release_vertex_normals(), release_face_normals(), release_halfedge_normals()has_vertex_normals(), has_face_normals(), has_halfedge_normals()
位置X(始终可用,无需请求)(不可移除)-
状态XXXXrequest_vertex_status(), request_face_status(), request_edge_status(), request_halfedge_status()release_vertex_status(), release_face_status(), release_edge_status(), release_halfedge_status()has_vertex_status(), has_face_status(), has_edge_status(), has_halfedge_status()
纹理坐标XXrequest_vertex_texcoords1D(), request_vertex_texcoords2D(), request_vertex_texcoords3D(), request_halfedge_texcoords1D(), request_halfedge_texcoords2D(), request_halfedge_texcoords3D()release_vertex_texcoords1D(), release_vertex_texcoords2D(), release_vertex_texcoords3D(), release_halfedge_texcoords1D(), release_halfedge_texcoords2D(), release_halfedge_texcoords3D()has_vertex_texcoords1D(), has_vertex_texcoords2D(), has_vertex_texcoords3D(), has_halfedge_texcoords1D(), has_halfedge_texcoords2D(), has_halfedge_texcoords3D()
纹理索引Xrequest_face_texture_index()release_face_texture_index()has_face_texture_index()
  • 动态请求:共 9 种属性可动态请求。
  • 参考计数器:请求属性时计数器增加,释放时减少,计数为 0 时属性移除。
  • 位置属性:始终可用,无法移除。
  • 状态属性:用于标记删除状态,详见 4.5 删除几何元素

4.1.2 使用标准属性的示例

以下示例展示如何请求和使用顶点法线和面颜色:

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

typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;

int main() {
    MyMesh mesh;

    // 请求顶点法线和面颜色
    mesh.request_vertex_normals();
    mesh.request_face_colors();

    // 添加顶点和面(示例)
    MyMesh::VertexHandle vh = mesh.add_vertex(MyMesh::Point(0, 0, 0));
    std::vector<MyMesh::VertexHandle> face_vhandles;
    face_vhandles.push_back(mesh.add_vertex(MyMesh::Point(1, 0, 0)));
    face_vhandles.push_back(mesh.add_vertex(MyMesh::Point(0, 1, 0)));
    face_vhandles.push_back(mesh.add_vertex(MyMesh::Point(0, 0, 1)));
    MyMesh::FaceHandle fh = mesh.add_face(face_vhandles);

    // 设置顶点法线
    mesh.set_normal(vh, MyMesh::Normal(0, 0, 1));

    // 设置面颜色
    mesh.set_color(fh, MyMesh::Color(1.0f, 0.0f, 0.0f)); // 红色

    // 获取并打印
    MyMesh::Normal n = mesh.normal(vh);
    MyMesh::Color c = mesh.color(fh);
    std::cout << "顶点法线: " << n << std::endl;
    std::cout << "面颜色: " << c << std::endl;

    // 释放属性
    mesh.release_vertex_normals();
    mesh.release_face_colors();

    return 0;
}

注意事项

  • 确保在使用属性前调用请求函数,否则会导致未定义行为。
  • 使用完毕后释放属性以减少内存占用。
  • 状态属性在删除几何元素时特别重要。

4.2 使用自定义属性

自定义属性(custom properties)允许开发者为网格元素动态附加任意数据,例如顶点的温度或面的权重。OpenMesh 提供了 OpenMesh::PropertyManager 类来简化属性的创建和管理。

4.2.1 自定义属性的类型

自定义属性可以附加到以下网格元素:

  • VProp:顶点属性
  • HProp:半边属性
  • EProp:边属性
  • FProp:面属性
  • MProp:网格属性

4.2.2 创建和使用自定义属性

自定义属性可以是临时的(作用域结束时移除)或命名的(持久存在)。以下是示例:

  • 临时顶点属性(存储温度):

    auto temperature = OpenMesh::VProp<double>(mesh);
    MyMesh::VertexHandle vh = mesh.add_vertex(MyMesh::Point(0, 0, 0));
    temperature[vh] = 1.0; // 设置温度
    std::cout << "顶点温度: " << temperature[vh] << std::endl;
    
  • 命名网格属性(存储描述):

    auto desc = OpenMesh::MProp<std::string>(mesh, "description");
    *desc = "这是一个非常棒的网格!";
    std::cout << "网格描述: " << *desc << std::endl;
    

4.2.3 自定义属性的生命周期

  • 临时属性:在 PropertyManager 对象超出作用域时自动移除。
  • 命名属性:通过名称持久存储,可通过 mesh.hasProperty("name") 检查是否存在。

4.2.4 低级别 API

开发者也可以使用低级别 API 管理属性,例如:

  • add_property():添加属性
  • get_property():获取属性
  • remove_property():移除属性
  • property():访问属性值

示例(低级别 API):

OpenMesh::VPropHandleT<double> temp_handle;
mesh.add_property(temp_handle, "temperature");
mesh.property(temp_handle, vh) = 1.0;

注意事项

  • 命名属性适合长期存储,临时属性适合短期计算。
  • 使用 PropertyManager 比低级别 API 更简洁,推荐优先使用。

4.3 网格特性和属性

网格特性(traits)允许开发者自定义网格元素的数据类型和附加属性。默认情况下,OpenMesh 使用 OpenMesh::DefaultTraits,定义了点(Point)、法线(Normal)等的数据类型。

4.3.1 自定义数据类型

可以通过派生 OpenMesh::DefaultTraits 来更改默认数据类型。例如,将点和法线改为双精度(double):

struct MyTraits : public OpenMesh::DefaultTraits {
    typedef OpenMesh::Vec3d Point; // 双精度点
    typedef OpenMesh::Vec3d Normal; // 双精度法线
};

typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh;

4.3.2 禁用 PrevHalfedge 属性

PrevHalfedge 属性加速半边遍历,但增加内存消耗。可以通过禁用它来节省内存:

struct MyTraits : public OpenMesh::DefaultTraits {
    HalfedgeAttributes(OpenMesh::Attributes::None); // 禁用 PrevHalfedge
};

内存节省:对于三角形网格(genus $g=0$),边数 $E \approx 3 \cdot V$,禁用 PrevHalfedge 可为每条边节省约 8 字节内存。

4.4 扩展网格特性

通过扩展网格特性,开发者可以为网格元素添加自定义数据成员和方法。这是一种静态配置方式,适合需要固定附加数据的场景。

4.4.1 示例:为顶点添加重心

以下示例为顶点添加一个重心(cog_)成员,用于平滑操作:

struct MyVertexTraits {
    OpenMesh::Vec3f cog_;
    void set_cog(const OpenMesh::Vec3f& _cog) { cog_ = _cog; }
    OpenMesh::Vec3f cog() const { return cog_; }
};

struct MyTraits : public OpenMesh::DefaultTraits {
    typedef MyVertexTraits VertexTraits;
};

typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh;

使用示例:

MyMesh mesh;
MyMesh::VertexHandle vh = mesh.add_vertex(MyMesh::Point(0, 0, 0));
mesh.data(vh).set_cog(OpenMesh::Vec3f(1, 1, 1));
std::cout << "顶点重心: " << mesh.data(vh).cog() << std::endl;

注意事项

  • 特性是静态的,无法在运行时更改。
  • 适合需要固定附加数据的场景,如算法特定的数据结构。

4.5 删除几何元素

删除几何元素(如顶点、边、面)需要先请求状态属性,标记元素为已删除,然后执行垃圾回收以移除它们。

4.5.1 请求状态属性

在删除操作前,必须请求状态属性:

  • 面:mesh.request_face_status()
  • 边:mesh.request_edge_status()
  • 顶点:mesh.request_vertex_status()

4.5.2 标记和删除

使用 delete_* 方法标记元素为已删除:

  • 删除面:mesh.delete_face(fh, false)
  • 删除顶点:mesh.delete_vertex(vh, false)

第二个参数决定是否自动删除孤立顶点(true 表示自动删除)。

4.5.3 垃圾回收

调用 mesh.garbage_collection() 移除标记为删除的元素:

mesh.garbage_collection();

4.5.4 示例:删除立方体的部分面

以下示例从立方体中删除除一个面外的所有面:

#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <vector>

typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;

int main() {
    MyMesh mesh;

    // 添加立方体顶点和面(示例)
    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));

    std::vector<MyMesh::VertexHandle> face_vhandles;
    std::vector<MyMesh::FaceHandle> fhandle;

    // 添加 6 个面
    face_vhandles = {vhandle[0], vhandle[1], vhandle[2], vhandle[3]};
    fhandle.push_back(mesh.add_face(face_vhandles));
    face_vhandles = {vhandle[4], vhandle[7], vhandle[6], vhandle[5]};
    fhandle.push_back(mesh.add_face(face_vhandles));
    face_vhandles = {vhandle[0], vhandle[3], vhandle[7], vhandle[4]};
    fhandle.push_back(mesh.add_face(face_vhandles));
    face_vhandles = {vhandle[1], vhandle[5], vhandle[6], vhandle[2]};
    fhandle.push_back(mesh.add_face(face_vhandles));
    face_vhandles = {vhandle[3], vhandle[2], vhandle[6], vhandle[7]};
    fhandle.push_back(mesh.add_face(face_vhandles));
    face_vhandles = {vhandle[0], vhandle[4], vhandle[5], vhandle[1]};
    fhandle.push_back(mesh.add_face(face_vhandles));

    // 请求状态属性
    mesh.request_face_status();
    mesh.request_vertex_status();
    mesh.request_edge_status();

    // 删除除 fhandle[1] 外的所有面
    for (size_t i = 0; i < fhandle.size(); ++i) {
        if (i != 1) {
            mesh.delete_face(fhandle[i], false);
        }
    }

    // 执行垃圾回收
    mesh.garbage_collection();

    return 0;
}

注意事项

  • 确保请求状态属性,否则删除操作会失败。
  • 垃圾回收是必需的,否则标记为删除的元素仍占用内存。
  • 半边无法直接删除,随父边删除。

4.6 总结

本章详细介绍了 OpenMesh 的高级功能,包括标准和自定义属性的使用、网格特性的自定义、网格扩展以及几何元素的删除。这些功能为开发者提供了灵活的工具,用于实现复杂的网格处理任务。通过提供的代码示例,开发者可以快速掌握这些技术,并将其应用于实际项目中。后续章节将进一步探讨 OpenMesh 的应用场景和优化技巧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值