Skip to content

Commit 0f2547e

Browse files
fernandotononclaudecursoragent
authored
feat(#407): native auto-rigging (Pinocchio is LGPL → native template embedding) (#754)
* feat(#407): native auto-rig core + CLI rig subcommand Pinocchio (Baran & Popović 2007) is LGPL-2.1, which conflicts with the project's statically-linked permissive-distribution stance — so, like #401 (Instant Meshes) and #402 (libigl/TetGen), this is a native from-scratch implementation of the published *algorithm* (skeleton-template embedding), zero new deps. - AutoRig (src/AutoRig.h/.cpp): Ogre-free pure-data core — built-in templates (humanoid 19-bone / biped / quadruped / generic), fitTemplate() maps a template's normalised joint graph into the mesh AABB then recentres flagged joints toward per-height-slab centroids (spine→medial line, limb roots inside the silhouette). rigEntity() builds an Ogre::Skeleton (parent-relative bone positions, setBindingPose), binds via mesh->_notifySkeleton + entity ->_initialise(true) — the _initialise is REQUIRED or the exporters (both gate on entity->hasSkeleton()) silently drop the new rig. - AutoRigController (QML singleton, mirrors SkinWeightsController) for the GUI. - CLI: `qtmesh rig <file> [--skeleton T] [--skin] [--up-axis x|y|z] -o out` (cmdRig) — import, rig, optionally chain SkinWeights::computeAndApply, export. Registered in run() dispatch + AppLaunchHandler subcommand list. - AutoRig_test.cpp: pure-data unit tests (template well-formedness, AABB containment, vertical ordering, degenerate-input robustness, string/JSON). - Sentry breadcrumb ai.assist.auto_rig. Verified end-to-end: static OBJ -> 19-bone humanoid + skin -> glTF export with 1 skin / 17 joints; FBX export carries the skeleton too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#407): MCP auto_rig tool + GUI Auto-Rig + rig CLI tests + docs - MCP: `auto_rig` { template, skin?, up_axis?, output_path? } (MCPServer::toolAutoRig) — rigs the selected static mesh, optional skin chain + optional re-export. Registered + advertised. Breadcrumb ai.assist.auto_rig. - GUI: AutoRigDialog.qml (template + up-axis pickers, "also skin" checkbox) driven by AutoRigController; new "Rigging" CollapsibleSection in Animation Mode → Mode Tools, gated on AutoRigController.hasRiggableSelection (a static mesh — already-rigged meshes show "Skinning" instead). Lazy-loaded Loader + openAutoRigDialog(), registered in qml_resources.qrc. - Tests: CLIPipeline_cmdrig_coverage_test.cpp (arg-validation + file-missing branches need no GL; success path skips gracefully without Xvfb). - CLAUDE.md: CLI examples (skin + rig), recognized-subcommand list, and a full AutoRig architecture entry (incl. the LGPL→native rationale, the _initialise(true) export gotcha, and documented quality limits). App + UnitTests build clean on macOS arm64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): compile AutoRig.cpp into the test common lib unit-tests-linux failed to link: AutoRig::* symbols undefined in libqtmesh_test_common.a (MCPServer::toolAutoRig and CLIPipeline::cmdRig reference them). The test target has its own TEST_SRC_FILES list separate from the app's src/CMakeLists.txt — add AutoRig.cpp + AutoRigController.cpp there, next to SkinWeights (same omission class as #738's PbrMapSynth gap). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: re-trigger CI for #407 (missed synchronize webhook) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(gui): blank QML panels + white models in installed builds (#755) * fix(gui): blank QML panels + white models in installed builds Two packaging/runtime bugs that only manifested in installed builds (Homebrew .app, .deb) — dev SDK runs masked both: 1. Blank white QML panels (Inspector / Context / Material docks). The Qt Quick *software* scene-graph backend was forced in the MainWindow ctor — AFTER QApplication, by which point Qt has already locked the default RHI. Moved QSG_RHI_BACKEND / setGraphicsApi(Software) to the top of main(), before QApplication (the only point where it takes effect). Replaced the now-dead call in mainwindow.cpp with a note. 2. White / untextured models in the macOS .app. Relative resource locations from resources.cfg were resolved against macBundlePath() (the .app bundle ROOT), but the media tree lives under Contents/MacOS/media (== applicationDirPath()). So <App>.app/media/... didn't exist, Ogre loaded no RTSS GLSL programs or textures, and every mesh rendered flat white. Resolve relative paths against applicationDirPath() first (matches Linux), with the bundle root kept as a fallback; only add a location if it exists. Verified: a macdeployqt'd bundle now loads media from Contents/MacOS/media and renders the mage.glb fully textured. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version to 3.9.1 (installed-build QML + macOS white-model fix) Bugfix release: QML software-backend set before QApplication, and macOS .app resource paths resolved against applicationDirPath() instead of the bundle root. Synced README + qtmesh action ref (verify-doc-versions gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): address review — QML registration, upAxis, error paths Code review (Codex + CodeRabbit) on the auto-rig PR: - CRITICAL: register AutoRigController as a QML singleton in mainwindow.cpp (PropertiesPanel URI) like the sibling controllers + add its kill(). With qt_add_qml_module disabled, QML_SINGLETON alone doesn't expose it, so the Rigging section/dialog would ReferenceError. Verified no error at runtime now. - CRITICAL: the dialog's Up-axis picker was ignored — autoRigSelected() didn't take upAxis. Added a `const QString& upAxis` param (controller maps x/y/z → Options::upAxis) and pass dialog.upAxes[dialog.upAxisIndex] from QML. - AutoRig::appendPositions: guard a null vbuf->lock() (shrink `out` back, return false) instead of dereferencing. - AutoRig::rigEntity: on _initialise failure, detach the half-built skeleton (mesh->_notifySkeleton(null)) before removing it, so hasSkeleton() resets and a retry / exporter doesn't pick up a partial rig. - MCP toolAutoRig: validate output_path type; a requested skin that fails is now a hard error (no unskinned export reported as success); export wrapped in the try/catch (also catches std::exception); Sentry breadcrumb no longer logs the full output path. - PropertiesPanel openAutoRigDialog(): handle Loader.Error to allow retry. App + UnitTests build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: key macOS caches on the resolved Xcode version (fix libz.tbd mismatch) build-macos kept failing with "No rule to make target '.../Xcode_26.5.../libz.tbd'" even after the Pin-Xcode step: the producer (build-n-cache-ogre-macos) resolved "newest" to Xcode 26.3 on its runner image and cached OGRE with 26.3's absolute libz.tbd path, while the consumer (build-macos) resolved 26.5 and linked against the missing path. "newest" (sort -V | tail -1) is NOT deterministic across the per-job runner images. Fold the resolved Xcode app name into XCODE_TAG (exported by the Pin step) and append it to all macOS assimp/ogre cache keys + restore-keys. A consumer on a different Xcode now cache-misses and rebuilds OGRE/Assimp against its own SDK instead of linking a stale path. (Belongs on master too — same deploy.yml.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: bust stale macOS OGRE cache (xcode263) — fixes libz.tbd link error build-macos (incl. the 3.9.1 release deploy) failed: No rule to make target '.../Xcode_26.5/.../libz.tbd', needed by QtMeshEditor Diagnosis: the Pin-Xcode step reliably selects Xcode 26.3 on ALL macOS jobs (verified across runs), so compilation is consistent — but the restored OGRE cache was built earlier under Xcode 26.5 and its CMake export hardcodes 26.5's libz.tbd path. Restoring that into a 26.3 build breaks the link. Fix: bump MACOS_CACHE_VERSION xcode26b → xcode263 so OGRE/Assimp are rebuilt under the currently-pinned Xcode (26.3) and the stale 26.5 cache is discarded. Also reverted the earlier XCODE_TAG-in-cache-key experiment: build-macos only RESTORES the OGRE cache (no rebuild step), so a per-job Xcode-keyed miss would leave it with no OGRE at all ("Could not find OGRE"). With Xcode pinned consistently, a plain version bump is the correct, sufficient fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: self-heal macOS OGRE cache across differing per-job Xcode images The real cause of the build-macos libz.tbd failure: the producer (build-n-cache-ogre-macos) and consumer (build-macos) run on DIFFERENT runner images whose "newest Xcode" differs — producer resolved Xcode 26.5 and cached OGRE with 26.5's absolute libz.tbd path baked into its CMake export; consumer resolved 26.3 and linked against the missing 26.5 path. Just pinning "newest" or bumping the cache version doesn't help because the two images disagree. Fix (self-healing): - Fold the resolved Xcode app name into XCODE_TAG and append it to all macOS assimp/ogre cache keys + restore-keys, so a job only restores a cache built under its OWN Xcode. - Give build-macos (consumer) the same "check out + build OGRE on cache miss" steps the producer has. When the consumer's Xcode differs from the producer's (cache miss), it rebuilds OGRE under its own SDK instead of failing on a stale libz.tbd path. This makes the macOS build robust regardless of which Xcode each runner image ships. (Bigger than the earlier one-line bump, but that couldn't fix a cross-image Xcode disagreement.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: keep assimp macOS cache Xcode-agnostic (only ogre is Xcode-keyed) Previous commit Xcode-keyed BOTH the assimp and ogre macOS caches. That broke build-macos on a runner whose Xcode differed from the producer's: assimp cache-missed (no assimp-build-on-miss exists) so find_package(assimp) failed with "Could not find a package configuration file provided by assimp". Assimp is a plain static lib that doesn't bake absolute SDK paths, so one assimp cache is valid across Xcode versions — revert XCODE_TAG on the 3 assimp keys, keeping it ONLY on the 2 ogre keys (ogre's CMake export DOES bake an absolute libz.tbd path, which is why ogre needs per-Xcode keying + the consumer's rebuild-on-miss). The shared assimp cache is then always present for the ogre rebuild to link against. Verified on the failing run: Qt + OGRE now resolve and link (no libz.tbd error); this removes the remaining assimp-not-found failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: fix flaky MainWindowTest.ModeBarLoadsAndModeChange (show window first) This test failed intermittently on CI (Xvfb) with: Value of: window->m_modeBarShell->isHidden() Actual: true Expected: false The fixture constructs MainWindow but never show()s it. QToolBar::isHidden() reflects effective visibility, which is only realized once the parent window is mapped — so under Xvfb the shell reports hidden and the assertion is racy. It hit BOTH this branch and the unrelated CI-only PR #756 (which has no source changes), confirming it's a pre-existing flake, not a regression. Fix: show() the window and processEvents() before the visibility assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: pin SDKROOT so CMake ZLIB resolves under the selected Xcode (macOS) build-macos still failed with the Xcode_26.5 libz.tbd path even after pinning DEVELOPER_DIR=Xcode_26.3 and rebuilding OGRE: CMake's find_package(ZLIB) resolved to the SDK that `xcrun` defaults to (26.5 on these images) rather than the xcode-select'd one, so the OGRE SDK's CMake export baked a 26.5 libz.tbd path that the cache then carried forward. Fix: export SDKROOT (from `xcrun --sdk macosx --show-sdk-path` under the pinned Xcode) in the Pin step, so clang AND CMake resolve system libs under the SAME pinned SDK on every macOS job. Bump MACOS_CACHE_VERSION → sdkpin1 to discard the OGRE caches that still carry the 26.5 path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: bust stale macOS OGRE cache (xcode263) — unblock 3.9.1 macOS deploy (#756) * ci: bust stale macOS OGRE cache (xcode263) — unblock the macOS deploy The 3.9.1 release deploy failed on build-macos: No rule to make target '.../Xcode_26.5/.../libz.tbd', needed by QtMeshEditor The Pin-Xcode step selects Xcode 26.3 consistently on all macOS jobs, but the OGRE cache under key 'xcode26b' was built earlier under Xcode 26.5 and its CMake export hardcodes 26.5's libz.tbd path. Restoring it into a 26.3 build breaks the link. Bump MACOS_CACHE_VERSION xcode26b → xcode263 so OGRE/Assimp rebuild under the pinned 26.3 and the stale cache is discarded. (Windows + Linux .deb artifacts already published for 3.9.1; this lets the macOS artifact + Homebrew cask update complete on a deploy re-run.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: self-heal macOS OGRE cache across differing per-job Xcode images The real cause of the build-macos libz.tbd failure: the producer (build-n-cache-ogre-macos) and consumer (build-macos) run on DIFFERENT runner images whose "newest Xcode" differs — producer resolved Xcode 26.5 and cached OGRE with 26.5's absolute libz.tbd path baked into its CMake export; consumer resolved 26.3 and linked against the missing 26.5 path. Just pinning "newest" or bumping the cache version doesn't help because the two images disagree. Fix (self-healing): - Fold the resolved Xcode app name into XCODE_TAG and append it to all macOS assimp/ogre cache keys + restore-keys, so a job only restores a cache built under its OWN Xcode. - Give build-macos (consumer) the same "check out + build OGRE on cache miss" steps the producer has. When the consumer's Xcode differs from the producer's (cache miss), it rebuilds OGRE under its own SDK instead of failing on a stale libz.tbd path. This makes the macOS build robust regardless of which Xcode each runner image ships. (Bigger than the earlier one-line bump, but that couldn't fix a cross-image Xcode disagreement.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: keep assimp macOS cache Xcode-agnostic (only ogre is Xcode-keyed) Previous commit Xcode-keyed BOTH the assimp and ogre macOS caches. That broke build-macos on a runner whose Xcode differed from the producer's: assimp cache-missed (no assimp-build-on-miss exists) so find_package(assimp) failed with "Could not find a package configuration file provided by assimp". Assimp is a plain static lib that doesn't bake absolute SDK paths, so one assimp cache is valid across Xcode versions — revert XCODE_TAG on the 3 assimp keys, keeping it ONLY on the 2 ogre keys (ogre's CMake export DOES bake an absolute libz.tbd path, which is why ogre needs per-Xcode keying + the consumer's rebuild-on-miss). The shared assimp cache is then always present for the ogre rebuild to link against. Verified on the failing run: Qt + OGRE now resolve and link (no libz.tbd error); this removes the remaining assimp-not-found failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: fix flaky MainWindowTest.ModeBarLoadsAndModeChange (show window first) This test failed intermittently on CI (Xvfb) with: Value of: window->m_modeBarShell->isHidden() Actual: true Expected: false The fixture constructs MainWindow but never show()s it. QToolBar::isHidden() reflects effective visibility, which is only realized once the parent window is mapped — so under Xvfb the shell reports hidden and the assertion is racy. It hit BOTH this branch and the unrelated CI-only PR #756 (which has no source changes), confirming it's a pre-existing flake, not a regression. Fix: show() the window and processEvents() before the visibility assertion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: pin SDKROOT so CMake ZLIB resolves under the selected Xcode (macOS) build-macos still failed with the Xcode_26.5 libz.tbd path even after pinning DEVELOPER_DIR=Xcode_26.3 and rebuilding OGRE: CMake's find_package(ZLIB) resolved to the SDK that `xcrun` defaults to (26.5 on these images) rather than the xcode-select'd one, so the OGRE SDK's CMake export baked a 26.5 libz.tbd path that the cache then carried forward. Fix: export SDKROOT (from `xcrun --sdk macosx --show-sdk-path` under the pinned Xcode) in the Pin step, so clang AND CMake resolve system libs under the SAME pinned SDK on every macOS job. Bump MACOS_CACHE_VERSION → sdkpin1 to discard the OGRE caches that still carry the 26.5 path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: pin EXACT Xcode 26.3 on all macOS jobs (stop per-image newest drift) (#758) Even with SDKROOT pinned, build-macos failed because the macOS jobs ran on runner images with different *newest* Xcodes: the ogre/assimp producers sometimes resolved Xcode 26.5 (newest on their image) and cached an SDK whose Codec_Assimp/ogre CMake export hardcodes 26.5's libz.tbd path, while the consumer (26.3) couldn't link it. "sort -V | tail -1" is non-deterministic across images. Pin a SPECIFIC Xcode (26.3) that's present on all current macos-latest images, falling back to newest only if absent — so producers and consumer always agree on the SDK. Bump MACOS_CACHE_VERSION → xc263pin to discard the assimp+ogre caches that still carry a 26.5 path. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(import): restore textured viewport and paint preview on File→Open (#757) * fix(import): restore textured viewport and paint preview on File→Open Rebind RTSS materials for newly imported entities (same path cloud downloads already used) and load texture-paint buffers from embedded/disk sources before GPU readback, which fails for many imported FBX textures on Linux. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(import): scope texture rebind per entity source file Use each mesh's qtme.source_path binding for sidecar texture lookup so multi-file File→Open imports cannot cross-bind common texture basenames. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: bump version to 3.9.2 Sync README and website pinned refs via sync-doc-versions-from-cmake.sh. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> * feat(#407): native auto-rig core + CLI rig subcommand Pinocchio (Baran & Popović 2007) is LGPL-2.1, which conflicts with the project's statically-linked permissive-distribution stance — so, like #401 (Instant Meshes) and #402 (libigl/TetGen), this is a native from-scratch implementation of the published *algorithm* (skeleton-template embedding), zero new deps. - AutoRig (src/AutoRig.h/.cpp): Ogre-free pure-data core — built-in templates (humanoid 19-bone / biped / quadruped / generic), fitTemplate() maps a template's normalised joint graph into the mesh AABB then recentres flagged joints toward per-height-slab centroids (spine→medial line, limb roots inside the silhouette). rigEntity() builds an Ogre::Skeleton (parent-relative bone positions, setBindingPose), binds via mesh->_notifySkeleton + entity ->_initialise(true) — the _initialise is REQUIRED or the exporters (both gate on entity->hasSkeleton()) silently drop the new rig. - AutoRigController (QML singleton, mirrors SkinWeightsController) for the GUI. - CLI: `qtmesh rig <file> [--skeleton T] [--skin] [--up-axis x|y|z] -o out` (cmdRig) — import, rig, optionally chain SkinWeights::computeAndApply, export. Registered in run() dispatch + AppLaunchHandler subcommand list. - AutoRig_test.cpp: pure-data unit tests (template well-formedness, AABB containment, vertical ordering, degenerate-input robustness, string/JSON). - Sentry breadcrumb ai.assist.auto_rig. Verified end-to-end: static OBJ -> 19-bone humanoid + skin -> glTF export with 1 skin / 17 joints; FBX export carries the skeleton too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#407): MCP auto_rig tool + GUI Auto-Rig + rig CLI tests + docs - MCP: `auto_rig` { template, skin?, up_axis?, output_path? } (MCPServer::toolAutoRig) — rigs the selected static mesh, optional skin chain + optional re-export. Registered + advertised. Breadcrumb ai.assist.auto_rig. - GUI: AutoRigDialog.qml (template + up-axis pickers, "also skin" checkbox) driven by AutoRigController; new "Rigging" CollapsibleSection in Animation Mode → Mode Tools, gated on AutoRigController.hasRiggableSelection (a static mesh — already-rigged meshes show "Skinning" instead). Lazy-loaded Loader + openAutoRigDialog(), registered in qml_resources.qrc. - Tests: CLIPipeline_cmdrig_coverage_test.cpp (arg-validation + file-missing branches need no GL; success path skips gracefully without Xvfb). - CLAUDE.md: CLI examples (skin + rig), recognized-subcommand list, and a full AutoRig architecture entry (incl. the LGPL→native rationale, the _initialise(true) export gotcha, and documented quality limits). App + UnitTests build clean on macOS arm64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): compile AutoRig.cpp into the test common lib unit-tests-linux failed to link: AutoRig::* symbols undefined in libqtmesh_test_common.a (MCPServer::toolAutoRig and CLIPipeline::cmdRig reference them). The test target has its own TEST_SRC_FILES list separate from the app's src/CMakeLists.txt — add AutoRig.cpp + AutoRigController.cpp there, next to SkinWeights (same omission class as #738's PbrMapSynth gap). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): address review — QML registration, upAxis, error paths Code review (Codex + CodeRabbit) on the auto-rig PR: - CRITICAL: register AutoRigController as a QML singleton in mainwindow.cpp (PropertiesPanel URI) like the sibling controllers + add its kill(). With qt_add_qml_module disabled, QML_SINGLETON alone doesn't expose it, so the Rigging section/dialog would ReferenceError. Verified no error at runtime now. - CRITICAL: the dialog's Up-axis picker was ignored — autoRigSelected() didn't take upAxis. Added a `const QString& upAxis` param (controller maps x/y/z → Options::upAxis) and pass dialog.upAxes[dialog.upAxisIndex] from QML. - AutoRig::appendPositions: guard a null vbuf->lock() (shrink `out` back, return false) instead of dereferencing. - AutoRig::rigEntity: on _initialise failure, detach the half-built skeleton (mesh->_notifySkeleton(null)) before removing it, so hasSkeleton() resets and a retry / exporter doesn't pick up a partial rig. - MCP toolAutoRig: validate output_path type; a requested skin that fails is now a hard error (no unskinned export reported as success); export wrapped in the try/catch (also catches std::exception); Sentry breadcrumb no longer logs the full output path. - PropertiesPanel openAutoRigDialog(): handle Loader.Error to allow retry. App + UnitTests build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#407): Mixamo-style marker placement + undoable rig/skin Marker-guided auto-rig refinement: the user clicks 10 humanoid markers on the mesh in the viewport (chin, L/R shoulder, L/R wrist, L/R hip, L/R knee, pelvis) and each anchors its joint while the limb/spine chains interpolate between the anchors — so the rig follows real (incl. cartoon) proportions instead of the fixed proportional template. - AutoRig::fitTemplateWithMarkers + layChain (generic anchor→tip chain): arms lay shoulder→arm→forearm→hand toward the wrist; legs lay hip socket→knee→foot; spine distributes Spine/Chest/Neck evenly between the marked pelvis and chin; hips carry unmarked thigh roots, explicit hip markers override. Every marker optional (empty set ≡ fitTemplate). - AutoRigController marker session (begin/skip/undo/cancel/commit) with ray-picked PT_SPHERE overlays; clicks routed via TransformOperator before the knife/select paths. AutoRigDialog made non-modal so viewport clicks reach the scene; onClosing cancels any active session. - Undo/redo: AutoRig::unrigEntity + AutoRigCommand wrap rig (+ optional skin) in one undoable unit; both GUI paths push through UndoManager. Single Ctrl+Z reverts rig and skin together. - Skeleton section extracted in the Inspector (skeleton/weights toggles no longer gated on animations). - Tests: marker order/labels, empty≡fitTemplate, arm/leg chain layout, shoulder/hip anchoring, spine interpolation, command error/undo branches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#407): move auto-rig UI inline into Inspector + fix Skip - Replace the modal AutoRigDialog with an inline Rigging section in the Inspector (riggingToolsComponent). Skeleton-type picker is a primary control; up-axis stays under "Advanced options". Smart show/hide: idle shows entry points + options, marker mode swaps to the guidance label + Skip/Undo/Cancel/Rig-from-markers controls. Section cancels any active marker session when it disappears (replaces the dialog's onClosing). - Markers only offered for the humanoid template (they're humanoid-specific). - Fix Skip: marker progress is now a CURSOR into the order list. Skip advances the cursor past a slot without storing a marker (joint keeps the template fit); the old code pushed an unset placeholder that didn't count as resolved, so the cursor stuck and Skip did nothing. Place advances + stores; Undo steps back, dropping the marker if that slot was placed. New markerPlacedCount drives "Rig from markers"; markerCount = resolved slots (placed + skipped) for the N/total readout. - Remove qml/AutoRigDialog.qml + its qrc entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): undoable rig crash — strip blend vertex elements on unrig Rigging with skin runs Ogre's _compileBoneAssignments, which adds BLEND_INDICES/BLEND_WEIGHTS vertex elements. On undo, clearing the bone-assignment list is NOT enough — Ogre only removes those elements inside compileBoneAssignments, which it skips when the list is empty, so the declaration kept advertising blend elements while the entity had no skeleton → null SkeletonInstance deref on the next _initialise/render. AutoRig::unrigEntity now explicitly strips the blend elements (unbind the buffer + removeElement) on shared + per-submesh vertex data, leaving a genuinely static mesh. AutoRigCommand also calls AutoRigController::notifyRiggingChanged on redo/undo — on undo BEFORE detaching, so any active skeleton-debug overlay tears down while the skeleton still exists (else it dangles), and the Inspector re-evaluates the Rigging/Skeleton sections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(#407): coherent marker inference + mesh-bounds clamping Replace per-marker patching with a resolve-then-lay model: fitTemplate gives a proportional baseline + template segment vectors, then every key joint (Head, L/R Shoulder, L/R Hand, Hips, L/R UpLeg, L/R Knee) is resolved marked → inferred-from-marked-neighbours → template, and the dependent chains (spine/arms/legs) are laid from those anchors. A partial marker set now yields an anatomically-sane skeleton instead of stranding unmarked joints at the template (no more shoulder-above-head). Inference: Hips ← up-leg midpoint + template rise; Head ← template offset above Hips; UpLeg ← mirror the other / pelvis + socket offset; Shoulder ← along the live Hips→Head line at the template height fraction + lateral offset (chin+hips imply sane shoulders), else mirror; Hand ← shoulder + template arm vector (marked shoulder + skipped wrist still lays a full arm); mirroring reflects across the auto-detected sagittal plane. Mesh-bounds clamp: inferred legs no longer punch through the model. Knee skipped → foot drops to the mesh floor (AABB mn[up]) below the up-leg, knee halfway between; marked knee → foot extrapolated but floor-clamped; knee always kept between up-leg and foot in the up axis. Empty marker set still early-returns fitTemplate unchanged. Tests added for each inference rule + the floor clamp. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(#407): update stale 6-marker test to 10; bust stale macOS assimp cache - AutoRigMarkers.OrderAndLabelsAreStable still asserted 6 markers (and order[5]==Hips) from the first marker commit; the set grew to 10 (added L/R shoulder + L/R hip). unit-tests-linux caught it. Assert 10 and use front()/back() so the count is the single source of truth. - Bump MACOS_CACHE_VERSION sdkpin1→sdkpin2: the assimp macOS cache was built under Xcode 26.5 and baked .../MacOSX26.5.sdk/.../libz.tbd into Codec_Assimp, so OGRE's cache-miss rebuild under the pinned 26.3 failed with "No rule to make target .../libz.tbd". Busting the cache rebuilds assimp under the pinned SDK. (Pre-existing CI infra issue, not from this feature.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 23e0ba2 commit 0f2547e

23 files changed

Lines changed: 3371 additions & 62 deletions

.github/workflows/deploy.yml

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ env:
2424
OGRE_VERSION: '14.5.2'
2525
# Bump to bust the macOS assimp/ogre caches. The cached OGRE/Assimp SDKs bake
2626
# an absolute Xcode SDK path (e.g. .../usr/lib/libz.tbd) into their CMake
27-
# export. The Pin-Xcode step below selects a CONSISTENT Xcode across all macOS
28-
# jobs (currently 26.3), but a cache built earlier under a different Xcode
29-
# (26.5) still carries that old libz.tbd path and, when restored into a 26.3
30-
# build, fails with "No rule to make target '.../Xcode_26.5/...libz.tbd'"
31-
# (this broke the 3.9.1 macOS deploy). Bump this whenever the pinned Xcode
32-
# changes so the SDK is rebuilt against it. (xcode263 = under Pin step Xcode 26.3.)
33-
MACOS_CACHE_VERSION: 'xc263pin'
27+
# export. The Pin-Xcode step also pins SDKROOT so CMake's ZLIB resolves under
28+
# the selected Xcode (xcode-select alone didn't stop find_package(ZLIB) from
29+
# picking xcrun's default 26.5 SDK). Bump this whenever the pinned Xcode/SDK
30+
# changes so the SDK is rebuilt against it and stale libz.tbd paths are
31+
# discarded. (sdkpin1 = first build under the SDKROOT-pinned environment;
32+
# sdkpin2 = bust the stale assimp cache that still baked the Xcode 26.5
33+
# libz.tbd path — "No rule to make target .../MacOSX26.5.sdk/.../libz.tbd"
34+
# when OGRE consumed it under the pinned 26.3.)
35+
MACOS_CACHE_VERSION: 'sdkpin2'
3436

3537
jobs:
3638
# send-slack-notification:
@@ -1574,20 +1576,11 @@ jobs:
15741576
steps:
15751577
- name: Pin newest stable Xcode (consistent SDK across all macOS jobs)
15761578
run: |
1577-
# All macOS jobs MUST use the SAME exact Xcode/SDK: the OGRE/Assimp
1579+
# All three macOS jobs must use the SAME Xcode/SDK: the OGRE/Assimp
15781580
# builds bake the active SDK's absolute libz.tbd path into their
1579-
# CMake export, and the consumer links against it. The per-job runner
1580-
# images carry different *newest* Xcodes (one job got 26.5, another
1581-
# 26.3), so "sort -V | tail -1" (newest) made jobs disagree and the
1582-
# cached SDK's baked path failed to link → "No rule to make target
1583-
# '.../Xcode_XX/...libz.tbd'". Pin a SPECIFIC version present on all
1584-
# current images (26.3); fall back to newest only if it's absent.
1585-
PIN="/Applications/Xcode_26.3.app/Contents/Developer"
1586-
if [ -d "$PIN" ]; then
1587-
DEV="$PIN"
1588-
else
1589-
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
1590-
fi
1581+
# CMake export, and the consumer build links against it. Different
1582+
# default Xcodes per job → "No rule to make target '<SDK>/libz.tbd'".
1583+
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
15911584
if [ -z "$DEV" ]; then DEV="$(xcode-select -p)"; fi
15921585
echo "Selected Xcode: $DEV"
15931586
sudo xcode-select -s "$DEV"
@@ -1659,14 +1652,7 @@ jobs:
16591652
steps:
16601653
- name: Pin newest stable Xcode (consistent SDK across all macOS jobs)
16611654
run: |
1662-
# Pin a SPECIFIC Xcode present on all macos-latest images (26.3) so
1663-
# producers and consumer agree on the SDK; "newest" drifts per image.
1664-
PIN="/Applications/Xcode_26.3.app/Contents/Developer"
1665-
if [ -d "$PIN" ]; then
1666-
DEV="$PIN"
1667-
else
1668-
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
1669-
fi
1655+
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
16701656
if [ -z "$DEV" ]; then DEV="$(xcode-select -p)"; fi
16711657
echo "Selected Xcode: $DEV"
16721658
sudo xcode-select -s "$DEV"
@@ -1747,14 +1733,7 @@ jobs:
17471733
steps:
17481734
- name: Pin newest stable Xcode (consistent SDK across all macOS jobs)
17491735
run: |
1750-
# Pin a SPECIFIC Xcode present on all macos-latest images (26.3) so
1751-
# producers and consumer agree on the SDK; "newest" drifts per image.
1752-
PIN="/Applications/Xcode_26.3.app/Contents/Developer"
1753-
if [ -d "$PIN" ]; then
1754-
DEV="$PIN"
1755-
else
1756-
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
1757-
fi
1736+
DEV=$(ls -d /Applications/Xcode_*.app/Contents/Developer 2>/dev/null | sort -V | tail -1)
17581737
if [ -z "$DEV" ]; then DEV="$(xcode-select -p)"; fi
17591738
echo "Selected Xcode: $DEV"
17601739
sudo xcode-select -s "$DEV"

CLAUDE.md

Lines changed: 5 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)