feat(animation): add FullHead convention — opt-in head animation in Full animations

FullStruggle, FullWalk etc. animate body+legs but preserve head tracking.
FullHeadStruggle, FullHeadWalk etc. also animate the head.
The 'Head' keyword in the animation name is the opt-in signal.
This commit is contained in:
NotEvil
2026-04-17 03:01:43 +02:00
parent 3d57d83a5b
commit 806a1e732d

View File

@@ -584,6 +584,10 @@ public final class GltfPoseConverter {
// The "gltf_" prefix is added by convertClipSelective, so check for "gltf_Full" // The "gltf_" prefix is added by convertClipSelective, so check for "gltf_Full"
boolean isFullBodyAnimation = animName != null && boolean isFullBodyAnimation = animName != null &&
animName.startsWith("gltf_Full"); animName.startsWith("gltf_Full");
// Head is protected by default — only enabled as a free bone when the animation
// name contains "Head" (e.g., FullHeadStruggle, FullHeadIdle).
// This lets artists opt-in per animation without affecting the item's regions.
boolean allowFreeHead = isFullBodyAnimation && animName.contains("Head");
String[] allParts = { String[] allParts = {
"head", "head",
@@ -606,14 +610,14 @@ public final class GltfPoseConverter {
isFullBodyAnimation && isFullBodyAnimation &&
enabledParts.contains(partName) && enabledParts.contains(partName) &&
partsWithKeyframes.contains(partName) && partsWithKeyframes.contains(partName) &&
!"head".equals(partName) (!"head".equals(partName) || allowFreeHead)
) { ) {
// Full-body animation: free part WITH keyframes — enable. // Full-body animation: free part WITH keyframes — enable.
// The "Full" prefix is the artist's explicit opt-in to animate // The "Full" prefix is the artist's explicit opt-in to animate
// bones outside their declared regions. // bones outside their declared regions.
// EXCEPTION: head is never enabled as a free bone — vanilla head // Head is protected by default (preserves vanilla head tracking).
// tracking (mouse look) is always preserved unless the item // Use "Head" in the animation name (e.g., FullHeadStruggle) to
// explicitly owns a head region (HEAD, EYES, EARS, MOUTH). // explicitly opt-in to head control for that animation.
part.fullyEnablePart(false); part.fullyEnablePart(false);
} else { } else {
// Non-Full animation, other item's part, or free part without keyframes. // Non-Full animation, other item's part, or free part without keyframes.