vcglib使用,转载的

先来看看 VCGlib 能做什么 :

最基本的,它提供 Mesh(triangular mesh,tetrahedralmesh,三角网格或四面体网格)数据结构的定义,该数据结构支持对 Mesh数据的快速访问(拓扑信息、空间查询等)以及高效执行网格上算法;
在 Mesh数据结构基础上,实现大量高效的网格算法,如网格修补、平滑、变形、曲率计算、细分、泊松盘采样、等值面计算等;
IO 支持,读写 PLY、OBJ、STL、3DS、OFF、DXF 等格式网格文件;
UI 支持,如 OpenGL 网格显示,Trackball 交互等。
VCGlib 的文档很简陋, 在线文档 并不是很全,可以自己用 Doxygen从下载的源代码生成 html API 文档 ,为此只需要(Windows 用户):

安装 Doxygen (Doxywizard) 和 Graphviz,将“Graphviz安装目录\Graphviz2.36\bin”添加到环境变量Path;
将本博客所附程序中的 “OpenGL Shadow_Libs\vcglib\docs\doxyfile-all”文件拷贝到下载的 VCGlib 文件夹下的 “vcglib\docs\Doxygen” 下,这个文件是我配置好的 Doxygen配置文件;
用 Doxywizard 打开上一步拷贝的文件,点击“Run” 选项卡下的 “Run doxygen” 按钮,生成的 API 文档将位于“vcglib\docs\Doxygen\html-all” 下(html-all 文件夹将有 129M 大小)。
VCGlib 是纯头文件库,要 安装 只需将下载 VCGlib库目录添加到程序的头文件包含路径(有些IO函数如读写PLY需要包含相应.cpp文件)。

后面按照如下步骤讲解:

定义 Mesh 类型;
访问及指定 Mesh 的顶点、三角形(对三角网格,如果是四面体网格则是四面体,这里默认只讲三角网格)等数据;
IO,读写 PLY、OBJ 等网格文件;
构造网格的拓扑信息,如顶点或三角形面法向量、三角形相邻三角形、边连接的三角形等信息;
网格处理,如法向量平滑、网格修补等。
定义 Mesh 类型的典型代码如下(API 文档主页 Basic Concepts,在线版 ):

#include “vcg/complex/complex.h”
// 类型声明
class MyVertex;class MyEdge;
class
抛开 MyUseTypes 不看,上面代码定义的网格类型为:

网格包含属性:顶点、边、三角形数组(std::vector<>);
每个顶点包含属性:空间坐标(3个float表示)、顶点法向量、标志位;
每个边包含属性:顶点指针(指向该边的两个顶点)、边-面邻接信息、标志位;
每个三角形面包含属性:顶点指针(指向该三角形的三个顶点)、面法向量、面-面邻接信息、标志位。
VCGlib 使用 Reference 数据结构,对每个边、面用指针记录其顶点、邻接面等信息,其他网格数据结构见wikipedia Polygon Mesh 条目。

为了做到足够通用, VCGlib 使用了C++ templatemetaprogramming(模板元编程)方法 。上面代码中的MyVertex、MyEdge、MyFace、GLMesh等类型包含哪些属性(模板参数)、属性的顺序(模板参数顺序)都是可以根据需要随意指定的(当然,必须包含足够的属性以执行相应网格算法),一般来说,最好使顶点、边、面包含标志位属性(BitFlags),BitFlags指示该顶点、边、面是否可写、可读、已删除(为了效率,例如,删除顶点操作可能并不立即删除顶点数据,而仅仅打个标志位,待所有操作完成再更新顶点数据)等。不去深入VCGlib元编程机理(说实话我还没弄清楚),可选个数模板参数是通过默认模板参数实现的,vcg::Vertex/Edge/Face<>将继承其模板参数。

下面列举所有可选的模板参数:

网格 vcg::tri::TriMesh<>最多可有四个参数:顶点容器、边容器、面容器、半边容器(vcg::HEdge<>);
顶点 vcg::Vertex<> 可以包含的属性有:坐标、法向量、颜色、纹理坐标、标志位、网格质量(网格在该点出优劣评价指标)、曲率、半径、顶点-边邻接信息、顶点-面邻接信息、顶点-半边邻接信息,等(API文档 Modules 选项卡 Vertex Components, 在线版 );
边 vcg::Edge<> 可以包含的属性有:顶点指针、颜色、标志位、网格质量、边-顶点邻接信息、边-边邻接信息、边-面邻接信息、边-半边邻接信息,等(API文档 Modules 选项卡 Edge Components, 在线版 );
面 vcg::Face<> 可以包含的属性有:顶点指针、法向量、颜色、标志位、网格质量、顶点-面邻接信息、面-边邻接信息、面-面邻接信息,等(API 文档Modules 选项卡 Face Components, 在线版 )。
访问 Mesh 数据示例代码如下:

