Building a Mesh Editor

Glenn Wysen - Spring 2020

teapot
Fig.1 - A simple teapot constructed from a triangle mesh.

Overview

This project revolved around how to model and represent 3D objects on a screen. The underlying concept is that all shapes can be composed of a collection of triangles in a mesh. Modeling shapes like this allows for several benefits including the ability to split edges, flip edges, and use loop subdivision to create smoother meshes with more triangles. This project also implements a basic de Casteljau's algorithm to draw curves in 2D space and surfaces in 3D space.

Bezier Curves With 1D de Casteljau Subdivision

De Casteljau's algorithm is a way of creating a Bezier curve through the linear interpolation of a set number of "anchor points". To start, all the points are connected, forming a series of lines. The basic idea of de Casteljau's algorithm is to take these lines and iterate through them with each pass reducing the number of points (and subsequently lines) by one every time. Eventually, after many interpolations, we will end up with a single point. This point lies on our final Bezier curve. The way that an iteration removes a point/line is to interpolate between the first and second point with a weight t (which ranges from 0 to 1) to get a new starting point. Next, we interpolate between the second and third points to get our second new point. This process continues until the n-1th and nth point get interpolated to get the n-1th point of our new series. Now we repeat the same algorithm over and over using the previously created points as our new reference until n reaches 1, leaving us with one point. If we go through with every value 0-1 for t all of the final points will trace out the Bezier curve.

castel gif
Fig.2 - A curve being built using de Casteljau's algorithm.
s curve
Fig.3 - Even complex curves can be made with this simple algorithm.

Average Normals for Half-Edge Meshes

I implemented the area-weighted vertex normals for each vertex by doing the following. First, I found any one half-edge pointing away from the vertex. Then using that half-edge to navigate around the local mesh, I found the area of the triangle it lay in and the normal vector of said triangle. After weighting each normal vector by the area of its triangle, I stored them in a vector and iterated around each triangle surrounding the vertex until I reached the first half-edge. I then added all of the weighted normal vectors to get a large vector pointing in the right direction. The last step was to normalize that large vector to be unit length. This process was repeated for every vertex in the mesh to obtain a smoothed out image (Fig.5).

pot without normalization
Fig.4 - A teapot without average normals implemented.
pot with normalization
Fig.5 - The same teapot after average normals were implemented.

Half-edge Flipping and Splitting

I implemented the an edge flip by carefully assigning every element (vertex, edge, half-edge, and face) that interacted with the edge I was flipping to a variable. I then went through where each element would end up after the flip and re-assigned them to the right ending elements. This involved re-assigning 21 total elements (ten half-edges, five edges, four vertices, and two faces).

I implemented edge splitting in a similar way to that of edge flipping. I drew out how all the elements in a two triangle mesh section would change and carefully kept track of which half-edges pointed where. The difference in this method was that it involved creating new elements, rather than only rearranging old ones. An issue I ran into while debugging was that I was calculating the center point of the split by averaging all four x, y, and z values from the four points surrounding the edge instead of simply interpolating between the two ends of the edge. This caused some slight confusion but was easy enough to fix with some quick edits.

normal teapot
Fig.6 - Teapot with no edges flipped or split.
split teapot
Fig.8 - Teapot with some split edges but no flipped edges.
flip teapot
Fig.7 - Teapot with some flipped edges but no split edges.
flip and split teapot
Fig.9 - Teapot with some flipped and split edges.

Loop Subdivision for Mesh Upsampling

I implemented loop subdivision using the following steps: First I marked every vertex and edge in the mesh as an "original element" by setting their isNew value to false. I then iterated through each vertex and found their new position according to the loop subdivision weights. I then did the same for the new vertices which would be halfway through each edge and set those positions in the newPosition variable in each edge iterator. Then I began the splitting and flipping process where every original edge was split, then certain edges were flipped to create an isotropic triangular mesh. Lastly, I updated each vertices new position according to what I calculated before. I ran into an infinite looping issue that was caused by not making sure I only split the original edges in the mesh. This was quickly remedied by checking breakpoints and fixing my iterator to advance past the edge I was splitting before splitting said edge. After running a few loop subdivisions, it appears that sharp edges get smoothed out into rounded points as can be seen in the cube that gets subdivided throughout Fig.10-Fig.13. Additionally, this cube looks to be slightly skewed, which is due to the shape of the edges in the original cube being only along certain diagonals.

cube 1
Fig.10 - A basic cube with zero subdivision loops.
cube 2
Fig.11 - The cube after one round of loop subdivision.
cube 3
Fig.12 - The cube after two rounds of loop subdivision.
cube 4
Fig.13 - The cube after three rounds of loop subdivision.

The skew in the cubes above can be fixed by splitting each edge on each face of the cube before running the mesh subdivisions (Fig.14-Fig.17). The reason why this works is because now the subdivision starts with a symmetric base instead of diagonals across each face of the cube.

cube 1
Fig.14 - The basic cube with split edges.
cube 2
Fig.15 - The cube after one round of loop subdivision.
cube 3
Fig.16 - The cube after two rounds of loop subdivision.
cube 4
Fig.17 - The cube after three rounds of loop subdivision.

Lastly, I wanted to show some examples of how loop subdivision can smooth out a more complex triangle mesh. Notice how by the second round of subdivision the model is already mostly smooth and eventually the benefits of additional smoothness are outweighed by the computational complexity of that much subdivision.

cow 1
Fig.18 - A basic cow mesh with a texture applied
cow 2
Fig.19 - The cow after one round of loop subdivision.
cow 3
Fig.20 - The cow after two rounds of loop subdivision.
cow 4
Fig.21 - The cow after three rounds of loop subdivision.