Skip to content

Epic: Quad-based mesh representation (n-gon support) #326

Description

@fernandotonon

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

  • EditableMesh round-trips n-gon faces losslessly through HalfEdgeMesh.
  • FBX / glTF / OBJ I/O preserves quad structure end-to-end (import → export → re-import).
  • Every existing topology op (extrude, bevel, knife, merge, delete, dissolve, subdivide, fill) has at least one quad-mesh unit test.
  • Catmull-Clark subdivide path documented and tested.
  • Loop cut implemented and bound to Ctrl+R.
  • No regression on triangle-only assets — all existing tests still pass.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions