feature/gltf-pipeline-v2 #18
@@ -126,11 +126,13 @@ public final class GltfAnimationApplier {
|
||||
String enabledKey = canonicalPartsKey(ownership.enabledParts());
|
||||
String partsKey = ownedKey + ";" + enabledKey;
|
||||
|
||||
// Build composite state key to avoid redundant updates
|
||||
// Build composite state key to detect context changes.
|
||||
// NOTE: This key does NOT include the variant name — that is resolved fresh
|
||||
// each time the context changes, enabling random variant selection.
|
||||
String stateKey = animSource + "|" + context.name() + "|" + partsKey;
|
||||
String currentKey = activeStateKeys.get(entity.getUUID());
|
||||
if (stateKey.equals(currentKey)) {
|
||||
return true; // Already active, no-op
|
||||
return true; // Same context, same parts — no need to re-resolve
|
||||
}
|
||||
|
||||
// === Layer 1: Context animation (base body posture) ===
|
||||
@@ -153,8 +155,7 @@ public final class GltfAnimationApplier {
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyframeAnimation itemAnim = itemAnimCache.get(itemCacheKey);
|
||||
if (itemAnim == null) {
|
||||
// Resolve animation data first (needed for variant resolution)
|
||||
GltfData animData = GlbAnimationResolver.resolveAnimationData(
|
||||
modelLoc,
|
||||
animationSource
|
||||
@@ -168,11 +169,22 @@ public final class GltfAnimationApplier {
|
||||
activeStateKeys.put(entity.getUUID(), stateKey);
|
||||
return false;
|
||||
}
|
||||
// Resolve which named animation to use (with fallback chain + variant selection)
|
||||
|
||||
// Resolve which named animation to use (with fallback chain + variant selection).
|
||||
// This must happen BEFORE the cache lookup because variant selection is random —
|
||||
// we want a fresh random pick each time the context changes, not a permanently
|
||||
// cached first pick.
|
||||
String glbAnimName = GlbAnimationResolver.resolve(
|
||||
animData,
|
||||
context
|
||||
);
|
||||
|
||||
// Include the resolved animation name in the cache key so different variants
|
||||
// (Struggle.1 vs Struggle.2) get separate cache entries.
|
||||
String variantCacheKey = itemCacheKey + "#" + (glbAnimName != null ? glbAnimName : "default");
|
||||
|
||||
KeyframeAnimation itemAnim = itemAnimCache.get(variantCacheKey);
|
||||
if (itemAnim == null) {
|
||||
// Pass both owned parts and enabled parts (owned + free) for selective enabling
|
||||
itemAnim = GltfPoseConverter.convertSelective(
|
||||
animData,
|
||||
@@ -180,7 +192,7 @@ public final class GltfAnimationApplier {
|
||||
ownership.thisParts(),
|
||||
ownership.enabledParts()
|
||||
);
|
||||
itemAnimCache.put(itemCacheKey, itemAnim);
|
||||
itemAnimCache.put(variantCacheKey, itemAnim);
|
||||
}
|
||||
|
||||
BondageAnimationManager.playDirect(entity, itemAnim);
|
||||
|
||||
@@ -587,7 +587,10 @@ public final class GltfPoseConverter {
|
||||
// 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");
|
||||
// FullHead prefix (e.g., FullHeadStruggle) opts into head as a free bone.
|
||||
// Use startsWith to avoid false positives (e.g., FullOverhead, FullAhead).
|
||||
boolean allowFreeHead = isFullBodyAnimation &&
|
||||
animName.startsWith("gltf_FullHead");
|
||||
|
||||
String[] allParts = {
|
||||
"head",
|
||||
|
||||
Reference in New Issue
Block a user