# Lecture_009_Meshes_and_Manifolds

Regular grid image:

• easy index, filter

• generality: can encode any image

• suffer from anisotropic

Manifold: there exists a linear transform on the surface of geometry such that the geometry can be smooth.

• manifold: stanford rabbit, donut

• non-manifold: cone, intersected plane

Manifold polygon mesh has fans, but not fins. Every edge is contained in only two polygons (no "fins"). The polygons containing each vertex make a single "fan".

Boundary: every boundary forms a loop.

• for polygon mesh: one polygon per boundary edge

• boundary is usually defined in 2D

## Datastructure

### Basic Datastructure

Usual Datastructure:

• array: constant lookup, coherent access

• linked list: linear lookup, incoherent, good amortized complexity

Well, in theoretical computer science, one should not allow random memory access be in constant time. This is because speed of light is bounded, you can't access memory that is very faraway from the signal in constant time.

### Graphics Datastructure

#### Polygon Soup (Cloud)

Polygon Soup: a list of triangles

• "triangle cloud" (triangle version of point cloud)

• redundant storage when vertex connected

• hard to edit data, but fine for rasterization

Adjacency List: split shape and connectivity.

• kinda like .obj file

• stores v vertex coordinate

• stores f faces by listing vertice

• very expensive to find neighborhood

# cube.obj
#

g cube

v  0.0  0.0  0.0
v  0.0  0.0  1.0
v  0.0  1.0  0.0
v  0.0  1.0  1.0
v  1.0  0.0  0.0
v  1.0  0.0  1.0
v  1.0  1.0  0.0
v  1.0  1.0  1.0

vn  0.0  0.0  1.0
vn  0.0  0.0 -1.0
vn  0.0  1.0  0.0
vn  0.0 -1.0  0.0
vn  1.0  0.0  0.0
vn -1.0  0.0  0.0

f  1//2  7//2  5//2
f  1//2  3//2  7//2
f  1//6  4//6  3//6
f  1//6  2//6  4//6
f  3//3  8//3  7//3
f  3//3  4//3  8//3
f  5//5  7//5  8//5
f  5//5  8//5  6//5
f  1//4  5//4  6//4
f  1//4  6//4  2//4
f  2//1  6//1  8//1
f  2//1  8//1  4//1


In above:

• v: vertex position

• vn: vertex normals

• f: faces

• l: lines

• g: group name

• o: object name

• s: whether smooth shading

Note that there are many representation of f faces in .obj file:

• f v1 v2 v3 ...: stores only vertex

• f v1/vt1 v2/vt2 v3/vt3 ...: stores vertex, and texture coordinates

• f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ... stores vertex, texture coordinates, vertex normals

• f v1//vn1 v2//vn2 v3//vn3 ...: stores vertex and normals

#### Incidence Matrix

Incidence Matrix:

• store vertex to edge, edge to face

• finding neighbores is $O(1)$

• large storage cost (can reduce to sparse matrix data structure)

• associative array (look up non-zero indice like a map or hash table)
• compressed column format: painful, but fast for matrix operations
• hard to change connectivity

• can store non-manifold

### Halfedge Data Structure

struct Halfedge {
Halfedge*  twin;
Halfedge* next;
Vertex* vertex; // by convention, vertex it come from
Edge* edge;
Face* face;

Vec2 corner_uv; // uv coordinate for this corner of the face
Vec3 corner_normal; // shading normal for this corner of the face
// Vertex normal provides information whether we need a hard or smooth edge
// Sometimes triangles mesh are not directly converted from a smooth surface,
// and vertex normals have to be computed on the fly.
// There are different techniques for computing vertex normals
}
struct Edge {
Halfedge* halfedge;

bool sharp; // should this edge be considered sharp when computing shading normals?
}
struct Face {
Halfedge* halfedge;

bool boundary; // is this a boundary loop? (boundary used to traverse mesh efficiently)
}
struct Vertex {
Halfedge* halfedge

Vec3 position; // location of the vertex
struct Bone_Weight {
uint32_t bone;
float weight;
};
std::vector< Bone_Weight > bone_weights; // how much this vertex follows each bone's transform (used for skinned meshes)
}


One can use minimal half-edge-like datastructure to do the job quickly cpp std::unordered_map<std::pair<Index, Index>, HalfedgeRef> halfedges; The above data structure represent a unordered map of half-edge. The std::pair<Index, Index> is the indice for vertex from and vertex to. It maps to a HalfedgeRef that is the address of the next half edge. When you want to walk back or find its twin, just look up the std::unordered_map.

Another trick is to store twin halfedges as even and odd indices instead of pointers.

Besides, we also define 2D mesh to have boundary for easy navigation. (in green shaded region as shown in images below)

If you want to visit all vertices of a face

Halfedge* h = f->halfedge;
do {
h = h->next;
}
while (h != h->halfedge);


If you want to visit all neighbors of a vertex

Halfedge* h = v->halfedge;
do {
h = h->twin->next;
}
while (h != v->halfedge);


The datastructure only make sense if mesh is manifold: - there is only one twin for any halfedge - twin->twin == this - twin != this - there is only one next halfedge - next == something - this == someone's next

Well, actually the connectivity should be manifold, but adjusting vertex position can generates non-manifold-looking geometry. The below image is still a valid geometry.

We can have multiple different representation of the same geometry because the pointers are different. It is non-trivial to have a one-to-one map between representation and geometry. One would build a total order of halfedge to correctly assign halfedge to faces, edges, and vertex.

Completeness: we should prove Edge Flip, Edge Split, Edge Collapse can achieve all operations

• all operations: transform one mesh into the other

• First Step: Edge Collapse all the way to a single triangle

• Second Step: Edge Split to desired mesh

With some thought, one can design halfedge type data structure with coherent data storage and support for non-manifold connectivity.

O(1) neighborhood No Yes Yes
Non-manifold Yes Yes No

### Mesh Validity

A Halfedge_Mesh structure is a sea of pointers. It is surprisingly easy to tangle these pointers into a mess that fails to represent a manifold mesh (or, really, a consistent mesh at all). In order to provide guidance on what constitutes a reasonable setting for a mesh, we define a valid mesh as one for which the following properties hold:

1. The mesh is self-contained. All pointers are to elements in the vertices, edges, faces, or halfedges lists.
2. Edges correspond to twinned halfedges. That is, for every edge e, e->halfedge(->twin)^n is a cycle of exactly two halfedges, and these are exactly the halfedges with h->edge equal to e.
3. Faces correspond to cycles of halfedges. That is, for every face f, f->halfedge(->next)^n is a cycle of at least three halfedges, and these are exactly the halfedges with h->face equal to f.
4. Vertices correspond to stars of halfedges. That is, for every vertex v, v->halfedge(->twin->next)^n is a cycle of at least two halfedges, and these are exactly the halfedges with h->vertex equal to v.
5. Vertices are not orphaned, nor is the surface adjacent to them hourglass-shaped. That is, vertices are adjacent to at least one non-boundary face and at most one boundary face.
6. Edges not orphaned. That is, edges are adjacent to at least one non-boundary face.
7. Faces are simple. That is, faces touch each vertex and edge at most once.