Project 2 | MeshEdit

Name: Star Li | SID: 3033789672


Overview

This project is about the implementation of some mesh geometry structures. In the first part (Part I and II), I implemented the Bezier curves and surfaces , which are powerful structures to represent smooth curves/surfaces in space with varying number of control points. In the rest of the project, I mainly worked with the half-edge data structure. Some local operations, such as vertex normal and edge flip/split, are implemented first and followed by the very important global loop subdivision algorithm, which enables smooth mesh upsampling.

Overall, this project brings me deeper understanding of OOP and data structure in C++, which I seldom worked with before. It also practices my debugging skills as the half-edge data structure is complex and involves multiple components. I'm especially impressed by the effect of applying simple algorithms (loop subdivision, vertex normal calculation etc.) on the mesh geometry and their execution speed.


Task 1: Bezier Curves with 1D de Casteljau Subdivision

Steps for implementing de Casteljau's algorithm to calcualte Bezier Curves

Bezier Curve at different evaluation level


xd
level 1
xd
level 2
xd
level 3
xd
level 4
xd
level 5
xd
level 6

Slightly different curve at different parameter t


xd

Task 2: Bezier Surfaces with Separable 1D de Casteljau

Extending de Casteljau algorithm to Bezier surfaces

Screenshot of teapot consist of Bezier Surfaces


xd

Task 3: Area-Weighted Vertex Normals

In this task, we try to find the normal of a vertex based on the normal of each incident faces weighted by their areas. We traverse the faces in a loop starting from the vertex's direct halfedge h. Inside the loop, we first obtain the face normal by calling h->face()->normal(), then we collect the positions of the three vertices a, b, c (we access the other two vertices with h->next()->vertex() and h->next()->next()->vertex()). The face area is then computed with the norm of cross product (a - b) X (a - c). Finally we move to the next face at the end of a loop with h = h->twin()->next() (terminate when we return to the original half-edge). The weighted normal in each loop is summed up and normalized at the end to speedup execution.

Screenshots of shading with & without vertex normals


xd
with vertex normal
xd
without vertex normal


Task 4: Edge Flip


xd
visualization of edge flip process

The implementation of edge flip is demanding as there are some design choices to make that would have impacts on later parts. The general flip operation is illustrated in the above image. More specifically, if the original mesh looks like the left geometry, then the left face is gonna be the down face and the right face be the up face after edge flip. In the code's level, the above is achieved in below steps.

My original implementation in step 2 above doesn't involve a checking process, i.e. I forcefully reset the half-edges of the vertices on two side of the flipped edge to some other ones. This is a bad move since I later used the original half-edge to store the new location of the vertex in loop subdivision algorithm. This took me a bunch of time to debug and taught me a lesson about the importance of keeping the structure's stability.

Example using edge flip


xd
before edge flip
xd
after edge flip


Task 5: Edge Split


xd
visualization of edge split process

Edge split is way more complicated than the edge flip done above since it involves creating new elements and moving more pointers. Some design choices are made as well. For the integrity of the structure and the ease for later part, I chose not to delete any element. Therefore, we can see that the original faces f1 and f2 as well as edges e5 are preserved and moved to the upper part after splitting. The specific steps are as followed:

Example using edge flip


xd
before edge split
xd
after edge split
xd
before edge split
xd
after a combination of flip and split (first step of loop subdivision)

I became more careful in my implementation in this part since it looks really hard, so no major bugs had occurred to me. The only exception is that I once forgot to move the pointer of h1 the new vertex as described in step 3 above.


Task 6: Loop Subdivision for Mesh Upsampling

In this very last (and hardest) task of the project, I took the hint in the project specification and my implementation is the following:

Once the previous tasks are finished, this part is no longer daunting for me and no major bug occurred. However, I did have interesting experience speeding up this process. Basically, I notice that new edges and vertices created are appended to the end of the internal linkedlist, which helps optimize since I can obtain the total number of original edges/vertices using int n = mesh.nEdges() and only iterate the first n items if we want to ignore newly created elements.

Observing sharp edges and corners

In general, the subdivision and vertex update makes the subdivided mesh smoother (at least C0 continuity) and smoother. However, sharp corners remains and is not smoothed as much. To fix this, I try to connect more neighboring vertices to the corner vertex, expecting that more vertices can "average out" more sharpness of the original mesh. (This method has its limitation since the weight (1 - n * u) is a constant regardless of neighbor amount). However, some small improvements can be observed.


xd
before upsampling, without preprocessing
xd
after upsampling, without preprocessing
xd
before upsampling, with preprocessing
xd
after upsampling, with preprocessing

Making the cube symmetric

As we try to subdivide the cub.dae, we can easily notice that the resulting meshes looks assymetric. Taking a look at the raw cube mesh we notice that on each face there's only one diagonal edge connecting a pair of vertices. This pair of vertices then have more neighbors than the other pair and is thus weighted differently in our updating formula. To resolve this issue, we can simply do an edge split on each of the six faces so that the other diagonal edge can be connected as well. This will perfectly make the upsampled mesh symmetric, as shown below.


xd
before upsampling, without preprocessing
xd
after upsampling, without preprocessing
xd
before upsampling, with preprocessing
xd
after upsampling, with preprocessing (symmetric!!)


Task 7: Design and Edit Your Own Mesh!

I learned and enjoyed creating my own mesh using blender. However, possibly due to the lighting issue, my .dae file cannot be loaded into our program. Though failed, I still wanna show my work here for memory.

xd
Hello Computer Graphics World!😄


Made with ❤ with Bootstrap, by Star Li (listar2000@berkeley.edu) My Website