Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
908e7df
feat(anim): Mixamo-style arm-space post-process (#854)
fernandotonon Jul 12, 2026
2bb663e
fix(anim): arm-space test crash + live/paused GUI, any-clip targeting…
fernandotonon Jul 12, 2026
3e3c9df
fix(anim): carry arm-space value across animation rename (#854)
fernandotonon Jul 12, 2026
ceaedfa
fix(anim): address arm-space review feedback (#854)
fernandotonon Jul 12, 2026
c24f8b6
test(anim): correct arm-space geometry assertions + debug trace (#854)
fernandotonon Jul 12, 2026
1313034
test(anim): finer arm-space idempotence trace (#854, debug)
fernandotonon Jul 12, 2026
5147bf6
fix(anim): invalidate track cache after arm-space keyframe edits (#854)
fernandotonon Jul 12, 2026
68af9a8
docs: note the keyframe-edit cache-invalidation gotcha (#854)
fernandotonon Jul 12, 2026
baa575a
test(anim): reset skeleton before measuring arm-space pose (#854)
fernandotonon Jul 12, 2026
2fffd37
test(anim): trace idempotence vectors (#854, debug)
fernandotonon Jul 12, 2026
50a5a15
test(anim): in-function arm-space delta+pose trace (#854, debug)
fernandotonon Jul 12, 2026
32424b5
fix(anim): store arm-space per skeleton instance, not a global map (#…
fernandotonon Jul 12, 2026
dd00dbc
docs(anim): arm-space storage is per-instance UserObjectBindings (#854)
fernandotonon Jul 12, 2026
6fa8c1c
ci(macos): retry create-dmg + hdiutil fallback in the Pack step
fernandotonon Jul 12, 2026
0e44ecf
ci(macos): fix unbound-variable in Pack retry loop
fernandotonon Jul 12, 2026
c357807
ci(macos): sanitize slash in DMG name — the real Pack failure cause
fernandotonon Jul 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2138,19 +2138,55 @@ jobs:
file ${{github.workspace}}/bin/QtMeshEditor.app/Contents/MacOS/QtMeshEditor

- name: Pack
run: |
run: |
set -euo pipefail
brew install create-dmg
sudo create-dmg \
--volname "QtMeshEditor Installer" \
--volicon "${{github.workspace}}/resources/icon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "QtMeshEditor.app" 200 190 \
--app-drop-link 600 185 \
QtMeshEditor-${{github.ref_name}}-MacOS.dmg \
${{github.workspace}}/bin/QtMeshEditor.app

# github.ref_name is "867/merge" on a PR — the slash makes
# create-dmg treat "QtMeshEditor-867/merge-MacOS.dmg" as a path,
# cd into the nonexistent "QtMeshEditor-867/" dir, and write the
# DMG under the wrong name (the real, deterministic cause of the
# macOS Pack failures on PRs, not a create-dmg flake). Sanitize
# any '/' out of the basename. Release tags (X.Y.Z) are unaffected.
REF="$(echo "${{github.ref_name}}" | tr '/' '-')"
DMG="QtMeshEditor-${REF}-MacOS.dmg"
APP="${{github.workspace}}/bin/QtMeshEditor.app"

# create-dmg also mounts a disk image + AppleScript-positions the
# icons, which can race on CI. Retry a few times, and if it still
# fails fall back to a plain hdiutil DMG so packaging never blocks
# the build on a transient tooling flake. The pretty layout is
# only cosmetic - the fallback DMG installs identically.
make_pretty_dmg() {
rm -f "$DMG"
sudo create-dmg \
--volname "QtMeshEditor Installer" \
--volicon "${{github.workspace}}/resources/icon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "QtMeshEditor.app" 200 190 \
--app-drop-link 600 185 \
"$DMG" "$APP"
}
ok=0
for attempt in 1 2 3; do
echo "create-dmg attempt ${attempt}..."
if make_pretty_dmg; then ok=1; break; fi
echo "create-dmg attempt ${attempt} failed; detaching stray mounts and retrying"
hdiutil detach "/Volumes/QtMeshEditor Installer" -force 2>/dev/null || true
sleep 5
done
if [ "$ok" -ne 1 ]; then
echo "create-dmg failed 3x - falling back to a plain hdiutil DMG"
rm -f "$DMG"
staging="$(mktemp -d)"
cp -R "$APP" "$staging/"
ln -s /Applications "$staging/Applications"
hdiutil create -volname "QtMeshEditor Installer" \
-srcfolder "$staging" -ov -format UDZO "$DMG"
fi
ls -la "$DMG"

- if: github.event_name == 'release' && github.event.action == 'published'
uses: actions/upload-artifact@v4
with:
Expand Down
3 changes: 3 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

168 changes: 165 additions & 3 deletions qml/PropertiesPanel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -7953,14 +7953,45 @@ Rectangle {
spacing: 4

property var entityGroups: PropertiesPanelController.animationData()
// #854: the clip the arm-space slider targets. Defaults to the last
// generated clip, but the per-row arm button can point it at ANY
// animation. Cleared on selection change.
property string lastGeneratedAnim: ""
property string armSpaceAnim: ""
property string armSpaceEntity: ""

function refreshAnimData() {
entityGroups = PropertiesPanelController.animationData()
}

// #854: point the arm-space slider at a clip. The adjustment
// STICKS to that clip (so it exports widened) and is independent
// per clip. Generation always produces a CLEAN clip (slider value
// never bakes into it), and the slider is seeded with the target
// clip's ACTUAL current angle — so switching clips shows the truth
// and a fresh generate shows 0. Closing the panel / reselecting
// just detaches the slider (no revert; the clip keeps its edit).
function setArmSpaceTarget(animName, entity) {
armSpaceAnim = animName
armSpaceEntity = entity
var cur = AnimationControlController.currentArmSpace(animName, entity)
armSpaceSlider.value = cur
armSpaceSlider.lastApplied = cur
}
function detachArmSpace() {
armSpaceAnim = ""
armSpaceEntity = ""
armSpaceSlider.value = 0
armSpaceSlider.lastApplied = 0
}
Comment on lines +7967 to +7986

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

setArmSpaceTarget triggers a spurious adjustArmSpace call when just retargeting the slider.

armSpaceSlider.value = cur is set before armSpaceSlider.lastApplied = cur. Since onValueChanged fires synchronously on the value assignment, it compares the new cur against the stale lastApplied (from whatever clip was previously targeted) and — if they differ — fires AnimationControlController.adjustArmSpace(...) even though this call is only meant to seed the slider with the clip's already-applied value. It's harmless in practice (the C++ side computes delta == 0 and no-ops), but it's a wasted round-trip plus an extra ui.action Sentry breadcrumb every time the user clicks the ↔ button or generates a new clip, none of which represents a real user edit. Compare with detachArmSpace() just below, which correctly clears armSpaceAnim before touching value.

🐛 Proposed fix
 function setArmSpaceTarget(animName, entity) {
     armSpaceAnim = animName
     armSpaceEntity = entity
     var cur = AnimationControlController.currentArmSpace(animName, entity)
-    armSpaceSlider.value = cur
     armSpaceSlider.lastApplied = cur
+    armSpaceSlider.value = cur
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// #854: point the arm-space slider at a clip. The adjustment
// STICKS to that clip (so it exports widened) and is independent
// per clip. Generation always produces a CLEAN clip (slider value
// never bakes into it), and the slider is seeded with the target
// clip's ACTUAL current angle — so switching clips shows the truth
// and a fresh generate shows 0. Closing the panel / reselecting
// just detaches the slider (no revert; the clip keeps its edit).
function setArmSpaceTarget(animName, entity) {
armSpaceAnim = animName
armSpaceEntity = entity
var cur = AnimationControlController.currentArmSpace(animName, entity)
armSpaceSlider.value = cur
armSpaceSlider.lastApplied = cur
}
function detachArmSpace() {
armSpaceAnim = ""
armSpaceEntity = ""
armSpaceSlider.value = 0
armSpaceSlider.lastApplied = 0
}
// `#854`: point the arm-space slider at a clip. The adjustment
// STICKS to that clip (so it exports widened) and is independent
// per clip. Generation always produces a CLEAN clip (slider value
// never bakes into it), and the slider is seeded with the target
// clip's ACTUAL current angle — so switching clips shows the truth
// and a fresh generate shows 0. Closing the panel / reselecting
// just detaches the slider (no revert; the clip keeps its edit).
function setArmSpaceTarget(animName, entity) {
armSpaceAnim = animName
armSpaceEntity = entity
var cur = AnimationControlController.currentArmSpace(animName, entity)
armSpaceSlider.lastApplied = cur
armSpaceSlider.value = cur
}
function detachArmSpace() {
armSpaceAnim = ""
armSpaceEntity = ""
armSpaceSlider.value = 0
armSpaceSlider.lastApplied = 0
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@qml/PropertiesPanel.qml` around lines 7967 - 7986, Update setArmSpaceTarget
so it assigns armSpaceSlider.lastApplied to cur before assigning
armSpaceSlider.value, preventing the synchronous onValueChanged handler from
treating retargeting as a user adjustment. Preserve the existing target
assignment and currentArmSpace lookup behavior.


Connections {
target: PropertiesPanelController
function onSelectionChanged() { refreshAnimData() }
function onSelectionChanged() {
lastGeneratedAnim = ""
detachArmSpace()
refreshAnimData()
}
function onAnimationStateChanged() { refreshAnimData() }
}

Expand All @@ -7984,6 +8015,27 @@ Rectangle {
verticalAlignment: TextInput.AlignVCenter
color: PropertiesPanelController.textColor; font.pixelSize: 11
clip: true
// selectByMouse lets a click position the caret AND take
// keyboard focus — without it a bare TextInput embedded
// in a QQuickWidget stays unfocused on click (the other
// working fields in this panel all set it). activeFocus-
// OnPress is the default but stated for clarity.
selectByMouse: true
activeFocusOnPress: true
// Belt-and-suspenders focus grab: after using the
// arm-space slider / arm buttons (which take focus and
// can leave the QQuickWidget's focus on the viewport),
// a plain click here didn't always re-focus the field.
// Force it on press and let the event through (accepted
// = false) so selectByMouse still positions the caret.
MouseArea {
anchors.fill: parent
cursorShape: Qt.IBeamCursor
onPressed: function(mouse) {
genPromptIn.forceActiveFocus()
mouse.accepted = false
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
visible: !genPromptIn.text && !genPromptIn.activeFocus
Expand All @@ -8008,8 +8060,19 @@ Rectangle {
? "Generating (experimental model)…"
: "Generating… (first use downloads the motion library)"
genStatus.isError = false
AnimationControlController.generateMotion(genPromptIn.text, 0.0,
useModelChk.checked)
// Generate a CLEAN clip — arm-space is always an
// explicit post-adjustment starting from 0, never baked
// into generation (a leftover slider value silently
// skewing every new clip is exactly the bug this fixes).
var gr = AnimationControlController.generateMotion(
genPromptIn.text, 0.0, useModelChk.checked,
0.0)
if (gr && gr.animation) {
lastGeneratedAnim = gr.animation
// Point the slider at the fresh clip at a neutral 0
// (reverts any prior target — see setArmSpaceTarget).
setArmSpaceTarget(gr.animation, gr.entity || "")
}
genBtnBusy = false
// generateMotion adds an AnimationState synchronously, but
// it lives on AnimationControlController — the Inspector
Expand Down Expand Up @@ -8046,6 +8109,62 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
}
}
// ── Arm space (#854): Mixamo-style widen/tuck post-process ────────
// Targets `armSpaceAnim` — the last generated clip by default, or
// any animation the user picks via the per-row arm button below.
// The op is ABSOLUTE + idempotent, so dragging can re-apply live at
// each value (no accumulation) and the slider maps straight to the
// stored angle.
Row {
width: parent.width - 16; spacing: 6
visible: armSpaceAnim.length > 0
Text {
text: "Arm space"
color: PropertiesPanelController.textColor; font.pixelSize: 10
anchors.verticalCenter: parent.verticalCenter
width: 62
elide: Text.ElideRight
}
Slider {
id: armSpaceSlider
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 62 - 42 - 12
from: -30; to: 45; value: 0; stepSize: 1
// Live update: adjustArmSpace is absolute+idempotent, so
// firing it per value while dragging just re-applies (never
// accumulates). Throttle to whole degrees via stepSize so we
// rewrite keyframes at most ~75 times across the range.
property real lastApplied: 0
onValueChanged: {
if (armSpaceAnim.length > 0
&& Math.round(value) !== Math.round(lastApplied)) {
lastApplied = value
AnimationControlController.adjustArmSpace(
armSpaceAnim, value, armSpaceEntity)
}
}
// Refresh the Inspector list only on release (the viewport
// itself updates live inside adjustArmSpace).
onPressedChanged: { if (!pressed) refreshAnimData() }
}
Text {
text: (armSpaceSlider.value > 0 ? "+" : "")
+ Math.round(armSpaceSlider.value) + "°"
color: PropertiesPanelController.textColor; font.pixelSize: 10
anchors.verticalCenter: parent.verticalCenter
width: 42; horizontalAlignment: Text.AlignRight
}
}
// Label showing which clip the slider affects (only when it's not
// the obvious just-generated one).
Text {
visible: armSpaceAnim.length > 0
width: parent.width - 16; wrapMode: Text.Wrap
text: "↑ adjusting \"" + armSpaceAnim + "\" (0 = no change). The "
+ "edit stays on this clip; click ↔ again to detach."
color: PropertiesPanelController.textColor; opacity: 0.5
font.pixelSize: 9
}
Text {
id: genStatus
property bool isError: false
Expand Down Expand Up @@ -8554,6 +8673,49 @@ Rectangle {
}
}

// Arm space (#854) — point the arm-space
// slider (above) at THIS clip. Works on any
// skeletal animation, not just generated
// ones. Highlights when it's the active target.
Rectangle {
id: armBtn
visible: grp.hasSkeleton
width: 22; height: 18; radius: 3
anchors.verticalCenter: parent.verticalCenter
property bool active: armSpaceAnim === modelData.name
&& armSpaceEntity === grp.entity
color: active ? PropertiesPanelController.highlightColor
: armMouse.pressed ? Qt.darker(PropertiesPanelController.headerColor, 1.2)
: armMouse.containsMouse ? Qt.lighter(PropertiesPanelController.headerColor, 1.2)
: PropertiesPanelController.headerColor
border.color: PropertiesPanelController.borderColor; border.width: 1
Text {
anchors.centerIn: parent
text: "↔" // ↔ widen/tuck
color: armBtn.active ? "white"
: PropertiesPanelController.textColor
font.pixelSize: 12
}
ToolTip.visible: armMouse.containsMouse
ToolTip.delay: 600
ToolTip.text: "Arm space — retarget the widen/tuck slider to this animation"
MouseArea {
id: armMouse; anchors.fill: parent; hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
// Toggle: detach if it's already
// the target (the clip keeps its
// edit), else target this clip
// (seeded with its real angle).
if (armBtn.active)
detachArmSpace()
else
setArmSpaceTarget(
modelData.name, grp.entity)
}
}
}

// Bake — resample / reduce every bone track in this
// animation. Mirrors the curve editor's per-bone Bake
// dropdown but applies to the whole animation in one
Expand Down
71 changes: 70 additions & 1 deletion src/AnimationControlController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1659,8 +1659,71 @@
return out;
}

bool AnimationControlController::adjustArmSpace(const QString& animName,
double degrees,
const QString& entityName)
{
Manager* mgr = Manager::getSingletonPtr();
if (!mgr) return false;
const std::string want = entityName.isEmpty()
? m_selectedEntityName : entityName.toStdString();
Ogre::Entity* entity = nullptr;
for (auto* e : mgr->getEntities()) {
if (!e || e->getMovableType() != "Entity" || !e->hasSkeleton()) continue;
if (want.empty() || e->getName() == want) { entity = e; break; }
}
if (!entity) return false;
Ogre::SkeletonPtr skel = entity->getMesh()->getSkeleton();
const std::string an = animName.toStdString();
if (!skel || !skel->hasAnimation(an)) return false;

SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
QStringLiteral("GUI arm_space %1 deg").arg(degrees));
if (!AnimationMerger::adjustArmSpace(skel.get(), an,
static_cast<float>(degrees)))
return false;

// Re-pose the mesh NOW, even when the clip is paused. Rewriting keyframes
// doesn't move the bones until the animation state re-applies; when
// playback is stopped nothing ticks it, so the viewport would stay frozen
// on the pre-edit pose until the user hits play. Mark the state dirty and
// re-stamp its current time to force an immediate re-evaluation (the same
// idiom as notifyOgreUpdate, but targeting THIS entity + clip so it works
// for any animation, not just the selected one).
entity->refreshAvailableAnimationState();
if (auto* states = entity->getAllAnimationStates()) {
states->_notifyDirty();
if (states->hasAnimationState(an)) {
auto* st = states->getAnimationState(an);
if (st->getEnabled())
st->setTimePosition(st->getTimePosition());
}
}
notifyExternalAnimationEdit();
return true;
}
Comment on lines +1662 to +1704

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Sentry breadcrumb category mismatch, and silent failure with no user feedback.

Two issues in this new method:

  1. The breadcrumb uses QStringLiteral("ai.assist.text_to_motion"), but this is a GUI slider-release action, not text-to-motion generation. As per path instructions, **/*.{cpp,h}: "use ui.action for toolbar/menu clicks, ai.tool_call for MCP invocations, and file.import/file.export for I/O." Grouping arm-space slider events under the text-to-motion category will muddy Sentry breadcrumb trails for both features.
  2. Every early-return path (!mgr, !entity, !skel/!hasAnimation, !AnimationMerger::adjustArmSpace(...)) just returns false with no signal emitted. Unlike generateMotion, which has generateMotionStatus(message, isError) for user feedback, adjustArmSpace has no equivalent — and the QML caller (armSpaceSlider.onPressedChanged) doesn't check the return value either. So if the adjustment silently fails (e.g. rig has no arm roles, animation renamed), the user gets zero indication anything went wrong; the slider just looks like it worked.
🐛 Proposed fix (category rename shown; consider adding a status signal for `#2`)
-    SentryReporter::addBreadcrumb(QStringLiteral("ai.assist.text_to_motion"),
+    SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
         QStringLiteral("GUI arm_space %1 deg").arg(degrees));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool AnimationControlController::adjustArmSpace(const QString& animName,
double degrees)
{
Manager* mgr = Manager::getSingletonPtr();
if (!mgr) return false;
Ogre::Entity* entity = nullptr;
for (auto* e : mgr->getEntities()) {
if (!e || e->getMovableType() != "Entity" || !e->hasSkeleton()) continue;
if (m_selectedEntityName.empty()
|| e->getName() == m_selectedEntityName) { entity = e; break; }
}
if (!entity) return false;
Ogre::SkeletonPtr skel = entity->getMesh()->getSkeleton();
const std::string an = animName.toStdString();
if (!skel || !skel->hasAnimation(an)) return false;
SentryReporter::addBreadcrumb(QStringLiteral("ai.assist.text_to_motion"),
QStringLiteral("GUI arm_space %1 deg").arg(degrees));
if (!AnimationMerger::adjustArmSpace(skel.get(), an,
static_cast<float>(degrees)))
return false;
// Refresh the viewport (the animation state's keyframes changed underneath
// it) exactly as the generate path does.
entity->refreshAvailableAnimationState();
notifyExternalAnimationEdit();
return true;
}
bool AnimationControlController::adjustArmSpace(const QString& animName,
double degrees)
{
Manager* mgr = Manager::getSingletonPtr();
if (!mgr) return false;
Ogre::Entity* entity = nullptr;
for (auto* e : mgr->getEntities()) {
if (!e || e->getMovableType() != "Entity" || !e->hasSkeleton()) continue;
if (m_selectedEntityName.empty()
|| e->getName() == m_selectedEntityName) { entity = e; break; }
}
if (!entity) return false;
Ogre::SkeletonPtr skel = entity->getMesh()->getSkeleton();
const std::string an = animName.toStdString();
if (!skel || !skel->hasAnimation(an)) return false;
SentryReporter::addBreadcrumb(QStringLiteral("ui.action"),
QStringLiteral("GUI arm_space %1 deg").arg(degrees));
if (!AnimationMerger::adjustArmSpace(skel.get(), an,
static_cast<float>(degrees)))
return false;
// Refresh the viewport (the animation state's keyframes changed underneath
// it) exactly as the generate path does.
entity->refreshAvailableAnimationState();
notifyExternalAnimationEdit();
return true;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/AnimationControlController.cpp` around lines 1662 - 1689, Update
AnimationControlController::adjustArmSpace to record this GUI slider action
under the ui.action breadcrumb category instead of ai.assist.text_to_motion. Add
or reuse a user-facing status signal analogous to generateMotionStatus, and emit
an appropriate error message on every early-return failure, including adjustment
failure, so callers receive feedback even when they ignore the boolean result.
Preserve the existing successful refresh and notification flow.

Source: Path instructions


double AnimationControlController::currentArmSpace(const QString& animName,
const QString& entityName)
{
Manager* mgr = Manager::getSingletonPtr();
if (!mgr) return 0.0;
const std::string want = entityName.isEmpty()
? m_selectedEntityName : entityName.toStdString();
for (auto* e : mgr->getEntities()) {
if (!e || e->getMovableType() != "Entity" || !e->hasSkeleton()) continue;
if (want.empty() || e->getName() == want) {
Ogre::SkeletonPtr skel = e->getMesh()->getSkeleton();
return skel ? AnimationMerger::currentArmSpace(
skel.get(), animName.toStdString()) : 0.0;
}
}
return 0.0;
}

QVariantMap AnimationControlController::generateMotion(const QString& prompt,
double duration, bool useModel)
double duration, bool useModel,
double armSpaceDeg)
{
QVariantMap out;
out["ok"] = false;
Expand Down Expand Up @@ -1750,15 +1813,20 @@
const std::string animName = ("generated_" + action).toStdString();
// Auto-rigged (no prior animation) meshes that face −Z would walk
// backward — detect facing from the mesh's foot region.
const bool yaw180 = AnimationMerger::detectBackwardFacing(entity);

Check warning on line 1816 in src/AnimationControlController.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This function should be declared "const".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ9Ubzm6b3Yg5Wvlnx1I&open=AZ9Ubzm6b3Yg5Wvlnx1I&pullRequest=867
const auto res = AnimationMerger::applyMotionClip(skel.get(), animName, quats, fps,
worldFrame, cmuRest,
/*refineWithModel=*/false,
/*refineStride=*/8, yaw180,
clipDirs);
if (!res.ok) return fail(res.error);
out["source"] = clipSource;

Check warning on line 1823 in src/AnimationControlController.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make the type of this variable a pointer-to-const. The current type of "e" is "class Ogre::Entity *".

See more on https://sonarcloud.io/project/issues?id=fernandotonon_QtMeshEditor&issues=AZ9Ubzm6b3Yg5Wvlnx1J&open=AZ9Ubzm6b3Yg5Wvlnx1J&pullRequest=867

// #854: optional Mixamo-style arm-space post-process.
if (std::abs(armSpaceDeg) > 1e-4)
AnimationMerger::adjustArmSpace(skel.get(), animName,
static_cast<float>(armSpaceDeg));

Comment thread
coderabbitai[bot] marked this conversation as resolved.
entity->refreshAvailableAnimationState();
// Make the generated clip the ONLY enabled animation. Ogre AVERAGES all
// enabled animation states, so leaving the import's auto-enabled clip (or
Expand Down Expand Up @@ -1794,6 +1862,7 @@
out["action"] = action;
out["source"] = clipSource;
out["animation"] = QString::fromStdString(animName);
out["entity"] = QString::fromStdString(entity->getName());
out["frames"] = res.frames;
out["length"] = res.length;
out["tracksWritten"] = res.tracksWritten;
Expand Down
Loading
Loading