VCGLib.docx

上传人:b****6 文档编号:7762941 上传时间:2023-01-26 格式:DOCX 页数:12 大小:64.29KB
下载 相关 举报
VCGLib.docx_第1页
第1页 / 共12页
VCGLib.docx_第2页
第2页 / 共12页
VCGLib.docx_第3页
第3页 / 共12页
VCGLib.docx_第4页
第4页 / 共12页
VCGLib.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

VCGLib.docx

《VCGLib.docx》由会员分享,可在线阅读,更多相关《VCGLib.docx(12页珍藏版)》请在冰豆网上搜索。

VCGLib.docx

VCGLib

VCGLib中邻接关系的实现

VCGLib中邻接关系的维护依赖于各种单形中存储的相关信息。

在VCGLib中几乎所有的算法实现都假设存在vcg:

:

face:

:

VertexRef,该属性存储了三个指向顶点对象的指针,可以通过V()函数访问。

面-面邻接关系

面之间的邻接关系存储于vcg:

:

face:

:

FFAdj(正四面体为vcg:

:

face:

:

TTAdj),该属性通过边来记录面之间的邻接关系。

下图显示了两个三角形面:

图中顶点编号从0到2,以逆时针顺序编号,边i(i=0..2)的两个端点分别为i和(i+1)%3,因此图中面f0和面f1的公共边对f0而言是0,对f1而言是1。

对于面f的每个边e,vcg:

:

face:

:

FFAdj存储以下信息:

∙FFp(e):

指向共享边e的面的指针,若e是border,则该指针指向自己;

∙FFi(e):

在指向的面中e的索引。

例如在上图中,有:

∙f1->FFp

(1)==f0

∙f1->FFi

(1)==0

∙f0->FFp(0)==f1

∙f0->FFi(0)==1

VCGLib中对于非流形的情况也有考虑,因为只是想简单了解,没有看。

 

Pos

三角网格中,Pos为一三元组,pos={v,e,f},e是f的边,v是e的端点。

下图以小三角形的形式显示了三角网格中的一些pos,在每个面中,每个小三角形指向一个顶点,倚靠一条边。

这样就能保证任意给定一个posc,若只改变c的三元组的一个分量,能够唯一确定一个邻居pos。

从一个pos移动到该pos的一个邻居的操作称为Flip,把改变顶点,边和面的Flip操作分别记做FlipV,FlipE和FlipF。

例如,对于上图中的c0而言,其三元组中除了顶点,其他分量都相同的pos只有一个,即c2。

记c2=FlipV(c1)。

类似地,有:

∙c2=FlipV(c1)

∙c0=FlipE(c1)

∙c3=FlipF(c0)

环绕v逆时针遍历:

∙c4=FlipE(FlipF(c0))

∙c5=FlipE(FlipF(c4))

在border上的情况:

∙c6=FlipE(FlipF(c5))

环绕v顺时针遍历:

∙c3=FlipE(FlipF(c6))

∙c1=FlipE(FlipF(c3))

border:

∙c0=FlipE(FlipF(c1))

可以发现,当两个flip嵌套操作的时候,根据pos与面的关系,可以实现顺时针或逆时针的pos转换。

并且由于面-面邻接关系的定义方式,当pos在border上的时候,FlipF操作会返回到pos本身所在的面。

下面的例子展示了如何使用pos在顶点周围迭代

/*vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp*/

 

#include//includethedefinitionofpos

 

//...includestodefineyourmeshtype

 

//classMyVertex:

...

classMyFace:

publicvcg:

:

FaceSimp2

:

face:

:

VertexRef,vcg:

:

face:

:

FFAdj>{};

 

voidOneRingNeighborhood(MyFace*f)

{

MyVertex*v=f->V(0);

MyFace*start=f;

vcg:

:

face:

:

Posp(f,0,v);

//constructorthattakesface,edgeandvertex

do

{

p.FlipF();

p.FlipE();

}while(p.f!

=start);

}

JumpingPos

JumpingPos类似Pos,但在遇到border的时候不会反弹回去,而是跨到下一个border-face上去。

下面这个例子中,上图中的p会从f0跨到f2。

/*vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp*/

 

#include//includethedefinitionofjumpingpos

 

//...includestodefineyourmeshtype

 

//classMyVertex:

...

classMyFace:

publicvcg:

:

FaceSimp2

:

face:

:

VertexRef,vcg:

:

face:

:

FFAdj>{};

 

voidOneRingNeighborhoodJP(MyFace*f)

{

MyVertex*v=f->V(0);

MyFace*start=f;

vcg:

:

face:

:

JumpingPosp(f,0,v);

//constructorthattakesface,edgeandvertex

do

{

p.NextFE();

}while(p.f!

=start);

}

 

