MayaLite is a desktop mesh editing tool built in C++/OpenGL for CIS 4600 at Penn. It replicates the core interaction model of Maya: a Qt panel lists every face, half-edge, and vertex in the scene, clicking any item highlights it in the GL viewport, and spin boxes let you nudge vertex positions numerically. Mesh data lives entirely in a half-edge data structure, which makes topological traversal and modification O(1) per element. Supported operations include OBJ import, edge bisection, face fan-triangulation, Catmull-Clark subdivision, and per-face color assignment via Qt's color picker dialog.
Every mesh element (vertex, face, half-edge) is stored as a QListWidgetItem subclass so the Qt list panels stay in sync with geometry without a separate mapping layer. Each HalfEdge carries pointers to its next, sym (twin), incident face, and tip vertex. Each Vertex stores one outgoing half-edge; each Face stores one bounding half-edge. A do-while loop over he = he->sym->next walks all edges around a vertex in O(valence), used by subdivision to accumulate neighbor data. Twin links are built during OBJ load via an unordered_map keyed on the ordered vertex-index pair, giving O(1) twin lookup per edge.
The parser reads v, vn, and f tokens from the OBJ file using a fixed-size character buffer and sscanf. It handles both the v/t/n triplet format and index-only faces. After the raw geometry pass, createHalfEdgeDataStructure() allocates one Vertex per parsed position and calls processFace() for each polygon. That function creates one HalfEdge per polygon edge, chains next pointers into a loop, and inserts each directed edge into the twin map. A second pass over the map wires all sym pointers. The result is a valid, traversable half-edge mesh regardless of polygon valence.
Subdivision runs in four passes. First, a face centroid is computed for every face via a half-edge loop and stored in an unordered_map<Face*, Vertex*>. Second, smooth edge midpoints are inserted: for each original half-edge not yet split, a new vertex is placed at (v1 + v2 + f1 + f2) / 4 where f1 and f2 are the centroids of the two adjacent faces, then divideHalfEdge() inserts the vertex into the loop. Third, original vertices are moved to the Catmull-Clark weighted average: ((n-2)/n) * P + (1/n^2) * sumMidpoints + (1/n^2) * sumCentroids, where n is vertex valence. Finally, new quad faces are constructed by adding two half-edges per original edge and stitching them into quads around each centroid. The resulting mesh has only quad faces, suitable for further subdivision passes.
The mesh is drawn using a Lambert GLSL shader. The fragment stage computes a diffuse term as dot(N, normalize(camPos - fragPos)) clamped to [0, 1] and adds a 0.2 ambient floor, scaling the per-face color stored in the vertex buffer. Selected half-edges and vertices are drawn in a separate flat shader pass with depth testing disabled so the selection highlight always renders on top. The vertex buffer is rebuilt from scratch on every structural edit using fan triangulation: each n-gon is decomposed into n-2 triangles from the first vertex, keeping the index buffer simple and avoiding the need for a geometry shader.