begin posture with animation
This commit is contained in:
parent
cdb118a83e
commit
d2e904144a
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -224,40 +224,14 @@ void FAnimNode_ElevenLabsPosture::Evaluate_AnyThread(FPoseContext& Output)
|
||||
|
||||
if (bUseChain)
|
||||
{
|
||||
// ── Multi-bone neck chain: distribute rotation across bones ──────
|
||||
// ── Multi-bone neck chain: per-bone swing-twist ──────────────────
|
||||
//
|
||||
// 1. Clean the total rotation ONCE via swing-twist on the tip bone
|
||||
// (removes parasitic ear-to-shoulder tilt).
|
||||
// 2. Slerp a fraction to each bone in the chain.
|
||||
// Each bone in the chain gets a fractional rotation (via Slerp weight).
|
||||
// The swing-twist decomposition is done PER-BONE using each bone's own
|
||||
// tilt axis. This prevents parasitic ear-to-shoulder tilt that occurred
|
||||
// when a single CleanRotation (derived from the tip bone) was applied
|
||||
// to bones with different local orientations.
|
||||
|
||||
// Find the tip bone (last valid bone) for swing-twist reference
|
||||
FCompactPoseBoneIndex TipBoneIdx(INDEX_NONE);
|
||||
for (int32 i = ChainBoneIndices.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
if (ChainBoneIndices[i].GetInt() != INDEX_NONE
|
||||
&& ChainBoneIndices[i].GetInt() < Output.Pose.GetNumBones())
|
||||
{
|
||||
TipBoneIdx = ChainBoneIndices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (TipBoneIdx.GetInt() == INDEX_NONE)
|
||||
{
|
||||
return; // No valid bones in chain
|
||||
}
|
||||
|
||||
// Swing-twist: remove tilt from the composed rotation
|
||||
const FQuat TipBoneRot = Output.Pose[TipBoneIdx].GetRotation();
|
||||
const FQuat Combined = CachedHeadRotation * TipBoneRot;
|
||||
const FVector BoneTiltAxis = TipBoneRot.RotateVector(FVector::RightVector);
|
||||
FQuat Swing, TiltTwist;
|
||||
Combined.ToSwingTwist(BoneTiltAxis, Swing, TiltTwist);
|
||||
|
||||
// Extract clean offset = Swing * Inverse(TipBoneRot)
|
||||
const FQuat CleanRotation = (Swing * TipBoneRot.Inverse()).GetNormalized();
|
||||
|
||||
// Distribute fractional rotation to each bone
|
||||
for (int32 i = 0; i < ChainBoneIndices.Num(); ++i)
|
||||
{
|
||||
const FCompactPoseBoneIndex BoneIdx = ChainBoneIndices[i];
|
||||
@ -267,10 +241,21 @@ void FAnimNode_ElevenLabsPosture::Evaluate_AnyThread(FPoseContext& Output)
|
||||
continue;
|
||||
}
|
||||
|
||||
const FQuat FractionalRot = FQuat::Slerp(FQuat::Identity, CleanRotation, ChainBoneWeights[i]);
|
||||
// Fractional rotation for this bone
|
||||
const FQuat FractionalRot = FQuat::Slerp(
|
||||
FQuat::Identity, CachedHeadRotation, ChainBoneWeights[i]);
|
||||
|
||||
// Compose with THIS bone's own rotation
|
||||
FTransform& BoneTransform = Output.Pose[BoneIdx];
|
||||
BoneTransform.SetRotation(
|
||||
(FractionalRot * BoneTransform.GetRotation()).GetNormalized());
|
||||
const FQuat BoneRot = BoneTransform.GetRotation();
|
||||
const FQuat Combined = FractionalRot * BoneRot;
|
||||
|
||||
// Swing-twist on THIS bone's tilt axis (removes roll/tilt)
|
||||
const FVector BoneTiltAxis = BoneRot.RotateVector(FVector::RightVector);
|
||||
FQuat Swing, TiltTwist;
|
||||
Combined.ToSwingTwist(BoneTiltAxis, Swing, TiltTwist);
|
||||
|
||||
BoneTransform.SetRotation(Swing.GetNormalized());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -34,6 +34,11 @@ UElevenLabsPostureComponent::UElevenLabsPostureComponent()
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
PrimaryComponentTick.TickGroup = TG_PrePhysics;
|
||||
bAutoActivate = true;
|
||||
|
||||
// Default neck bone chain for MetaHuman (neck_01 → neck_02 → head)
|
||||
NeckBoneChain.Add({ FName(TEXT("neck_01")), 0.25f });
|
||||
NeckBoneChain.Add({ FName(TEXT("neck_02")), 0.35f });
|
||||
NeckBoneChain.Add({ FName(TEXT("head")), 0.40f });
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@ -77,7 +77,7 @@ public:
|
||||
* Useful for actors without a skeleton (e.g. (0,0,160) for eye-level). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ToolTip = "Offset from target actor origin.\nE.g. (0,0,160) for eye-level."))
|
||||
FVector TargetOffset = FVector(0.0f, 0.0f, 160.0f);
|
||||
FVector TargetOffset = FVector(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// ── Angle limits (degrees) ───────────────────────────────────────────────
|
||||
//
|
||||
@ -91,45 +91,55 @@ public:
|
||||
/** Maximum head yaw rotation in degrees. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0", ClampMax = "90"))
|
||||
float MaxHeadYaw = 35.0f;
|
||||
float MaxHeadYaw = 40.0f;
|
||||
|
||||
/** Maximum head pitch rotation in degrees. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0", ClampMax = "90"))
|
||||
float MaxHeadPitch = 25.0f;
|
||||
float MaxHeadPitch = 30.0f;
|
||||
|
||||
/** Maximum horizontal eye angle in degrees. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0", ClampMax = "90"))
|
||||
float MaxEyeHorizontal = 30.0f;
|
||||
float MaxEyeHorizontal = 15.0f;
|
||||
|
||||
/** Maximum vertical eye angle in degrees. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0", ClampMax = "90"))
|
||||
float MaxEyeVertical = 20.0f;
|
||||
float MaxEyeVertical = 10.0f;
|
||||
|
||||
// ── Smoothing speeds ─────────────────────────────────────────────────────
|
||||
|
||||
/** Body rotation interpolation speed (lower = slower, more natural). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||
float BodyInterpSpeed = 2.0f;
|
||||
float BodyInterpSpeed = 4.0f;
|
||||
|
||||
/** Head rotation interpolation speed. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||
float HeadInterpSpeed = 5.0f;
|
||||
float HeadInterpSpeed = 4.0f;
|
||||
|
||||
/** Eye movement interpolation speed (higher = snappier). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||
float EyeInterpSpeed = 8.0f;
|
||||
float EyeInterpSpeed = 5.0f;
|
||||
|
||||
/** Interpolation speed when returning to neutral (TargetActor is null). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||
float ReturnToNeutralSpeed = 3.0f;
|
||||
|
||||
// ── Animation compensation ──────────────────────────────────────────────
|
||||
|
||||
/** How much the look-at overrides the animation's head rotation.
|
||||
* 1.0 = full override — head always points at target regardless of animation.
|
||||
* 0.0 = pure additive — posture stacks on top of animation (old behavior).
|
||||
* Default: 1.0 for conversational AI (always look at who you talk to). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float AnimationCompensation = 1.0f;
|
||||
|
||||
// ── Forward offset ──────────────────────────────────────────────────────
|
||||
|
||||
/** Yaw offset (degrees) between the actor's forward (+X) and the mesh's
|
||||
@ -139,7 +149,7 @@ public:
|
||||
* -90 = mesh faces -Y */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||
meta = (ClampMin = "-180", ClampMax = "180"))
|
||||
float MeshForwardYawOffset = 0.0f;
|
||||
float MeshForwardYawOffset = 90.0f;
|
||||
|
||||
// ── Head bone ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user