顶点-面(VF)的邻接关系

VCGlib中,顶点与面之间也实现了邻接关系,即给定一个顶点v,可以找出所有与其关联的面。

设v_star=(f0,f1,f2,…,fk)为一个列表,该列表中的元素都是与顶点v关联的面。

可以通过以下属性索引v_star:

∙vcg:

:

vertex:

:

VFAdj:

这是一个顶点的属性,包含了指向f0的指针

∙vcg:

:

face:

:

VFAdj:

这是面的属性,该面三个顶点中的每一个顶点都包含了指向v_star列表中下一个面的指针

这两个属性不仅是指针,还包含顶点在所指向的面中的索引。

例如上图中,有:

v.VFp()==f2

v.VFi()==0

f2->VFp(0)==f3

f2->VFi(0)==1   

//f2中索引为0的顶点(即v)指向了面f3,而v在f3中的索引为1

f3->VFp

(1)==f1

f3->VFi

(1)==2

f1->VFp

(2)==f0

f1->VFi

(2)==2

f0->VFp

(2)==NULL

f0->VFi

(2)==–1

VCGLib 使用说明

能做什么 :

1)最基本的,它提供Mesh(triangularmesh,tetrahedralmesh,三角网格或四面体网格)数据结构的定义,该数据结构支持对Mesh数据的快速访问(拓扑信息、空间查询等)以及高效执行网格上算法;

2)在Mesh数据结构基础上,实现大量高效的网格算法,如网格修补、平滑、变形、曲率计算、细分、泊松盘采样、等值面计算等;

3)IO支持,读写PLY、OBJ、STL、3DS、OFF、DXF等格式网格文件;

4)UI支持,如OpenGL网格显示,Trackball交互等。

使用说明

1)定义Mesh类型;

