Releases: fernandotonon/QtMeshEditor
3.2.0
Paint tools — image editing, smart-select, detached editor, per-tool toolbar
This release wraps three rounds of paint-tool work into one cut.
Foreground / Background colors (toolbar swatch)
- Photoshop / GIMP-style FG/BG swatch in the left toolbar with click-to-pick, ⇄ swap, ◰ reset.
- Defaults: FG = Fern green (
#71BC78), BG = black. - Erase tool now paints with the BG color (set BG alpha to 0 for the old "erase to transparent" behaviour).
Smart-select (magic wand)
- New ✨/wand tool in the toolbar with click-and-drag tolerance scrubbing — drag horizontally during the stroke to widen / narrow the selection live, no separate slider.
- Selection mask renders on both the 2D thumbnail (yellow tint + black outline) and the 3D model (matching textured overlay).
- Mask actions (Fill FG / Fill BG / Delete / Invert / All / None) all undoable.
- Click outside the mesh while Wand is active clears the selection.
Detached Texture Editor window
- Full-size paint canvas opened from the Inspector — live-syncs with the 3D viewport in both directions.
- Bottom action bar: Save / Load / Save to Original / mask actions.
- UV-island toggle on the top toolbar (shared state with the inspector thumbnail).
Per-tool toolbar (3.2.0)
- Brush, Wand, Bucket, Eraser, Eyedropper, Smudge each have their own left-toolbar button. Green when paint mode is on + target is Texture, grey otherwise.
- Brush button is now a tool selector — the Off/Vertex/Texture switch in the right Inspector is the only master gate.
- Square brush shape added to the brush popup. Axis-aligned constant-strength stamp in 2D, cube AABB in 3D. Hover outline matches the shape.
- Brush radius outline drawn on both the 2D thumbnail and the detached editor canvas.
Non-destructive paint
- Strokes no longer overwrite the source texture file. Painted pixels live in memory +
EmbeddedTextureCache(used by FBX/glTF export) until the user explicitly clicks Save to Original or saves to a new file.
Stability
- Mesh removal while paint or wand-selection is active no longer crashes.
- Vertex → Texture target switch tears down the texture session cleanly.
- Overlay cleanup no longer dereferences potentially-freed entities.
- Per-tool toolbar wiring no longer captures a stack-local vector by reference (was crashing on the Texture-mode toggle mid-iteration).
Tests
- New
PaintSelectionMask_test(10 cases) covering smart-select, combine modes, bbox/count caching. - 6 new cases for the Square brush across
TexturePaintBuffer_testandEditableMesh_test.
Pull requests included: #529, #530, #531.
Preview
monk_to_mage_gif.mp4
3.1.0
Summary
QtMeshEditor 3.1.0 includes the merged work since 3.0.0:
#494GPU memory and VRAM reporting#495Material Editor dark mode on Linux and opt-in scan simplify#496textured RSD import/export round-trip#497draw-call analysis and merge suggestions#498vertex-cache optimization and ACMR reporting#499single-pass mesh decimation#500smooth vertex colours and MAT lit/unlit support#501Ogre-backed ACMR and per-file cleanup#502retire Assimp from inspection and route throughextractMeshInfo#503scene-save n-gon round-trip plus the3.1.0version bump#504finish-line Ogre-only--fixpath and five new quality rules
Highlights
- Scene save/load now preserves n-gon face bindings through
.scene.gltfand.scene.glb. - PS1/RSD support now keeps textured round-trips and lit/unlit state intact.
- Performance inspection and scan tooling moved further toward Ogre-backed metrics and cleanup.
- The release version and documentation pins are aligned to
3.1.0.
Preview
RSD Textures, lit/unlit, vertex color smoothing
| Blender | QtMeshEditor |
|---|---|
![]() |
![]() |
Assets from https://github.com/rubixcube6/PlayStation-RSD-exporter-for-Blender-3.2.1
GPU memory and VRAM reporting, vertex-cache optimization and ACMR reporting
Decimate (reduce the poly count, like LOD, but for the main mesh)

3.0.0
Highlights
This release is a major milestone for QtMeshEditor, with a full UI redesign, PBR material workflows, and several new asset and texture tools.
User interface
- Major UI redesign — refreshed layout, panels, and overall editor experience for a more modern and efficient workflow.
Materials & rendering
- PBR material support — work with physically based materials in the editor and pipeline, aligned with common game-engine expectations.
Import & formats
- PlayStation 1 asset support — import and work with RSD, Psy-Q PLY, and MAT data alongside existing mesh formats (see project documentation for format notes).
Texture & image tools
- Normal map from height — generate tangent-space normal maps from height or bump sources using a Sobel filter, with strength and channel convention options (e.g. OpenGL vs DirectX).
- Texture channel packing — pack multiple grayscale inputs into a single RGB/RGBA texture (e.g. ORM / MR style workflows) with live preview in the material tools.
Upgrade notes
- Application version is 3.0.0 (see
CMakeLists.txt). Update any pinned CI references (fernandotonon/QtMeshEditor@…, Dockerghcr.io/fernandotonon/qtmesh:…) if you rely on semver-pinned workflows.
Preview
Progress
| V1 | V2 | V3 |
|---|---|---|
![]() |
![]() |
![]() |
New UI
Pack Texture Channels
Generate Normal Map
Material Editor
PlayStation files support
Full changelog: compare to the previous release on GitHub, or browse commits on master leading to this tag.
2.36.0
Highlights
Two big things this release:
- Slice E shipped: PBR material templates (#396). Three new presets — Metallic-Roughness, Specular-Glossiness, Unlit PBR — with the canonical 6-slot layout (
albedo / normal_map / metallic / roughness / ao / emissive) wired through GUI, CLI, and MCP. The non-shader slots (albedo,ao,emissive) render via FFP texture-op approximations so they're visibly active in slice E;metallicandroughnessget FFP fakes too, with the real BRDF landing in slice F. - Editor mode surfaces modernized (#432). New mode bar, inspector tabs, bottom context panel, and clearer "is this a node or mesh transform?" indicator.
Slice E — PBR material templates (#396)
Presets
Metallic-Roughness— neutral mid-gray albedo, shininess 40, lighting onSpecular-Glossiness— brighter specular response, shininess 60Unlit PBR— lighting off, white diffuse, all 6 slots present for layout consistency- All three create the 6 canonical TUS slots (
albedo,normal_map,metallic,roughness,ao,emissive) and tag the pass with apbr_workflowuser-object binding so slice F's PBR shader can detect intent without parsing the preset name.
Inspector — new "PBR" category in the Apply Preset panel with M-R, S-G, Unlit chips. Material Editor's 📝 Templates menu also gets three PBR script templates so users can drop the 6-slot layout straight into the script editor.
CLI
qtmesh material model.fbx --preset "Metallic-Roughness" -o out.fbx
qtmesh material --list-presetsWrites a sidecar .material script alongside the output mesh. Non-zero exit codes on missing material or sidecar write failure (used to silently succeed). file.import / file.export breadcrumbs added for parity with other CLI subcommands.
MCP — list_material_presets and apply_material_preset tools. SERVER_VERSION bumped to 1.6.0. Apply tool accepts an optional mesh/entity arg and uses QScopeGuard to restore the prior selection (nodes + sub-entities) even when applyPreset throws.
FFP slot wiring — slice E's "make-something-visible" interim:
| Slot | Op | Effect |
|---|---|---|
albedo |
MODULATE(TEXTURE, DIFFUSE) |
textured base colour |
ao |
MODULATE(TEXTURE, DIFFUSE) |
darkens the lit base (independent of TUS chain order) |
emissive |
ADD(TEXTURE, CURRENT) |
additive glow, visible with ambient = black |
metallic |
ADD_SIGNED(TEXTURE, CURRENT) |
brightens / tints toward metal in textured regions |
roughness |
MODULATE_X2(TEXTURE, CURRENT) |
brightens smooth (low-roughness) regions |
normal_map |
non-FFP | wired through SRS_NORMALMAP via RTShaderHelper::applyNormalMap |
Slice F replaces these with a real PBR SubRenderState — slot names + workflow tag are forward-compatible.
Bug fixes shipped while iterating slice E
- Fix: bone-gizmo branch in
TransformOperator::mousePressEventwas capturing every translate click once a skinned model auto-selected a bone, then silently returning whenboneCanTranslate()was false (rigged non-root bone). Net effect: imported skinned models couldn't be moved at all. Restructured the gate so translate falls through to entity-translate when the selected bone can't translate; rotate/scale always go through the bone path. Extracted decision intoTransformOperator::shouldRouteToBoneGizmofor unit testing. - Fix: opening the Material Editor silently dropped the normal-map effect because
updateMaterialTextremoved all RTSS-generated techniques without re-wiringSRS_NORMALMAP. The next render regenerated a vanilla RTSS technique. Now re-runsRTShaderHelper::applyNormalMapafter dropping the cached techniques. - Fix:
"Metallic-Roughness".startsWith("Metal")matched the legacy Metal preset path before reaching the PBR branch. Reordered exact-name PBR matches to the top of the chain. - Fix:
Material::compile()defaultsautoManageTextureUnits=trueand on Linux CI the first compile in a freshOgre::Rootcould split the 6-TUS pass into multiple hardware passes (cap query returning low value mid-init). Now compiles withautoManageTextureUnits=falseto preserve the structural slot count.
Editor mode surfaces (#432)
- New
EditorModeControllerplus QML mode bar / bottom context panel / inspector tabs. - Transform target now explicitly says "Node Placement" vs. "Mesh Geometry"; edit mode always reports Mesh Geometry.
- Default viewport MSAA bumped to 4×; near-clip default set to 0.01.
- Focused gtest coverage for the new controller, viewport defaults, and dock-state restoration.
Tests + quality
- 5 new tests in
MaterialPresetLibraryTestscovering preset name presence, 6-slot creation, workflow tagging, idempotent re-apply, and per-slot FFP colour-op semantics (PbrSlotColourOpsApproximatePbrSemantics). - 5 new
TransformOperatorTestcases forshouldRouteToBoneGizmocovering no-bone, rotate/scale always-route, translate-respecting-canTranslate (the bug-fix case), and non-transform states. - 5 new
CLIPipelineCmdMaterialErrorcases covering missing args, unknown preset, non-existent file, and the standalone--list-presetsexit. - SonarCloud quality gate: passed. Coverage on new code: 47.8%.
Versioning
CMakeLists.txt already bumped to 2.36.0 by #432. Sentry + macOS bundle plist + WinGet manifest follow automatically.
🤖 Generated with Claude Code
2.35.0
Highlights
The big one this release: the animation curve editor is now production-ready. Edit Bezier curves in-place, then bake them into Ogre TransformKeyFrames so playback matches the visual. Plus full keyboard/trackpad ergonomics, an explicit Bake menu with adaptive and exact-FPS modes, and CLI/MCP exposure for headless export pipelines.
Also: PlayStation 1 .TMD mesh + .TIM texture import.
Curve editor — finished (#392, #395)
Live editing, no more T-pose blink
- Drag keyframe squares by value (Y) or time (X); Shift constrains to the dominant axis.
- Drag tangent handles to reshape curves in real time.
setKeyframeValuePreview/moveKeyframePreviewwrite directly during drag without pushing per-event undo commands; oneSetKeyframeValueCommand+MoveKeyframeCommandlands on release.- Right-click a keyframe square → Bezier / Linear / Stepped / Auto interp-mode menu.
Navigation
- Two-finger trackpad pan (horizontal = time, vertical = value) via
WheelHandlerwithpixelDelta. - Cmd/Ctrl+wheel zooms time, Shift+wheel zooms value, +/− toolbar buttons mirror.
- ⤢ fits the whole animation to the canvas.
- Horizontal
ScrollBar.AlwaysOnfor clips wider than the viewport.
Bake / Reduce
- New themed dropdown matches the inspector look: Sparse / Medium / Dense (adaptive Douglas-Peucker simplification with per-level baseline pre-decimation so repeat clicks converge), and Set to 10 / 15 / 30 / 60 FPS (re-grids to a uniform N-FPS layout regardless of source density).
ResampleCurveCommandsnapshots the original curve before stripping interior keys, so non-resampled channels lerp between bracketing original anchors instead of just the segment endpoints — mid-clip poses survive whole-clip bakes.- Quaternion components written via curve evaluation are now re-normalized so Ogre's track Slerp doesn't drift.
DecimateTrackCommandpowers the reduce path with a single-step undo; first/last keyframes always preserved.
Per-animation Bake in the Inspector animation list, sitting next to the Simplify button. One undo macro covers every bone track. Unauthored channels skipped on adaptive modes; fixed-FPS modes process everything.
CLI + MCP exposure (part of #395)
qtmesh anim model.fbx --bake-fps 30 -o uniform.fbx # re-grid to uniform 30 FPS
qtmesh anim model.fbx --bake-fps 60 --animation \"Run\" -o out.fbxNew MCP tool bake_animation_fps (entity_name / animation_name / fps) — SERVER_VERSION bumped to 1.5.0. Mixamo-style export pipelines can now request a target FPS without opening the GUI.
Animation undo correctness (#390)
- All keyframe undo commands (
BoneTransform,Move,Set,Add,Delete,BulkKeyframe) now resolve their skeleton lazily by entity name through a newSkeletonResolverutility instead of storing a rawOgre::Skeleton*. - Fixes use-after-free crashes when an entity is reloaded or a skeleton is rebuilt while undo entries sit on the stack.
- Undo commands no-op gracefully when the entity is gone.
Bone-drag regression coverage (#393)
Two new BoneDragRelease tests covering the setUpdate-sequence gap:
EntityTickBetweenDragsDoesNotAccumulatedrivesentity->_updateAnimation()+skeleton->_updateTransforms()between two auto-key-off drags to confirm no leaked state.NeedUpdateBetweenDragsDoesNotAccumulateforces a derived-transform recompute mid-sequence.
PlayStation 1 import (#394)
.TMDmesh import/export across GUI + CLI..TIMtexture decode + Material Editor browse-and-load.- Sibling
*.TIMfiles auto-apply to per-importTMD/<name>materials when UVs exist.
Notable fixes
- ThemedComboBox: native top-level popup (Qt 6.8+
popupType: Popup.Window) so long bake menus aren't clipped by their host QQuickWidget; scroll snap-back fixed viahighlightFollowsCurrentItem: false. - Bake-while-paused no longer flashes the model to T-pose after the post-undo skeleton reset.
- Whole-animation Bake @ 60 FPS no longer freezes the UI (per-segment dope-sheet refresh storm coalesced into one).
- The Bake dropdown's last entry was being silently ignored due to a hand-counted upper bound — replaced with
index < model.lengthso the off-by-one can't recur.
Compatibility
- Qt 6.9.3, Ogre 14.5.x, Assimp 6.0.4 — unchanged from 2.34.
- MCP
SERVER_VERSION1.4.0 → 1.5.0.
Preview
PS1 models from: https://mbdesigns.itch.io/ps1-homebrew-source-code-rendering-3d-graphics-with-psyq
2.34.0
Highlights
This release lands Phase 5 of the animation system (#260) — a complete in-app animation authoring loop:
- Bone manipulation gizmo (W/E/R) with auto-key support — drag bones in the viewport, write keyframes automatically.
- Multi-bone dope sheet with multi-select, copy/paste, bulk move.
- Per-channel curve editor with selectable interpolation modes.
- Two-way blend preview + bake-to-clip for layered animation authoring.
- Playback speed + loop region + auto-key toggle in the Animation Control panel.
- Full undo/redo for all keyframe and bone-pose edits.
✨ Features
Bone gizmo with W/E/R triad (#385, #386, #388 — slices 1-3 of #358)
- Click a bone visual in the viewport to select it; rigged + non-rigged bones both pickable.
- W/E/R drives translate/rotate/scale on the selected bone, anchored to the bone's world frame, following animation playback every frame.
- Translation safety: rigged non-root bones blocked (would tear the rig); root bones (locomotion) and unrigged sockets (sword/shield/hat) freely translatable.
- Yellow visualization for skeleton roots in the debug overlay so users can identify them at a glance.
- Auto-key OFF + animation enabled = drag preview (reverts on release). Auto-key OFF + no animation = T-pose authoring (commits to bind via
setInitialState). Auto-key ON = writes keyframe at scrub time. - BlendMask muting during drag prevents the curve from fighting the manual edit.
Animation Phase 5 (epic #260)
- Slice A (#356): playback speed multiplier + loop region (in/out points) + auto-key toggle.
- Slice B (#361): two-way animation blender with live preview slider; bake to a new clip.
- Slice C (#373): multi-bone dope sheet showing all tracked bones at a glance.
- Slice D1 (#375): dope-sheet multi-select with shift/ctrl, copy/paste keyframes (JSON), bulk-move on dt offset.
- Slice D2 (#377): per-channel rows (Tx, Ty, Tz, Rx, Ry, Rz, Rw, Sx, Sy, Sz) so users see and edit individual channels.
- Slice D3a/b (#379, #381): curve editor data model + read-only view, then per-channel data + interpolation-mode picker (Bezier / Linear / Stepped / Auto).
Keyframe authoring fixes
addKeyframecaptures live bone pose (#384): was producing identity-ish keyframes by re-sampling the curve. Now reads the bone's actual local TRS at the slider time.- Auto-key toggle (#384): hands-off recording — every transform commit (scene-node or bone) writes a keyframe at the current scrub time.
- Lazy track creation: editing a non-rigged bone with auto-key on creates the track on first edit, so the pose persists into the animation and survives export.
Undo/redo coverage (#388)
- New
AddKeyframeCommand+DeleteKeyframeCommandmake +KF and -KF round-trippable throughCtrl+Z/Ctrl+Shift+Z. - New
BoneTransformCommandwithbindModeflag covers both transient gizmo edits and bind-pose updates (setInitialState). - The
SkeletonDebugoverlay refreshes immediately on undo/redo (was stale until the next animation tick). - Bone selection preserved across undo/redo (was resetting to first bone).
Vertex paint UX (#351)
QML falloff slider + vertex color preview toggle for the brush tool. Carried over from the 2.33.0 timeframe.
Quad-mesh follow-up (#346, #354)
- Vertex colors export with
.meshsidecar materials (#346). - Catmull-Clark subdivision no longer wipes painted vertex colors (#354).
🐛 Bug fixes
SkeletonDebugtoggle off→on crash (#385): leaked instance viamShowSkeleton.remove()had a stale QTimer touching dangling Ogre entities → SIGSEGV. Nowdelete sd;after the QMap remove.updateAnimationTreecascade reset (#385):QUndoStack::indexChangedwas triggeringselectAnimationon every undo push, which reset the slider + bone selection. Now early-returns when the tree contents are unchanged.- Bone-drag teleport / accumulation (#386): animation tracks were overwriting
_setDerivedPositionedits each frame. Mute the curve viaBlendMaskfor the dragged bone, restore exactly the pre-drag mask values on release (preserves layered/masked setups). - Parentless-bone
_setDerivedPositionis no-op (#386): Ogre skips whenmParent == null. Fall back tosetPosition()for true skeleton roots. - Press-time axis lock (#386): a stale
mTransformVectorfrom the previous drag (or cursor drift onto a neighboring arrow) could lock the wrong axis. Now locked at press time, not on first move. - Crash after undo + slider scrub (#388):
m_selectedTrackwas a dangling pointer afterAddKeyframeCommand::undodestroyed a lazy-created track. NewonUndoRedoCommandApplied()invalidates cached pointers and re-resolves.
🧪 Tests
- 12 new bone-gizmo /
BoneDragReleasetests covering all three release-path semantics (revert / commit / commit-bind), accumulation regressions, and the parentless-bone fallback. - 8 new keyframe-undo tests: all three
AddKeyframeCommandmodes (track-created / keyframe-created / keyframe-updated),DeleteKeyframeCommandround-trip, missing-keyframe defensiveness. - 3 integration tests driving real
addKeyframe/deleteKeyframepaths through the QUndoStack, including a regression for the crash-after-undo scenario. - 4
boneCanTranslatetests (null / root / rigged / unrigged). - Test stability fixes (#355): CLI mesh import + headless Ogre tests.
- Coverage uplift (#359, #348): broader Sonar coverage + ccache for the test build to speed unit-tests-linux.
🔧 Internal
- New
BoneDragReleasehelper class extracts bone-drag end-of-drag rules into testable code (revert / commit / commit-bind decision tree). - Native quad coverage tests (#353) for topology ops audit (#326).
- Extensive Codex / CodeRabbit review feedback addressed across the bone-gizmo PRs.
Filed for follow-up
- #387 (resolved in #388): wrap
addKeyframewrites in an undo command — done. - #389: replace raw
Ogre::Skeleton*in keyframe undo commands with a re-resolveable handle (consistency across all 5 commands; tracked for next iteration).
Preview

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


