Compare commits
3 Commits
7c235106d6
...
c3abc420cf
| Author | SHA1 | Date | |
|---|---|---|---|
| c3abc420cf | |||
| 81bcd67428 | |||
| 250ae5e04d |
Binary file not shown.
@ -28,7 +28,7 @@ void FAnimNode_ElevenLabsPosture::Initialize_AnyThread(const FAnimationInitializ
|
|||||||
// Find the ElevenLabsPostureComponent on the owning actor.
|
// Find the ElevenLabsPostureComponent on the owning actor.
|
||||||
PostureComponent.Reset();
|
PostureComponent.Reset();
|
||||||
CachedEyeCurves.Reset();
|
CachedEyeCurves.Reset();
|
||||||
CachedHeadRotation = FRotator::ZeroRotator;
|
CachedHeadRotation = FQuat::Identity;
|
||||||
HeadBoneIndex = FCompactPoseBoneIndex(INDEX_NONE);
|
HeadBoneIndex = FCompactPoseBoneIndex(INDEX_NONE);
|
||||||
|
|
||||||
if (const FAnimInstanceProxy* Proxy = Context.AnimInstanceProxy)
|
if (const FAnimInstanceProxy* Proxy = Context.AnimInstanceProxy)
|
||||||
@ -142,7 +142,7 @@ void FAnimNode_ElevenLabsPosture::Update_AnyThread(const FAnimationUpdateContext
|
|||||||
|
|
||||||
// Cache posture data from the component (game thread safe copy).
|
// Cache posture data from the component (game thread safe copy).
|
||||||
CachedEyeCurves.Reset();
|
CachedEyeCurves.Reset();
|
||||||
CachedHeadRotation = FRotator::ZeroRotator;
|
CachedHeadRotation = FQuat::Identity;
|
||||||
|
|
||||||
if (PostureComponent.IsValid())
|
if (PostureComponent.IsValid())
|
||||||
{
|
{
|
||||||
@ -218,11 +218,51 @@ void FAnimNode_ElevenLabsPosture::Evaluate_AnyThread(FPoseContext& Output)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// ── PRODUCTION: Apply real head rotation ─────────────────────────
|
// ── PRODUCTION: Apply real head rotation ─────────────────────────
|
||||||
if (!CachedHeadRotation.IsNearlyZero(0.1f))
|
if (!CachedHeadRotation.Equals(FQuat::Identity, 0.001f))
|
||||||
{
|
{
|
||||||
const FQuat HeadOffset = CachedHeadRotation.Quaternion();
|
const FQuat BoneRot = HeadTransform.GetRotation();
|
||||||
// Pre-multiply: apply offset in parent space (neck)
|
const FQuat Combined = CachedHeadRotation * BoneRot;
|
||||||
HeadTransform.SetRotation((HeadOffset * HeadTransform.GetRotation()).GetNormalized());
|
|
||||||
|
// Remove Y-axis tilt from final rotation
|
||||||
|
FQuat Swing, TiltTwist;
|
||||||
|
Combined.ToSwingTwist(FVector::RightVector, Swing, TiltTwist);
|
||||||
|
|
||||||
|
HeadTransform.SetRotation(Swing.GetNormalized());
|
||||||
|
|
||||||
|
// ── Diagnostic log (every ~60 frames) ──
|
||||||
|
static int32 DiagCounter = 0;
|
||||||
|
if (++DiagCounter % 60 == 0)
|
||||||
|
{
|
||||||
|
const FRotator OffsetRot = CachedHeadRotation.Rotator();
|
||||||
|
const FRotator BoneRefRot = BoneRot.Rotator();
|
||||||
|
const FRotator CombRot = Combined.Rotator();
|
||||||
|
const FRotator SwingRot = Swing.Rotator();
|
||||||
|
const FRotator TiltRot = TiltTwist.Rotator();
|
||||||
|
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT("=== HEAD ROTATION DIAG ==="));
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT(" Offset Q: X=%.4f Y=%.4f Z=%.4f W=%.4f (P=%.1f Y=%.1f R=%.1f)"),
|
||||||
|
CachedHeadRotation.X, CachedHeadRotation.Y,
|
||||||
|
CachedHeadRotation.Z, CachedHeadRotation.W,
|
||||||
|
OffsetRot.Pitch, OffsetRot.Yaw, OffsetRot.Roll);
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT(" BoneRef Q: X=%.4f Y=%.4f Z=%.4f W=%.4f (P=%.1f Y=%.1f R=%.1f)"),
|
||||||
|
BoneRot.X, BoneRot.Y, BoneRot.Z, BoneRot.W,
|
||||||
|
BoneRefRot.Pitch, BoneRefRot.Yaw, BoneRefRot.Roll);
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT(" Combined Q: X=%.4f Y=%.4f Z=%.4f W=%.4f (P=%.1f Y=%.1f R=%.1f)"),
|
||||||
|
Combined.X, Combined.Y, Combined.Z, Combined.W,
|
||||||
|
CombRot.Pitch, CombRot.Yaw, CombRot.Roll);
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT(" Swing Q: X=%.4f Y=%.4f Z=%.4f W=%.4f (P=%.1f Y=%.1f R=%.1f)"),
|
||||||
|
Swing.X, Swing.Y, Swing.Z, Swing.W,
|
||||||
|
SwingRot.Pitch, SwingRot.Yaw, SwingRot.Roll);
|
||||||
|
UE_LOG(LogElevenLabsPostureAnimNode, Warning,
|
||||||
|
TEXT(" TiltTwist: X=%.4f Y=%.4f Z=%.4f W=%.4f (P=%.1f Y=%.1f R=%.1f)"),
|
||||||
|
TiltTwist.X, TiltTwist.Y, TiltTwist.Z, TiltTwist.W,
|
||||||
|
TiltRot.Pitch, TiltRot.Yaw, TiltRot.Roll);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -230,10 +270,11 @@ void FAnimNode_ElevenLabsPosture::Evaluate_AnyThread(FPoseContext& Output)
|
|||||||
|
|
||||||
void FAnimNode_ElevenLabsPosture::GatherDebugData(FNodeDebugData& DebugData)
|
void FAnimNode_ElevenLabsPosture::GatherDebugData(FNodeDebugData& DebugData)
|
||||||
{
|
{
|
||||||
|
const FRotator DebugRot = CachedHeadRotation.Rotator();
|
||||||
FString DebugLine = FString::Printf(
|
FString DebugLine = FString::Printf(
|
||||||
TEXT("ElevenLabs Posture (eyes: %d curves, head: Y=%.1f P=%.1f)"),
|
TEXT("ElevenLabs Posture (eyes: %d curves, head: Y=%.1f P=%.1f)"),
|
||||||
CachedEyeCurves.Num(),
|
CachedEyeCurves.Num(),
|
||||||
CachedHeadRotation.Yaw, CachedHeadRotation.Pitch);
|
DebugRot.Yaw, DebugRot.Pitch);
|
||||||
DebugData.AddDebugItem(DebugLine);
|
DebugData.AddDebugItem(DebugLine);
|
||||||
BasePose.GatherDebugData(DebugData);
|
BasePose.GatherDebugData(DebugData);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -275,13 +275,16 @@ void UElevenLabsPostureComponent::TickComponent(
|
|||||||
{
|
{
|
||||||
FScopeLock Lock(&PostureDataLock);
|
FScopeLock Lock(&PostureDataLock);
|
||||||
|
|
||||||
// MetaHuman head bone axis mapping (independent quaternions to avoid
|
// MetaHuman head bone axis mapping:
|
||||||
// diagonal coupling that FRotator causes when both axes are non-zero):
|
// Z-axis rotation = nod up/down → our HeadPitch
|
||||||
// Z-axis rotation = nod up/down → our HeadPitch
|
|
||||||
// X-axis rotation = turn left/right → our HeadYaw
|
// X-axis rotation = turn left/right → our HeadYaw
|
||||||
|
//
|
||||||
|
// Store raw composed quaternion. The parasitic Y-axis tilt from
|
||||||
|
// composition is removed in the AnimNode AFTER composing with the
|
||||||
|
// bone's reference rotation (which also contributes Y-coupling).
|
||||||
const FQuat NodQuat(FVector::UpVector, FMath::DegreesToRadians(-CurrentHeadPitch));
|
const FQuat NodQuat(FVector::UpVector, FMath::DegreesToRadians(-CurrentHeadPitch));
|
||||||
const FQuat TurnQuat(FVector::ForwardVector, FMath::DegreesToRadians(CurrentHeadYaw));
|
const FQuat TurnQuat(FVector::ForwardVector, FMath::DegreesToRadians(CurrentHeadYaw));
|
||||||
CurrentHeadRotation = (TurnQuat * NodQuat).Rotator();
|
CurrentHeadRotation = TurnQuat * NodQuat;
|
||||||
|
|
||||||
// Eye yaw is negated to match ARKit curve direction convention.
|
// Eye yaw is negated to match ARKit curve direction convention.
|
||||||
UpdateEyeCurves(-CurrentEyeYaw, CurrentEyePitch);
|
UpdateEyeCurves(-CurrentEyeYaw, CurrentEyePitch);
|
||||||
|
|||||||
@ -62,9 +62,9 @@ private:
|
|||||||
* Copied from the component during Update (game thread safe). */
|
* Copied from the component during Update (game thread safe). */
|
||||||
TMap<FName, float> CachedEyeCurves;
|
TMap<FName, float> CachedEyeCurves;
|
||||||
|
|
||||||
/** Head rotation offset (yaw + pitch) to apply to the head bone.
|
/** Head rotation offset as quaternion (no Euler round-trip, avoids
|
||||||
* Copied from the component during Update. */
|
* parasitic tilt on diagonals). Copied from the component during Update. */
|
||||||
FRotator CachedHeadRotation = FRotator::ZeroRotator;
|
FQuat CachedHeadRotation = FQuat::Identity;
|
||||||
|
|
||||||
/** Resolved head bone index in the skeleton. */
|
/** Resolved head bone index in the skeleton. */
|
||||||
FCompactPoseBoneIndex HeadBoneIndex = FCompactPoseBoneIndex(INDEX_NONE);
|
FCompactPoseBoneIndex HeadBoneIndex = FCompactPoseBoneIndex(INDEX_NONE);
|
||||||
|
|||||||
@ -139,10 +139,10 @@ public:
|
|||||||
return CurrentEyeCurves;
|
return CurrentEyeCurves;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get current head rotation offset (yaw + pitch, applied by AnimNode).
|
/** Get current head rotation offset (applied by AnimNode as FQuat to avoid
|
||||||
|
* Euler round-trip that reintroduces parasitic tilt on diagonals).
|
||||||
* Thread-safe copy. */
|
* Thread-safe copy. */
|
||||||
UFUNCTION(BlueprintCallable, Category = "ElevenLabs|Posture")
|
FQuat GetCurrentHeadRotation() const
|
||||||
FRotator GetCurrentHeadRotation() const
|
|
||||||
{
|
{
|
||||||
FScopeLock Lock(&PostureDataLock);
|
FScopeLock Lock(&PostureDataLock);
|
||||||
return CurrentHeadRotation;
|
return CurrentHeadRotation;
|
||||||
@ -194,8 +194,8 @@ private:
|
|||||||
/** 8 ARKit eye look curves (eyeLookUpLeft, eyeLookDownRight, etc.). */
|
/** 8 ARKit eye look curves (eyeLookUpLeft, eyeLookDownRight, etc.). */
|
||||||
TMap<FName, float> CurrentEyeCurves;
|
TMap<FName, float> CurrentEyeCurves;
|
||||||
|
|
||||||
/** Head bone rotation offset (Yaw + Pitch). */
|
/** Head bone rotation offset as quaternion (no Euler round-trip). */
|
||||||
FRotator CurrentHeadRotation = FRotator::ZeroRotator;
|
FQuat CurrentHeadRotation = FQuat::Identity;
|
||||||
|
|
||||||
/** Cached skeletal mesh component on the owning actor. */
|
/** Cached skeletal mesh component on the owning actor. */
|
||||||
TWeakObjectPtr<USkeletalMeshComponent> CachedMesh;
|
TWeakObjectPtr<USkeletalMeshComponent> CachedMesh;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user