访问及指定Mesh的顶点、三角形(对三角网格,如果是四面体网格则是四面体,这里默认只讲三角网格)等数据;`

定义Mesh类型的典型代码如下(API文档主页 BasicConcepts, 在线版 ):

#include "vcg/complex/complex.h" 

//类型声明 

class MyVertex;

class MyEdge;

class MyFace;

typedef vcg:

:

UsedTypes< vcg:

:

Use:

:

AsVertexType, vcg:

:

Use :

:

AsEdgeType, vcg:

:

Use:

:

AsFaceType> MyUsedTypes; 

//顶点类型 

class MyVertex:

 publicvcg:

:

Vertex<MyUsedTypes, vcg:

:

vertex:

:

Coord3f, vcg:

:

vertex:

:

Normal3f,vcg:

:

vertex:

:

BitFlags > {};

 //边类型 

class MyEdge:

 publicvcg:

:

Edge<MyUsedTypes, vcg:

:

edge:

:

VertexRef, vcg:

:

edge:

:

EFAdj,vcg:

:

edge:

:

BitFlags > {}; 

//面类型,三角形

class MyFace:

 publiccvcg:

:

Face<MyUsedTypes, vcg:

:

face:

:

VertexRef, vcg:

:

face:

:

Normal3f,vcg:

:

face:

:

FFAdj, vcg:

:

face:

:

BitFlags > {}; 

//网格类型 

Typedefvcg:

:

tri:

:

TriMesh< std:

:

vector, std:

:

vector, std:

:

vector > GLMesh;

抛开MyUseTypes不看,上面代码定义的网格类型为:

网格包含属性:

顶点、边、三角形数组(std:

:

vector<>);

每个顶点包含属性:

空间坐标(3个float表示)、顶点法向量、标志位;

每个边包含属性:

顶点指针(指向该边的两个顶点)、边-面邻接信息、标志位;

每个三角形面包含属性:

顶点指针(指向该三角形的三个顶点)、面法向量、面-面邻接信息、标志位。

VCGlib使用Reference数据结构,对每个边、面用指针记录其顶点、邻接面等信息,其他网格数据结构见wikipediaPolygonMesh条目 。

为了做到足够通用, VCGlib使用了C++templatemetaprogramming(模板元编程)方法 。

上面代码中的MyVertex、MyEdge、MyFace、GLMesh等类型包含哪些属性(模板参数)、属性的顺序(模板参数顺序)都是可以根据需要随意指定的(当然,必须包含足够的属性以执行相应网格算法),一般来说,最好使顶点、边、面包含标志位属性(BitFlags),BitFlags指示该顶点、边、面是否可写、可读、已删除(为了效率,例如,删除顶点操作可能并不立即删除顶点数据,而仅仅打个标志位,待所有操作完成再更新顶点数据)等。

不去深入VCGlib元编程机理(说实话我还没弄清楚),可选个数模板参数是通过默认模板参数实现的,vcg:

:

Vertex/Edge/Face<>将继承其模板参数。

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

网格vcg:

:

tri:

:

TriMesh<>最多可有四个参数:

顶点容器、边容器、面容器、半边容器(vcg:

:

HEdge<>);

顶点vcg:

:

Vertex<>   可以包含的属性有:

坐标、法向量、颜色、纹理坐标、标志位、网格质量(网格在该点出优劣评价指标)、曲率、半径、顶点-边邻接信息、顶点-面邻接信息、顶点-半边邻接信息,等(API文档Modules选项卡VertexComponents, 在线版 );

边vcg:

:

Edge<>   可以包含的属性有:

顶点指针、颜色、标志位、网格质量、边-顶点邻接信息、边-边邻接信息、边-面邻接信息、边-半边邻接信息,等(API文档Modules选项卡EdgeComponents, 在线版);

面vcg:

:

Face<>   可以包含的属性有:

顶点指针、法向量、颜色、标志位、网格质量、顶点-面邻接信息、面-边邻接信息、面-面邻接信息,等(API文档Modules选项卡FaceComponents, 在线版 )。

访问Mesh数据示例代码如下:

// loadmesh ...

 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_ti=0;ii)

if(vs[i].IsD()) 

continue; 

//dosomethingforeachvertexvs[i]... 

} // 除非已经删除了所有标记为已删除元素的存储数据,

比如:

 

vcg:

:

tri:

:

Allocator:

:

CompactVertexVector(mesh);vcg:

:

tri:

:

Allocator:

:

CompactEdgeVector(mesh);vcg:

:

tri:

:

Allocator:

:

CompactFaceVector(mesh); 

for(size_ti=0;ii)

// dosomethingforeachfacefs[i]... 

}

填充(Fill)Mesh数据的示例代码如下 (API文档主页Creatinganddestroyingelements, 在线版 ,代码摘自那里 ):

 VCGlibReference数据结构,依赖于指针,直接操作顶点、边、面数组mesh.vert/edge/face可能产生std:

:

vector<> 存储重新分配,此时,相关指针将失效,vcg:

:

tri:

:

Allocator<> 处理这些问题

GLMeshm;

GLMesh:

:

VertexIteratorvi =vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddVertices(m, 3);

GLMesh:

:

FaceIteratorfi =vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddFaces(m, 1);

GLMesh:

:

VertexPointerivp[4];

ivp[0]=&*vi;

vi->P()=GLMesh:

:

CoordType(0.0f,0.0f,0.0f);

++vi;

ivp[1]=&*vi;

vi->P()=GLMesh:

:

CoordType(1.0f,0.0f,0.0f);

++vi;

ivp[2]=&*vi;

vi->P()=GLMesh:

:

CoordType(0.0f,1.0f,0.0f);

++vi;

fi->V(0)=ivp[0];

fi->V

(1)=ivp[1];

fi->V

(2)=ivp[2]; 

 

// Alternative,morecompact,methodforaddingasinglevertex 

ivp[3]=&*vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddVertex(m,GLMesh:

:

CoordType(1.0f,1.0f,0.0f)); 

// Alternative,methodforaddingasingleface(onceyouhavethevertexpointers)

 vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddFace(m,ivp[1],ivp[0],ivp[3]); 

// 同理,如果自己保存了顶点等数据指针,需要在修改顶点、边、面数组后更新该指针-------------------- 

// apotentiallydangerouspointertoameshelement

GLMesh:

:

FacePointerfp=&m.face[0];

vcg:

:

tri:

:

Allocator<GLMesh>:

:

PointerUpdater<GLMesh:

:

FacePointer> pu; 

// nowthefppointercouldbenomorevalidduetoeventualre-allocationofthem.face 

vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddVertices(m,3);vcg:

:

tri:

:

Allocator<GLMesh>:

:

AddFaces(m,1,pu); 

// checkifanupdateofthepointerisneededanddoit.

 if(pu.NeedUpdate())

pu.Update(fp);

// 删除元素的代码如下

vcg:

:

tri:

:

Allocator<GLMesh>:

:

DeleteFace(m,m.face[0]); 

// 拷贝网格的代码如下,GLMesh没有拷贝构造函数,也没有operator=

GLMeshm2;

vcg:

:

tri:

:

Append<GLMesh,GLMesh>:

:

MeshCopy(m2,m);

 

2)IO,读写PLY、OBJ等网格文件;

IO,读写网格文件示例代码如下(API文档主页 Loadingandsavingmeshes, 在线版 ):

// 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"

GLMeshm; 

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";

}

 // somemodification 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",

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1