void UAnimGraphNode_BoneDrivenController::Draw(FPrimitiveDrawInterface* PDI, USkeletalMeshComponent* SkelMeshComp) const { static const float ArrowHeadWidth = 5.0f; static const float ArrowHeadHeight = 8.0f; const int32 SourceIdx = SkelMeshComp->GetBoneIndex(Node.SourceBone.BoneName); const int32 TargetIdx = SkelMeshComp->GetBoneIndex(Node.TargetBone.BoneName); if ((SourceIdx != INDEX_NONE) && (TargetIdx != INDEX_NONE)) { const FTransform SourceTM = SkelMeshComp->GetSpaceBases()[SourceIdx] * SkelMeshComp->ComponentToWorld; const FTransform TargetTM = SkelMeshComp->GetSpaceBases()[TargetIdx] * SkelMeshComp->ComponentToWorld; PDI->DrawLine(TargetTM.GetLocation(), SourceTM.GetLocation(), FLinearColor(0.0f, 0.0f, 1.0f), SDPG_Foreground, 0.5f); const FVector ToTarget = TargetTM.GetTranslation() - SourceTM.GetTranslation(); const FVector UnitToTarget = ToTarget.GetSafeNormal(); FVector Midpoint = SourceTM.GetTranslation() + 0.5f * ToTarget + 0.5f * UnitToTarget * ArrowHeadHeight; FVector YAxis; FVector ZAxis; UnitToTarget.FindBestAxisVectors(YAxis, ZAxis); const FMatrix ArrowMatrix(UnitToTarget, YAxis, ZAxis, Midpoint); DrawConnectedArrow(PDI, ArrowMatrix, FLinearColor(0.0f, 1.0f, 0.0), ArrowHeadHeight, ArrowHeadWidth, SDPG_Foreground); PDI->DrawPoint(SourceTM.GetTranslation(), FLinearColor(0.8f, 0.8f, 0.2f), 5.0f, SDPG_Foreground); PDI->DrawPoint(SourceTM.GetTranslation() + ToTarget, FLinearColor(0.8f, 0.8f, 0.2f), 5.0f, SDPG_Foreground); } }
void FConstraintInstance::SetRefFrame(EConstraintFrame::Type Frame, const FTransform& RefFrame) { #if WITH_PHYSX PxJointActorIndex::Enum PxFrame = U2PConstraintFrame(Frame); #endif if(Frame == EConstraintFrame::Frame1) { Pos1 = RefFrame.GetTranslation(); PriAxis1 = RefFrame.GetUnitAxis( EAxis::X ); SecAxis1 = RefFrame.GetUnitAxis( EAxis::Y ); } else { Pos2 = RefFrame.GetTranslation(); PriAxis2 = RefFrame.GetUnitAxis( EAxis::X ); SecAxis2 = RefFrame.GetUnitAxis( EAxis::Y ); } #if WITH_PHYSX if (PxD6Joint* Joint = GetUnbrokenJoint()) { PxTransform PxRefFrame = U2PTransform(RefFrame); Joint->setLocalPose(PxFrame, PxRefFrame); } #endif }
void FAnimNode_HandIKRetargeting::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { checkSlow(OutBoneTransforms.Num() == 0); // Get component space transforms for all of our IK and FK bones. FTransform const RightHandFKTM = MeshBases.GetComponentSpaceTransform(RightHandFK.BoneIndex); FTransform const LeftHandFKTM = MeshBases.GetComponentSpaceTransform(LeftHandFK.BoneIndex); FTransform const RightHandIKTM = MeshBases.GetComponentSpaceTransform(RightHandIK.BoneIndex); FTransform const LeftHandIKTM = MeshBases.GetComponentSpaceTransform(LeftHandIK.BoneIndex); // Compute weight FK and IK hand location. And translation from IK to FK. FVector const FKLocation = FMath::Lerp<FVector>(LeftHandFKTM.GetTranslation(), RightHandFKTM.GetTranslation(), HandFKWeight); FVector const IKLocation = FMath::Lerp<FVector>(LeftHandIKTM.GetTranslation(), RightHandIKTM.GetTranslation(), HandFKWeight); FVector const IK_To_FK_Translation = FKLocation - IKLocation; // If we're not translating, don't send any bones to update. if (!IK_To_FK_Translation.IsNearlyZero()) { // Move desired bones for (int32 BoneIndex = 0; BoneIndex < IKBonesToMove.Num(); BoneIndex++) { FBoneReference const & BoneReference = IKBonesToMove[BoneIndex]; if (BoneReference.IsValid(RequiredBones)) { FTransform BoneTransform = MeshBases.GetComponentSpaceTransform(BoneReference.BoneIndex); BoneTransform.AddToTranslation(IK_To_FK_Translation); OutBoneTransforms.Add(FBoneTransform(BoneReference.BoneIndex, BoneTransform)); } } } }
void UDebugSkelMeshComponent::ConsumeRootMotion(const FVector& FloorMin, const FVector& FloorMax) { if (bPreviewRootMotion) { if (UAnimInstance* AnimInst = GetAnimInstance()) { FRootMotionMovementParams ExtractedRootMotion = AnimInst->ConsumeExtractedRootMotion(); if (ExtractedRootMotion.bHasRootMotion) { AddLocalTransform(ExtractedRootMotion.RootMotionTransform); //Handle moving component so that it stays within the editor floor FTransform CurrentTransform = GetRelativeTransform(); FVector Trans = CurrentTransform.GetTranslation(); Trans.X = WrapInRange(Trans.X, FloorMin.X, FloorMax.X); Trans.Y = WrapInRange(Trans.Y, FloorMin.Y, FloorMax.Y); CurrentTransform.SetTranslation(Trans); SetRelativeTransform(CurrentTransform); } } } else { SetWorldTransform(FTransform()); } }
void FAnimNode_CopyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer & RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // Pass through if we're not doing anything. if( !bCopyTranslation && !bCopyRotation && !bCopyScale ) { return; } // Get component space transform for source and current bone. FTransform SourceBoneTM = MeshBases.GetComponentSpaceTransform(SourceBone.BoneIndex); FTransform CurrentBoneTM = MeshBases.GetComponentSpaceTransform(TargetBone.BoneIndex); // Copy individual components if (bCopyTranslation) { CurrentBoneTM.SetTranslation( SourceBoneTM.GetTranslation() ); } if (bCopyRotation) { CurrentBoneTM.SetRotation( SourceBoneTM.GetRotation() ); } if (bCopyScale) { CurrentBoneTM.SetScale3D( SourceBoneTM.GetScale3D() ); } // Output new transform for current bone. OutBoneTransforms.Add( FBoneTransform(TargetBone.BoneIndex, CurrentBoneTM) ); }
void AProjectTapGameMode::StartPlay() { Super::StartPlay(); auto gameState = GetGameState<AProjectTapGameState>(); ABallPawn* ball = nullptr; if ( UWorld* world = GetWorld() ) { AActor* playerStart = FindPlayerStart( 0 , FString( "Player" ) ); FTransform playerTransform = playerStart->GetTransform(); if ( ABallPlayerStart* realPlayerStart = Cast<ABallPlayerStart>( playerStart ) ) { auto possibleCamera = realPlayerStart->camera == nullptr ? nullptr : Cast<UProjectTapCameraComponent>( realPlayerStart->camera->GetComponentByClass( UProjectTapCameraComponent::StaticClass() ) ); FActorSpawnParameters params; ball = world->SpawnActor<ABallPawn>( ABallPawn::StaticClass() , playerTransform.GetTranslation() , FRotator( playerTransform.GetRotation() ) , params ); if ( ball != nullptr ) { ball->AddVelocity( realPlayerStart->initialVelocity , realPlayerStart->GetActorLocation() ); if ( possibleCamera != nullptr && realPlayerStart->followPlayer ) { ball->setCamera( realPlayerStart ); possibleCamera = ball->GetCamera(); } } gameState->SetCamera( possibleCamera ); isMenu = realPlayerStart->GameMode == CustomGameMode::GAME_MODE_MAIN_MENU; if ( realPlayerStart->music != nullptr )musicPlayer->SetSound( realPlayerStart->music ); } else { FActorSpawnParameters params; ball = world->SpawnActor<ABallPawn>( ABallPawn::StaticClass() , playerTransform.GetTranslation() , FRotator( playerTransform.GetRotation() ) , params ); } gameState->SetPlayer(ball); } musicPlayer->Play(); musicPlayer->SetVolumeMultiplier( 0 ); gameState->SetGameState( CustomGameState::GAME_STATE_PLAYING ); if ( isMenu ) gameState->SetGameMode( CustomGameMode::GAME_MODE_MAIN_MENU ); }
FQuat FAnimNode_RotationMultiplier::ExtractAngle(const FTransform& RefPoseTransform, const FTransform& LocalBoneTransform, const EBoneAxis Axis) { // local bone transform with reference rotation FTransform ReferenceBoneTransform = RefPoseTransform; ReferenceBoneTransform.SetTranslation(LocalBoneTransform.GetTranslation()); // find delta angle between the two quaternions X Axis. const FVector RotationAxis = GetAxisVector(Axis); const FVector LocalRotationVector = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); const FVector ReferenceRotationVector = ReferenceBoneTransform.GetRotation().RotateVector(RotationAxis); const FQuat LocalToRefQuat = FQuat::FindBetween(LocalRotationVector, ReferenceRotationVector); checkSlow( LocalToRefQuat.IsNormalized() ); // Rotate parent bone atom from position in local space to reference skeleton // Since our rotation rotates both vectors with shortest arc // we're essentially left with a quaternion that has angle difference with reference skeleton version const FQuat BoneQuatAligned = LocalToRefQuat* LocalBoneTransform.GetRotation(); checkSlow( BoneQuatAligned.IsNormalized() ); // Find that delta angle const FQuat DeltaQuat = (ReferenceBoneTransform.GetRotation().Inverse()) * BoneQuatAligned; checkSlow( DeltaQuat.IsNormalized() ); #if 0 //DEBUG_TWISTBONECONTROLLER UE_LOG(LogSkeletalControl, Log, TEXT("\t ExtractAngle, Bone: %s"), *SourceBone.BoneName.ToString()); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t Bone Quat: %s, Rot: %s, AxisX: %s"), *LocalBoneTransform.GetRotation().ToString(), *LocalBoneTransform.GetRotation().Rotator().ToString(), *LocalRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneRef Quat: %s, Rot: %s, AxisX: %s"), *ReferenceBoneTransform.GetRotation().ToString(), *ReferenceBoneTransform.GetRotation().Rotator().ToString(), *ReferenceRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t LocalToRefQuat Quat: %s, Rot: %s"), *LocalToRefQuat.ToString(), *LocalToRefQuat.Rotator().ToString() ); const FVector BoneQuatAlignedX = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneQuatAligned Quat: %s, Rot: %s, AxisX: %s"), *BoneQuatAligned.ToString(), *BoneQuatAligned.Rotator().ToString(), *BoneQuatAlignedX.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuat Quat: %s, Rot: %s"), *DeltaQuat.ToString(), *DeltaQuat.Rotator().ToString() ); FTransform BoneAtomAligned(BoneQuatAligned, ReferenceBoneTransform.GetTranslation()); const FQuat DeltaQuatAligned = FQuat::FindBetween(BoneAtomAligned.GetScaledAxis( EAxis::X ), ReferenceBoneTransform.GetScaledAxis( EAxis::X )); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuatAligned Quat: %s, Rot: %s"), *DeltaQuatAligned.ToString(), *DeltaQuatAligned.Rotator().ToString() ); FVector DeltaAxis; float DeltaAngle; DeltaQuat.ToAxisAndAngle(DeltaAxis, DeltaAngle); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaAxis: %s, DeltaAngle: %f"), *DeltaAxis.ToString(), DeltaAngle ); #endif return DeltaQuat; }
FTransform USkeletalMeshComponent::ConvertLocalRootMotionToWorld(const FTransform & InTransform) { // Make sure component to world is up to date if (!bWorldToComponentUpdated) { UpdateComponentToWorld(); } const FTransform NewWorldTransform = InTransform * ComponentToWorld; const FVector DeltaWorldTranslation = NewWorldTransform.GetTranslation() - ComponentToWorld.GetTranslation(); const FQuat DeltaWorldRotation = ComponentToWorld.GetRotation().Inverse() * NewWorldTransform.GetRotation(); const FTransform DeltaWorldTransform(DeltaWorldRotation, DeltaWorldTranslation); UE_LOG(LogRootMotion, Log, TEXT("ConvertLocalRootMotionToWorld LocalT: %s, LocalR: %s, WorldT: %s, WorldR: %s."), *InTransform.GetTranslation().ToCompactString(), *InTransform.GetRotation().Rotator().ToCompactString(), *DeltaWorldTransform.GetTranslation().ToCompactString(), *DeltaWorldTransform.GetRotation().Rotator().ToCompactString() ); return DeltaWorldTransform; }
FBox FKSphereElem::CalcAABB(const FTransform& BoneTM, float Scale) const { FTransform ElemTM = GetTransform(); ElemTM.ScaleTranslation( FVector(Scale) ); ElemTM *= BoneTM; const FVector BoxCenter = ElemTM.GetTranslation(); const FVector BoxExtents(Radius * Scale); return FBox(BoxCenter - BoxExtents, BoxCenter + BoxExtents); }
void AActor::EditorApplyTranslation(const FVector& DeltaTranslation, bool bAltDown, bool bShiftDown, bool bCtrlDown) { if( RootComponent != NULL ) { FTransform NewTransform = GetRootComponent()->GetComponentTransform(); NewTransform.SetTranslation(NewTransform.GetTranslation() + DeltaTranslation); GetRootComponent()->SetWorldTransform(NewTransform); } else { UE_LOG(LogActor, Warning, TEXT("WARNING: EditorApplyTranslation %s has no root component"), *GetName() ); } }
float FAttenuationSettings::AttenuationEvalCapsule(const FTransform& SoundTransform, const FVector ListenerLocation, const float DistanceScale) const { float Distance = 0.f; const float CapsuleHalfHeight = AttenuationShapeExtents.X; const float CapsuleRadius = AttenuationShapeExtents.Y; // Capsule devolves to a sphere if HalfHeight <= Radius if (CapsuleHalfHeight <= CapsuleRadius ) { Distance = FMath::Max(FVector::Dist( SoundTransform.GetTranslation(), ListenerLocation ) - CapsuleRadius, 0.f); } else { const FVector PointOffset = (CapsuleHalfHeight - CapsuleRadius) * SoundTransform.GetUnitAxis( EAxis::Z ); const FVector StartPoint = SoundTransform.GetTranslation() + PointOffset; const FVector EndPoint = SoundTransform.GetTranslation() - PointOffset; Distance = FMath::PointDistToSegment(ListenerLocation, StartPoint, EndPoint) - CapsuleRadius; } return AttenuationEval(Distance, FalloffDistance, DistanceScale); }
void FTransformCurve::UpdateOrAddKey(const FTransform& NewKey, float CurrentTime) { TranslationCurve.UpdateOrAddKey(NewKey.GetTranslation(), CurrentTime); // pitch, yaw, roll order - please check Evaluate function FVector RotationAsVector; FRotator Rotator = NewKey.GetRotation().Rotator(); RotationAsVector.X = Rotator.Roll; RotationAsVector.Y = Rotator.Pitch; RotationAsVector.Z = Rotator.Yaw; RotationCurve.UpdateOrAddKey(RotationAsVector, CurrentTime); ScaleCurve.UpdateOrAddKey(NewKey.GetScale3D(), CurrentTime); }
void FAnimNode_CopyBone::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // Pass through if we're not doing anything. if( !bCopyTranslation && !bCopyRotation && !bCopyScale ) { return; } // Get component space transform for source and current bone. const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex SourceBoneIndex = SourceBone.GetCompactPoseIndex(BoneContainer); FCompactPoseBoneIndex TargetBoneIndex = TargetBone.GetCompactPoseIndex(BoneContainer); FTransform SourceBoneTM = MeshBases.GetComponentSpaceTransform(SourceBoneIndex); FTransform CurrentBoneTM = MeshBases.GetComponentSpaceTransform(TargetBoneIndex); if(ControlSpace != BCS_ComponentSpace) { // Convert out to selected space FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, SourceBoneTM, SourceBoneIndex, ControlSpace); FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, CurrentBoneTM, TargetBoneIndex, ControlSpace); } // Copy individual components if (bCopyTranslation) { CurrentBoneTM.SetTranslation( SourceBoneTM.GetTranslation() ); } if (bCopyRotation) { CurrentBoneTM.SetRotation( SourceBoneTM.GetRotation() ); } if (bCopyScale) { CurrentBoneTM.SetScale3D( SourceBoneTM.GetScale3D() ); } if(ControlSpace != BCS_ComponentSpace) { // Convert back out if we aren't operating in component space FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, CurrentBoneTM, TargetBoneIndex, ControlSpace); } // Output new transform for current bone. OutBoneTransforms.Add(FBoneTransform(TargetBoneIndex, CurrentBoneTM)); }
void UPhysicsHandleComponent::UpdateHandleTransform(const FTransform& NewTransform) { if(!KinActorData) { return; } #if WITH_PHYSX bool bChangedPosition = true; bool bChangedRotation = true; PxRigidDynamic* KinActor = KinActorData; // Check if the new location is worthy of change PxVec3 PNewLocation = U2PVector(NewTransform.GetTranslation()); PxVec3 PCurrentLocation = KinActor->getGlobalPose().p; if((PNewLocation - PCurrentLocation).magnitudeSquared() <= 0.01f*0.01f) { PNewLocation = PCurrentLocation; bChangedPosition = false; } // Check if the new rotation is worthy of change PxQuat PNewOrientation = U2PQuat(NewTransform.GetRotation()); PxQuat PCurrentOrientation = KinActor->getGlobalPose().q; if(!(FMath::Abs(PNewOrientation.dot(PCurrentOrientation)) < (1.f - KINDA_SMALL_NUMBER))) { PNewOrientation = PCurrentOrientation; bChangedRotation = false; } // Don't call moveKinematic if it hasn't changed - that will stop bodies from going to sleep. if (bChangedPosition || bChangedRotation) { KinActor->setKinematicTarget(PxTransform(PNewLocation, PNewOrientation)); //LOC_MOD //PxD6Joint* Joint = (PxD6Joint*) HandleData; //if(Joint)// && (PNewLocation - PCurrentLocation).magnitudeSquared() > 0.01f*0.01f) //{ // PxRigidActor* Actor0, *Actor1; // Joint->getActors(Actor0, Actor1); // //Joint->setDrivePosition(PxTransform(Actor0->getGlobalPose().transformInv(PNewLocation))); // Joint->setDrivePosition(PxTransform::createIdentity()); // //Joint->setDriveVelocity(PxVec3(0), PxVec3(0)); //} } #endif // WITH_PHYSX }
//============================================================================= static void TransformToSteamSpace(const FTransform& In, vr::HmdMatrix34_t& Out, float WorldToMeterScale) { const FRotator InRot = In.Rotator(); FRotator OutRot(InRot.Yaw, -InRot.Roll, -InRot.Pitch); const FVector InPos = In.GetTranslation(); FVector OutPos(InPos.Y, InPos.Z, -InPos.X); OutPos /= WorldToMeterScale; const FVector InScale = In.GetScale3D(); FVector OutScale(InScale.Y, InScale.Z, -InScale.X); OutScale /= WorldToMeterScale; Out = FSteamVRHMD::ToHmdMatrix34(FTransform(OutRot, OutPos, OutScale).ToMatrixNoScale()); }
bool UPrimitiveComponent::GetRigidBodyState(FRigidBodyState& OutState, FName BoneName) { FBodyInstance* BI = GetBodyInstance(BoneName); if (BI && BI->IsInstanceSimulatingPhysics()) { FTransform BodyTM = BI->GetUnrealWorldTransform(); OutState.Position = BodyTM.GetTranslation(); OutState.Quaternion = BodyTM.GetRotation(); OutState.LinVel = BI->GetUnrealWorldVelocity(); OutState.AngVel = BI->GetUnrealWorldAngularVelocity(); OutState.Flags = (BI->IsInstanceAwake() ? ERigidBodyFlags::None : ERigidBodyFlags::Sleeping); return true; } return false; }
void UAURSmoothingFilterKalman::Measurement(FTransform const & MeasuredTransform) { try { FVector translation = MeasuredTransform.GetTranslation(); FString msg = "In: " + translation.ToString(); //FQuat rotation = MeasuredTransform.GetRotation(); TransformAsMat.at<float>(0) = translation.X; TransformAsMat.at<float>(1) = translation.Y; TransformAsMat.at<float>(2) = translation.Z; //TransformAsMat.at<float>(3) = rotation.X * rotation.W; //TransformAsMat.at<float>(4) = rotation.Y * rotation.W; //TransformAsMat.at<float>(5) = rotation.Z * rotation.W; cv::Mat FilterResult = Filter.predict(); Filter.correct(TransformAsMat); translation.Set(FilterResult.at<float>(0), FilterResult.at<float>(1), FilterResult.at<float>(2)); msg += "\nOut: " + translation.ToString(); UE_LOG(LogAUR, Log, TEXT("%s"), *msg); //FVector rot_axis(FilterResult.at<float>(3), FilterResult.at<float>(4), FilterResult.at<float>(5)); //float rot_magnitude = rot_axis.Size(); //rot_axis.Normalize(); //rotation.X = rot_axis.X; //rotation.Y = rot_axis.Y; //rotation.Z = rot_axis.Z; //rotation.W = rot_magnitude; CurrentTransform.SetComponents(MeasuredTransform.GetRotation(), translation, FVector(1, 1, 1)); } catch (cv::Exception& e) { FString exception_msg(e.what()); UE_LOG(LogAUR, Error, TEXT("Kalman: %s"), *exception_msg) CurrentTransform = MeasuredTransform; } }
void UGripMotionControllerComponent::UpdatePhysicsHandleTransform(const FBPActorGripInformation &GrippedActor, const FTransform& NewTransform) { if (!GrippedActor.Actor && !GrippedActor.Component) return; FBPActorPhysicsHandleInformation * HandleInfo = GetPhysicsGrip(GrippedActor); if (!HandleInfo || !HandleInfo->KinActorData) return; #if WITH_PHYSX bool bChangedPosition = true; bool bChangedRotation = true; PxRigidDynamic* KinActor = HandleInfo->KinActorData; PxScene* PScene = GetPhysXSceneFromIndex(HandleInfo->SceneIndex); SCOPED_SCENE_WRITE_LOCK(PScene); // Check if the new location is worthy of change PxVec3 PNewLocation = U2PVector(NewTransform.GetTranslation()); PxVec3 PCurrentLocation = KinActor->getGlobalPose().p; if ((PNewLocation - PCurrentLocation).magnitudeSquared() <= 0.01f*0.01f) { PNewLocation = PCurrentLocation; bChangedPosition = false; } // Check if the new rotation is worthy of change PxQuat PNewOrientation = U2PQuat(NewTransform.GetRotation()); PxQuat PCurrentOrientation = KinActor->getGlobalPose().q; if ((FMath::Abs(PNewOrientation.dot(PCurrentOrientation)) > (1.f - SMALL_NUMBER))) { PNewOrientation = PCurrentOrientation; bChangedRotation = false; } // Don't call moveKinematic if it hasn't changed - that will stop bodies from going to sleep. if (bChangedPosition || bChangedRotation) { KinActor->setKinematicTarget(PxTransform(PNewLocation, PNewOrientation)); } #endif // WITH_PHYSX }
float FAttenuationSettings::AttenuationEvalCone(const FTransform& SoundTransform, const FVector ListenerLocation, const float DistanceScale) const { const FVector SoundForward = SoundTransform.GetUnitAxis( EAxis::X ); float VolumeMultiplier = 1.f; const FVector Origin = SoundTransform.GetTranslation() - (SoundForward * ConeOffset); const float Distance = FMath::Max(FVector::Dist( Origin, ListenerLocation ) - AttenuationShapeExtents.X, 0.f); VolumeMultiplier *= AttenuationEval(Distance, FalloffDistance, DistanceScale); if (VolumeMultiplier > 0.f) { const float theta = FMath::RadiansToDegrees(fabsf(FMath::Acos( FVector::DotProduct(SoundForward, (ListenerLocation - Origin).GetSafeNormal())))); VolumeMultiplier *= AttenuationEval(theta - AttenuationShapeExtents.Y, AttenuationShapeExtents.Z, 1.0f); } return VolumeMultiplier; }
void UDebugSkelMeshComponent::ConsumeRootMotion(const FVector& FloorMin, const FVector& FloorMax) { //Extract root motion regardless of where we use it so that we don't hit //problems with it building up in the instance FRootMotionMovementParams ExtractedRootMotion; if (UAnimInstance* AnimInst = GetAnimInstance()) { ExtractedRootMotion = AnimInst->ConsumeExtractedRootMotion(1.f); } if (bPreviewRootMotion) { if (ExtractedRootMotion.bHasRootMotion) { AddLocalTransform(ExtractedRootMotion.RootMotionTransform); //Handle moving component so that it stays within the editor floor FTransform CurrentTransform = GetRelativeTransform(); FVector Trans = CurrentTransform.GetTranslation(); Trans.X = WrapInRange(Trans.X, FloorMin.X, FloorMax.X); Trans.Y = WrapInRange(Trans.Y, FloorMin.Y, FloorMax.Y); CurrentTransform.SetTranslation(Trans); SetRelativeTransform(CurrentTransform); } } else { if (TurnTableMode == EPersonaTurnTableMode::Stopped) { SetWorldTransform(FTransform()); } else { SetRelativeLocation(FVector::ZeroVector); } } }
void UAnimCompositeBase::ExtractRootMotionFromTrack(const FAnimTrack &SlotAnimTrack, float StartTrackPosition, float EndTrackPosition, FRootMotionMovementParams &RootMotion) const { TArray<FRootMotionExtractionStep> RootMotionExtractionSteps; SlotAnimTrack.GetRootMotionExtractionStepsForTrackRange(RootMotionExtractionSteps, StartTrackPosition, EndTrackPosition); UE_LOG(LogRootMotion, Log, TEXT("\tUAnimMontage::ExtractRootMotionForTrackRange, NumSteps: %d, StartTrackPosition: %.3f, EndTrackPosition: %.3f"), RootMotionExtractionSteps.Num(), StartTrackPosition, EndTrackPosition); // Go through steps sequentially, extract root motion, and accumulate it. // This has to be done in order so root motion translation & rotation is applied properly (as translation is relative to rotation) for (int32 StepIndex = 0; StepIndex < RootMotionExtractionSteps.Num(); StepIndex++) { const FRootMotionExtractionStep & CurrentStep = RootMotionExtractionSteps[StepIndex]; if (CurrentStep.AnimSequence->bEnableRootMotion) { FTransform DeltaTransform = CurrentStep.AnimSequence->ExtractRootMotionFromRange(CurrentStep.StartPosition, CurrentStep.EndPosition); RootMotion.Accumulate(DeltaTransform); UE_LOG(LogRootMotion, Log, TEXT("\t\tCurrentStep: %d, StartPos: %.3f, EndPos: %.3f, Anim: %s DeltaTransform Translation: %s, Rotation: %s"), StepIndex, CurrentStep.StartPosition, CurrentStep.EndPosition, *CurrentStep.AnimSequence->GetName(), *DeltaTransform.GetTranslation().ToCompactString(), *DeltaTransform.GetRotation().Rotator().ToCompactString()); } } }
FCreatureBoneData ACreatureActor::GetBluePrintBoneData(FString name_in, bool world_transform) { FCreatureBoneData ret_data; for (size_t i = 0; i < bone_data.Num(); i++) { if (bone_data[i].name == name_in) { ret_data = bone_data[i]; if (world_transform) { FTransform xform = GetTransform(); FVector world_location = xform.GetTranslation(); ret_data.point1 = xform.TransformPosition(ret_data.point1); ret_data.point2 = xform.TransformPosition(ret_data.point2); } break; } } return ret_data; }
void UGripMotionControllerComponent::TickGrip() { if (GrippedActors.Num()) { FTransform WorldTransform; FTransform InverseTransform = this->GetComponentTransform().Inverse(); for (int i = GrippedActors.Num() - 1; i >= 0; --i) { if (GrippedActors[i].Actor || GrippedActors[i].Component) { // GetRelativeTransformReverse had some serious f*****g floating point errors associated with it that was f*****g everything up // Not sure whats wrong with the function but I might want to push a patch out eventually WorldTransform = GrippedActors[i].RelativeTransform.GetRelativeTransform(InverseTransform); UPrimitiveComponent *root = GrippedActors[i].Component; AActor *actor = GrippedActors[i].Actor; if (!root) root = Cast<UPrimitiveComponent>(GrippedActors[i].Actor->GetRootComponent()); if (!root) continue; if (!actor) actor = root->GetOwner(); if (!actor) continue; if (bIsServer) { // Handle collision intersection detection, this is used to intelligently manage some of the networking and late update features. switch (GrippedActors[i].GripCollisionType.GetValue()) { case EGripCollisionType::InteractiveCollisionWithPhysics: case EGripCollisionType::InteractiveHybridCollisionWithSweep: { TArray<FOverlapResult> Hits; FComponentQueryParams Params(NAME_None, this->GetOwner()); Params.bTraceAsyncScene = root->bCheckAsyncSceneOnMove; Params.AddIgnoredActor(actor); Params.AddIgnoredActors(root->MoveIgnoreActors); if(GetWorld()->ComponentOverlapMulti(Hits, root, root->GetComponentLocation(), root->GetComponentQuat(), Params)) { GrippedActors[i].bColliding = true; } else { GrippedActors[i].bColliding = false; } } // Skip collision intersects with these types, they dont need it case EGripCollisionType::PhysicsOnly: case EGripCollisionType::SweepWithPhysics: case EGripCollisionType::InteractiveCollisionWithSweep: default:break; } } // Need to figure out best default behavior /*if (GrippedActors[i].bHasSecondaryAttachment && GrippedActors[i].SecondaryAttachment) { WorldTransform.SetRotation((WorldTransform.GetLocation() - GrippedActors[i].SecondaryAttachment->GetComponentLocation()).ToOrientationRotator().Quaternion()); }*/ if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveCollisionWithPhysics) { UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform); } else if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveCollisionWithSweep) { if (bIsServer) { FHitResult OutHit; // Need to use without teleport so that the physics velocity is updated for when the actor is released to throw root->SetWorldTransform(WorldTransform, true, &OutHit); if (OutHit.bBlockingHit) { GrippedActors[i].bColliding = true; if (!actor->bReplicateMovement) actor->SetReplicateMovement(true); } else { GrippedActors[i].bColliding = false; if (actor->bReplicateMovement) actor->SetReplicateMovement(false); } } else { if (!GrippedActors[i].bColliding) { root->SetWorldTransform(WorldTransform, true); } } } else if (GrippedActors[i].GripCollisionType == EGripCollisionType::InteractiveHybridCollisionWithSweep) { // Need to use without teleport so that the physics velocity is updated for when the actor is released to throw //if (bIsServer) //{ FBPActorPhysicsHandleInformation * GripHandle = GetPhysicsGrip(GrippedActors[i]); if (!GrippedActors[i].bColliding) { // Make sure that there is no collision on course before turning off collision and snapping to controller if (bIsServer && actor && actor->bReplicateMovement) { FCollisionQueryParams QueryParams(NAME_None, false, this->GetOwner()); FCollisionResponseParams ResponseParam; root->InitSweepCollisionParams(QueryParams, ResponseParam); QueryParams.AddIgnoredActor(actor); QueryParams.AddIgnoredActors(root->MoveIgnoreActors); if (GetWorld()->SweepTestByChannel(root->GetComponentLocation(), WorldTransform.GetLocation(), root->GetComponentQuat(), ECollisionChannel::ECC_Visibility, root->GetCollisionShape(), QueryParams, ResponseParam)) { GrippedActors[i].bColliding = true; } else { GrippedActors[i].bColliding = false; } } // If still not colliding if (!GrippedActors[i].bColliding) { if (bIsServer && actor->bReplicateMovement) // So we don't call on on change event over and over locally actor->SetReplicateMovement(false); if (bIsServer && GripHandle) { DestroyPhysicsHandle(GrippedActors[i]); if(GrippedActors[i].Actor) GrippedActors[i].Actor->DisableComponentsSimulatePhysics(); else root->SetSimulatePhysics(false); } root->SetWorldTransform(WorldTransform, false); } else { if (bIsServer && GrippedActors[i].bColliding && GripHandle) UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform); } } else if (GrippedActors[i].bColliding && !GripHandle) { if (bIsServer && !actor->bReplicateMovement) actor->SetReplicateMovement(true); root->SetSimulatePhysics(true); if (bIsServer) SetUpPhysicsHandle(GrippedActors[i]); } else { if (bIsServer && GrippedActors[i].bColliding && GripHandle) UpdatePhysicsHandleTransform(GrippedActors[i], WorldTransform); } } else if (GrippedActors[i].GripCollisionType == EGripCollisionType::SweepWithPhysics) { if (bIsServer) { FVector OriginalPosition(root->GetComponentLocation()); FRotator OriginalOrientation(root->GetComponentRotation()); FVector NewPosition(WorldTransform.GetTranslation()); FRotator NewOrientation(WorldTransform.GetRotation()); // Now sweep collision separately so we can get hits but not have the location altered if (bUseWithoutTracking || NewPosition != OriginalPosition || NewOrientation != OriginalOrientation) { FVector move = NewPosition - OriginalPosition; // ComponentSweepMulti does nothing if moving < KINDA_SMALL_NUMBER in distance, so it's important to not try to sweep distances smaller than that. const float MinMovementDistSq = (FMath::Square(4.f*KINDA_SMALL_NUMBER)); if (bUseWithoutTracking || move.SizeSquared() > MinMovementDistSq || NewOrientation != OriginalOrientation) { if (CheckComponentWithSweep(root, move, NewOrientation, false)) { } } } // Move the actor, we are not offsetting by the hit result anyway root->SetWorldTransform(WorldTransform, false); } else { // Move the actor, we are not offsetting by the hit result anyway root->SetWorldTransform(WorldTransform, false); } } else if (GrippedActors[i].GripCollisionType == EGripCollisionType::PhysicsOnly) { // Move the actor, we are not offsetting by the hit result anyway root->SetWorldTransform(WorldTransform, false); } } else { if (bIsServer) { DestroyPhysicsHandle(GrippedActors[i]); GrippedActors.RemoveAt(i); // If it got garbage collected then just remove the pointer, won't happen with new uproperty use, but keeping it here anyway } } } } }
void FAnimNode_CopyBoneDelta::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { if(!bCopyTranslation && !bCopyRotation && !bCopyScale) { return; } const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); FCompactPoseBoneIndex SourceBoneIndex = SourceBone.GetCompactPoseIndex(BoneContainer); FCompactPoseBoneIndex TargetBoneIndex = TargetBone.GetCompactPoseIndex(BoneContainer); FTransform SourceTM = MeshBases.GetComponentSpaceTransform(SourceBoneIndex); FTransform TargetTM = MeshBases.GetComponentSpaceTransform(TargetBoneIndex); // Convert to parent space FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, SourceTM, SourceBoneIndex, BCS_ParentBoneSpace); FAnimationRuntime::ConvertCSTransformToBoneSpace(SkelComp, MeshBases, TargetTM, TargetBoneIndex, BCS_ParentBoneSpace); // Ref pose transform FTransform RefLSTransform = SkelComp->SkeletalMesh->RefSkeleton.GetRefBonePose()[SourceBone.GetMeshPoseIndex().GetInt()]; // Get transform relative to ref pose SourceTM.SetToRelativeTransform(RefLSTransform); if(CopyMode == CopyBoneDeltaMode::Accumulate) { if(bCopyTranslation) { TargetTM.AddToTranslation(SourceTM.GetTranslation() * TranslationMultiplier); } if(bCopyRotation) { FVector Axis; float Angle; SourceTM.GetRotation().ToAxisAndAngle(Axis, Angle); TargetTM.SetRotation(FQuat(Axis, Angle * RotationMultiplier) * TargetTM.GetRotation()); } if(bCopyScale) { TargetTM.SetScale3D(TargetTM.GetScale3D() * (SourceTM.GetScale3D() * ScaleMultiplier)); } } else //CopyMode = CopyBoneDeltaMode::Copy { if(bCopyTranslation) { TargetTM.SetTranslation(SourceTM.GetTranslation() * TranslationMultiplier); } if(bCopyRotation) { FVector Axis; float Angle; SourceTM.GetRotation().ToAxisAndAngle(Axis, Angle); TargetTM.SetRotation(FQuat(Axis, Angle * RotationMultiplier)); } if(bCopyScale) { TargetTM.SetScale3D(SourceTM.GetScale3D() * ScaleMultiplier); } } // Back out to component space FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, TargetTM, TargetBoneIndex, BCS_ParentBoneSpace); OutBoneTransforms.Add(FBoneTransform(TargetBoneIndex, TargetTM)); }
FString UKismetStringLibrary::Conv_TransformToString(const FTransform& InTrans) { return FString::Printf(TEXT("Translation: %s Rotation: %s Scale %s"), *InTrans.GetTranslation().ToString(), *InTrans.Rotator().ToString(), *InTrans.GetScale3D().ToString()); }
/** Extract RootMotion Transform from a contiguous Track position range. * *CONTIGUOUS* means that if playing forward StartTractPosition < EndTrackPosition. * No wrapping over if looping. No jumping across different sections. * So the AnimMontage has to break the update into contiguous pieces to handle those cases. * * This does handle Montage playing backwards (StartTrackPosition > EndTrackPosition). * * It will break down the range into steps if needed to handle looping animations, or different animations. * These steps will be processed sequentially, and output the RootMotion transform in component space. */ FTransform UAnimMontage::ExtractRootMotionFromTrackRange(float StartTrackPosition, float EndTrackPosition) const { FRootMotionMovementParams RootMotion; // For now assume Root Motion only comes from first track. if( SlotAnimTracks.Num() > 0 ) { const FAnimTrack& SlotAnimTrack = SlotAnimTracks[0].AnimTrack; // Get RootMotion pieces from this track. // We can deal with looping animations, or multiple animations. So we break those up into sequential operations. // (Animation, StartFrame, EndFrame) so we can then extract root motion sequentially. TArray<FRootMotionExtractionStep> RootMotionExtractionSteps; SlotAnimTrack.GetRootMotionExtractionStepsForTrackRange(RootMotionExtractionSteps, StartTrackPosition, EndTrackPosition); UE_LOG(LogRootMotion, Log, TEXT("\tUAnimMontage::ExtractRootMotionForTrackRange, NumSteps: %d, StartTrackPosition: %.3f, EndTrackPosition: %.3f"), RootMotionExtractionSteps.Num(), StartTrackPosition, EndTrackPosition); // Process root motion steps if any if( RootMotionExtractionSteps.Num() > 0 ) { FTransform InitialTransform, StartTransform, EndTransform; // Go through steps sequentially, extract root motion, and accumulate it. // This has to be done in order so root motion translation & rotation is applied properly (as translation is relative to rotation) for(int32 StepIndex=0; StepIndex<RootMotionExtractionSteps.Num(); StepIndex++) { const FRootMotionExtractionStep& CurrentStep = RootMotionExtractionSteps[StepIndex]; CurrentStep.AnimSequence->ExtractRootTrack(0.f, InitialTransform, NULL); CurrentStep.AnimSequence->ExtractRootTrack(CurrentStep.StartPosition, StartTransform, NULL); CurrentStep.AnimSequence->ExtractRootTrack(CurrentStep.EndPosition, EndTransform, NULL); // Transform to Component Space Rotation (inverse root transform from first frame) const FTransform RootToComponentRot = FTransform(InitialTransform.GetRotation().Inverse()); StartTransform = RootToComponentRot * StartTransform; EndTransform = RootToComponentRot * EndTransform; FTransform DeltaTransform = EndTransform.GetRelativeTransform(StartTransform); // Filter out rotation if( !bEnableRootMotionRotation ) { DeltaTransform.SetRotation(FQuat::Identity); } // Filter out translation if( !bEnableRootMotionTranslation ) { DeltaTransform.SetTranslation(FVector::ZeroVector); } RootMotion.Accumulate(DeltaTransform); UE_LOG(LogRootMotion, Log, TEXT("\t\tCurrentStep: %d, StartPos: %.3f, EndPos: %.3f, Anim: %s DeltaTransform Translation: %s, Rotation: %s"), StepIndex, CurrentStep.StartPosition, CurrentStep.EndPosition, *CurrentStep.AnimSequence->GetName(), *DeltaTransform.GetTranslation().ToCompactString(), *DeltaTransform.GetRotation().Rotator().ToCompactString() ); } } } UE_LOG(LogRootMotion, Log, TEXT("\tUAnimMontage::ExtractRootMotionForTrackRange RootMotionTransform: Translation: %s, Rotation: %s"), *RootMotion.RootMotionTransform.GetTranslation().ToCompactString(), *RootMotion.RootMotionTransform.GetRotation().Rotator().ToCompactString() ); return RootMotion.RootMotionTransform; }
bool UAnimExporterITP::ExportText(const FExportObjectInnerContext* Context, UObject* Object, const TCHAR* Type, FOutputDevice& Ar, FFeedbackContext* Warn, uint32 PortFlags /*= 0*/) { UAnimSequence* AnimSeq = CastChecked<UAnimSequence>(Object); USkeleton* Skeleton = AnimSeq->GetSkeleton(); const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); USkeletalMesh* SkelMesh = Skeleton->GetPreviewMesh(); if (AnimSeq->SequenceLength == 0.f) { // something is wrong return false; } const float FrameRate = AnimSeq->NumFrames / AnimSeq->SequenceLength; // Open another archive FArchive* File = IFileManager::Get().CreateFileWriter(*UExporter::CurrentFilename); // Let's try the header... File->Logf(TEXT("{")); File->Logf(TEXT("\t\"metadata\":{")); File->Logf(TEXT("\t\t\"type\":\"itpanim\",")); File->Logf(TEXT("\t\t\"version\":2")); File->Logf(TEXT("\t},")); File->Logf(TEXT("\t\"sequence\":{")); File->Logf(TEXT("\t\t\"frames\":%d,"), AnimSeq->NumFrames); File->Logf(TEXT("\t\t\"length\":%f,"), AnimSeq->SequenceLength); File->Logf(TEXT("\t\t\"bonecount\":%d,"), RefSkeleton.GetNum()); File->Logf(TEXT("\t\t\"tracks\":[")); bool firstOutput = false; for (int32 BoneIndex = 0; BoneIndex < RefSkeleton.GetNum(); ++BoneIndex) { //int32 BoneTreeIndex = Skeleton->GetSkeletonBoneIndexFromMeshBoneIndex(SkelMesh, BoneIndex); int32 BoneTrackIndex = Skeleton->GetAnimationTrackIndex(BoneIndex, AnimSeq); if (BoneTrackIndex == INDEX_NONE) { // If this sequence does not have a track for the current bone, then skip it continue; } if (firstOutput) { File->Logf(TEXT("\t\t\t},")); } firstOutput = true; File->Logf(TEXT("\t\t\t{")); File->Logf(TEXT("\t\t\t\t\"bone\":%d,"), BoneIndex); File->Logf(TEXT("\t\t\t\t\"transforms\":[")); float AnimTime = 0.0f; float AnimEndTime = AnimSeq->SequenceLength; // Subtracts 1 because NumFrames includes an initial pose for 0.0 second double TimePerKey = (AnimSeq->SequenceLength / (AnimSeq->NumFrames - 1)); const float AnimTimeIncrement = TimePerKey; bool bLastKey = false; // Step through each frame and add the bone's transformation data while (!bLastKey) { const TArray<FBoneNode>& BoneTree = Skeleton->GetBoneTree(); FTransform BoneAtom; AnimSeq->GetBoneTransform(BoneAtom, BoneTrackIndex, AnimTime, true); bLastKey = AnimTime >= AnimEndTime; File->Logf(TEXT("\t\t\t\t\t{")); FQuat rot = BoneAtom.GetRotation(); // For the root bone, we need to fix-up the rotation because Unreal exports // animations with Y-forward for some reason (maybe because Maya?) if (BoneIndex == 0) { FQuat addRot(FVector(0.0f, 0.0f, 1.0f), -1.57f); rot = addRot * rot; } File->Logf(TEXT("\t\t\t\t\t\t\"rot\":[%f,%f,%f,%f],"), rot.X, rot.Y, rot.Z, rot.W); FVector trans = BoneAtom.GetTranslation(); // Sanjay: If it's skeleton retargeting, change the translation to be from the ref pose skeleton if (BoneTree[BoneIndex].TranslationRetargetingMode == EBoneTranslationRetargetingMode::Skeleton) { const FTransform& BoneTransform = RefSkeleton.GetRefBonePose()[BoneIndex]; trans = BoneTransform.GetTranslation(); } File->Logf(TEXT("\t\t\t\t\t\t\"trans\":[%f,%f,%f]"), trans.X, trans.Y, trans.Z); if (!bLastKey) { File->Logf(TEXT("\t\t\t\t\t},")); } else { File->Logf(TEXT("\t\t\t\t\t}")); } AnimTime += AnimTimeIncrement; } File->Logf(TEXT("\t\t\t\t]"), BoneIndex); } File->Logf(TEXT("\t\t\t}")); File->Logf(TEXT("\t\t]")); File->Logf(TEXT("\t}")); File->Logf(TEXT("}")); delete File; return true; }
void FAnimNode_Trail::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); if( ChainLength < 2 ) { return; } // The incoming BoneIndex is the 'end' of the spline chain. We need to find the 'start' by walking SplineLength bones up hierarchy. // Fail if we walk past the root bone. int32 WalkBoneIndex = TrailBone.BoneIndex; TArray<int32> ChainBoneIndices; ChainBoneIndices.AddZeroed(ChainLength); ChainBoneIndices[ChainLength - 1] = WalkBoneIndex; for (int32 i = 1; i < ChainLength; i++) { // returns to avoid a crash // @TODO : shows an error message why failed if (WalkBoneIndex == 0) { return; } // Get parent bone. WalkBoneIndex = RequiredBones.GetParentBoneIndex(WalkBoneIndex); //Insert indices at the start of array, so that parents are before children in the array. int32 TransformIndex = ChainLength - (i + 1); ChainBoneIndices[TransformIndex] = WalkBoneIndex; } OutBoneTransforms.AddZeroed(ChainLength); // If we have >0 this frame, but didn't last time, record positions of all the bones. // Also do this if number has changed or array is zero. bool bHasValidStrength = (Alpha > 0.f); if(TrailBoneLocations.Num() != ChainLength || (bHasValidStrength && !bHadValidStrength)) { TrailBoneLocations.Empty(); TrailBoneLocations.AddZeroed(ChainLength); for(int32 i=0; i<ChainBoneIndices.Num(); i++) { int32 ChildIndex = ChainBoneIndices[i]; FTransform ChainTransform = MeshBases.GetComponentSpaceTransform(ChildIndex); TrailBoneLocations[i] = ChainTransform.GetTranslation(); } OldLocalToWorld = SkelComp->GetTransformMatrix(); } bHadValidStrength = bHasValidStrength; // transform between last frame and now. FMatrix OldToNewTM = OldLocalToWorld * SkelComp->GetTransformMatrix().InverseFast(); // Add fake velocity if present to all but root bone if(!FakeVelocity.IsZero()) { FVector FakeMovement = -FakeVelocity * ThisTimstep; if (bActorSpaceFakeVel && SkelComp->GetOwner()) { const FTransform BoneToWorld(SkelComp->GetOwner()->GetActorRotation(), SkelComp->GetOwner()->GetActorLocation()); FakeMovement = BoneToWorld.TransformVector(FakeMovement); } FakeMovement = SkelComp->GetTransformMatrix().InverseTransformVector(FakeMovement); // Then add to each bone for(int32 i=1; i<TrailBoneLocations.Num(); i++) { TrailBoneLocations[i] += FakeMovement; } } // Root bone of trail is not modified. int32 RootIndex = ChainBoneIndices[0]; FTransform ChainTransform = MeshBases.GetComponentSpaceTransform(RootIndex); OutBoneTransforms[0] = FBoneTransform(RootIndex, ChainTransform); TrailBoneLocations[0] = ChainTransform.GetTranslation(); // Starting one below head of chain, move bones. for(int32 i=1; i<ChainBoneIndices.Num(); i++) { // Parent bone position in component space. int32 ParentIndex = ChainBoneIndices[i-1]; FVector ParentPos = TrailBoneLocations[i-1]; FVector ParentAnimPos = MeshBases.GetComponentSpaceTransform(ParentIndex).GetTranslation(); // Child bone position in component space. int32 ChildIndex = ChainBoneIndices[i]; FVector ChildPos = OldToNewTM.TransformPosition(TrailBoneLocations[i]); // move from 'last frames component' frame to 'this frames component' frame FVector ChildAnimPos = MeshBases.GetComponentSpaceTransform(ChildIndex).GetTranslation(); // Desired parent->child offset. FVector TargetDelta = (ChildAnimPos - ParentAnimPos); // Desired child position. FVector ChildTarget = ParentPos + TargetDelta; // Find vector from child to target FVector Error = ChildTarget - ChildPos; // Calculate how much to push the child towards its target float Correction = FMath::Clamp<float>(ThisTimstep * TrailRelaxation, 0.f, 1.f); // Scale correction vector and apply to get new world-space child position. TrailBoneLocations[i] = ChildPos + (Error * Correction); // If desired, prevent bones stretching too far. if(bLimitStretch) { float RefPoseLength = TargetDelta.Size(); FVector CurrentDelta = TrailBoneLocations[i] - TrailBoneLocations[i-1]; float CurrentLength = CurrentDelta.Size(); // If we are too far - cut it back (just project towards parent particle). if( (CurrentLength - RefPoseLength > StretchLimit) && CurrentLength > SMALL_NUMBER ) { FVector CurrentDir = CurrentDelta / CurrentLength; TrailBoneLocations[i] = TrailBoneLocations[i-1] + (CurrentDir * (RefPoseLength + StretchLimit)); } } // Modify child matrix OutBoneTransforms[i] = FBoneTransform(ChildIndex, MeshBases.GetComponentSpaceTransform(ChildIndex)); OutBoneTransforms[i].Transform.SetTranslation(TrailBoneLocations[i]); // Modify rotation of parent matrix to point at this one. // Calculate the direction that parent bone is currently pointing. FVector CurrentBoneDir = OutBoneTransforms[i-1].Transform.TransformVector( GetAlignVector(ChainBoneAxis, bInvertChainBoneAxis) ); CurrentBoneDir = CurrentBoneDir.SafeNormal(SMALL_NUMBER); // Calculate vector from parent to child. FVector NewBoneDir = FVector(OutBoneTransforms[i].Transform.GetTranslation() - OutBoneTransforms[i - 1].Transform.GetTranslation()).SafeNormal(SMALL_NUMBER); // Calculate a quaternion that gets us from our current rotation to the desired one. FQuat DeltaLookQuat = FQuat::FindBetween(CurrentBoneDir, NewBoneDir); FTransform DeltaTM( DeltaLookQuat, FVector(0.f) ); // Apply to the current parent bone transform. FTransform TmpMatrix = FTransform::Identity; TmpMatrix.CopyRotationPart(OutBoneTransforms[i - 1].Transform); TmpMatrix = TmpMatrix * DeltaTM; OutBoneTransforms[i - 1].Transform.CopyRotationPart(TmpMatrix); } // For the last bone in the chain, use the rotation from the bone above it. OutBoneTransforms[ChainLength - 1].Transform.CopyRotationPart(OutBoneTransforms[ChainLength - 2].Transform); // Update OldLocalToWorld OldLocalToWorld = SkelComp->GetTransformMatrix(); }
void FAnimNode_TwoBoneIK::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer & RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { check(OutBoneTransforms.Num() == 0); // Get indices of the lower and upper limb bones and check validity. bool bInvalidLimb = false; const int32 EndBoneIndex = IKBone.BoneIndex; const int32 LowerLimbIndex = RequiredBones.GetParentBoneIndex(EndBoneIndex); if (LowerLimbIndex == INDEX_NONE) { bInvalidLimb = true; } const int32 UpperLimbIndex = RequiredBones.GetParentBoneIndex(LowerLimbIndex); if (UpperLimbIndex == INDEX_NONE) { bInvalidLimb = true; } const bool bInBoneSpace = (EffectorLocationSpace == BCS_ParentBoneSpace) || (EffectorLocationSpace == BCS_BoneSpace); const int32 EffectorSpaceBoneIndex = bInBoneSpace ? RequiredBones.GetPoseBoneIndexForBoneName(EffectorSpaceBoneName) : INDEX_NONE; if (bInBoneSpace && ((EffectorSpaceBoneIndex == INDEX_NONE) || !RequiredBones.Contains(EffectorSpaceBoneIndex))) { bInvalidLimb = true; } // If we walked past the root, this controlled is invalid, so return no affected bones. if( bInvalidLimb ) { return; } // Get Local Space transforms for our bones. We do this first in case they already are local. // As right after we get them in component space. (And that does the auto conversion). // We might save one transform by doing local first... const FTransform EndBoneLocalTransform = MeshBases.GetLocalSpaceTransform(IKBone.BoneIndex); // Now get those in component space... FTransform UpperLimbCSTransform = MeshBases.GetComponentSpaceTransform(UpperLimbIndex); FTransform LowerLimbCSTransform = MeshBases.GetComponentSpaceTransform(LowerLimbIndex); FTransform EndBoneCSTransform = MeshBases.GetComponentSpaceTransform(IKBone.BoneIndex); // Get current position of root of limb. // All position are in Component space. const FVector RootPos = UpperLimbCSTransform.GetTranslation(); const FVector InitialJointPos = LowerLimbCSTransform.GetTranslation(); const FVector InitialEndPos = EndBoneCSTransform.GetTranslation(); // Transform EffectorLocation from EffectorLocationSpace to ComponentSpace. FTransform EffectorTransform(EffectorLocation); FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, EffectorTransform, EffectorSpaceBoneIndex, EffectorLocationSpace); // This is our reach goal. FVector DesiredPos = EffectorTransform.GetTranslation(); FVector DesiredDelta = DesiredPos - RootPos; float DesiredLength = DesiredDelta.Size(); // Check to handle case where DesiredPos is the same as RootPos. FVector DesiredDir; if (DesiredLength < (float)KINDA_SMALL_NUMBER) { DesiredLength = (float)KINDA_SMALL_NUMBER; DesiredDir = FVector(1,0,0); } else { DesiredDir = DesiredDelta / DesiredLength; } // Get joint target (used for defining plane that joint should be in). FTransform JointTargetTransform(JointTargetLocation); const int32 JointTargetSpaceBoneIndex = (JointTargetLocationSpace == BCS_ParentBoneSpace || JointTargetLocationSpace == BCS_BoneSpace) ? RequiredBones.GetPoseBoneIndexForBoneName(JointTargetSpaceBoneName) : INDEX_NONE; FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, JointTargetTransform, JointTargetSpaceBoneIndex, JointTargetLocationSpace); FVector JointTargetPos = JointTargetTransform.GetTranslation(); FVector JointTargetDelta = JointTargetPos - RootPos; float JointTargetLength = JointTargetDelta.Size(); // Same check as above, to cover case when JointTarget position is the same as RootPos. FVector JointPlaneNormal, JointBendDir; if (JointTargetLength < (float)KINDA_SMALL_NUMBER) { JointBendDir = FVector(0,1,0); JointPlaneNormal = FVector(0,0,1); } else { JointPlaneNormal = DesiredDir ^ JointTargetDelta; // If we are trying to point the limb in the same direction that we are supposed to displace the joint in, // we have to just pick 2 random vector perp to DesiredDir and each other. if (JointPlaneNormal.Size() < (float)KINDA_SMALL_NUMBER) { DesiredDir.FindBestAxisVectors(JointPlaneNormal, JointBendDir); } else { JointPlaneNormal.Normalize(); // Find the final member of the reference frame by removing any component of JointTargetDelta along DesiredDir. // This should never leave a zero vector, because we've checked DesiredDir and JointTargetDelta are not parallel. JointBendDir = JointTargetDelta - ((JointTargetDelta | DesiredDir) * DesiredDir); JointBendDir.Normalize(); } } // Find lengths of upper and lower limb in the ref skeleton. // Use actual sizes instead of ref skeleton, so we take into account translation and scaling from other bone controllers. float LowerLimbLength = (InitialEndPos - InitialJointPos).Size(); float UpperLimbLength = (InitialJointPos - RootPos).Size(); float MaxLimbLength = LowerLimbLength + UpperLimbLength; if (bAllowStretching) { const float ScaleRange = StretchLimits.Y - StretchLimits.X; if( ScaleRange > KINDA_SMALL_NUMBER && MaxLimbLength > KINDA_SMALL_NUMBER ) { const float ReachRatio = DesiredLength / MaxLimbLength; const float ScalingFactor = (StretchLimits.Y - 1.f) * FMath::Clamp<float>((ReachRatio - StretchLimits.X) / ScaleRange, 0.f, 1.f); if (ScalingFactor > KINDA_SMALL_NUMBER) { LowerLimbLength *= (1.f + ScalingFactor); UpperLimbLength *= (1.f + ScalingFactor); MaxLimbLength *= (1.f + ScalingFactor); } } } FVector OutEndPos = DesiredPos; FVector OutJointPos = InitialJointPos; // If we are trying to reach a goal beyond the length of the limb, clamp it to something solvable and extend limb fully. if (DesiredLength > MaxLimbLength) { OutEndPos = RootPos + (MaxLimbLength * DesiredDir); OutJointPos = RootPos + (UpperLimbLength * DesiredDir); } else { // So we have a triangle we know the side lengths of. We can work out the angle between DesiredDir and the direction of the upper limb // using the sin rule: const float TwoAB = 2.f * UpperLimbLength * DesiredLength; const float CosAngle = (TwoAB != 0.f) ? ((UpperLimbLength*UpperLimbLength) + (DesiredLength*DesiredLength) - (LowerLimbLength*LowerLimbLength)) / TwoAB : 0.f; // If CosAngle is less than 0, the upper arm actually points the opposite way to DesiredDir, so we handle that. const bool bReverseUpperBone = (CosAngle < 0.f); // If CosAngle is greater than 1.f, the triangle could not be made - we cannot reach the target. // We just have the two limbs double back on themselves, and EndPos will not equal the desired EffectorLocation. if ((CosAngle > 1.f) || (CosAngle < -1.f)) { // Because we want the effector to be a positive distance down DesiredDir, we go back by the smaller section. if (UpperLimbLength > LowerLimbLength) { OutJointPos = RootPos + (UpperLimbLength * DesiredDir); OutEndPos = OutJointPos - (LowerLimbLength * DesiredDir); } else { OutJointPos = RootPos - (UpperLimbLength * DesiredDir); OutEndPos = OutJointPos + (LowerLimbLength * DesiredDir); } } else { // Angle between upper limb and DesiredDir const float Angle = FMath::Acos(CosAngle); // Now we calculate the distance of the joint from the root -> effector line. // This forms a right-angle triangle, with the upper limb as the hypotenuse. const float JointLineDist = UpperLimbLength * FMath::Sin(Angle); // And the final side of that triangle - distance along DesiredDir of perpendicular. // ProjJointDistSqr can't be neg, because JointLineDist must be <= UpperLimbLength because appSin(Angle) is <= 1. const float ProjJointDistSqr = (UpperLimbLength*UpperLimbLength) - (JointLineDist*JointLineDist); // although this shouldn't be ever negative, sometimes Xbox release produces -0.f, causing ProjJointDist to be NaN // so now I branch it. float ProjJointDist = (ProjJointDistSqr>0.f)? FMath::Sqrt(ProjJointDistSqr) : 0.f; if( bReverseUpperBone ) { ProjJointDist *= -1.f; } // So now we can work out where to put the joint! OutJointPos = RootPos + (ProjJointDist * DesiredDir) + (JointLineDist * JointBendDir); } } // Update transform for upper bone. { // Get difference in direction for old and new joint orientations FVector const OldDir = (InitialJointPos - RootPos).SafeNormal(); FVector const NewDir = (OutJointPos - RootPos).SafeNormal(); // Find Delta Rotation take takes us from Old to New dir FQuat const DeltaRotation = FQuat::FindBetween(OldDir, NewDir); // Rotate our Joint quaternion by this delta rotation UpperLimbCSTransform.SetRotation( DeltaRotation * UpperLimbCSTransform.GetRotation() ); // And put joint where it should be. UpperLimbCSTransform.SetTranslation( RootPos ); // Order important. First bone is upper limb. OutBoneTransforms.Add( FBoneTransform(UpperLimbIndex, UpperLimbCSTransform) ); } // Update transform for lower bone. { // Get difference in direction for old and new joint orientations FVector const OldDir = (InitialEndPos - InitialJointPos).SafeNormal(); FVector const NewDir = (OutEndPos - OutJointPos).SafeNormal(); // Find Delta Rotation take takes us from Old to New dir FQuat const DeltaRotation = FQuat::FindBetween(OldDir, NewDir); // Rotate our Joint quaternion by this delta rotation LowerLimbCSTransform.SetRotation( DeltaRotation * LowerLimbCSTransform.GetRotation() ); // And put joint where it should be. LowerLimbCSTransform.SetTranslation( OutJointPos ); // Order important. Second bone is lower limb. OutBoneTransforms.Add( FBoneTransform(LowerLimbIndex, LowerLimbCSTransform) ); } // Update transform for end bone. { if( bTakeRotationFromEffectorSpace ) { EndBoneCSTransform.SetRotation( EffectorTransform.GetRotation() ); } else if( bMaintainEffectorRelRot ) { EndBoneCSTransform = EndBoneLocalTransform * LowerLimbCSTransform; } // Set correct location for end bone. EndBoneCSTransform.SetTranslation(OutEndPos); // Order important. Third bone is End Bone. OutBoneTransforms.Add( FBoneTransform(IKBone.BoneIndex, EndBoneCSTransform) ); } // Make sure we have correct number of bones check(OutBoneTransforms.Num() == 3); }
bool UnFbx::FFbxImporter::ImportAnimation(USkeleton* Skeleton, UAnimSequence * DestSeq, const FString& FileName, TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, FbxAnimStack* CurAnimStack, const int32 ResampleRate, const FbxTimeSpan AnimTimeSpan) { // @todo : the length might need to change w.r.t. sampling keys FbxTime SequenceLength = AnimTimeSpan.GetDuration(); float PreviousSequenceLength = DestSeq->SequenceLength; // if you have one pose(thus 0.f duration), it still contains animation, so we'll need to consider that as MINIMUM_ANIMATION_LENGTH time length DestSeq->SequenceLength = FGenericPlatformMath::Max<float>(SequenceLength.GetSecondDouble(), MINIMUM_ANIMATION_LENGTH); if(PreviousSequenceLength > MINIMUM_ANIMATION_LENGTH && DestSeq->RawCurveData.FloatCurves.Num() > 0) { // The sequence already existed when we began the import. We need to scale the key times for all curves to match the new // duration before importing over them. This is to catch any user-added curves float ScaleFactor = DestSeq->SequenceLength / PreviousSequenceLength; for(FFloatCurve& Curve : DestSeq->RawCurveData.FloatCurves) { Curve.FloatCurve.ScaleCurve(0.0f, ScaleFactor); } } if (ImportOptions->bDeleteExistingMorphTargetCurves) { for (int32 CurveIdx=0; CurveIdx<DestSeq->RawCurveData.FloatCurves.Num(); ++CurveIdx) { auto& Curve = DestSeq->RawCurveData.FloatCurves[CurveIdx]; if (Curve.GetCurveTypeFlag(ACF_DrivesMorphTarget)) { DestSeq->RawCurveData.FloatCurves.RemoveAt(CurveIdx, 1, false); --CurveIdx; } } DestSeq->RawCurveData.FloatCurves.Shrink(); } // // import blend shape curves // { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Morph Target Curves"), true); for ( int32 NodeIndex = 0; NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); const int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); FString BlendShapeName = UTF8_TO_TCHAR(MakeName(BlendShape->GetName())); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { FString ChannelName = UTF8_TO_TCHAR(MakeName(Channel->GetName())); // Maya adds the name of the blendshape and an underscore to the front of the channel name, so remove it if(ChannelName.StartsWith(BlendShapeName)) { ChannelName = ChannelName.Right(ChannelName.Len() - (BlendShapeName.Len()+1)); } FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { FFormatNamedArguments Args; Args.Add(TEXT("BlendShape"), FText::FromString(ChannelName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingMorphTargetCurvesDetail", "Importing Morph Target Curves [{BlendShape}]"), Args); GWarn->StatusUpdate(NodeIndex + 1, NodeArray.Num(), StatusUpate); // now see if we have one already exists. If so, just overwrite that. if not, add new one. ImportCurveToAnimSequence(DestSeq, *ChannelName, Curve, ACF_DrivesMorphTarget | ACF_TriggerEvent, AnimTimeSpan, 0.01f /** for some reason blend shape values are coming as 100 scaled **/); } } } } } } GWarn->EndSlowTask(); } // // importing custom attribute START // if (ImportOptions->bImportCustomAttribute) { GWarn->BeginSlowTask( LOCTEXT("BeginImportMorphTargetCurves", "Importing Custom Attirubte Curves"), true); const int32 TotalLinks = SortedLinks.Num(); int32 CurLinkIndex=0; for(auto Node: SortedLinks) { FbxProperty Property = Node->GetFirstProperty(); while (Property.IsValid()) { FbxAnimCurveNode* CurveNode = Property.GetCurveNode(); // do this if user defined and animated and leaf node if( CurveNode && Property.GetFlag(FbxPropertyAttr::eUserDefined) && CurveNode->IsAnimated() && IsSupportedCurveDataType(Property.GetPropertyDataType().GetType()) ) { FString CurveName = UTF8_TO_TCHAR(CurveNode->GetName()); UE_LOG(LogFbx, Log, TEXT("CurveName : %s"), *CurveName ); int32 TotalCount = CurveNode->GetChannelsCount(); for (int32 ChannelIndex=0; ChannelIndex<TotalCount; ++ChannelIndex) { FbxAnimCurve * AnimCurve = CurveNode->GetCurve(ChannelIndex); FString ChannelName = CurveNode->GetChannelName(ChannelIndex).Buffer(); if (AnimCurve) { FString FinalCurveName; if (TotalCount == 1) { FinalCurveName = CurveName; } else { FinalCurveName = CurveName + "_" + ChannelName; } FFormatNamedArguments Args; Args.Add(TEXT("CurveName"), FText::FromString(FinalCurveName)); const FText StatusUpate = FText::Format(LOCTEXT("ImportingCustomAttributeCurvesDetail", "Importing Custom Attribute [{CurveName}]"), Args); GWarn->StatusUpdate(CurLinkIndex + 1, TotalLinks, StatusUpate); ImportCurveToAnimSequence(DestSeq, FinalCurveName, AnimCurve, ACF_DefaultCurve, AnimTimeSpan); } } } Property = Node->GetNextProperty(Property); } CurLinkIndex++; } GWarn->EndSlowTask(); } // importing custom attribute END const bool bSourceDataExists = (DestSeq->SourceRawAnimationData.Num() > 0); TArray<AnimationTransformDebug::FAnimationTransformDebugData> TransformDebugData; int32 TotalNumKeys = 0; const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); // import animation { GWarn->BeginSlowTask( LOCTEXT("BeginImportAnimation", "Importing Animation"), true); TArray<struct FRawAnimSequenceTrack>& RawAnimationData = bSourceDataExists? DestSeq->SourceRawAnimationData : DestSeq->RawAnimationData; DestSeq->TrackToSkeletonMapTable.Empty(); DestSeq->AnimationTrackNames.Empty(); RawAnimationData.Empty(); TArray<FName> FbxRawBoneNames; FillAndVerifyBoneNames(Skeleton, SortedLinks, FbxRawBoneNames, FileName); UnFbx::FFbxImporter* FbxImporter = UnFbx::FFbxImporter::GetInstance(); const bool bPreserveLocalTransform = FbxImporter->GetImportOptions()->bPreserveLocalTransform; // Build additional transform matrix UFbxAnimSequenceImportData* TemplateData = Cast<UFbxAnimSequenceImportData>(DestSeq->AssetImportData); FbxAMatrix FbxAddedMatrix; BuildFbxMatrixForImportTransform(FbxAddedMatrix, TemplateData); FMatrix AddedMatrix = Converter.ConvertMatrix(FbxAddedMatrix); const int32 NumSamplingKeys = FMath::FloorToInt(AnimTimeSpan.GetDuration().GetSecondDouble() * ResampleRate); const FbxTime TimeIncrement = (NumSamplingKeys > 1)? AnimTimeSpan.GetDuration() / (NumSamplingKeys - 1) : AnimTimeSpan.GetDuration(); for(int32 SourceTrackIdx = 0; SourceTrackIdx < FbxRawBoneNames.Num(); ++SourceTrackIdx) { int32 NumKeysForTrack = 0; // see if it's found in Skeleton FName BoneName = FbxRawBoneNames[SourceTrackIdx]; int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(BoneName); // update status FFormatNamedArguments Args; Args.Add(TEXT("TrackName"), FText::FromName(BoneName)); Args.Add(TEXT("TotalKey"), FText::AsNumber(NumSamplingKeys)); Args.Add(TEXT("TrackIndex"), FText::AsNumber(SourceTrackIdx+1)); Args.Add(TEXT("TotalTracks"), FText::AsNumber(FbxRawBoneNames.Num())); const FText StatusUpate = FText::Format(LOCTEXT("ImportingAnimTrackDetail", "Importing Animation Track [{TrackName}] ({TrackIndex}/{TotalTracks}) - TotalKey {TotalKey}"), Args); GWarn->StatusForceUpdate(SourceTrackIdx + 1, FbxRawBoneNames.Num(), StatusUpate); if (BoneTreeIndex!=INDEX_NONE) { bool bSuccess = true; FRawAnimSequenceTrack RawTrack; RawTrack.PosKeys.Empty(); RawTrack.RotKeys.Empty(); RawTrack.ScaleKeys.Empty(); AnimationTransformDebug::FAnimationTransformDebugData NewDebugData; FbxNode* Link = SortedLinks[SourceTrackIdx]; FbxNode * LinkParent = Link->GetParent(); for(FbxTime CurTime = AnimTimeSpan.GetStart(); CurTime <= AnimTimeSpan.GetStop(); CurTime += TimeIncrement) { // save global trasnform FbxAMatrix GlobalMatrix = Link->EvaluateGlobalTransform(CurTime); // we'd like to verify this before going to Transform. // currently transform has tons of NaN check, so it will crash there FMatrix GlobalUEMatrix = Converter.ConvertMatrix(GlobalMatrix); if (GlobalUEMatrix.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidTransform", "Track {0} contains invalid transform. Could not import the track."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } FTransform GlobalTransform = Converter.ConvertTransform(GlobalMatrix); if (GlobalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealTransform", "Track {0} did not yeild valid transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } // debug data, including import transformation FTransform AddedTransform(AddedMatrix); NewDebugData.SourceGlobalTransform.Add(GlobalTransform * AddedTransform); FTransform LocalTransform; if( !bPreserveLocalTransform && LinkParent) { // I can't rely on LocalMatrix. I need to recalculate quaternion/scale based on global transform if Parent exists FbxAMatrix ParentGlobalMatrix = Link->GetParent()->EvaluateGlobalTransform(CurTime); FTransform ParentGlobalTransform = Converter.ConvertTransform(ParentGlobalMatrix); LocalTransform = GlobalTransform.GetRelativeTransform(ParentGlobalTransform); NewDebugData.SourceParentGlobalTransform.Add(ParentGlobalTransform); } else { FbxAMatrix& LocalMatrix = Link->EvaluateLocalTransform(CurTime); FbxVector4 NewLocalT = LocalMatrix.GetT(); FbxVector4 NewLocalS = LocalMatrix.GetS(); FbxQuaternion NewLocalQ = LocalMatrix.GetQ(); LocalTransform.SetTranslation(Converter.ConvertPos(NewLocalT)); LocalTransform.SetScale3D(Converter.ConvertScale(NewLocalS)); LocalTransform.SetRotation(Converter.ConvertRotToQuat(NewLocalQ)); NewDebugData.SourceParentGlobalTransform.Add(FTransform::Identity); } if(TemplateData && BoneTreeIndex == 0) { // If we found template data earlier, apply the import transform matrix to // the root track. LocalTransform.SetFromMatrix(LocalTransform.ToMatrixWithScale() * AddedMatrix); } if (LocalTransform.ContainsNaN()) { bSuccess = false; AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(LOCTEXT("Error_InvalidUnrealLocalTransform", "Track {0} did not yeild valid local transform. Please report this to animation team."), FText::FromName(BoneName))), FFbxErrors::Animation_TransformError); break; } RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D()); RawTrack.PosKeys.Add(LocalTransform.GetTranslation()); RawTrack.RotKeys.Add(LocalTransform.GetRotation()); NewDebugData.RecalculatedLocalTransform.Add(LocalTransform); ++NumKeysForTrack; } if (bSuccess) { //add new track int32 NewTrackIdx = RawAnimationData.Add(RawTrack); DestSeq->AnimationTrackNames.Add(BoneName); NewDebugData.SetTrackData(NewTrackIdx, BoneTreeIndex, BoneName); // add mapping to skeleton bone track DestSeq->TrackToSkeletonMapTable.Add(FTrackToSkeletonMap(BoneTreeIndex)); TransformDebugData.Add(NewDebugData); } } TotalNumKeys = FMath::Max( TotalNumKeys, NumKeysForTrack ); } DestSeq->NumFrames = TotalNumKeys; GWarn->EndSlowTask(); } // compress animation { GWarn->BeginSlowTask( LOCTEXT("BeginCompressAnimation", "Compress Animation"), true); GWarn->StatusForceUpdate(1, 1, LOCTEXT("CompressAnimation", "Compressing Animation")); // if source data exists, you should bake it to Raw to apply if(bSourceDataExists) { DestSeq->BakeTrackCurvesToRawAnimation(); } else { // otherwise just compress DestSeq->PostProcessSequence(); } // run debug mode AnimationTransformDebug::OutputAnimationTransformDebugData(TransformDebugData, TotalNumKeys, RefSkeleton); GWarn->EndSlowTask(); } return true; }