17#include <util/ue-header-guard-begin.h>
18#include "Math/Matrix.h"
19#include "Components/CapsuleComponent.h"
20#include "DrawDebugHelpers.h"
21#include "Kismet/KismetMathLibrary.h"
22#include <util/ue-header-guard-end.h>
24#include <unordered_set>
31# pragma clang diagnostic push
32# pragma clang diagnostic ignored "-Wdeprecated-register"
33# pragma clang diagnostic ignored "-Wmisleading-indentation"
34# pragma clang diagnostic ignored "-Wint-in-bool-context"
35# pragma clang diagnostic ignored "-Wdeprecated-declarations"
36# pragma clang diagnostic ignored "-Wshadow"
39#include <Eigen/Cholesky>
40#include <Eigen/Eigenvalues>
42# pragma clang diagnostic pop
45#define SPRINGVEGETATIONLOGS 0
47#define COLLISIONLOGS 0
48#define ACCUMULATIONLOGS 0
49#define FICTITIOUSFORCELOGS 0
52#if SOLVERLOGS && SPRINGVEGETATIONLOGS
53#define SOLVER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
55#define SOLVER_LOG(...)
57#if COLLISIONLOGS && SPRINGVEGETATIONLOGS
58#define COLLISION_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
60#define COLLISION_LOG(...)
62#if ACCUMULATIONLOGS && SPRINGVEGETATIONLOGS
63#define ACC_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
67#if FICTITIOUSFORCELOGS && SPRINGVEGETATIONLOGS
68#define FICT_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
72#if OTHERLOGS && SPRINGVEGETATIONLOGS
73#define OTHER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
84 Eigen::Vector3d
Force = Eigen::Vector3d::Zero();
85 Eigen::Vector3d
Torque = Eigen::Vector3d::Zero();
98 float HalfCircle = 180.f;
99 float FullCircle = 360.f;
100 auto GetDeltaAngle = [&](
float Angle1,
float Angle2) ->
float
104 Angle1 = FullCircle + Angle1;
108 Angle2 = FullCircle + Angle2;
110 float Diff = fmod(Angle1 - Angle2 + HalfCircle, FullCircle) - HalfCircle;
111 return Diff < -HalfCircle ? Diff + FullCircle : Diff;
115 GetDeltaAngle(Rotator1.Pitch, Rotator2.Pitch),
116 GetDeltaAngle(Rotator1.Yaw, Rotator2.Yaw),
117 GetDeltaAngle(Rotator1.Roll, Rotator2.Roll)
124 return n < 0.0f ? -1.0f : 1.0f;
129 std::stringstream ss;
131 return carla::rpc::ToFString(ss.str());
133static Eigen::Matrix3d
OuterProduct(
const Eigen::Vector3d& V1,
const Eigen::Vector3d& V2)
135 return V1 * V2.transpose();
139 return V1 * V1.transpose();
144 return Eigen::Vector3d(V1.X, V1.Y, -V1.Z);
148 return FVector(V1(0), V1(1), -V1(2));
152 Eigen::Matrix3d EigenMatrix;
153 EigenMatrix << Matrix.M[0][0], Matrix.M[0][1], -Matrix.M[0][2],
154 Matrix.M[1][0], Matrix.M[1][1], -Matrix.M[1][2],
155 Matrix.M[2][0], Matrix.M[2][1], -Matrix.M[2][2];
156 return EigenMatrix.transpose();
160 FMatrix Matrix = Transform.ToMatrixNoScale();
165 return Eigen::Vector3d(
166 FMath::DegreesToRadians(Rotator.Roll),
167 FMath::DegreesToRadians(Rotator.Pitch),
168 -FMath::DegreesToRadians(Rotator.Yaw));
173 FMath::RadiansToDegrees(
Vector(1)),
174 -FMath::RadiansToDegrees(
Vector(2)),
175 FMath::RadiansToDegrees(
Vector(0)));
188 for (
int i = 1; i <
Joints.Num(); i++)
196 for (
int i = 0; i <
Joints.Num(); i++)
199 FString ChildrenIds =
"";
202 ChildrenIds += FString::FromInt(ChildrenId) +
", ";
204 OTHER_LOG(Warning,
"Joint %d, Children: %s.", i, *ChildrenIds);
212 for (
int i = 0; i <
Joints.Num(); i++)
223 std::vector<int> JointsToVisit;
224 JointsToVisit.emplace_back(0);
226 while (JointsToVisit.size())
229 JointsToVisit.pop_back();
234 JointsToVisit.emplace_back(ChildrenId);
240 FString IdOrder =
"";
241 FString NameOrder =
"";
246 IdOrder += FString::FromInt(Id) +
" ";
250 OTHER_LOG(Warning,
"Tree order: %s", *IdOrder);
251 OTHER_LOG(Warning,
"Tree order (names): %s", *NameOrder);
260 if (Joint.JointName == BoneName)
262 Joint.ExternalForces += Force;
271 Joint.ExternalForces = FVector(0, 0, 0);
275USpringBasedVegetationComponent::USpringBasedVegetationComponent(
const FObjectInitializer& ObjectInitializer)
276 : Super(ObjectInitializer)
278 PrimaryComponentTick.bCanEverTick =
true;
279 PrimaryComponentTick.bStartWithTickEnabled =
true;
282void USpringBasedVegetationComponent::GenerateSkeletonHierarchy()
284 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::GenerateSkeletonHierarchy);
285 OTHER_LOG(Warning,
"Get skeleton hierarchy");
289 UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
290 SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
292 TArray<FName> BoneNames;
293 SkeletalMesh->GetBoneNames(BoneNames);
295 for (int32 i = 0; i < BoneNames.Num(); i++)
297 FName& BoneName = BoneNames[i];
298 FString BoneNameString = BoneName.ToString();
299 bool bIsFixedJoint = FixedJointsList.Contains(BoneNameString) ||
300 (DebugJointsToSimulate.Num() && !DebugJointsToSimulate.Contains(BoneNameString));
301 FName ParentBoneName = SkeletalMesh->GetParentBone(BoneName);
302 int32 ParentIndex = SkeletalMesh->GetBoneIndex(ParentBoneName);
303 Skeleton.Joints.Add(
FSkeletonJoint{ i, ParentIndex, BoneNameString, bIsFixedJoint });
304 OTHER_LOG(Log,
"Added bone %s with id %d and parent %d", *BoneNameString, i, ParentIndex);
307 Skeleton.ComputeChildrenJointsAndBones();
308 Skeleton.ComputeEndJoints();
311 auto* AnimInst = SkeletalMesh->GetAnimInstance();
314 OTHER_LOG(Error,
"Could not get animation instance.");
317 UWalkerAnim* WalkerAnim = Cast<UWalkerAnim>(AnimInst);
320 OTHER_LOG(Error,
"Could not get UWalkerAnim.");
325 FPoseSnapshot TempSnapshot;
326 SkeletalMesh->SnapshotPose(TempSnapshot);
329 WalkerAnim->
Snap = TempSnapshot;
331 for (
int i = 0; i < Skeleton.Joints.Num(); ++i)
334 FTransform JointTransform = SkeletalMesh->GetSocketTransform(FName(*Joint.
JointName), ERelativeTransformSpace::RTS_ParentBoneSpace);
341 FVector BoneCOM = Joint.
Transform.GetLocation() * 0.5f;
342 float BoneLength = Joint.
Transform.GetLocation().Size();
343 ParentJoint.
Bones.Add({ 10, BoneLength, BoneCOM });
347 UpdateGlobalTransform();
350void USpringBasedVegetationComponent::BeginPlay()
352 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::BeginPlay);
354 OTHER_LOG(Warning,
"USpringBasedVegetationComponent::BeginPlay");
355 OTHER_LOG(Warning,
"Params: BaseSpringStrength %f, Num joints: %d, CollisionForceParameter %f", BaseSpringStrength, Skeleton.Joints.Num(), CollisionForceParameter);
358 UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
359 SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
363 SetComponentTickEnabled(
false);
364 OTHER_LOG(Error,
"Could not find skeletal mesh component.");
375 SkeletalMesh->OnComponentHit.AddDynamic(
this, &USpringBasedVegetationComponent::OnCollisionEvent);
377 if (!Skeleton.Joints.Num())
379 GenerateSkeletonHierarchy();
382 UpdateGlobalTransform();
383 GenerateCollisionCapsules();
384 if (bAutoComputeStrength)
386 ComputeSpringStrengthForBranches();
389 JointCollisionList.resize(Skeleton.Joints.Num());
392void USpringBasedVegetationComponent::ResetComponent()
394 Skeleton.ClearExternalForces();
395 SkeletalMesh->ResetAllBodiesSimulatePhysics();
398void USpringBasedVegetationComponent::GenerateCollisionCapsules()
404 if (Bone.Length < MinBoneLength)
408 UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(GetOwner());
409 Capsule->AttachToComponent(SkeletalMesh, FAttachmentTransformRules::KeepRelativeTransform, FName(*Joint.
JointName));
410 Capsule->RegisterComponent();
412 FRotator BoneRotation = UKismetMathLibrary::MakeRotFromZ(Bone.CenterOfMass.GetSafeNormal());
413 FTransform CapsuleTransform(BoneRotation, Bone.CenterOfMass, FVector(1, 1, 1));
414 Capsule->SetRelativeTransform(CapsuleTransform);
415 Capsule->SetCapsuleHalfHeight(Bone.Length * 0.5f);
416 Capsule->SetCapsuleRadius(CapsuleRadius);
419 Capsule->SetGenerateOverlapEvents(
false);
420 Capsule->SetCollisionProfileName(
"BlockAll");
421 Capsule->OnComponentHit.AddDynamic(
this, &USpringBasedVegetationComponent::OnCollisionEvent);
425 Capsule->SetGenerateOverlapEvents(
true);
426 Capsule->SetCollisionProfileName(
"OverlapAll");
427 Capsule->OnComponentBeginOverlap.AddDynamic(
this, &USpringBasedVegetationComponent::OnBeginOverlapEvent);
428 Capsule->OnComponentEndOverlap.AddDynamic(
this, &USpringBasedVegetationComponent::OnEndOverlapEvent);
431 BoneCapsules.Add(Capsule);
432 CapsuleToJointId.Add(Capsule, Joint.
JointId);
437void USpringBasedVegetationComponent::ComputeSpringStrengthForBranches()
439 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeSpringStrengthForBranches);
440 FTransform RootTransform = Skeleton.GetRootJoint().GlobalTransform;
441 FVector RootLocation = RootTransform.GetLocation();
443 FVector TreeAxis = FVector(0, 0, 1);
447 FVector ClosestPoint;
448 float HorizontalDistance = FMath::PointDistToLine(JointLocation, TreeAxis, RootLocation, ClosestPoint);
449 float VerticalDistance = FVector::Distance(ClosestPoint, RootLocation);
452 BaseSpringStrength - HorizontalFallof * HorizontalDistance - VerticalFallof * VerticalDistance,
459void USpringBasedVegetationComponent::EndPlay(
const EEndPlayReason::Type EndPlayReason)
461 for (UPrimitiveComponent* Capsule : BoneCapsules)
463 Capsule->DestroyComponent();
465 BoneCapsules.Empty();
469void USpringBasedVegetationComponent::ComputePerJointProperties(
470 std::vector<FJointProperties>& JointLocalPropertiesList,
471 std::vector<FJointProperties>& JointPropertiesList)
473 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputePerJointProperties);
480 if (!Joint.
Bones.Num())
485 Properties.
Mass += Bone.Mass;
486 FVector GlobalCenterOfMass = Joint.
GlobalTransform.TransformPositionNoScale(Bone.CenterOfMass);
492 Eigen::Vector3d GravityForce =
ToEigenVector(Gravity) / 100.f;
501 float CylinderRadius = 0.1f;
502 float CylinderHeight = Bone.Length / 100.f;
503 Eigen::Matrix3d LocalCylinderInertia;
504 LocalCylinderInertia <<
505 0.5 * Bone.Mass * CylinderRadius * CylinderRadius, 0.f, 0.f,
506 0.f, 1.f / 12.f * Bone.Mass * (3 * CylinderRadius * CylinderRadius + CylinderHeight * CylinderHeight), 0.f,
507 0.f, 0.f, 1.f / 12.f * Bone.Mass * (3 * CylinderRadius * CylinderRadius + CylinderHeight * CylinderHeight);
508 Eigen::Vector3d BoneVector =
ToEigenVector(Bone.CenterOfMass) / 100.f;
509 Eigen::Vector3d LocalV1 = BoneVector.normalized();
510 Eigen::Vector3d LocalV2 = LocalV1.cross(Eigen::Vector3d(0, 1, 0));
511 if (LocalV2.norm() == 0)
512 LocalV2 = LocalV1.cross(Eigen::Vector3d(0, 0, 1));
514 Eigen::Vector3d LocalV3 = LocalV1.cross(LocalV2);
515 Eigen::Matrix3d LocalToJointMatrix;
516 LocalToJointMatrix << LocalV1, LocalV2, LocalV3;
517 Eigen::Matrix3d JointSpaceInertia = LocalToJointMatrix * LocalCylinderInertia * (LocalToJointMatrix.transpose());
527void USpringBasedVegetationComponent::ComputeCompositeBodyContribution(
528 std::vector<FJointProperties>& JointLocalPropertiesList,
529 std::vector<FJointProperties>& JointPropertiesList)
531 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeCompositeBodyContribution);
532 for (
int Id : Skeleton.EndToRootOrder)
543 Eigen::Vector3d CenterOfMass = JointLocalProperties.
Mass * JointLocalProperties.
CenterOfMass;
545 JointProperties.
Mass = JointLocalProperties.
Mass;
546 for (
int ChildrenId : Joint.ChildrenIds)
551 JointProperties.
Mass += ChildrenProperties.
Mass;
556 JointProperties.
Force = JointLocalProperties.
Force;
557 for (
int ChildrenId : Joint.ChildrenIds)
561 JointProperties.
Force += ChildrenProperties.
Force;
566 for (
int ChildrenId : Joint.ChildrenIds)
572 JointProperties.
Torque += ChildrenProperties.
Torque + (ChildrenJointGlobalPosition - ParentJointGlobalPosition).cross(ChildrenProperties.
Force);
577 for (
int ChildrenId : Joint.ChildrenIds)
589void USpringBasedVegetationComponent::ComputeFictitiousForces(
590 std::vector<FJointProperties>& JointLocalPropertiesList,
591 std::vector<FJointProperties>& JointPropertiesList)
593 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeFictitiousForces);
597 for (
int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
599 int Id = Skeleton.RootToEndOrder[i];
606 Eigen::Matrix3d GlobalToJointMatrix = JointToGlobalMatrix.transpose();
608 Eigen::Matrix3d GlobalToParentJointMatrix = ParentJointToGlobalMatrix.transpose();
615 + ParentProperties.
AngularVelocity.cross(JointGlobalPosition - ParentJointGlobalPosition);
620 + ParentProperties.
LinearAcceleration.cross(JointGlobalPosition - ParentJointGlobalPosition)
623 Eigen::Vector3d CompositeCenterOfMass = JointProperties.
CenterOfMass;
624 Eigen::Vector3d CompositeLinearVelocity = JointProperties.
LinearVelocity
625 + JointProperties.
AngularVelocity.cross(CompositeCenterOfMass - JointGlobalPosition);
631 Eigen::Vector3d TotalTorque = (CompositeCenterOfMass - JointGlobalPosition).cross(TotalForces);
638void USpringBasedVegetationComponent::ResolveContactsAndCollisions(
639 std::vector<FJointProperties>& JointLocalPropertiesList,
640 std::vector<FJointProperties>& JointPropertiesList)
642 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ResolveContactsAndCollisions);
645 for (
auto& Joint : JointCollisionList)
647 Joint.CanRest =
true;
650 for (
auto& ActorCapsules : OverlappingActors)
652 TRACE_CPUPROFILER_EVENT_SCOPE(ActorLoop);
653 AActor* CollidingActor = ActorCapsules.Key;
656 UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(CollidingActor->GetRootComponent());
660 const FVector PrimitiveVelocity = Primitive->GetComponentVelocity();
661 const Eigen::Vector3d ColliderVelocity =
ToEigenVector(PrimitiveVelocity) / 100.f;
662 const FVector Impulse = (Primitive->GetMass() * PrimitiveVelocity);
663 const Eigen::Vector3d CollisionImpulse = 0 *
ToEigenVector(Impulse) / 100.f;
664 TArray<UPrimitiveComponent*>& CollidingCapsules = ActorCapsules.Value;
665 for (UPrimitiveComponent* Capsule : CollidingCapsules)
667 TRACE_CPUPROFILER_EVENT_SCOPE(CapsuleLoop);
670 const FVector CapsuleLocation = Capsule->GetComponentLocation();
671 FVector PrimitiveLocation = Primitive->GetComponentLocation();
672 PrimitiveLocation = PrimitiveLocation + Primitive->GetUpVector() * VehicleCenterZOffset;
673 FVector ClosestPointOnCapsule;
674 float DistanceOnCapsule;
675 FHitResult HitResult;
677 FVector ClosestPointOnCollider;
678 float DistanceToCollider;
680 TRACE_CPUPROFILER_EVENT_SCOPE(ColliderPenetrationDistance);
681 DistanceOnCapsule = Capsule->GetClosestPointOnCollision(PrimitiveLocation, ClosestPointOnCapsule);
682 FVector LineDirection = (ClosestPointOnCapsule - PrimitiveLocation).GetSafeNormal();
683 FVector LineTraceStart = ClosestPointOnCapsule + LineTraceMaxDistance * LineDirection;
684 FVector LineTraceEnd = ClosestPointOnCapsule;
685 HitFound = Primitive->LineTraceComponent(HitResult,
686 LineTraceStart, LineTraceEnd, FCollisionQueryParams());
687 ClosestPointOnCollider = HitResult.Location;
688 DistanceToCollider = (ClosestPointOnCollider - ClosestPointOnCapsule).Size();
689 if (DebugEnableVisualization)
691 DrawDebugLine(GetWorld(), LineTraceStart, LineTraceEnd, FColor::Orange,
false, 0.1f, 0.0f, 1.f);
699 if (DebugEnableVisualization)
701 TRACE_CPUPROFILER_EVENT_SCOPE(DebugEnableVisualization);
702 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, ClosestPointOnCollider, FColor::Green,
false, 0.1f, 0.0f, 1.f);
703 static constexpr float DEBUG_SPHERE_SIZE = 5.0f;
704 DrawDebugSphere(GetWorld(), ClosestPointOnCapsule, DEBUG_SPHERE_SIZE, 64, FColor(255, 0, 255, 255));
705 DrawDebugSphere(GetWorld(), ClosestPointOnCollider, DEBUG_SPHERE_SIZE, 64, FColor(255, 0, 255, 255));
706 DrawDebugSphere(GetWorld(), CapsuleLocation, DEBUG_SPHERE_SIZE, 64, FColor(255, 255, 255, 255));
707 DrawDebugSphere(GetWorld(), PrimitiveLocation, DEBUG_SPHERE_SIZE, 64, FColor(255, 255, 255, 255));
710 const int JointId = CapsuleToJointId[Capsule];
715 const Eigen::Vector3d CapsulePosition =
ToEigenVector(CapsuleLocation) / 100.f;
716 const Eigen::Vector3d PointOnCapsulePosition =
ToEigenVector(ClosestPointOnCapsule) / 100.f;
717 const Eigen::Vector3d ColliderPosition =
ToEigenVector(ClosestPointOnCollider) / 100.f;
718 Eigen::Vector3d CollisionTorque = Eigen::Vector3d::Zero();
721 const FRotator CurrRotator = Joint.
Transform.Rotator();
723 const FRotator DeltaRotator =
726 const Eigen::Vector3d JointCapsuleVector = JointGlobalPosition - CapsulePosition;
727 const Eigen::Vector3d RepulsionForce = SpringTorque.cross(JointCapsuleVector) * JointCapsuleVector.squaredNorm();
729 FVector RepulsionForceUE = -
ToUnrealVector(RepulsionForce) * 100.f;
730 Primitive->AddForceAtLocation(RepulsionForceUE, ClosestPointOnCollider);
733 float ForceFactor = 1.f;
738 float ProportionalConstant = 1.f / (FMath::Pow(ForceMaxDistance, ForceDistanceFalloffExponent));
739 ForceFactor = ProportionalConstant * FMath::Pow(DistanceToCollider, ForceDistanceFalloffExponent);
740 ForceFactor = FMath::Clamp(ForceFactor, MinForceFactor, 1.f);
744 float Factor = 1.0f - ((JointCollision.
Iteration / 100.0f) * RestFactor);
745 const Eigen::Vector3d OverlappingForces = (ColliderPosition - PointOnCapsulePosition).normalized() * CollisionForceParameter * ForceFactor * Factor * Joint.
CollisionForceProportionalFactor;
746 Primitive->AddForceAtLocation(-
ToUnrealVector(OverlappingForces) * 100.f, ClosestPointOnCollider);
747 CollisionTorque += (PointOnCapsulePosition - JointGlobalPosition).cross(CollisionImpulse + OverlappingForces);
748 JointProperties.
Torque += CollisionTorque;
753 int TempId = JointId;
757 TempJointCollision.
CanRest =
false;
761 }
while (TempId != -1);
763 if (DebugEnableVisualization)
765 static constexpr float DEBUG_SPHERE_SIZE = 5.0f;
767 const FVector
Start = Capsule->GetComponentLocation();
768 const FVector
End = Primitive->GetComponentLocation();
769 const FColor LineColor(FColor::Green);
770 DrawDebugLine(GetWorld(), Start, End, LineColor,
false, 0.1f, 0.0f, 1.f);
771 DrawDebugLine(GetWorld(), ClosestPointOnCollider, ClosestPointOnCollider + RepulsionForceUE.GetSafeNormal() * 20.f, FColor::Red,
false, 0.1f, 0.0f, 1.f);
772 FVector UEOverlapForces =
ToUnrealVector(OverlappingForces) * 100.f;
773 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, ClosestPointOnCapsule + UEOverlapForces.GetSafeNormal() * 20.f, FColor::Turquoise,
false, 0.1f, 0.0f, 1.f);
775 DrawDebugSphere(GetWorld(), UECOM, DEBUG_SPHERE_SIZE, 64, FColor::Emerald);
777 DrawDebugSphere(GetWorld(), UEJointPos, DEBUG_SPHERE_SIZE, 64, FColor::Purple);
778 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, UEJointPos, FColor::Cyan,
false, 0.1f, 0.0f, 1.f);
784 for (
auto& Joint : JointCollisionList)
788 if (Joint.Iteration > 1)
795 if (Joint.Iteration <= 95)
797 Joint.Iteration += 5;
803void USpringBasedVegetationComponent::SolveEquationOfMotion(
804 std::vector<FJointProperties>& JointPropertiesList,
807 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::SolveEquationOfMotion);
819 if (DebugEnableVisualization)
824 FVector
End = Skeleton.Joints[Joint.
ParentId].GlobalTransform.GetLocation();
827 DrawDebugLine(GetWorld(), Start, End, FColor::Red,
false, 0.3f, 0.0f, 1.f);
831 DrawDebugLine(GetWorld(), Start, End, FColor::Blue,
false, 0.1f, 0.0f, 1.f);
836 float Mass = JointProperties.
Mass;
839 Eigen::Matrix3d JointSpaceIntertiaTensor = JointProperties.
Mass *
OuterProduct(CenterToJoint, CenterToJoint)
840 + GlobalToJointMatrix * JointProperties.
InertiaTensor * GlobalToJointMatrix.transpose();
843 Eigen::Matrix3d I = JointSpaceIntertiaTensor;
848 K << SpringStrength * SpringStrengthMulFactor.X, 0.f, 0.f,
849 0.f, SpringStrength* SpringStrengthMulFactor.Y, 0.f,
850 0.f, 0.f, SpringStrength* SpringStrengthMulFactor.Z;
851 Eigen::LLT<Eigen::Matrix3d> lltofI(I);
852 Eigen::Matrix3d L = lltofI.matrixL();
853 Eigen::Matrix3d Linv = L.inverse();
854 Eigen::EigenSolver<Eigen::Matrix3d> EigenDecomposition(Linv * K * Linv.transpose());
855 Eigen::Matrix3d Lambda = EigenDecomposition.eigenvalues().real().asDiagonal();
856 Eigen::Matrix3d X = EigenDecomposition.eigenvectors().real();
858 Eigen::Matrix3d U = Linv.transpose() * X;
860 Eigen::Matrix3d Uinv = X.transpose() * L.transpose();
861 Eigen::Vector3d Coeffsb = Eigen::Vector3d(alpha, alpha, alpha) + beta * Lambda * Eigen::Vector3d(1, 1, 1);
862 Eigen::Vector3d Coeffsk = Lambda * Eigen::Vector3d(1, 1, 1);
863 Eigen::Vector3d Coeffsf = U.transpose() * Torque;
868 FString StringGlobalToJoint =
EigenToFString(GlobalToJointMatrix);
870 SOLVER_LOG(Log,
"Bone %s: \n I: \n %s \n Tau: \n %s \n U: \n %s \n Lambda: \n %s \n X: \n %s \n GlobalToJoint \n %s \n CenterToJoint \n %s", *Joint.
JointName, *StringI, *StringTorque, *StringU, *StringLambda, *
EigenToFString(X), *StringGlobalToJoint, *StringCenterToJoint);
872 FRotator CurrRotator = Joint.
Transform.Rotator();
875 FRotator DeltaRotator =
879 float Factor = 1.0f - ((JointCollision.
Iteration / 100.0f) * RestFactor);
881 AngularVelocity *= Factor;
887 Eigen::Vector3d NewTheta(0.f, 0.f, 0.f);
888 Eigen::Vector3d NewThetaVelocity(0.f, 0.f, 0.f);
889 Eigen::Vector3d NewThetaAccel(0.f, 0.f, 0.f);
890 Eigen::Vector3d Discriminant = Coeffsb.cwiseProduct(Coeffsb) - 4 * Coeffsk;
892 for (
int i = 0; i < 3; i++)
894 double b = Coeffsb(i);
895 double k = Coeffsk(i);
896 double f = Coeffsf(i);
897 double discriminant = Discriminant(i);
898 double theta0 = InitialTheta(i);
900 SOLVER_LOG(Log,
"Bone %s, component %d, b %f, k %f, f %f, discriminant %f, theta0 %f", *Joint.
JointName, i, b, k, f, discriminant, theta0);
903 NewTheta(i) = theta0;
906 double dtheta0 = InitialThetaVelocity(i);
907 double deltatheta = 0;
908 double angularvelocity = 0;
909 float angularaccel = 0;
910 if (discriminant > 0)
912 double r1 = (-b + FMath::Sqrt(discriminant)) * 0.5f;
913 double r2 = (-b - FMath::Sqrt(discriminant)) * 0.5f;
914 double c1 = (r2 * (theta0 - f / k) - dtheta0) / (r2 - r1);
915 double c2 = (dtheta0 - c1 * r1) / r2;
916 deltatheta = c1 * std::exp(r1 * DeltaTime) + c2 * std::exp(r2 * DeltaTime) + f / k;
917 angularvelocity = c1 * r1 * std::exp(r1 * DeltaTime) + c2 * r2 * std::exp(r2 * DeltaTime);
918 SOLVER_LOG(Log,
"r1 %f, r2 %f, c1 %f, c2 %f, deltatheta %f", r1, r2, c1, c2, deltatheta);
920 else if (discriminant == 0)
923 double c1 = theta0 - f / k;
924 double c2 = dtheta0 - c1 * r;
925 deltatheta = (c1 + c2 * DeltaTime) * std::exp(r * DeltaTime) + f / k;
926 angularvelocity = (c1 * r + c2 + c2 * r * DeltaTime) * std::exp(r * DeltaTime);
927 SOLVER_LOG(Log,
"r %f, c1 %f, c2 %f, deltatheta %f", r, c1, c2, deltatheta);
931 double gamma = -b / 2.f;
932 double mu = FMath::Sqrt(FMath::Abs(discriminant)) / 2.f;
933 double c1 = theta0 - f / k;
934 double c2 = (c1 * gamma - dtheta0) / mu;
936 c1 * std::exp(gamma * DeltaTime) * std::cos(mu * DeltaTime) +
937 c2 * std::exp(gamma * DeltaTime) * std::sin(mu * DeltaTime) + f / k;
939 c1 * std::exp(gamma * DeltaTime) * (gamma * std::cos(mu * DeltaTime) + mu * std::sin(mu * DeltaTime)) +
940 c2 * std::exp(gamma * DeltaTime) * (gamma * std::sin(mu * DeltaTime) - mu * std::cos(mu * DeltaTime));
941 SOLVER_LOG(Log,
"gamma %f, mu %f, c1 %f, c2 %f, deltatheta %f", gamma, mu, c1, c2, deltatheta);
943 angularaccel = f - b * angularvelocity - k * deltatheta;
944 if (!FMath::IsNaN(deltatheta))
946 NewTheta(i) = deltatheta;
947 if (angularvelocity > 1e-4)
949 NewThetaVelocity(i) = angularvelocity;
951 if (angularaccel > 1e-2)
953 NewThetaAccel(i) = angularaccel;
957 Eigen::Vector3d FinalNewTheta = U * NewTheta;
958 Eigen::Vector3d FinalNewThetaVelocity = U * NewThetaVelocity;
959 Eigen::Vector3d FinalNewThetaAccel = U * NewThetaAccel;
961 auto NewPitch = FMath::RadiansToDegrees(FinalNewTheta(1));
962 auto NewYaw = FMath::RadiansToDegrees(FinalNewTheta(2));
963 auto NewRoll = FMath::RadiansToDegrees(FinalNewTheta(0));
969 RestRotator.Pitch + NewPitch,
970 RestRotator.Yaw - NewYaw,
971 RestRotator.Roll + NewRoll);
972 SOLVER_LOG(Log,
"FinalNewTheta \n %s \n FinalNewThetaVelocity \n %s \n FinalNewThetaAccel \n %s",
974 SOLVER_LOG(Log,
"New angle %s, new angular velocity %s, new angular accel %s",
975 *NewAngle.ToString(), *NewAngularVelocity.ToString(), *NewAngularAccel.ToString());
976 Joint.
Transform.SetRotation(NewAngle.Quaternion());
982void USpringBasedVegetationComponent::TickComponent(
984 enum ELevelTick TickType,
985 FActorComponentTickFunction* ThisTickFunction)
987 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::TickComponent);
988 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
990 float DeltaTimeFinal = DeltaTime;
991 if (DeltaTimeOverride > 0)
992 DeltaTimeFinal = DeltaTimeOverride;
994 std::vector<FJointProperties> JointPropertiesList;
995 JointPropertiesList.resize(Skeleton.Joints.Num());
996 std::vector<FJointProperties> JointLocalPropertiesList;
997 JointLocalPropertiesList.resize(Skeleton.Joints.Num());
999 ComputePerJointProperties(JointLocalPropertiesList, JointPropertiesList);
1001 ResolveContactsAndCollisions(JointLocalPropertiesList, JointPropertiesList);
1003 ComputeCompositeBodyContribution(JointLocalPropertiesList, JointPropertiesList);
1005 ComputeFictitiousForces(JointLocalPropertiesList, JointPropertiesList);
1007 SolveEquationOfMotion(JointPropertiesList, DeltaTimeFinal);
1009 UpdateSkeletalMesh();
1010 UpdateGlobalTransform();
1011 Skeleton.ClearExternalForces();
1014void USpringBasedVegetationComponent::OnCollisionEvent(
1015 UPrimitiveComponent* HitComponent,
1017 UPrimitiveComponent* OtherComponent,
1018 FVector NormalImpulse,
1019 const FHitResult& Hit)
1022 if (OtherActor == GetOwner())
1025 if (OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
1028 if (DebugEnableAllCollisions)
1035 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1039 COLLISION_LOG(LogCarla, Log, TEXT(
"Collision with bone %s, with impulse %s"), *Hit.MyBoneName.ToString(), *NormalImpulse.ToString());
1040 Skeleton.AddForce(Hit.MyBoneName.ToString(), NormalImpulse);
1043void USpringBasedVegetationComponent::OnBeginOverlapEvent(
1044 UPrimitiveComponent* OverlapComponent,
1046 UPrimitiveComponent* OtherComponent,
1047 int32 OtherBodyIndex,
1049 const FHitResult& SweepResult)
1052 if (OtherActor == GetOwner())
1055 if (OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
1058 if (DebugEnableAllCollisions)
1065 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1070 if (!OverlappingActors.Contains(OtherActor))
1072 OverlappingActors.Add(OtherActor);
1074 TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1075 if (!OverlappingCapsules.Contains(OverlapComponent))
1077 OverlappingCapsules.Add(OverlapComponent);
1081void USpringBasedVegetationComponent::OnEndOverlapEvent(
1082 UPrimitiveComponent* OverlapComponent,
1084 UPrimitiveComponent* OtherComponent,
1085 int32 OtherBodyIndex)
1088 if (OtherActor == GetOwner())
1091 if (OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
1094 if (DebugEnableAllCollisions)
1101 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1106 if (!OverlappingActors.Contains(OtherActor))
1108 TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1109 if (OverlappingCapsules.Contains(OverlapComponent))
1111 OverlappingCapsules.RemoveSingle(OverlapComponent);
1112 if (OverlappingCapsules.Num() == 0)
1114 OverlappingActors.Remove(OtherActor);
1119void USpringBasedVegetationComponent::UpdateSkeletalMesh()
1121 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateSkeletalMesh);
1123 auto* AnimInst = SkeletalMesh->GetAnimInstance();
1124 if (!AnimInst)
return;
1125 UWalkerAnim* WalkerAnim = Cast<UWalkerAnim>(AnimInst);
1126 if (!WalkerAnim)
return;
1129 if (WalkerAnim->
Snap.BoneNames.Num() == 0)
1132 SkeletalMesh->SnapshotPose(WalkerAnim->
Snap);
1135 TMap<FName, FTransform> JointsMap;
1136 for (
int i = 0; i < Skeleton.Joints.Num(); ++i)
1139 FName JointName = FName(*Joint.
JointName);
1140 JointsMap.Add(JointName, Joint.
Transform);
1144 for (
int i = 0; i < WalkerAnim->
Snap.BoneNames.Num(); ++i)
1146 FTransform* Trans = JointsMap.Find(WalkerAnim->
Snap.BoneNames[i]);
1149 WalkerAnim->
Snap.LocalTransforms[i] = *Trans;
1154void USpringBasedVegetationComponent::UpdateGlobalTransform()
1156 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateGlobalTransform);
1157 FTransform InitialTransform = SkeletalMesh->GetOwner()->GetActorTransform();
1161 for (
int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
1163 int Id = Skeleton.RootToEndOrder[i];
static Eigen::Vector3d ToEigenVector(const FVector &V1)
static FVector ToUnrealVector(const Eigen::Vector3d &V1)
static Eigen::Matrix3d OuterProduct(const Eigen::Vector3d &V1, const Eigen::Vector3d &V2)
#define COLLISION_LOG(...)
static FRotator EigenVectorToRotator(const Eigen::Vector3d &Vector)
FRotator GetDeltaRotator(const FRotator &Rotator1, const FRotator &Rotator2)
static Eigen::Matrix3d ToEigenMatrix(const FMatrix &Matrix)
static FString EigenToFString(T &t)
static Eigen::Vector3d RotatorToEigenVector(const FRotator &Rotator)
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
void SetParametersToComponent()
Base class for CARLA wheeled vehicles.
geom::Transform Transform
Eigen::Vector3d LocalAngularAcceleration
Eigen::Matrix3d InertiaTensor
Eigen::Vector3d LinearVelocity
Eigen::Vector3d AngularVelocity
Eigen::Matrix3d JointToGlobalMatrix
Eigen::Vector3d LinearAcceleration
Eigen::Vector3d FictitiousTorque
Eigen::Vector3d AngularAcceleration
Eigen::Vector3d CenterOfMass
TArray< int > RootToEndOrder
void ClearExternalForces()
void AddForce(const FString &BoneName, const FVector &Force)
TArray< FSkeletonJoint > Joints
TArray< int > EndToRootOrder
void ComputeChildrenJointsAndBones()
TArray< FSkeletonBone > Bones
FRotator AngularAcceleration
FTransform GlobalTransform
float CollisionForceProportionalFactor
FTransform GlobalInverseTransform
TArray< int > ChildrenIds