void FAnimNode_AnimDynamics::InitPhysics(USkeletalMeshComponent* Component, FCSPose<FCompactPose>& MeshBases) { // Clear up any existing physics data TermPhysics(); const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); // List of bone indices in the chain. TArray<int32> ChainBoneIndices; TArray<FName> ChainBoneNames; if(ChainEnd.IsValid(BoneContainer)) { // Add the end of the chain. We have to walk from the bottom upwards to find a chain // as walking downwards doesn't guarantee a single end point. ChainBoneIndices.Add(ChainEnd.BoneIndex); ChainBoneNames.Add(ChainEnd.BoneName); int32 ParentBoneIndex = BoneContainer.GetParentBoneIndex(ChainEnd.BoneIndex); // Walk up the chain until we either find the top or hit the root bone while(ParentBoneIndex != 0) { ChainBoneIndices.Add(ParentBoneIndex); ChainBoneNames.Add(Component->GetBoneName(ParentBoneIndex)); if(ParentBoneIndex == BoundBone.BoneIndex) { // Found the top of the chain break; } ParentBoneIndex = BoneContainer.GetParentBoneIndex(ParentBoneIndex); } // Bail if we can't find a chain, and let the user know if(ParentBoneIndex != BoundBone.BoneIndex) { UE_LOG(LogAnimation, Error, TEXT("AnimDynamics: Attempted to find bone chain starting at %s and ending at %s but failed."), *BoundBone.BoneName.ToString(), *ChainEnd.BoneName.ToString()); return; } } else { // No chain specified, just use the bound bone ChainBoneIndices.Add(BoundBone.BoneIndex); ChainBoneNames.Add(BoundBone.BoneName); } Bodies.Reserve(ChainBoneIndices.Num()); // Walk backwards here as the chain was discovered in reverse order for (int32 Idx = ChainBoneIndices.Num() - 1; Idx >= 0; --Idx) { TArray<FAnimPhysShape> BodyShapes; BodyShapes.Add(FAnimPhysShape::MakeBox(BoxExtents)); FBoneReference LinkBoneRef; LinkBoneRef.BoneName = ChainBoneNames[Idx]; LinkBoneRef.Initialize(BoneContainer); // Calculate joint offsets by looking at the length of the bones and extending the provided offset if (BoundBoneReferences.Num() > 0) { FTransform CurrentBoneTransform = MeshBases.GetComponentSpaceTransform(LinkBoneRef.GetCompactPoseIndex(BoneContainer)); FTransform PreviousBoneTransform = MeshBases.GetComponentSpaceTransform(BoundBoneReferences.Last().GetCompactPoseIndex(BoneContainer)); FVector PreviousAnchor = PreviousBoneTransform.TransformPosition(-LocalJointOffset); float DistanceToAnchor = (PreviousBoneTransform.GetTranslation() - CurrentBoneTransform.GetTranslation()).Size() * 0.5f; if(LocalJointOffset.SizeSquared() < SMALL_NUMBER) { // No offset, just use the position between chain links as the offset // This is likely to just look horrible, but at least the bodies will // be placed correctly and not stack up at the top of the chain. JointOffsets.Add(PreviousAnchor - CurrentBoneTransform.GetTranslation()); } else { // Extend offset along chain. JointOffsets.Add(LocalJointOffset.GetSafeNormal() * DistanceToAnchor); } } else { // No chain to worry about, just use the specified offset. JointOffsets.Add(LocalJointOffset); } BoundBoneReferences.Add(LinkBoneRef); FTransform BodyTransform = MeshBases.GetComponentSpaceTransform(LinkBoneRef.GetCompactPoseIndex(BoneContainer)); BodyTransform.SetTranslation(BodyTransform.GetTranslation() + BodyTransform.GetRotation().RotateVector(-LocalJointOffset)); FAnimPhysLinkedBody NewChainBody(BodyShapes, BodyTransform.GetTranslation(), LinkBoneRef); FAnimPhysRigidBody& PhysicsBody = NewChainBody.RigidBody.PhysBody; PhysicsBody.Pose.Orientation = BodyTransform.GetRotation(); PhysicsBody.PreviousOrientation = PhysicsBody.Pose.Orientation; PhysicsBody.NextOrientation = PhysicsBody.Pose.Orientation; PhysicsBody.CollisionType = CollisionType; switch(PhysicsBody.CollisionType) { case AnimPhysCollisionType::CustomSphere: PhysicsBody.SphereCollisionRadius = SphereCollisionRadius; break; case AnimPhysCollisionType::InnerSphere: PhysicsBody.SphereCollisionRadius = BoxExtents.GetAbsMin() / 2.0f; break; case AnimPhysCollisionType::OuterSphere: PhysicsBody.SphereCollisionRadius = BoxExtents.GetAbsMax() / 2.0f; break; default: break; } if (bOverrideLinearDamping) { PhysicsBody.bLinearDampingOverriden = true; PhysicsBody.LinearDamping = LinearDampingOverride; } if (bOverrideAngularDamping) { PhysicsBody.bAngularDampingOverriden = true; PhysicsBody.AngularDamping = AngularDampingOverride; } PhysicsBody.GravityScale = GravityScale; // Link to parent if (Bodies.Num() > 0) { NewChainBody.ParentBody = &Bodies.Last().RigidBody; } Bodies.Add(NewChainBody); } BaseBodyPtrs.Empty(); for(FAnimPhysLinkedBody& Body : Bodies) { BaseBodyPtrs.Add(&Body.RigidBody.PhysBody); } // Set up transient constraint data const bool bXAxisLocked = ConstraintSetup.LinearXLimitType != AnimPhysLinearConstraintType::Free && ConstraintSetup.LinearAxesMin.X - ConstraintSetup.LinearAxesMax.X == 0.0f; const bool bYAxisLocked = ConstraintSetup.LinearYLimitType != AnimPhysLinearConstraintType::Free && ConstraintSetup.LinearAxesMin.Y - ConstraintSetup.LinearAxesMax.Y == 0.0f; const bool bZAxisLocked = ConstraintSetup.LinearZLimitType != AnimPhysLinearConstraintType::Free && ConstraintSetup.LinearAxesMin.Z - ConstraintSetup.LinearAxesMax.Z == 0.0f; ConstraintSetup.bLinearFullyLocked = bXAxisLocked && bYAxisLocked && bZAxisLocked; // Cache physics settings to avoid accessing UPhysicsSettings continuously if(UPhysicsSettings* Settings = UPhysicsSettings::Get()) { MaxPhysicsDeltaTime = Settings->MaxPhysicsDeltaTime; MaxSubstepDeltaTime = Settings->MaxSubstepDeltaTime; MaxSubsteps = Settings->MaxSubsteps; } else { MaxPhysicsDeltaTime = (1.0f/30.0f); MaxSubstepDeltaTime = (1.0f/60.0f); MaxSubsteps = 4; } bRequiresInit = false; }
void FAnimNode_AimOffsetLookAt::UpdateFromLookAtTarget(FPoseContext& LocalPoseContext) { const FBoneContainer& RequiredBones = LocalPoseContext.Pose.GetBoneContainer(); if (RequiredBones.GetSkeletalMeshAsset()) { const USkeletalMeshSocket* Socket = RequiredBones.GetSkeletalMeshAsset()->FindSocket(SourceSocketName); if (Socket) { const FTransform SocketLocalTransform = Socket->GetSocketLocalTransform(); FBoneReference SocketBoneReference; SocketBoneReference.BoneName = Socket->BoneName; SocketBoneReference.Initialize(RequiredBones); if (SocketBoneReference.IsValid(RequiredBones)) { const FCompactPoseBoneIndex SocketBoneIndex = SocketBoneReference.GetCompactPoseIndex(RequiredBones); FCSPose<FCompactPose> GlobalPose; GlobalPose.InitPose(LocalPoseContext.Pose); USkeletalMeshComponent* Component = LocalPoseContext.AnimInstanceProxy->GetSkelMeshComponent(); AActor* Actor = Component ? Component->GetOwner() : nullptr; if (Component && Actor && BlendSpace) { const FTransform ActorTransform = Actor->GetTransform(); const FTransform BoneTransform = GlobalPose.GetComponentSpaceTransform(SocketBoneIndex); const FTransform SocketWorldTransform = SocketLocalTransform * BoneTransform * Component->ComponentToWorld; // Convert Target to Actor Space const FTransform TargetWorldTransform(LookAtLocation); const FVector DirectionToTarget = ActorTransform.InverseTransformVectorNoScale(TargetWorldTransform.GetLocation() - SocketWorldTransform.GetLocation()).GetSafeNormal(); const FVector CurrentDirection = ActorTransform.InverseTransformVectorNoScale(SocketWorldTransform.GetUnitAxis(EAxis::X)); const FVector AxisX = FVector::ForwardVector; const FVector AxisY = FVector::RightVector; const FVector AxisZ = FVector::UpVector; const FVector2D CurrentCoords = FMath::GetAzimuthAndElevation(CurrentDirection, AxisX, AxisY, AxisZ); const FVector2D TargetCoords = FMath::GetAzimuthAndElevation(DirectionToTarget, AxisX, AxisY, AxisZ); const FVector BlendInput( FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.X - CurrentCoords.X)), FRotator::NormalizeAxis(FMath::RadiansToDegrees(TargetCoords.Y - CurrentCoords.Y)), 0.f); // Set X and Y, so ticking next frame is based on correct weights. X = BlendInput.X; Y = BlendInput.Y; // Generate BlendSampleDataCache from inputs. BlendSpace->GetSamplesFromBlendInput(BlendInput, BlendSampleDataCache); if (CVarAimOffsetLookAtDebug.GetValueOnAnyThread() == 1) { DrawDebugLine(Component->GetWorld(), SocketWorldTransform.GetLocation(), TargetWorldTransform.GetLocation(), FColor::Green); DrawDebugLine(Component->GetWorld(), SocketWorldTransform.GetLocation(), SocketWorldTransform.GetLocation() + SocketWorldTransform.GetUnitAxis(EAxis::X) * (TargetWorldTransform.GetLocation() - SocketWorldTransform.GetLocation()).Size(), FColor::Red); DrawDebugCoordinateSystem(Component->GetWorld(), ActorTransform.GetLocation(), ActorTransform.GetRotation().Rotator(), 100.f); FString DebugString = FString::Printf(TEXT("Socket (X:%f, Y:%f), Target (X:%f, Y:%f), Result (X:%f, Y:%f)") , FMath::RadiansToDegrees(CurrentCoords.X) , FMath::RadiansToDegrees(CurrentCoords.Y) , FMath::RadiansToDegrees(TargetCoords.X) , FMath::RadiansToDegrees(TargetCoords.Y) , BlendInput.X , BlendInput.Y); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0.f, FColor::Red, DebugString, false); } } } } } }