Overview
Migrate the mesh editor from a triangle-only internal representation to an n-gon-aware one (quads as the primary case, but the data model supports any polygon ≥ 3 vertices). The half-edge structure already supports n-gons via appendFace; the bottleneck is EditableMesh, the import/export pipeline, and the GPU upload path — all of which assume triangles today.
Why
- Loop cut, Catmull-Clark subdivision, edge rings, and most modern topology workflows are quad-native operations.
- Source assets (FBX, glTF, OBJ) frequently ship with quads; we currently triangulate at import and lose that information forever.
- Subdivide, bevel, extrude, and fill produce more predictable results on quad meshes than on triangle meshes.
Out of scope (explicitly)
- Pure n-gon (5+) editing UX — supported by the data model but not surfaced in the toolbar. Quads + triangles are the primary edit-mode targets.
- Subdivision-surface preview rendering. The internal Catmull-Clark refines geometry per-op, not as a live preview.
Proposed chunks (each a separate PR onto feat/quads)
1. Foundation: EditableFace data model
- Add
EditableFace struct to EditableMesh.h — n-vertex polygon (std::vector<unsigned> indices instead of fixed-3 array).
EditableSubMesh gains a std::vector<EditableFace> faces alongside the existing std::vector<EditableTriangle> triangles. Both populated coexisting; only one is canonical — faces if non-empty, else fall back to triangles.
- Round-trip tests verifying both shapes survive the EditableMesh ↔ HalfEdgeMesh boundary.
- No behavior change at this stage — every existing path still uses triangles.
2. GPU upload + render
- Triangulate
EditableFace at upload time so Ogre still gets triangle index buffers.
- Triangle-mesh rendering must be byte-identical before/after.
- Quad-mesh rendering produces the expected fan-triangulated geometry.
3. Importer quad detection
- Assimp / glTF / FBX importers: emit
EditableFace when the source had quads (not the pre-triangulated tris Assimp returns by default).
- Disable
aiProcess_Triangulate for the quad-preserving import path; add a per-mesh switch so legacy triangle-only assets still work.
- Round-trip test: import a quad cube → verify
EditableSubMesh::faces has 6 quads.
4. Half-edge n-gon audit
- The HE structure already supports n-gons via
appendFace. Audit every op for n-gon correctness:
splitEdge, splitFace — currently triangle-only MVP. Lift the guard or document the n-gon limitation.
bevelEdges, bevelVertices — re-validate the chamfer math under quad neighbors.
extrudeFaces, extrudeEdges — should preserve quad faces, not split them.
dissolveEdges, dissolveVertices — already produce n-gons naturally; verify.
subdivideFaces, fillSelection — designed for triangles; need quad paths.
cutPath (knife) — triangle-only walking algorithm; needs n-gon traversal.
- Each op gets a quad-mesh test.
5. Topology ops, quad-aware
- Subdivide — gain a Catmull-Clark mode for quads (proper subdivision-surface math). Keep the existing 1-to-4 triangle split as the fallback for tri faces.
- Bevel / Extrude — preserve quad structure where the operation produces quads naturally (no forced triangulation of side walls).
- Delete / Dissolve — emit n-gons where the result is naturally an n-gon (e.g. dissolving an interior edge of two quads → one n-gon, not two triangles via the diagonal trick).
6. Exporter quad preservation
- FBX / glTF / OBJ writers emit quads (not pre-triangulated tris) when the source mesh has them.
- Round-trip preservation test: import quad asset → no edits → export → re-import → identical face count.
- This must ship before loop cut so the editor's I/O story is end-to-end quad-aware.
7. Loop Cut
- Loop Cut (
Ctrl+R): hover an edge → walk the opposite-edge ring across quads (stop at non-quads or boundaries) → preview the cut → click to commit.
- Mouse wheel adds parallel cuts; slide adjusts the cut position along the edge after placement.
- Trivially implementable on a quad-aware HE mesh.
Acceptance criteria
Release strategy
Stack the chunks onto a long-lived feat/quads branch (not master). Each chunk PR merges into feat/quads, gets reviewed, gets CI green. When all chunks land, single merge into master + release. This keeps each review tractable while still releasing atomically.
Closes
Towards the modern Edit Mode workflow. Loop cut from #259 will land as part of chunk 7.
Overview
Migrate the mesh editor from a triangle-only internal representation to an n-gon-aware one (quads as the primary case, but the data model supports any polygon ≥ 3 vertices). The half-edge structure already supports n-gons via
appendFace; the bottleneck isEditableMesh, the import/export pipeline, and the GPU upload path — all of which assume triangles today.Why
Out of scope (explicitly)
Proposed chunks (each a separate PR onto
feat/quads)1. Foundation:
EditableFacedata modelEditableFacestruct toEditableMesh.h— n-vertex polygon (std::vector<unsigned> indicesinstead of fixed-3 array).EditableSubMeshgains astd::vector<EditableFace> facesalongside the existingstd::vector<EditableTriangle> triangles. Both populated coexisting; only one is canonical —facesif non-empty, else fall back totriangles.2. GPU upload + render
EditableFaceat upload time so Ogre still gets triangle index buffers.3. Importer quad detection
EditableFacewhen the source had quads (not the pre-triangulated tris Assimp returns by default).aiProcess_Triangulatefor the quad-preserving import path; add a per-mesh switch so legacy triangle-only assets still work.EditableSubMesh::faceshas 6 quads.4. Half-edge n-gon audit
appendFace. Audit every op for n-gon correctness:splitEdge,splitFace— currently triangle-only MVP. Lift the guard or document the n-gon limitation.bevelEdges,bevelVertices— re-validate the chamfer math under quad neighbors.extrudeFaces,extrudeEdges— should preserve quad faces, not split them.dissolveEdges,dissolveVertices— already produce n-gons naturally; verify.subdivideFaces,fillSelection— designed for triangles; need quad paths.cutPath(knife) — triangle-only walking algorithm; needs n-gon traversal.5. Topology ops, quad-aware
6. Exporter quad preservation
7. Loop Cut
Ctrl+R): hover an edge → walk the opposite-edge ring across quads (stop at non-quads or boundaries) → preview the cut → click to commit.Acceptance criteria
Ctrl+R.Release strategy
Stack the chunks onto a long-lived
feat/quadsbranch (not master). Each chunk PR merges intofeat/quads, gets reviewed, gets CI green. When all chunks land, single merge into master + release. This keeps each review tractable while still releasing atomically.Closes
Towards the modern Edit Mode workflow. Loop cut from #259 will land as part of chunk 7.