Releases: fernandotonon/QtMeshEditor
2.33.0
Highlights
This release wraps up the quad-mesh epic (#326) with end-to-end n-gon preservation through FBX and OBJ I/O, plus a final bug-fix sweep from the chunk merge review.
✨ Features
N-gon export (chunk 6 — #349)
Quad and n-gon meshes now survive a full import → edit → export → re-import round trip on FBX and OBJ:
- Custom binary FBX writer emits
PolygonVertexIndexwith the FBX last-index-bitwise-NOT polygon-end convention. - Assimp-based OBJ exporter emits
aiFacewith N indices (>3 vertices). - Per-submesh n-gon faces stash on the live
Ogre::Mesh::UserObjectBindings(qtme.faces.<i>) so they survive across edit-mode commits, even afterqtme.source_pathis cleared. - Edit-mode re-entry rehydrates
EditableSubMesh::facesfrom the binding so post-edit n-gon ops keep working.
Format support matrix
| Format | Preserves n-gons? | Why |
|---|---|---|
| FBX | ✅ Yes | Custom binary writer |
| OBJ | ✅ Yes | Assimp respects aiFace arity |
| glTF / GLB | ❌ No | Spec only allows TRIANGLES — preserving quads requires FB_ngon_encoding (vendor extension), out of scope |
Ogre .mesh |
❌ No | Format only stores triangle index buffers |
| Collada / 3DS / STL / PLY / X | ❌ No | Assimp writers triangulate |
Vertex paint UX (#351)
QML falloff slider + vertex color preview toggle for the brush tool.
🐛 Bug fixes (#352 — sweep from PR #347 review)
8 fixes from CodeRabbit / Codex follow-up:
- Loop cut rejects mixed quad/tri adjacency upfront (was producing one-sided cuts on the quad side). +1 unit test.
- Wireframe on tri-only submeshes inside mixed meshes now coexists with the n-gon boundary overlay (was disappearing entirely).
buildSubMeshBuffersexplicitly clears the GPU vertex/index buffers when a submesh has no triangles to draw (fixes stale geometry rendering after deleting the last face).selectedFacesAsHEFaceIndicesnow compacts invalid-face indices in the per-face mapping (was silently mis-targeting face-mode ops when invalid faces shifted later HE-face indices).canConvertToQuads()predicate replaces the previous!isMeshQuadBased()check on the toolbar (mixed meshes no longer wrongly disable the action).convertToQuadscorrectly returns non-zero on promote-only runs.deselectFacemirrorsselectFace's vertex/edge dilation (no more stuck partial selections after ctrl-deselect).- Test fixture uses a real translate instead of
Vector3::ZEROso the assertion stays meaningful if zero-delta short-circuit is ever added.
Two items from the sweep tracked for future work: vertex knife OnVertex near-duplicate, subdivideFacesToQuads T-junctions on partial selections.
🔧 Internal
- CI test build cache (#348): ccache layered on the SonarCloud build-wrapper.
unit-tests-linuxjob dropped from ~25 min to ~7 min on warm cache (97.88% hit rate observed).
Preview

2.32.0
Highlights
- Add vertex paint tool in Edit Mode toolbar (after Fill) with brush settings menu (color, radius, strength).
- Fix vertex color packing/unpacking to ensure correct RGB rendering across render systems/platforms.
- Add projected brush ring preview that updates on hover for more precise feedback.
- Improve paint UX (tool exclusivity, cursor behavior) and add Sentry breadcrumbs for paint actions.
Tests
- Expanded unit test coverage for vertex paint color parsing and vertex color round-trip behavior.
Preview

2.31.0
Highlights
Edit Mode: new topology operations (Phase 4)
- Merge Vertices: merge at center/first/last, plus merge-by-distance (shortcut: M).
- Delete / Dissolve: delete selection with X; dissolve with Ctrl/Cmd+X (keeps the surrounding region watertight).
- Subdivide / Fill: subdivide selected faces/edges; fill selected vertices or closed edge loops. In Edit Mode, F fills when possible, otherwise it falls back to Frame Selection.
Scan/CLI: more robust scan --fix + better reporting
- Fixes crashes in the FBX redundant-keyframe fix path (safer material handling, better headless Ogre initialization, empty-track guard).
- Prevents “fixes” that would increase file size: if output is larger it’s reported as [skipped] and the original file is kept.
- Improved scan output/summary with [fixed] / [skipped] tags, plus totals for bytes saved and keys removed.
- Redundant keyframe detection default threshold set to 40%.
Viewport defaults
- FSAA/MSAA now defaults to 0 when unset (users can opt in via settings). This avoids black viewport issues on some Linux installs.
Web
- Bumped
postcssto 8.5.12 (security + fixes).
Preview
Notable PRs
2.30.0
Highlights
🎞️ Tolerance-based animation simplifier (#310)
A new operation that removes redundant keyframes whose values match the lerp/slerp of their neighbors within tolerance. First/last keys and sharp pose changes are preserved, so Mixamo-style baked clips shed 40–60% of keys without visible drift.
Surfaced everywhere:
- CLI:
qtmesh anim file.fbx --simplify | --analyze, with--preset {conservative|balanced|aggressive}plus per-axis--toleranceand--rotation-tolerance-degoverrides. - Scan rule
redundant_keyframes_pct: warns when projected savings exceed a threshold; reports"X% redundant keyframes (N/M). Simplify it to save ~Y. Original size: A, projected size: B". - MCP tools
simplify_animation+analyze_animation(preset + per-axis overrides). - Inspector: per-animation Simplify button (✂) plus a per-entity tolerance preset selector. Tooltip previews redundancy % under the current preset.
The default preset is Balanced (~1mm translation, 0.5° rotation). On a Mixamo Rumba Dancing FBX (1.89 MB):
- Conservative: 7.9% redundant
- Balanced: 42.1% redundant
- Aggressive: 63.8% redundant
📦 FBX exports embed textures (#311)
FBX outputs now embed texture data inline via Video.Content, producing a single self-contained file. No more sidecar .material scripts or extracted texture images alongside the .fbx — the Mixamo-style portable export experience now works out of the box.
Other changes
- MCP
SERVER_VERSIONbumped to 1.4.0 (new tools). - New shared
AnimationMerger::tolerancesForPresetso CLI/MCP/Inspector share the preset table. ThemedComboBoxnow usesThemeManagerso it works in any panel; aliased into the inspector resource prefix.
What's Changed
- feat(anim): tolerance-based redundant-keyframe simplifier by @fernandotonon in #310
- FBX export: embed textures (no sidecar .material/.png) by @fernandotonon in #311
Preview
Full Changelog: 2.29.0...2.30.0
2.29.0
Highlights
- Knife tool: multi-point cuts with live preview.
- Bevel Vertex: rounded corner cuts with segment/profile control.
- Antialias options in the preferences.
- .vrm file support
Fixes
- qtmesh scan: correctly handles animation-only FBX (no more false load errors from Assimp 'incomplete' scenes).
- Import: treat .vrm as supported mesh format (Assimp glTF2 path).
2.28.2
Highlights
Bevel — Concave/Convex profiles now carve into adjacent faces (#301)
Previously the bevel's profile graph only shaped the chamfer strip itself — the faces perpendicular to the beveled edge (the "adjacent" faces the bevel cuts corners from) kept a straight diagonal cut, hiding the profile from most camera angles. With this release the adjacent faces follow the chamfer curve too, so concave profiles actually carve a groove through the cube and convex profiles form a rounded fillet — from every angle.
Under the hood:
- Chain intermediates are now built before the neighbor-face retriangulation, so both code paths can splice them into the cut polyline.
- A new ear-clipping helper handles the resulting concave polygons correctly (a plain fan would emit a straight diagonal across the arc).
- Insertion direction on each endpoint is chosen so chain edges pair opposite to the chamfer strip's emissions, keeping the mesh manifold.
- Endpoint corner caps are skipped for shaped profiles when the neighbor splice already closes the chamfer's end; a chain-polyline cap is emitted instead when no neighbor coverage is available (e.g., boundary endpoints, smooth meshes).
Tests
Four new unit tests cover:
- Adjacent faces reference the strict chain-interior intermediates for concave profiles.
- Same for convex profiles.
- Original corner vertex is unreferenced when shaped (cap skip).
- Flat chamfer remains manifold — regression guard for the cap-skip change.
Preview

2.28.1
Highlights
Bevel — Segments + Profile (#298)
New bevel-session controls that appear while a bevel is active (between Cmd+B/Ctrl+B and the commit/cancel click):
- Segments spinbox (1–16): subdivide the chamfer strip into N quads along the profile curve. A single-segment bevel is the classic flat chamfer; raising the count lets you shape the transition.
- 2D Profile graph: a draggable graph control replaces the old profile slider. For N segments you get N-1 interior control points, one per chord intermediate. Drag a point vertically to bulge that slice outward (convex, toward the original sharp edge) or inward (concave, into the solid). Double-click the graph to reset to a flat chamfer. The graph only appears when segments > 1 — a single-segment bevel has no interior points to shape.
The underlying HalfEdgeMesh::bevelEdges API now accepts an optional per-segment profilePoints vector for direct control; when omitted, the existing profile scalar drives a sin-envelope curve for backwards-compatible single-number control. All 30+ existing bevel tests pass unchanged, and 15 new tests cover the vector overload and the per-segment Qt API end-to-end.
Under the hood
EditModeControllerexposesbevelSessionActiveValue,bevelSegmentsValue, andbevelProfilePointsListasQ_PROPERTY(withNOTIFY bevelProfilePointsChanged) so QML bindings receive live values, not method references. Fixes the silent-binding bug where session-aware UI rendered with stale state.- Resizing the segment count now resamples the existing profile points linearly onto the new resolution, preserving the shape the user drew instead of resetting to flat.
- Test fixture uses unique mesh names per test + removes the mesh in TearDown, so the new E2E tests don't trip Ogre's resource-manager "already exists" exception on rerun.
Preview

2.28.0
Highlights
Phase 4 — Bevel Edges (#295)
- Interactive edge bevel tool with
Cmd+B(macOS) /Ctrl+B(Windows/Linux), multi-face corner fan, chamfer-end cap tracking, and a symmetric offset scaffold. - New
BevelGizmofor interactive width drag. - Post-pass hole filler: scans for bevel-introduced boundary loops in a zone around new offset vertices and bevel-touched verts, fan-triangulates closed loops, and uses geometry-first winding (alignment with neighbor triangles).
- Repair zone is expanded by position-coincident duplicates so cross-submesh seam cracks can close on multi-submesh character meshes.
- Walker recovers outer loops after sub-loop extraction at figure-8 junctions and salvages open walks whose endpoints are position-coincident (submesh-seam closure).
- 65 HalfEdge/bevel unit tests — including 30 random smooth-fan variants and multiple character-bevel fixtures — pass on every platform.
macOS Homebrew bundle (#296)
- Fixes
plugin "qtquickcontrols2plugin" not foundon the installed Homebrew bundle. Root cause: QML plugin dylibs kept their Qt-SDK rpath@loader_path/../../../libwhich resolves to a nonexistent path inside a.appbundle. CI now adds@executable_path/../Frameworksto every plugin dylib underContents/PlugIns/qml/aftermacdeployqtruns so plugin framework dependencies resolve at runtime.
WinGet publishing (#293)
- Switched release automation from manual git push to
wingetcreate --submit, which uses the Contents API (onlypublic_repoPAT scope) instead of the git database API.
Transform / gizmo fixes bundled in #295
- Sub-entity scale path no longer compounds across mouse-move events (mirrors the existing entity undo-previous-factor pattern).
- Edit-mode TS_SCALE now honors snap (matches object-mode).
- After
snapScale, per-component clamp to 0.01 so snapping a near-min delta can't collapse geometry. OgreWidgetgates per-frame gizmo scaling to the active viewport (fixes multi-viewport last-listener-wins).scaleFromSnapshotcomputes soft-selection weights from press-time snapshot positions so vertices near the radius boundary can't drift out of the soft zone mid-drag.
Misc
EditModeControllerdestructor no longer propagates exceptions (SonarCloud S1048).isManifold()test helper now rejects same-direction shared edges, matching its documented invariant.- Cube-perimeter bevel sweep now fails on rejected bevels instead of silently skipping.
Known limitation
Some bevels on curved multi-submesh character meshes (e.g., Mixamo Lead Jab) still produce small sliver holes that are neither closed loops nor seam cracks. Closing those requires preserving source-file quad topology through the import pipeline and running the bevel on logical quads — a larger refactor deferred to a follow-up.
Preview

2.27.1
macOS packaging fix
This is a macOS-only patch release. Installed bundles from 2.20 onward shipped with incomplete QML module support, causing the right-side Inspector panel to render blank and the Material editor list to fail to open on Homebrew-installed copies. Source builds were unaffected.
Fix
- CI now runs
macdeployqton the macOS app bundle after the manual framework copy.macdeployqtscans the bundled QML for imports and pulls in the missing module plugins (QtQuick.Controls,QtQuick.Layouts,QtQuick.Dialogs,QtQuick.Effects,QtQuick.Templates,QtQuick.NativeStyle, …) plus their backing frameworks. - Step now fails the job on
macdeployqterror instead of swallowing the exit code. - Existing
install_name_tool -add_rpathcalls are now idempotent so they don't collide with rpathsmacdeployqtalready set.
Upgrade
- Homebrew:
brew upgrade --cask qtmesheditor - WinGet / Linux / Windows bundles unchanged from 2.27.0 — no functional change on those platforms.
2.27.0
Phase 4: Extrude
New mesh editing operation: Extrude faces in Edit Mode.
Features
- Extrude works in Face selection mode for both static and skeletal meshes
- Cmd+E (macOS) / Ctrl+E (Linux/Windows) keyboard shortcut, or click the Extrude button in the Inspector panel
- Full undo/redo support
- Preserves textures, normals, tangents (bump maps), bone weights, and animations through the topology change
- New vertices are offset 0.01 along the face normal so the extruded face is immediately visible — drag with W to translate
Bonus
- Fixed WinGet CI publishing: releases 2.21–2.26.3 silently failed to publish to microsoft/winget-pkgs (403 Permission denied on git push). Fixed with token-embedded URL.
Internal
- New HalfEdgeMesh data structure powering topology operations (45 unit tests)
- Refactored helpers in HalfEdgeMesh and EditableMesh remove ~285 lines of duplicated buffer / edge-rebuild code
Known limitations
- Edge / vertex extrude not yet wired up (face only for now)
Preview
