2021SC@SDUSC
Dust3D在网格无缝缝合时使用了半边数据结构存储相关数据。
表示多边形网格的一个常用方式就是使用共享的顶点列表和面的列表。这样的表示方法在许多情况下都非常方便和高效,但是在某些特定的领域,反而会效率比较低。
例如,网格简化通常需要把一条边折叠成一个顶点。这个操作需要删除与这条边邻接的面并且更新面中存储的与这条边相关的顶点数据。这种多边形“手术”需要我们了解网格的组成部分的邻接关系,例如面和顶点。虽然我们肯定可以用之前提及的网格表示方法来实现网格简化,但是代价会比较高。许多情况下需要遍历面或者点的列表,或者二者都有。
在一个多边形网格上其他类型的的临近查询包括:
- 哪条边用到了这个点
- 哪条边用到这个点
- 哪个面临近这条边
- 哪条边临近这个面
- 哪个面临近这个面
为了使得这些类型的临近查询更加有效,发展出更加精细的边界表示方案(b-reps),更加明确地建模表示点、边和面,并且也临近信息也相应地储存进去了。
这些表示类型中最常见的其中一种是“翼边数据结构”。其中每条边包含了指向其两个顶点,两个邻接面的指针以及指向从其终点延伸出去的四条边的指针。这种结构可以让我们在常数时间内判断哪些点与面与该边连接,但是面对其他类型的查询,这种结构会带来更大的开销。
“半边数据结构”是一种略微复杂的边表示方法,并且在其上做之前提到的所有操作都可以在常数时间完成。更优秀的是,即使包含了面、顶点和边的邻接信息,数据结构的大小是固定的且紧凑的。
翼边结构
一种常见的数据结构就是翼边结构(The Winged-Edge Structure),其特点是将邻接关系都储存在网格的边上,翼边结构最大的优点在于其不仅能用在三角网格上还能用在任意形状的网格上,数据结构如下:
- 对每个面,储存其中的一个边索引
- 对每条边,储存其两个顶点,左右两个面,左边面与连接的两条边,右边面与之连接的两条边
- 对每个点,储存其对应的一个边索引
翼边结构的另一大优点就是在索引邻接关系的时候非常方便,因为边储存了足够多的信息,利用这个结构我们可以在网格中自由检索。
半边结构
半边数据结构(Half-Edge Data Structure )是一个以网格边为基础的数据结构,在这种数据结构中,我们认为网格的每条边被划分为两条方向相反的半边。
之所以称为半边数据结构,是因为在这个结构中并不会存储网格的边的信息,取而代之的是半边(half-edges)。半边这么叫,就是因为它其实是一条边的一半,等于是把一条边保持长度不变,形式上分为两条半边(因为按照定义边没有宽度或者至少是单位宽度,这就是一个假象划分)。这两条半边组合在一起称为一条边,也就是一条边等于一对半边。半边是有方向的,并且一条边的一对半边有相反的方向。
半边数据结构的三个重要的数据结构——顶点、半边、面片
- 顶点(Vertex):包含出半边(OutgoingHalfedge)的指针或索引,在半边数据结构中的点储存着x,y,z的位置和以其为起始点的半边的指针
- 半边(HalfEdge):包含终点(StartVertex)、邻接面(AdjacentFace)、下一条半边(NextHalfedge)、相反边(opposite)的指针或索引
- 面片(Face):包含一条起始边(FirstHalfedge)的指针或索引对于一个半边数据结构的简单形式,一个面仅仅需要储存一个围绕它的边的指针,在一些特定场合可能要求我们储存比如材质和法向一类的信息。和上面一样,虽然有很多边围绕着面,我们只需要储存其中一条,而无所谓是哪一条
convertHalfEdgesToEdgeLoops()
MeshRecombiner类中的convertHalfEdgesToEdgeLoops()将半边结构转化为循环边列表。首先将每条半边存储在vertexLinkMap中,然后从第一个顶点向后遍历,当元素数未超过序列容量限制时,向edgeLoop中加入顶点,直至形成闭环。
bool MeshRecombiner::convertHalfEdgesToEdgeLoops