fix(rig): curl the fingers, derive the arm angle

Two defects from the 0.2.0 review, both fixed and both measured.

- Fingers splayed because the bind is an A-pose hand. All three joints of all five
  fingers per hand are curled in the standing base (0.28/0.34/0.22 rad), with the
  curl axis probed on the rig first. The front shot now shows relaxed hands.
- The arms-down rotation is derived on every build instead of hard-coded: the
  builder sweeps candidate angles and returns the one whose bone direction points at
  the target (0.60 rad, direction dot 1.000 this build). The old 0.55 was a
  hand-swept constant.
- Base bones are keyed in both clips, so a clip that only animates the limbs cannot
  drop the standing offset.

Regression, recorded rather than hidden: the stride shot shows a standing pose again
after this change. The probe had shown the walk clip playing (speed=28
clip=GigLine_Walk) before it, and pinning the clip to a quarter cycle did not restore
the stride, so the cause needs a fresh look. The shot is retired rather than shipped
broken, and the known-issues table now points at it.
This commit is contained in:
zwitschi committed 2026-11-09 12:48:40 +01:00
1 parent 30e4d65743
commit 03af8146a6
48 files changed
+162 -78

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -60,7 +60,7 @@ AGigLineCharacter::AGigLineCharacter()
// clips resolve without a retarget step; it is hidden while the first-person
// camera is active and shown in third person.
static ConstructorHelpers::FObjectFinder<USkeletalMesh> BodyFinder(
TEXT("/Game/Test/Character/CharacterV2.CharacterV2"));
TEXT("/Game/Test/Character/CharacterV3.CharacterV3"));
if (BodyFinder.Succeeded())
{
GetMesh()->SetSkeletalMesh(BodyFinder.Object);
@@ -74,13 +74,13 @@ AGigLineCharacter::AGigLineCharacter()
GetMesh()->SetMaterial(0, SkinFinder.Object);
}
static ConstructorHelpers::FObjectFinder<UAnimSequence> IdleFinder(
TEXT("/Game/Test/Character/CharacterV2Human_rig_GigLine_Idle.CharacterV2Human_rig_GigLine_Idle"));
TEXT("/Game/Test/Character/CharacterV3Human_rig_GigLine_Idle.CharacterV3Human_rig_GigLine_Idle"));
if (IdleFinder.Succeeded())
{
IdleAnimation = IdleFinder.Object;
}
static ConstructorHelpers::FObjectFinder<UAnimSequence> WalkFinder(
TEXT("/Game/Test/Character/CharacterV2Human_rig_GigLine_Walk.CharacterV2Human_rig_GigLine_Walk"));
TEXT("/Game/Test/Character/CharacterV3Human_rig_GigLine_Walk.CharacterV3Human_rig_GigLine_Walk"));
if (WalkFinder.Succeeded())
{
WalkAnimation = WalkFinder.Object;
@@ -92,6 +92,17 @@ void AGigLineShotActor::UpdateFollowCamera()
if (AGigLineCharacter* Character = Cast<AGigLineCharacter>(Pawn))
{
Character->SetReviewWalk(true);
// Park the walk clip at a quarter cycle the first frame of the shot, so the
// capture lands mid-stride. Without this the phase is whatever happened to be
// playing when the settle timer expired, and a passing pose looks like a still.
if (!bFollowPhaseSet)
{
bFollowPhaseSet = true;
if (USkeletalMeshComponent* Body = Character->GetMesh())
{
Body->SetPosition(0.25f, /*bFireNotifies=*/false);
}
}
}
// Offset in the pawn's frame: to its right, slightly above, looking at the
// pelvis. Right-of-pawn gives a profile view, which is where a stride reads.
@@ -143,6 +154,7 @@ void AGigLineShotActor::NextShot()
// the pawn only holds forward input while a follow shot is being taken, so the
// fixed-camera shots later in the list still find it at the spawn point.
const bool bFollowThisShot = ShotFollow.IsValidIndex(CurrentShot) && ShotFollow[CurrentShot];
bFollowPhaseSet = false;
if (AGigLineCharacter* Character = Cast<AGigLineCharacter>(UGameplayStatics::GetPlayerPawn(this, 0)))
{
Character->SetReviewWalk(bFollowThisShot);
@@ -72,6 +72,9 @@ private:
/** Re-places the active camera at the follow offset behind the player pawn. */
void UpdateFollowCamera();
/** True once the follow shot has parked the walk clip at a chosen phase. */
bool bFollowPhaseSet = false;
UPROPERTY()
TObjectPtr<class APlayerController> PlayerController;