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)
|
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
|
// Each bone in the chain gets a fractional rotation (via Slerp weight).
|
||||||
// (removes parasitic ear-to-shoulder tilt).
|
// The swing-twist decomposition is done PER-BONE using each bone's own
|
||||||
// 2. Slerp a fraction to each bone in the chain.
|
// 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)
|
for (int32 i = 0; i < ChainBoneIndices.Num(); ++i)
|
||||||
{
|
{
|
||||||
const FCompactPoseBoneIndex BoneIdx = ChainBoneIndices[i];
|
const FCompactPoseBoneIndex BoneIdx = ChainBoneIndices[i];
|
||||||
@ -267,10 +241,21 @@ void FAnimNode_ElevenLabsPosture::Evaluate_AnyThread(FPoseContext& Output)
|
|||||||
continue;
|
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];
|
FTransform& BoneTransform = Output.Pose[BoneIdx];
|
||||||
BoneTransform.SetRotation(
|
const FQuat BoneRot = BoneTransform.GetRotation();
|
||||||
(FractionalRot * BoneTransform.GetRotation()).GetNormalized());
|
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
|
else
|
||||||
|
|||||||
@ -34,6 +34,11 @@ UElevenLabsPostureComponent::UElevenLabsPostureComponent()
|
|||||||
PrimaryComponentTick.bCanEverTick = true;
|
PrimaryComponentTick.bCanEverTick = true;
|
||||||
PrimaryComponentTick.TickGroup = TG_PrePhysics;
|
PrimaryComponentTick.TickGroup = TG_PrePhysics;
|
||||||
bAutoActivate = true;
|
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). */
|
* Useful for actors without a skeleton (e.g. (0,0,160) for eye-level). */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ToolTip = "Offset from target actor origin.\nE.g. (0,0,160) for eye-level."))
|
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) ───────────────────────────────────────────────
|
// ── Angle limits (degrees) ───────────────────────────────────────────────
|
||||||
//
|
//
|
||||||
@ -91,45 +91,55 @@ public:
|
|||||||
/** Maximum head yaw rotation in degrees. */
|
/** Maximum head yaw rotation in degrees. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0", ClampMax = "90"))
|
meta = (ClampMin = "0", ClampMax = "90"))
|
||||||
float MaxHeadYaw = 35.0f;
|
float MaxHeadYaw = 40.0f;
|
||||||
|
|
||||||
/** Maximum head pitch rotation in degrees. */
|
/** Maximum head pitch rotation in degrees. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0", ClampMax = "90"))
|
meta = (ClampMin = "0", ClampMax = "90"))
|
||||||
float MaxHeadPitch = 25.0f;
|
float MaxHeadPitch = 30.0f;
|
||||||
|
|
||||||
/** Maximum horizontal eye angle in degrees. */
|
/** Maximum horizontal eye angle in degrees. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0", ClampMax = "90"))
|
meta = (ClampMin = "0", ClampMax = "90"))
|
||||||
float MaxEyeHorizontal = 30.0f;
|
float MaxEyeHorizontal = 15.0f;
|
||||||
|
|
||||||
/** Maximum vertical eye angle in degrees. */
|
/** Maximum vertical eye angle in degrees. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0", ClampMax = "90"))
|
meta = (ClampMin = "0", ClampMax = "90"))
|
||||||
float MaxEyeVertical = 20.0f;
|
float MaxEyeVertical = 10.0f;
|
||||||
|
|
||||||
// ── Smoothing speeds ─────────────────────────────────────────────────────
|
// ── Smoothing speeds ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Body rotation interpolation speed (lower = slower, more natural). */
|
/** Body rotation interpolation speed (lower = slower, more natural). */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||||
float BodyInterpSpeed = 2.0f;
|
float BodyInterpSpeed = 4.0f;
|
||||||
|
|
||||||
/** Head rotation interpolation speed. */
|
/** Head rotation interpolation speed. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||||
float HeadInterpSpeed = 5.0f;
|
float HeadInterpSpeed = 4.0f;
|
||||||
|
|
||||||
/** Eye movement interpolation speed (higher = snappier). */
|
/** Eye movement interpolation speed (higher = snappier). */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||||
float EyeInterpSpeed = 8.0f;
|
float EyeInterpSpeed = 5.0f;
|
||||||
|
|
||||||
/** Interpolation speed when returning to neutral (TargetActor is null). */
|
/** Interpolation speed when returning to neutral (TargetActor is null). */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "0.1", ClampMax = "20"))
|
meta = (ClampMin = "0.1", ClampMax = "20"))
|
||||||
float ReturnToNeutralSpeed = 3.0f;
|
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 ──────────────────────────────────────────────────────
|
// ── Forward offset ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Yaw offset (degrees) between the actor's forward (+X) and the mesh's
|
/** Yaw offset (degrees) between the actor's forward (+X) and the mesh's
|
||||||
@ -139,7 +149,7 @@ public:
|
|||||||
* -90 = mesh faces -Y */
|
* -90 = mesh faces -Y */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ElevenLabs|Posture",
|
||||||
meta = (ClampMin = "-180", ClampMax = "180"))
|
meta = (ClampMin = "-180", ClampMax = "180"))
|
||||||
float MeshForwardYawOffset = 0.0f;
|
float MeshForwardYawOffset = 90.0f;
|
||||||
|
|
||||||
// ── Head bone ────────────────────────────────────────────────────────────
|
// ── Head bone ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user