// load mesh …
int i=0, j=0;
// 见 vcg::tri::TriMesh<> -------------------------------------------------------------
mesh.VN(); mesh.EN(); mesh.FN(); // 顶点、边、面个数,可能小于 vs/es/fs.size()
// 因为有些元素被删除时仅仅打了标志位而并未删除存储数据
std::vector& vs = mesh.vert; // 顶点数组
std::vector& es = mesh.edge; // 边数组
std::vector& fs = mesh.face; // 面数组
// 见 vcg::Vertex<> 及其 模板参数 -------------------------------------------------------
GLMesh::VertexType& v = mesh.vert[i]; // 第 i 个顶点,假设 v.isD()==false,即未标志为已删除
v.P().Z(); v.P().V(j); // 顶点坐标,其xyz分量
v.N().X(); // 顶点法向,其x分量
// 见 vcg::Edge<> 及其 模板参数 ---------------------------------------------------------
GLMesh::EdgeType& e = mesh.edge[i]; // 第 i 个边,假设 e.isD()==false
GLMesh::VertexType* pve = e.V(j); // j=0,1,边的两个端点顶点的指针
GLMesh::FaceType* pfa = e.EFp(); // 边-面邻接信息,该边连接的第一个面
// 见 vcg::Face<> 及其 模板参数 ---------------------------------------------------------
GLMesh::FaceType& f = mesh.face[i]; // 第 i 个面(三角形),假设 f.isD()==false
GLMesh::VertexType* pvf = f.V(j); // j=0,1,2,三角形面的三个顶点的指针
f.N(); // 面的法向量
GLMesh::FaceType* pfb = f.FFp(j); // 面-面邻接信息,j=0,1,2,面 f 通过其第j个边连接的第一个面
// 可以通过返回的引用(左值)修改数据,但不要随便修改,见下文 ------------------------------------
v.P().Y() += 3.2f;
e.V(j) = &v;
f.V(j) = &v;
// 遍历所有顶点、边、面需要跳过标记为已删除的元素 ---------------------------------------------
for(size_t i=0; ii){
if(vs[i].IsD()) continue;
// do some thing for each vertex vs[i] …
}
// 除非已经删除了所有标记为已删除元素的存储数据,比如:
vcg::tri::Allocator::CompactVertexVector(mesh);
vcg::tri::Allocator::CompactEdgeVector(mesh);
vcg::tri::Allocator::CompactFaceVector(mesh);
for(size_t i=0; ii){
// do some thing for each face fs[i] …
}
填充(Fill)Mesh 数据的示例代码如下 (API 文档主页 Creating and destroyingelements, 在线版 ,代码摘自那里):

// VCGlib Reference 数据结构,依赖于指针,直接操作顶点、边、面数组 mesh.vert/edge/face 可能
// 产生 std::vector<> 存储重新分配,此时,相关指针将失效,vcg::tri::Allocator<> 处理这些问题
GLMesh m;
GLMesh::VertexIterator vi = vcg::tri::Allocator<GLMesh>::AddVertices(m, 3);
GLMesh::FaceIterator fi = vcg::tri::Allocator<GLMesh>::AddFaces(m, 1);
GLMesh::VertexPointer ivp4;
ivp[0]=&*vi; vi->P()=GLMesh::CoordType(0.0f,0.0f,0.0f); ++vi;
ivp1=&*vi; vi->P()=GLMesh::CoordType(1.0f,0.0f,0.0f); ++vi;
ivp2=&*vi; vi->P()=GLMesh::CoordType(0.0f,1.0f,0.0f); ++vi;
fi->V(0)=ivp[0]; fi->V(1)=ivp1; fi->V(2)=ivp2;
// Alternative, more compact, method for adding a single vertex
ivp3= &*vcg::tri::Allocator<GLMesh>::AddVertex(m,GLMesh::CoordType(1.0f,1.0f,0.0f));
// Alternative, method for adding a single face (once you have the vertex pointers)
vcg::tri::Allocator<GLMesh>::AddFace(m, ivp1,ivp[0],ivp3);

// 同理,如果自己保存了顶点等数据指针,需要在修改顶点、边、面数组后更新该指针 --------------------
// a potentially dangerous pointer to a mesh element
GLMesh::FacePointer fp = &m.face[0];
vcg::tri::Allocator<GLMesh>::PointerUpdater<GLMesh::FacePointer> pu;
// now the fp pointer could be no more valid due to eventual re-allocation of the m.face
vcg::tri::Allocator<GLMesh>::AddVertices(m,3);
vcg::tri::Allocator<GLMesh>::AddFaces(m,1,pu);
// check if an update of the pointer is needed and do it.
if(pu.NeedUpdate()) pu.Update(fp);

// 删除元素的代码如下 --------------------------------------------------------------------
vcg::tri::Allocator<GLMesh>::DeleteFace(m,m.face[0]);

