9#include "Math/Matrix.h"
10#include "Components/CapsuleComponent.h"
11#include "DrawDebugHelpers.h"
12#include "Kismet/KismetMathLibrary.h"
15#include <unordered_set>
24#define SPRINGVEGETATIONLOGS 0
26#define COLLISIONLOGS 0
27#define ACCUMULATIONLOGS 0
28#define FICTITIOUSFORCELOGS 0
31#if SOLVERLOGS && SPRINGVEGETATIONLOGS
32#define SOLVER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
34#define SOLVER_LOG(...)
36#if COLLISIONLOGS && SPRINGVEGETATIONLOGS
37#define COLLISION_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
39#define COLLISION_LOG(...)
41#if ACCUMULATIONLOGS && SPRINGVEGETATIONLOGS
42#define ACC_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
46#if FICTITIOUSFORCELOGS && SPRINGVEGETATIONLOGS
47#define FICT_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
51#if OTHERLOGS && SPRINGVEGETATIONLOGS
52#define OTHER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
60 float HalfCircle = 180.f;
61 float FullCircle = 360.f;
62 auto GetDeltaAngle = [&](
float Angle1,
float Angle2) ->
float
66 Angle1 = FullCircle + Angle1;
70 Angle2 = FullCircle + Angle2;
72 float Diff = fmod( Angle1 - Angle2 + HalfCircle , FullCircle) - HalfCircle;
73 return Diff < -HalfCircle ? Diff + FullCircle : Diff;
77 GetDeltaAngle(Rotator1.Pitch, Rotator2.Pitch),
78 GetDeltaAngle(Rotator1.Yaw, Rotator2.Yaw),
79 GetDeltaAngle(Rotator1.Roll, Rotator2.Roll)
86 return n < 0.0f ? -1.0f : 1.0f;
93 return carla::rpc::ToFString(ss.str());
95static Eigen::Matrix3d
OuterProduct(
const Eigen::Vector3d& V1,
const Eigen::Vector3d& V2)
97 return V1 * V2.transpose();
101 return V1 * V1.transpose();
106 return Eigen::Vector3d(V1.X, V1.Y, -V1.Z);
110 return FVector(V1(0), V1(1), -V1(2));
114 Eigen::Matrix3d EigenMatrix;
115 EigenMatrix << Matrix.M[0][0], Matrix.M[0][1], -Matrix.M[0][2],
116 Matrix.M[1][0], Matrix.M[1][1], -Matrix.M[1][2],
117 Matrix.M[2][0], Matrix.M[2][1], -Matrix.M[2][2];
118 return EigenMatrix.transpose();
122 FMatrix Matrix = Transform.ToMatrixNoScale();
127 return Eigen::Vector3d(
128 FMath::DegreesToRadians(Rotator.Roll),
129 FMath::DegreesToRadians(Rotator.Pitch),
130 -FMath::DegreesToRadians(Rotator.Yaw));
135 FMath::RadiansToDegrees(Vector(1)),
136 -FMath::RadiansToDegrees(Vector(2)),
137 FMath::RadiansToDegrees(Vector(0)));
150 for (
int i = 1; i <
Joints.Num(); i++)
158 for (
int i = 0; i <
Joints.Num(); i++)
161 FString ChildrenIds =
"";
164 ChildrenIds += FString::FromInt(ChildrenId) +
", ";
166 OTHER_LOG(Warning,
"Joint %d, Children: %s.", i, *ChildrenIds);
174 for (
int i = 0; i <
Joints.Num(); i++)
185 std::vector<int> JointsToVisit;
186 JointsToVisit.emplace_back(0);
188 while (JointsToVisit.size())
191 JointsToVisit.pop_back();
196 JointsToVisit.emplace_back(ChildrenId);
202 FString IdOrder =
"";
203 FString NameOrder =
"";
208 IdOrder += FString::FromInt(Id) +
" ";
209 NameOrder +=
Joints[Id].JointName +
" ";
212 OTHER_LOG(Warning,
"Tree order: %s", *IdOrder);
213 OTHER_LOG(Warning,
"Tree order (names): %s", *NameOrder);
222 if(Joint.JointName == BoneName)
224 Joint.ExternalForces += Force;
233 Joint.ExternalForces = FVector(0,0,0);
237USpringBasedVegetationComponent::USpringBasedVegetationComponent(
const FObjectInitializer& ObjectInitializer)
238 : Super(ObjectInitializer)
240 PrimaryComponentTick.bCanEverTick =
true;
241 PrimaryComponentTick.bStartWithTickEnabled =
true;
244void USpringBasedVegetationComponent::GenerateSkeletonHierarchy()
246 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::GenerateSkeletonHierarchy);
247 OTHER_LOG(Warning,
"Get skeleton hierarchy");
251 UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
252 SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
254 TArray<FName> BoneNames;
255 SkeletalMesh->GetBoneNames(BoneNames);
257 for (int32 i = 0; i < BoneNames.Num(); i++)
259 FName& BoneName = BoneNames[i];
260 FString BoneNameString = BoneName.ToString();
261 bool bIsFixedJoint = FixedJointsList.Contains(BoneNameString) ||
262 (DebugJointsToSimulate.Num() && !DebugJointsToSimulate.Contains(BoneNameString));
263 FName ParentBoneName = SkeletalMesh->GetParentBone(BoneName);
264 int32 ParentIndex = SkeletalMesh->GetBoneIndex(ParentBoneName);
265 Skeleton.Joints.Add(
FSkeletonJoint{i, ParentIndex, BoneNameString, bIsFixedJoint});
266 OTHER_LOG(Log,
"Added bone %s with id %d and parent %d", *BoneNameString, i, ParentIndex);
269 Skeleton.ComputeChildrenJointsAndBones();
270 Skeleton.ComputeEndJoints();
273 auto *AnimInst = SkeletalMesh->GetAnimInstance();
276 OTHER_LOG(Error,
"Could not get animation instance.");
279 UWalkerAnim *WalkerAnim = Cast<UWalkerAnim>(AnimInst);
282 OTHER_LOG(Error,
"Could not get UWalkerAnim.");
287 FPoseSnapshot TempSnapshot;
288 SkeletalMesh->SnapshotPose(TempSnapshot);
291 WalkerAnim->
Snap = TempSnapshot;
293 for (
int i=0; i<Skeleton.Joints.Num(); ++i)
296 FTransform JointTransform = SkeletalMesh->GetSocketTransform(FName(*Joint.
JointName), ERelativeTransformSpace::RTS_ParentBoneSpace);
303 FVector BoneCOM = Joint.
Transform.GetLocation()*0.5f;
304 float BoneLength = Joint.
Transform.GetLocation().Size();
305 ParentJoint.
Bones.Add({10, BoneLength, BoneCOM});
309 UpdateGlobalTransform();
312void USpringBasedVegetationComponent::BeginPlay()
314 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::BeginPlay);
316 OTHER_LOG(Warning,
"USpringBasedVegetationComponent::BeginPlay");
317 OTHER_LOG(Warning,
"Params: BaseSpringStrength %f, Num joints: %d, CollisionForceParameter %f", BaseSpringStrength, Skeleton.Joints.Num(), CollisionForceParameter);
320 UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
321 SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
325 SetComponentTickEnabled(
false);
326 OTHER_LOG(Error,
"Could not find skeletal mesh component.");
337 SkeletalMesh->OnComponentHit.AddDynamic(
this, &USpringBasedVegetationComponent::OnCollisionEvent);
339 if (!Skeleton.Joints.Num())
341 GenerateSkeletonHierarchy();
344 UpdateGlobalTransform();
345 GenerateCollisionCapsules();
346 if(bAutoComputeStrength)
348 ComputeSpringStrengthForBranches();
351 JointCollisionList.resize(Skeleton.Joints.Num());
354void USpringBasedVegetationComponent::ResetComponent()
356 Skeleton.ClearExternalForces();
357 SkeletalMesh->ResetAllBodiesSimulatePhysics();
360void USpringBasedVegetationComponent::GenerateCollisionCapsules()
366 if (Bone.Length < MinBoneLength)
370 UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(GetOwner());
371 Capsule->AttachToComponent(SkeletalMesh, FAttachmentTransformRules::KeepRelativeTransform, FName(*Joint.
JointName));
372 Capsule->RegisterComponent();
374 FRotator BoneRotation = UKismetMathLibrary::MakeRotFromZ(Bone.CenterOfMass.GetSafeNormal());
375 FTransform CapsuleTransform(BoneRotation, Bone.CenterOfMass, FVector(1,1,1));
376 Capsule->SetRelativeTransform(CapsuleTransform);
377 Capsule->SetCapsuleHalfHeight(Bone.Length*0.5f);
378 Capsule->SetCapsuleRadius(CapsuleRadius);
381 Capsule->SetGenerateOverlapEvents(
false);
382 Capsule->SetCollisionProfileName(
"BlockAll");
383 Capsule->OnComponentHit.AddDynamic(
this, &USpringBasedVegetationComponent::OnCollisionEvent);
387 Capsule->SetGenerateOverlapEvents(
true);
388 Capsule->SetCollisionProfileName(
"OverlapAll");
389 Capsule->OnComponentBeginOverlap.AddDynamic(
this, &USpringBasedVegetationComponent::OnBeginOverlapEvent);
390 Capsule->OnComponentEndOverlap.AddDynamic(
this, &USpringBasedVegetationComponent::OnEndOverlapEvent);
393 BoneCapsules.Add(Capsule);
394 CapsuleToJointId.Add(Capsule, Joint.
JointId);
399void USpringBasedVegetationComponent::ComputeSpringStrengthForBranches()
401 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeSpringStrengthForBranches);
402 FTransform RootTransform = Skeleton.GetRootJoint().GlobalTransform;
403 FVector RootLocation = RootTransform.GetLocation();
405 FVector TreeAxis = FVector(0,0,1);
409 FVector ClosestPoint;
410 float HorizontalDistance = FMath::PointDistToLine(JointLocation, TreeAxis, RootLocation, ClosestPoint);
411 float VerticalDistance = FVector::Distance(ClosestPoint, RootLocation);
414 BaseSpringStrength - HorizontalFallof*HorizontalDistance - VerticalFallof*VerticalDistance,
421void USpringBasedVegetationComponent::EndPlay(
const EEndPlayReason::Type EndPlayReason)
425 Capsule->DestroyComponent();
427 BoneCapsules.Empty();
431void USpringBasedVegetationComponent::ComputePerJointProperties(
432 std::vector<FJointProperties>& JointLocalPropertiesList,
433 std::vector<FJointProperties>& JointPropertiesList)
435 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputePerJointProperties);
442 if (!Joint.
Bones.Num())
447 Properties.
Mass += Bone.Mass;
448 FVector GlobalCenterOfMass = Joint.
GlobalTransform.TransformPositionNoScale(Bone.CenterOfMass);
463 float CylinderRadius = 0.1f;
464 float CylinderHeight = Bone.Length/100.f;
465 Eigen::Matrix3d LocalCylinderInertia;
466 LocalCylinderInertia <<
467 0.5 * Bone.Mass*CylinderRadius*CylinderRadius, 0.f, 0.f,
468 0.f, 1.f/12.f * Bone.Mass*(3*CylinderRadius*CylinderRadius + CylinderHeight*CylinderHeight), 0.f,
469 0.f, 0.f, 1.f/12.f * Bone.Mass*(3*CylinderRadius*CylinderRadius + CylinderHeight*CylinderHeight);
470 Eigen::Vector3d BoneVector =
ToEigenVector(Bone.CenterOfMass)/100.f;
471 Eigen::Vector3d LocalV1 = BoneVector.normalized();
472 Eigen::Vector3d LocalV2 = LocalV1.cross(Eigen::Vector3d(0,1,0));
473 if (LocalV2.norm() == 0)
474 LocalV2 = LocalV1.cross(Eigen::Vector3d(0,0,1));
476 Eigen::Vector3d LocalV3 = LocalV1.cross(LocalV2);
477 Eigen::Matrix3d LocalToJointMatrix;
478 LocalToJointMatrix << LocalV1, LocalV2, LocalV3;
479 Eigen::Matrix3d JointSpaceInertia = LocalToJointMatrix*LocalCylinderInertia*(LocalToJointMatrix.transpose());
489void USpringBasedVegetationComponent::ComputeCompositeBodyContribution(
490 std::vector<FJointProperties>& JointLocalPropertiesList,
491 std::vector<FJointProperties>& JointPropertiesList)
493 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeCompositeBodyContribution);
494 for (
int Id : Skeleton.EndToRootOrder)
505 Eigen::Vector3d CenterOfMass = JointLocalProperties.
Mass*JointLocalProperties.
CenterOfMass;
507 JointProperties.
Mass = JointLocalProperties.
Mass;
508 for(
int ChildrenId : Joint.ChildrenIds)
513 JointProperties.
Mass += ChildrenProperties.
Mass;
518 JointProperties.
Force = JointLocalProperties.
Force;
519 for(
int ChildrenId : Joint.ChildrenIds)
523 JointProperties.
Force += ChildrenProperties.
Force;
528 for(
int ChildrenId : Joint.ChildrenIds)
534 JointProperties.
Torque += ChildrenProperties.
Torque + (ChildrenJointGlobalPosition - ParentJointGlobalPosition).cross(ChildrenProperties.
Force);
539 for(
int ChildrenId : Joint.ChildrenIds)
551void USpringBasedVegetationComponent::ComputeFictitiousForces(
552 std::vector<FJointProperties>& JointLocalPropertiesList,
553 std::vector<FJointProperties>& JointPropertiesList)
555 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeFictitiousForces);
559 for (
int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
561 int Id = Skeleton.RootToEndOrder[i];
568 Eigen::Matrix3d GlobalToJointMatrix = JointToGlobalMatrix.transpose();
570 Eigen::Matrix3d GlobalToParentJointMatrix = ParentJointToGlobalMatrix.transpose();
577 + ParentProperties.
AngularVelocity.cross(JointGlobalPosition - ParentJointGlobalPosition);
582 + ParentProperties.
LinearAcceleration.cross(JointGlobalPosition - ParentJointGlobalPosition)
585 Eigen::Vector3d CompositeCenterOfMass = JointProperties.
CenterOfMass;
586 Eigen::Vector3d CompositeLinearVelocity = JointProperties.
LinearVelocity
587 + JointProperties.
AngularVelocity.cross(CompositeCenterOfMass - JointGlobalPosition);
593 Eigen::Vector3d TotalTorque = (CompositeCenterOfMass - JointGlobalPosition).cross(TotalForces);
600void USpringBasedVegetationComponent::ResolveContactsAndCollisions(
601 std::vector<FJointProperties>& JointLocalPropertiesList,
602 std::vector<FJointProperties>& JointPropertiesList)
604 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ResolveContactsAndCollisions);
607 for (
auto &Joint : JointCollisionList)
609 Joint.CanRest =
true;
612 for (
auto& ActorCapsules : OverlappingActors)
614 TRACE_CPUPROFILER_EVENT_SCOPE(ActorLoop);
615 AActor* CollidingActor = ActorCapsules.Key;
618 UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(CollidingActor->GetRootComponent());
622 const FVector PrimitiveVelocity = Primitive->GetComponentVelocity();
623 const Eigen::Vector3d ColliderVelocity =
ToEigenVector(PrimitiveVelocity) / 100.f;
624 const FVector Impulse = (Primitive->GetMass() * PrimitiveVelocity);
625 const Eigen::Vector3d CollisionImpulse = 0*
ToEigenVector(Impulse) / 100.f;
626 TArray<UPrimitiveComponent*>& CollidingCapsules = ActorCapsules.Value;
629 TRACE_CPUPROFILER_EVENT_SCOPE(CapsuleLoop);
632 const FVector CapsuleLocation = Capsule->GetComponentLocation();
633 FVector PrimitiveLocation = Primitive->GetComponentLocation();
634 PrimitiveLocation = PrimitiveLocation + Primitive->GetUpVector()*VehicleCenterZOffset;
635 FVector ClosestPointOnCapsule;
636 float DistanceOnCapsule;
637 FHitResult HitResult;
639 FVector ClosestPointOnCollider;
640 float DistanceToCollider;
642 TRACE_CPUPROFILER_EVENT_SCOPE(ColliderPenetrationDistance);
643 DistanceOnCapsule = Capsule->GetClosestPointOnCollision(PrimitiveLocation, ClosestPointOnCapsule);
644 FVector LineDirection = (ClosestPointOnCapsule - PrimitiveLocation).GetSafeNormal();
645 FVector LineTraceStart = ClosestPointOnCapsule + LineTraceMaxDistance*LineDirection;
646 FVector LineTraceEnd = ClosestPointOnCapsule;
647 HitFound = Primitive->LineTraceComponent(HitResult,
648 LineTraceStart, LineTraceEnd, FCollisionQueryParams());
649 ClosestPointOnCollider = HitResult.Location;
650 DistanceToCollider = (ClosestPointOnCollider - ClosestPointOnCapsule).Size();
651 if (DebugEnableVisualization)
653 DrawDebugLine(GetWorld(), LineTraceStart, LineTraceEnd, FColor::Orange,
false, 0.1f, 0.0f, 1.f);
661 if (DebugEnableVisualization)
663 TRACE_CPUPROFILER_EVENT_SCOPE(DebugEnableVisualization);
664 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, ClosestPointOnCollider, FColor::Green,
false, 0.1f, 0.0f, 1.f);
665 static constexpr float DEBUG_SPHERE_SIZE = 5.0f;
666 DrawDebugSphere(GetWorld(), ClosestPointOnCapsule, DEBUG_SPHERE_SIZE, 64, FColor(255, 0, 255, 255));
667 DrawDebugSphere(GetWorld(), ClosestPointOnCollider, DEBUG_SPHERE_SIZE, 64, FColor(255, 0, 255, 255));
668 DrawDebugSphere(GetWorld(), CapsuleLocation, DEBUG_SPHERE_SIZE, 64, FColor(255, 255, 255, 255));
669 DrawDebugSphere(GetWorld(), PrimitiveLocation, DEBUG_SPHERE_SIZE, 64, FColor(255, 255, 255, 255));
672 const int JointId = CapsuleToJointId[Capsule];
677 const Eigen::Vector3d CapsulePosition =
ToEigenVector(CapsuleLocation) / 100.f;
678 const Eigen::Vector3d PointOnCapsulePosition =
ToEigenVector(ClosestPointOnCapsule) / 100.f;
679 const Eigen::Vector3d ColliderPosition =
ToEigenVector(ClosestPointOnCollider) / 100.f;
680 Eigen::Vector3d CollisionTorque = Eigen::Vector3d::Zero();
683 const FRotator CurrRotator = Joint.
Transform.Rotator();
685 const FRotator DeltaRotator =
688 const Eigen::Vector3d JointCapsuleVector = JointGlobalPosition - CapsulePosition;
689 const Eigen::Vector3d RepulsionForce = SpringTorque.cross(JointCapsuleVector) * JointCapsuleVector.squaredNorm();
691 FVector RepulsionForceUE = -
ToUnrealVector(RepulsionForce) * 100.f;
692 Primitive->AddForceAtLocation(RepulsionForceUE, ClosestPointOnCollider);
695 float ForceFactor = 1.f;
700 float ProportionalConstant = 1.f/(FMath::Pow(ForceMaxDistance, ForceDistanceFalloffExponent));
701 ForceFactor = ProportionalConstant * FMath::Pow(DistanceToCollider, ForceDistanceFalloffExponent);
702 ForceFactor = FMath::Clamp(ForceFactor, MinForceFactor, 1.f);
706 float Factor = 1.0f - ((JointCollision.
Iteration / 100.0f) * RestFactor);
707 const Eigen::Vector3d OverlappingForces = (ColliderPosition - PointOnCapsulePosition).normalized() * CollisionForceParameter * ForceFactor * Factor * Joint.
CollisionForceProportionalFactor;
708 Primitive->AddForceAtLocation(-
ToUnrealVector(OverlappingForces) * 100.f, ClosestPointOnCollider);
709 CollisionTorque += (PointOnCapsulePosition - JointGlobalPosition).cross(CollisionImpulse + OverlappingForces);
710 JointProperties.
Torque += CollisionTorque;
715 int TempId = JointId;
719 TempJointCollision.
CanRest =
false;
724 while (TempId != -1);
726 if (DebugEnableVisualization)
728 static constexpr float DEBUG_SPHERE_SIZE = 5.0f;
730 const FVector Start = Capsule->GetComponentLocation();
731 const FVector
End = Primitive->GetComponentLocation();
732 const FColor LineColor(FColor::Green);
733 DrawDebugLine(GetWorld(), Start, End, LineColor,
false, 0.1f, 0.0f, 1.f);
734 DrawDebugLine(GetWorld(), ClosestPointOnCollider, ClosestPointOnCollider+RepulsionForceUE.GetSafeNormal()*20.f, FColor::Red,
false, 0.1f, 0.0f, 1.f);
735 FVector UEOverlapForces =
ToUnrealVector(OverlappingForces)*100.f;
736 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, ClosestPointOnCapsule+UEOverlapForces.GetSafeNormal()*20.f, FColor::Turquoise,
false, 0.1f, 0.0f, 1.f);
738 DrawDebugSphere(GetWorld(), UECOM, DEBUG_SPHERE_SIZE, 64, FColor::Emerald);
740 DrawDebugSphere(GetWorld(), UEJointPos, DEBUG_SPHERE_SIZE, 64, FColor::Purple);
741 DrawDebugLine(GetWorld(), ClosestPointOnCapsule, UEJointPos, FColor::Cyan,
false, 0.1f, 0.0f, 1.f);
747 for (
auto &Joint : JointCollisionList)
751 if (Joint.Iteration > 1)
758 if (Joint.Iteration <= 95)
760 Joint.Iteration += 5;
766void USpringBasedVegetationComponent::SolveEquationOfMotion(
767 std::vector<FJointProperties>& JointPropertiesList,
770 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::SolveEquationOfMotion);
782 if (DebugEnableVisualization)
787 FVector
End = Skeleton.Joints[Joint.
ParentId].GlobalTransform.GetLocation();
790 DrawDebugLine(GetWorld(), Start, End, FColor::Red,
false, 0.3f, 0.0f, 1.f);
794 DrawDebugLine(GetWorld(), Start, End, FColor::Blue,
false, 0.1f, 0.0f, 1.f);
799 float Mass = JointProperties.
Mass;
802 Eigen::Matrix3d JointSpaceIntertiaTensor = JointProperties.
Mass*
OuterProduct(CenterToJoint, CenterToJoint)
803 + GlobalToJointMatrix*JointProperties.
InertiaTensor*GlobalToJointMatrix.transpose();
806 Eigen::Matrix3d I = JointSpaceIntertiaTensor;
811 K << SpringStrength*SpringStrengthMulFactor.X,0.f,0.f,
812 0.f,SpringStrength*SpringStrengthMulFactor.Y,0.f,
813 0.f,0.f,SpringStrength*SpringStrengthMulFactor.Z;
814 Eigen::LLT<Eigen::Matrix3d> lltofI (I);
815 Eigen::Matrix3d L = lltofI.matrixL();
816 Eigen::Matrix3d Linv = L.inverse();
817 Eigen::EigenSolver<Eigen::Matrix3d> EigenDecomposition(Linv*K*Linv.transpose());
818 Eigen::Matrix3d Lambda = EigenDecomposition.eigenvalues().real().asDiagonal();
819 Eigen::Matrix3d X = EigenDecomposition.eigenvectors().real();
821 Eigen::Matrix3d U = Linv.transpose()*X;
823 Eigen::Matrix3d Uinv = X.transpose()*L.transpose();
824 Eigen::Vector3d Coeffsb = Eigen::Vector3d(alpha,alpha,alpha) + beta*Lambda*Eigen::Vector3d(1,1,1);
825 Eigen::Vector3d Coeffsk = Lambda*Eigen::Vector3d(1,1,1);
826 Eigen::Vector3d Coeffsf = U.transpose()*Torque;
831 FString StringGlobalToJoint =
EigenToFString(GlobalToJointMatrix);
833 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);
835 FRotator CurrRotator = Joint.
Transform.Rotator();
838 FRotator DeltaRotator =
842 float Factor = 1.0f - ((JointCollision.
Iteration / 100.0f) * RestFactor);
844 AngularVelocity *= Factor;
850 Eigen::Vector3d NewTheta (0.f,0.f,0.f);
851 Eigen::Vector3d NewThetaVelocity (0.f,0.f,0.f);
852 Eigen::Vector3d NewThetaAccel (0.f,0.f,0.f);
853 Eigen::Vector3d Discriminant = Coeffsb.cwiseProduct(Coeffsb) - 4*Coeffsk;
855 for (
int i = 0; i < 3; i++)
857 double b = Coeffsb(i);
858 double k = Coeffsk(i);
859 double f = Coeffsf(i);
860 double discriminant = Discriminant(i);
861 double theta0 = InitialTheta(i);
863 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);
866 NewTheta(i) = theta0;
869 double dtheta0 = InitialThetaVelocity(i);
870 double deltatheta = 0;
871 double angularvelocity = 0;
872 float angularaccel = 0;
873 if (discriminant > 0)
875 double r1 = (-b + FMath::Sqrt(discriminant))*0.5f;
876 double r2 = (-b - FMath::Sqrt(discriminant))*0.5f;
877 double c1 = (r2*(theta0 - f/k) - dtheta0)/(r2-r1);
878 double c2 = (dtheta0 - c1*r1)/r2;
879 deltatheta = c1*std::exp(r1*DeltaTime) + c2*std::exp(r2*DeltaTime) + f/k;
880 angularvelocity = c1*r1*std::exp(r1*DeltaTime) + c2*r2*std::exp(r2*DeltaTime);
881 SOLVER_LOG(Log,
"r1 %f, r2 %f, c1 %f, c2 %f, deltatheta %f", r1, r2, c1, c2, deltatheta);
883 else if (discriminant == 0)
886 double c1 = theta0 - f/k;
887 double c2 = dtheta0 - c1*r;
888 deltatheta = (c1 + c2*DeltaTime)*std::exp(r*DeltaTime) + f/k;
889 angularvelocity = (c1*r + c2 + c2*r*DeltaTime)*std::exp(r*DeltaTime);
890 SOLVER_LOG(Log,
"r %f, c1 %f, c2 %f, deltatheta %f", r, c1, c2, deltatheta);
894 double gamma = -b/2.f;
895 double mu = FMath::Sqrt(FMath::Abs(discriminant))/2.f;
896 double c1 = theta0 - f/k;
897 double c2 = (c1*gamma - dtheta0)/mu;
899 c1*std::exp(gamma*DeltaTime)*std::cos(mu*DeltaTime) +
900 c2*std::exp(gamma*DeltaTime)*std::sin(mu*DeltaTime) + f/k;
902 c1*std::exp(gamma*DeltaTime)*(gamma*std::cos(mu*DeltaTime) + mu*std::sin(mu*DeltaTime)) +
903 c2*std::exp(gamma*DeltaTime)*(gamma*std::sin(mu*DeltaTime) - mu*std::cos(mu*DeltaTime));
904 SOLVER_LOG(Log,
"gamma %f, mu %f, c1 %f, c2 %f, deltatheta %f", gamma, mu, c1, c2, deltatheta);
906 angularaccel = f - b*angularvelocity - k*deltatheta;
907 if (!FMath::IsNaN(deltatheta))
909 NewTheta(i) = deltatheta;
910 if (angularvelocity > 1e-4)
912 NewThetaVelocity(i) = angularvelocity;
914 if (angularaccel > 1e-2)
916 NewThetaAccel(i) = angularaccel;
920 Eigen::Vector3d FinalNewTheta = U*NewTheta;
921 Eigen::Vector3d FinalNewThetaVelocity = U*NewThetaVelocity;
922 Eigen::Vector3d FinalNewThetaAccel = U*NewThetaAccel;
924 auto NewPitch = FMath::RadiansToDegrees(FinalNewTheta(1));
925 auto NewYaw = FMath::RadiansToDegrees(FinalNewTheta(2));
926 auto NewRoll = FMath::RadiansToDegrees(FinalNewTheta(0));
932 RestRotator.Pitch + NewPitch,
933 RestRotator.Yaw - NewYaw,
934 RestRotator.Roll + NewRoll);
935 SOLVER_LOG(Log,
"FinalNewTheta \n %s \n FinalNewThetaVelocity \n %s \n FinalNewThetaAccel \n %s",
937 SOLVER_LOG(Log,
"New angle %s, new angular velocity %s, new angular accel %s",
938 *NewAngle.ToString(), *NewAngularVelocity.ToString(), *NewAngularAccel.ToString());
939 Joint.
Transform.SetRotation(NewAngle.Quaternion());
945void USpringBasedVegetationComponent::TickComponent(
947 enum ELevelTick TickType,
948 FActorComponentTickFunction * ThisTickFunction)
950 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::TickComponent);
951 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
953 float DeltaTimeFinal = DeltaTime;
954 if (DeltaTimeOverride > 0)
955 DeltaTimeFinal = DeltaTimeOverride;
957 std::vector<FJointProperties> JointPropertiesList;
958 JointPropertiesList.resize(Skeleton.Joints.Num());
959 std::vector<FJointProperties> JointLocalPropertiesList;
960 JointLocalPropertiesList.resize(Skeleton.Joints.Num());
962 ComputePerJointProperties(JointLocalPropertiesList, JointPropertiesList);
964 ResolveContactsAndCollisions(JointLocalPropertiesList, JointPropertiesList);
966 ComputeCompositeBodyContribution(JointLocalPropertiesList, JointPropertiesList);
968 ComputeFictitiousForces(JointLocalPropertiesList, JointPropertiesList);
970 SolveEquationOfMotion(JointPropertiesList, DeltaTimeFinal);
972 UpdateSkeletalMesh();
973 UpdateGlobalTransform();
974 Skeleton.ClearExternalForces();
977void USpringBasedVegetationComponent::OnCollisionEvent(
981 FVector NormalImpulse,
982 const FHitResult& Hit)
985 if (OtherActor == GetOwner())
988 if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
991 if (DebugEnableAllCollisions)
998 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1002 COLLISION_LOG(LogCarla, Log, TEXT(
"Collision with bone %s, with impulse %s"), *Hit.MyBoneName.ToString(), *NormalImpulse.ToString());
1003 Skeleton.AddForce(Hit.MyBoneName.ToString(), NormalImpulse);
1006void USpringBasedVegetationComponent::OnBeginOverlapEvent(
1010 int32 OtherBodyIndex,
1012 const FHitResult& SweepResult)
1015 if (OtherActor == GetOwner())
1018 if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
1021 if (DebugEnableAllCollisions)
1028 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1033 if (!OverlappingActors.Contains(OtherActor))
1035 OverlappingActors.Add(OtherActor);
1037 TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1038 if (!OverlappingCapsules.Contains(OverlapComponent))
1040 OverlappingCapsules.Add(OverlapComponent);
1044void USpringBasedVegetationComponent::OnEndOverlapEvent(
1048 int32 OtherBodyIndex)
1051 if (OtherActor == GetOwner())
1054 if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) !=
nullptr)
1057 if (DebugEnableAllCollisions)
1064 Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1069 if (!OverlappingActors.Contains(OtherActor))
1071 TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1072 if (OverlappingCapsules.Contains(OverlapComponent))
1074 OverlappingCapsules.RemoveSingle(OverlapComponent);
1075 if (OverlappingCapsules.Num() == 0)
1077 OverlappingActors.Remove(OtherActor);
1082void USpringBasedVegetationComponent::UpdateSkeletalMesh()
1084 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateSkeletalMesh);
1086 auto *AnimInst = SkeletalMesh->GetAnimInstance();
1087 if (!AnimInst)
return;
1088 UWalkerAnim *WalkerAnim = Cast<UWalkerAnim>(AnimInst);
1089 if (!WalkerAnim)
return;
1092 if (WalkerAnim->
Snap.BoneNames.Num() == 0)
1095 SkeletalMesh->SnapshotPose(WalkerAnim->
Snap);
1098 TMap<FName, FTransform> JointsMap;
1099 for (
int i=0; i<Skeleton.Joints.Num(); ++i)
1102 FName JointName = FName(*Joint.
JointName);
1103 JointsMap.Add(JointName, Joint.
Transform);
1107 for (
int i=0; i<WalkerAnim->
Snap.BoneNames.Num(); ++i)
1109 FTransform *Trans = JointsMap.Find(WalkerAnim->
Snap.BoneNames[i]);
1112 WalkerAnim->
Snap.LocalTransforms[i] = *Trans;
1117void USpringBasedVegetationComponent::UpdateGlobalTransform()
1119 TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateGlobalTransform);
1120 FTransform InitialTransform = SkeletalMesh->GetOwner()->GetActorTransform();
1124 for (
int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
1126 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::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 GolbalInverseTransform
TArray< int > ChildrenIds