CS 184/284A: Computer Graphics and Imaging, Spring 2024

Homework 2:Meshedit

Author: Jian Yu🐟, Xiaoyu Zhu🐷

Overview

In this assignment, we delve into the intricacies of mesh editing, focusing on the implementation of Bezier curves and surfaces, triangle meshes, and half-edge data structures. Our work encompasses a range of tasks including the rendering of Bezier curves via 1D de Casteljau subdivision, manipulation of Bezier surfaces, and enhancements to mesh structure through operations such as vertex normalization, edge flipping, and edge splitting. Further, we explore mesh upsampling via Loop subdivision and tackle additional challenges such as handling boundary edges and implementing 3 subdivision. Through a combination of theoretical understanding and practical application, we aim to demonstrate the power and versatility of mesh editing techniques in computer graphics.

Section I: Bezier Curves and Surfaces

Part 1: Bezier Curves with 1D de Casteljau Subdivision

Briefly explanation about de Casteljau's algorithm and our implemention

 

Part 2: Bezier Surfaces with Separable 1D de Casteljau

Our implemention:

In conclusion, the implemented functions, evaluateStep and evaluate, extend the 1D de Casteljau algorithm to handle surfaces by treating the evaluation in two separate steps, one for each parameter direction.

Result:

teapot

Section II: Triangle Meshes and Half-Edge Data Structure

Part 3: Area-Weighted Vertex Normals

Our implemention

  1. Initialize a zero vector vertex_normal to accumulate area-weighted normals.
  2. Start iterating over each adjacent face of the vertex using the half-edge iterator h. Each halfedge can point to a face, and use h->twin()->next() can help us traverse to the next halfedge, pointing to the next face.
  3. For each face, first use h->vetex()->position, h->next()->vetex()->position and h->next()->next()->vetex()->position to get position of three vertices. Then compute two vectors (representing two sides originating from the vertex) by subtract any pair of posistions, and calculate their cross product to obtain the normal vector. The result is a normal vector of the face and its magnitude represents twice the area of the face, hence this step effectively provides both the normal vector and its area weighting.
  4. Accumulate the calculated normal vector to vertex_normal.
  5. After iteration, normalize the vertex_normal to get a unit vector.
  6. Return the normalized vertex_normal as the vertex's area-weighted normal vector.

Result:

 

The left the picture is the result before pressing "Q" and the right one is the result after pressing "Q"

Part 4: Edge Flip

The flipEdge(EdgeIter e0) function starts by checking if the given edge is a boundary edge, in which case it returns immediately without performing the flip. This check ensures that the operation does not attempt to modify the mesh in a way that would result in an invalid state.

Next we follow the instruction picture in Correctly Implementing Edge Flip / Split / Collapse , shown as below:

There's no element is added or deleted in "flip" operation, so we first collect all necessary mesh elements and then reassign their pointers according to the new configuration.

Firstly, we identify and collect references to all elements involved in the operation:

The reassignment of mesh elements involves updating the pointers of half-edges, vertices, edges, and faces to reflect the new connectivity after the edge flip:

 

At first, when test on the teapot.dae file, everything runs smoothly, except for we can't use key 'Q' to smooth the shade after we flip any edge. Therefore, we didn't pay enough attention. However, when we tested on the cube.dae file after implementing Task 6, we discovered the issue. After flipping, some edges of certain faces can't be choosen by mouse (e.g., the choosen one below), which means there only are two halfedges without the edge element. Further vestigation revealed that one of the two half-edges correctly pointed to its twin() counterpart, but the reverse direction presented a problem.

split

So we checked the implemention instruction picture again and realized that we had only reassigned half-edges within the two faces, namely h1, h2, h4, and h5, assuming that the flip operation did not affect half-edges outside these faces, such as h6 to h9, and thus skipped them. However, in reality, the twin() of these half-edges changed and needed to be reassigned. Otherwise, it would result in the twin() of two half-edges of an edge not pointing to each other, causing issues with edge display. By reassigning the twin() of h6 to h9, we resolved this problem.

Part 5: Edge Split

Here is the result of a mesh before and after some edge splits(3x3 in the middle):

And here is the result of a mesh before and after a combination of both edge splits and edge flips:

(The original one, flip three edges, choose one edge, then split it)

Luckly we finished this part smoothly:)

boundary_split

We want to split e0 by adding a new vertex on the middle, so there will be a new vertex called v3 and other new elements include new edges, halfedges and faces as above. So if the input edge ->isBoundary() is True, we first get h0 by

This code insures h0 is in the face f0, instead of on the boundary. Then we can follow h0 to collect elements as in task4 and 5. There is a little trick we use to collect h4 and h5 correctly:

These are all elements affected by operation "split". Next we create new vertex, halfedges, edges and face, update their pointer relationship according to the illustration and return v3 in the end.

Part 6: Loop Subdivision for Mesh Upsampling

Implemention