// 拷贝网格的代码如下,GLMesh 没有拷贝构造函数,也没有 operator= -----------------------------
GLMesh m2;
vcg::tri::Append<GLMesh,GLMesh>::MeshCopy(m2,m);
IO,读写网格文件示例代码如下(API 文档主页 Loading and savingmeshes, 在线版 ):

// Mesh 文件一般至少包含顶点数组信息,还可以包含连接信息(三角形)、顶点法向量、顶点颜色、面颜色、
// 面法向量、纹理坐标等等属性,用 mask 的二进制位来标记或控制读取或写入了 Mesh 文件的哪些属性
// 见 vcg::tri::io::Mask,读取 PLY 需要包含文件 “vcglib/wrap/ply/plylib.cpp”(见这里)
// 头文件包含:#include “wrap/io_trimesh/import.h” #include “wrap/io_trimesh/export.h”
GLMesh m; int mask;
// 读取 PLY 文件,并检查返回值,参数 mask 为可选,mask 是返回参数:读入了哪些属性
if( vcg::tri::io::ImporterPLY::Open(m, “file_to_open.ply”, mask)
!= vcg::ply::E_NOERROR ) {
std::cout << “Load PLY file ERROR\n”;
}
// some modification to m and mask …
// 保存 PLY 文件,mask 是输入参数,控制 m 的哪些属性被写入到文件
vcg::tri::io::ExporterPLY<GLMesh>::Save(m, “file_to_save.ply”, mask);
// 读取或写入 OBJ 文件的代码,mask 作用同上
if( vcg::tri::io::ImporterOBJ<GLMesh>::Open(m, “file_to_open.obj”, mask)
!= vcg::tri::io::ImporterOBJ<GLMesh>::E_NOERROR ) {
std::cout << “Load OBJ file ERROR\n”;
}
// some modification to m and mask …
vcg::tri::io::ExporterOBJ<GLMesh>::Save(m, “file_to_save.obj”, mask);
// 读取、写入网格文件,将根据文件扩展名自动匹配文件格式 ---------------------------------------
int oerr = vcg::tri::io::Importer<GLMesh>::Open(m, “file_to_open.off”, mask);
if( oerr != 0 ){
std::cout << "Load mesh file ERROR: "
<< vcg::tri::io::Importer<GLMesh>::ErrorMsg(oerr) << ‘\n’;
}
// some modification to m and mask …
int serr = vcg::tri::io::Exporter<GLMesh>::Save(m, “file_to_save.3ds”, mask);
if( serr != 0 ){
std::cout << "Save mesh file ERROR: "
<< vcg::tri::io::Exporter<GLMesh>::ErrorMsg(oerr) << ‘\n’;
}

构造网格拓扑信息示例代码如下 (API 文档主页 Adjacency andTopology, 在线版 ):

// load mesh …
vcg::tri::UpdateNormal<GLMesh>::PerFaceNormalized(mesh); // 计算顶点法向量,并单位化
vcg::tri::UpdateNormal<GLMesh>::PerVertexNormalized(mesh); // 计算面法向量,并单位化
vcg::tri::UpdateTopology<GLMesh>::FaceFace(mesh); // 计算面-面邻接信息
vcg::tri::UpdateTopology<GLMesh>::AllocateEdge(mesh); // 计算边-面邻接信息,需要面-面信息
vcg::Matrix44f mat(&glm::translate(glm::vec3(1,2,3))[0][0]);
vcg::tri::UpdatePosition<GLMesh>::Matrix(mesh, mat, true); // 更新顶点位置,并更新法向量
// 在调用 UpdateTopology<>::FaceFace() 和 UpdateTopology<>::AllocateEdge() 后就构造了边到面
// 的信息,对于 manifold 网格,每个边必连接两个三角形面,下面代码对边 i 查找其连接的面 fa 和 fb
int i=0; GLMesh::EdgeType& e = mesh.edge[i];
GLMesh::FaceType* fa = e.EFp();
GLMesh::FaceType* fb = fa->FFp(e.EFi());
在准备这篇博客之初,研究 VCGlib 时,发现了 VCGlib 的一个 BUG,已经报告给开发者并得到确认( 见这里 ,看看时间,发现这篇博客因为一些原因拖了20多天…)。

网格处理示例代码如下:

vcg::tri::Clean<GLMesh>::RemoveDuplicateVertex(mesh); // 去除重合的顶点
vcg::tri::Smooth<GLMesh>::VertexNormalLaplacian(mesh, 5); // 平滑顶点法向量
float maxSizeHole = 2.0f; // fill 所有直径小于 maxSizeHole 的洞
vcg::tri::Hole<GLMesh>::EarCuttingIntersectionFill
<vcg::tri::SelfIntersectionEar>>(mesh, maxSizeHole, false);
进一步学习的资源:

下载的 VCGlib 源代码 “vcglib\apps\sample” 下的官方示例代码;
源代码,结合 API 文档;
基于 VCGlib 的软件 MeshLab ,可以用于网格文件处理。@TOC

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值