CARLA
SpringBasedVegetationComponent.cpp
Go to the documentation of this file.
1 // Copyright (c) 2022 Computer Vision Center (CVC) at the Universitat Autonoma
2 // de Barcelona (UAB).
3 //
4 // This work is licensed under the terms of the MIT license.
5 // For a copy, see <https://opensource.org/licenses/MIT>.
6 
9 #include "Math/Matrix.h"
10 #include "Components/CapsuleComponent.h"
11 #include "DrawDebugHelpers.h"
12 #include "Kismet/KismetMathLibrary.h"
13 #include "BaseVegetationActor.h"
15 #include <unordered_set>
16 #include <vector>
17 #include <cmath>
18 #include <sstream>
19 
21 #include "carla/rpc/String.h"
23 
24 #define SPRINGVEGETATIONLOGS 0
25 #define SOLVERLOGS 0
26 #define COLLISIONLOGS 0
27 #define ACCUMULATIONLOGS 0
28 #define FICTITIOUSFORCELOGS 0
29 #define OTHERLOGS 0
30 
31 #if SOLVERLOGS && SPRINGVEGETATIONLOGS
32 #define SOLVER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
33 #else
34 #define SOLVER_LOG(...)
35 #endif
36 #if COLLISIONLOGS && SPRINGVEGETATIONLOGS
37 #define COLLISION_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
38 #else
39 #define COLLISION_LOG(...)
40 #endif
41 #if ACCUMULATIONLOGS && SPRINGVEGETATIONLOGS
42 #define ACC_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
43 #else
44 #define ACC_LOG(...)
45 #endif
46 #if FICTITIOUSFORCELOGS && SPRINGVEGETATIONLOGS
47 #define FICT_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
48 #else
49 #define FICT_LOG(...)
50 #endif
51 #if OTHERLOGS && SPRINGVEGETATIONLOGS
52 #define OTHER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
53 #else
54 #define OTHER_LOG(...)
55 #endif
56 
57 
58 FRotator GetDeltaRotator(const FRotator & Rotator1, const FRotator & Rotator2)
59 {
60  float HalfCircle = 180.f;
61  float FullCircle = 360.f;
62  auto GetDeltaAngle = [&](float Angle1, float Angle2) -> float
63  {
64  if (Angle1 < 0)
65  {
66  Angle1 = FullCircle + Angle1;
67  }
68  if (Angle2 < 0)
69  {
70  Angle2 = FullCircle + Angle2;
71  }
72  float Diff = fmod( Angle1 - Angle2 + HalfCircle , FullCircle) - HalfCircle;
73  return Diff < -HalfCircle ? Diff + FullCircle : Diff;
74  };
75 
76  return FRotator(
77  GetDeltaAngle(Rotator1.Pitch, Rotator2.Pitch),
78  GetDeltaAngle(Rotator1.Yaw, Rotator2.Yaw),
79  GetDeltaAngle(Rotator1.Roll, Rotator2.Roll)
80  );
81 }
82 
83 template <class T>
84 static T GetSign(T n)
85 {
86  return n < 0.0f ? -1.0f : 1.0f;
87 }
88 template <class T>
89 static FString EigenToFString(T& t)
90 {
91  std::stringstream ss;
92  ss << t;
93  return carla::rpc::ToFString(ss.str());
94 }
95 static Eigen::Matrix3d OuterProduct(const Eigen::Vector3d& V1, const Eigen::Vector3d& V2)
96 {
97  return V1 * V2.transpose();
98 }
99 static Eigen::Matrix3d OuterProduct(const Eigen::Vector3d& V1)
100 {
101  return V1 * V1.transpose();
102 }
103 // convert to right handed frame by flipping z coordinate
104 static Eigen::Vector3d ToEigenVector(const FVector& V1)
105 {
106  return Eigen::Vector3d(V1.X, V1.Y, -V1.Z);
107 }
108 static FVector ToUnrealVector(const Eigen::Vector3d& V1)
109 {
110  return FVector(V1(0), V1(1), -V1(2));
111 }
112 static Eigen::Matrix3d ToEigenMatrix(const FMatrix& Matrix) // Matrix[row][column]
113 {
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();
119 }
120 static Eigen::Matrix3d ToEigenMatrix(const FTransform& Transform)
121 {
122  FMatrix Matrix = Transform.ToMatrixNoScale(); // Matrix[row][column]
123  return ToEigenMatrix(Matrix);
124 }
125 static Eigen::Vector3d RotatorToEigenVector(const FRotator& Rotator)
126 {
127  return Eigen::Vector3d(
128  FMath::DegreesToRadians(Rotator.Roll),
129  FMath::DegreesToRadians(Rotator.Pitch),
130  -FMath::DegreesToRadians(Rotator.Yaw));
131 }
132 static FRotator EigenVectorToRotator(const Eigen::Vector3d& Vector)
133 {
134  return FRotator(
135  FMath::RadiansToDegrees(Vector(1)),
136  -FMath::RadiansToDegrees(Vector(2)),
137  FMath::RadiansToDegrees(Vector(0)));
138 }
139 
141 {
142  Joints.Empty();
143  EndJoints.Empty();
144  EndToRootOrder.Empty();
145  RootToEndOrder.Empty();
146 }
148 {
149  TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ComputeChildrenJointsAndBones);
150  for (int i = 1; i < Joints.Num(); i++)
151  {
152  FSkeletonJoint& Joint = Joints[i];
153  FSkeletonJoint& ParentJoint = Joints[Joint.ParentId];
154  ParentJoint.ChildrenIds.Add(i);
155  }
156 
157  // debug
158  for (int i = 0; i < Joints.Num(); i++)
159  {
160  FSkeletonJoint& Joint = Joints[i];
161  FString ChildrenIds = "";
162  for (int ChildrenId : Joint.ChildrenIds)
163  {
164  ChildrenIds += FString::FromInt(ChildrenId) + ", ";
165  }
166  OTHER_LOG(Warning, "Joint %d, Children: %s.", i, *ChildrenIds);
167  }
168 }
169 
171 {
172  TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ComputeEndJoints);
173  EndJoints.Empty();
174  for (int i = 0; i < Joints.Num(); i++)
175  {
176  FSkeletonJoint& Joint = Joints[i];
177  if (!Joint.ChildrenIds.Num())
178  {
179  EndJoints.Add(i);
180  }
181  }
182 
183  // build traversal order so that parent nodes are visited before any children
184  RootToEndOrder.Empty();
185  std::vector<int> JointsToVisit;
186  JointsToVisit.emplace_back(0); // root element in the hierarchy
187  RootToEndOrder.Add(0);
188  while (JointsToVisit.size())
189  {
190  FSkeletonJoint& Joint = Joints[JointsToVisit.back()];
191  JointsToVisit.pop_back();
192 
193  for (int ChildrenId : Joint.ChildrenIds)
194  {
195  RootToEndOrder.Add(ChildrenId);
196  JointsToVisit.emplace_back(ChildrenId);
197  }
198  }
199 
200  // build the order in reverse, children visited before parents
201  EndToRootOrder.Empty();
202  FString IdOrder = "";
203  FString NameOrder = "";
204  for(int i = RootToEndOrder.Num() - 1; i >= 0; i--)
205  {
206  int Id = RootToEndOrder[i];
207  EndToRootOrder.Add(Id);
208  IdOrder += FString::FromInt(Id) + " ";
209  NameOrder += Joints[Id].JointName + " ";
210  }
211  //debug
212  OTHER_LOG(Warning, "Tree order: %s", *IdOrder);
213  OTHER_LOG(Warning, "Tree order (names): %s", *NameOrder);
214  OTHER_LOG(Warning, "Num elements: %d", EndToRootOrder.Num());
215 }
216 
217 void FSkeletonHierarchy::AddForce(const FString& BoneName, const FVector& Force)
218 {
219  TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::AddForce);
220  for (FSkeletonJoint& Joint : Joints)
221  {
222  if(Joint.JointName == BoneName)
223  {
224  Joint.ExternalForces += Force;
225  }
226  }
227 }
229 {
230  TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ClearExternalForces);
231  for (FSkeletonJoint& Joint : Joints)
232  {
233  Joint.ExternalForces = FVector(0,0,0);
234  }
235 }
236 
237 USpringBasedVegetationComponent::USpringBasedVegetationComponent(const FObjectInitializer& ObjectInitializer)
238  : Super(ObjectInitializer)
239 {
240  PrimaryComponentTick.bCanEverTick = true;
241  PrimaryComponentTick.bStartWithTickEnabled = true;
242 }
243 
244 void USpringBasedVegetationComponent::GenerateSkeletonHierarchy()
245 {
246  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::GenerateSkeletonHierarchy);
247  OTHER_LOG(Warning, "Get skeleton hierarchy");
248  // Get skeleton hierarchy
249  if (!SkeletalMesh)
250  {
251  UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
252  SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
253  }
254  TArray<FName> BoneNames;
255  SkeletalMesh->GetBoneNames(BoneNames);
256  Skeleton.Clear();
257  for (int32 i = 0; i < BoneNames.Num(); i++)
258  {
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);
267  }
268 
269  Skeleton.ComputeChildrenJointsAndBones();
270  Skeleton.ComputeEndJoints();
271 
272  // Get resting pose for bones
273  auto *AnimInst = SkeletalMesh->GetAnimInstance();
274  if (!AnimInst)
275  {
276  OTHER_LOG(Error, "Could not get animation instance.");
277  return;
278  }
279  UWalkerAnim *WalkerAnim = Cast<UWalkerAnim>(AnimInst);
280  if (!WalkerAnim)
281  {
282  OTHER_LOG(Error, "Could not get UWalkerAnim.");
283  return;
284  }
285 
286  // get current pose
287  FPoseSnapshot TempSnapshot;
288  SkeletalMesh->SnapshotPose(TempSnapshot);
289 
290  // copy pose
291  WalkerAnim->Snap = TempSnapshot;
292 
293  for (int i=0; i<Skeleton.Joints.Num(); ++i)
294  {
295  FSkeletonJoint& Joint = Skeleton.Joints[i];
296  FTransform JointTransform = SkeletalMesh->GetSocketTransform(FName(*Joint.JointName), ERelativeTransformSpace::RTS_ParentBoneSpace);
297  Joint.Transform = JointTransform;
298  Joint.RestingAngles = JointTransform.Rotator();
299  OTHER_LOG(Log, "Getting info for bone %s, %f, %f, %f, %f", *Joint.JointName, Joint.RestingAngles.Pitch, Joint.RestingAngles.Yaw, Joint.RestingAngles.Roll);
300  if(i > 0)
301  {
302  FSkeletonJoint& ParentJoint = Skeleton.Joints[Joint.ParentId];
303  FVector BoneCOM = Joint.Transform.GetLocation()*0.5f;
304  float BoneLength = Joint.Transform.GetLocation().Size();
305  ParentJoint.Bones.Add({10, BoneLength, BoneCOM});
306  }
307  }
308 
309  UpdateGlobalTransform();
310 }
311 
312 void USpringBasedVegetationComponent::BeginPlay()
313 {
314  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::BeginPlay);
315  Super::BeginPlay();
316  OTHER_LOG(Warning, "USpringBasedVegetationComponent::BeginPlay");
317  OTHER_LOG(Warning, "Params: BaseSpringStrength %f, Num joints: %d, CollisionForceParameter %f", BaseSpringStrength, Skeleton.Joints.Num(), CollisionForceParameter);
318  if (!SkeletalMesh)
319  {
320  UActorComponent* Component = GetOwner()->GetComponentByClass(USkeletalMeshComponent::StaticClass());
321  SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
322  }
323  if (!SkeletalMesh)
324  {
325  SetComponentTickEnabled(false);
326  OTHER_LOG(Error, "Could not find skeletal mesh component.");
327  return;
328  }
329 
330  ABaseVegetationActor* BaseVegetation = Cast<ABaseVegetationActor>(GetOwner());
331  if(BaseVegetation)
332  {
333  BaseVegetation->SetParametersToComponent();
334  }
335 
336  // set callbacks
337  SkeletalMesh->OnComponentHit.AddDynamic(this, &USpringBasedVegetationComponent::OnCollisionEvent);
338 
339  if (!Skeleton.Joints.Num())
340  {
341  GenerateSkeletonHierarchy();
342  }
343 
344  UpdateGlobalTransform();
345  GenerateCollisionCapsules();
346  if(bAutoComputeStrength)
347  {
348  ComputeSpringStrengthForBranches();
349  }
350 
351  JointCollisionList.resize(Skeleton.Joints.Num());
352 }
353 
354 void USpringBasedVegetationComponent::ResetComponent()
355 {
356  Skeleton.ClearExternalForces();
357  SkeletalMesh->ResetAllBodiesSimulatePhysics();
358 }
359 
360 void USpringBasedVegetationComponent::GenerateCollisionCapsules()
361 {
362  for (FSkeletonJoint& Joint : Skeleton.Joints)
363  {
364  for (FSkeletonBone& Bone : Joint.Bones)
365  {
366  if (Bone.Length < MinBoneLength)
367  {
368  continue;
369  }
370  UCapsuleComponent* Capsule = NewObject<UCapsuleComponent>(GetOwner());
371  Capsule->AttachToComponent(SkeletalMesh, FAttachmentTransformRules::KeepRelativeTransform, FName(*Joint.JointName));
372  Capsule->RegisterComponent();
373  // create rotation from z direction to align the capsule
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);
379  if (Joint.bIsStatic)
380  {
381  Capsule->SetGenerateOverlapEvents(false);
382  Capsule->SetCollisionProfileName("BlockAll");
383  Capsule->OnComponentHit.AddDynamic(this, &USpringBasedVegetationComponent::OnCollisionEvent);
384  }
385  else
386  {
387  Capsule->SetGenerateOverlapEvents(true);
388  Capsule->SetCollisionProfileName("OverlapAll");
389  Capsule->OnComponentBeginOverlap.AddDynamic(this, &USpringBasedVegetationComponent::OnBeginOverlapEvent);
390  Capsule->OnComponentEndOverlap.AddDynamic(this, &USpringBasedVegetationComponent::OnEndOverlapEvent);
391  }
392 
393  BoneCapsules.Add(Capsule);
394  CapsuleToJointId.Add(Capsule, Joint.JointId);
395  }
396  }
397 }
398 
399 void USpringBasedVegetationComponent::ComputeSpringStrengthForBranches()
400 {
401  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeSpringStrengthForBranches);
402  FTransform RootTransform = Skeleton.GetRootJoint().GlobalTransform;
403  FVector RootLocation = RootTransform.GetLocation();
404  // FVector TreeAxis = RootTransform.GetRotation().GetUpVector();
405  FVector TreeAxis = FVector(0,0,1);
406  for (FSkeletonJoint& Joint : Skeleton.Joints)
407  {
408  FVector JointLocation = Joint.GlobalTransform.GetLocation();
409  FVector ClosestPoint;
410  float HorizontalDistance = FMath::PointDistToLine(JointLocation, TreeAxis, RootLocation, ClosestPoint);
411  float VerticalDistance = FVector::Distance(ClosestPoint, RootLocation);
412 
413  Joint.SpringStrength = FMath::Max(
414  BaseSpringStrength - HorizontalFallof*HorizontalDistance - VerticalFallof*VerticalDistance,
415  MinSpringStrength);
416 
417  OTHER_LOG(Log, "Joint: %s, location %s, Strength %f", *Joint.JointName, *JointLocation.ToString(), Joint.SpringStrength);
418  }
419 }
420 
421 void USpringBasedVegetationComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
422 {
423  for (UPrimitiveComponent* Capsule : BoneCapsules)
424  {
425  Capsule->DestroyComponent();
426  }
427  BoneCapsules.Empty();
428 }
429 
430 // Compute a single joint properties (Center of Mass, Inertia, Forces and Torque)
431 void USpringBasedVegetationComponent::ComputePerJointProperties(
432  std::vector<FJointProperties>& JointLocalPropertiesList,
433  std::vector<FJointProperties>& JointPropertiesList)
434 {
435  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputePerJointProperties);
436  for(FSkeletonJoint& Joint : Skeleton.Joints)
437  {
438  FJointProperties& Properties = JointLocalPropertiesList[Joint.JointId];
439  Eigen::Vector3d JointGlobalPosition = ToEigenVector(Joint.GlobalTransform.GetLocation())/100.f;
441  JointPropertiesList[Joint.JointId].JointToGlobalMatrix = Properties.JointToGlobalMatrix;
442  if (!Joint.Bones.Num())
443  continue;
444  // COM and mass
445  for (FSkeletonBone& Bone : Joint.Bones)
446  {
447  Properties.Mass += Bone.Mass;
448  FVector GlobalCenterOfMass = Joint.GlobalTransform.TransformPositionNoScale(Bone.CenterOfMass);
449  Properties.CenterOfMass += Bone.Mass*ToEigenVector(GlobalCenterOfMass)/100.f;
450  }
451  Properties.CenterOfMass = Properties.CenterOfMass/Properties.Mass;
452 
453  // force
454  Eigen::Vector3d GravityForce = ToEigenVector(Gravity)/100.f; // world space gravity
455  Properties.Force = Properties.Mass * GravityForce + ToEigenVector(Joint.ExternalForces)/100.f;
456  // torque
457  Properties.Torque = (Properties.CenterOfMass - JointGlobalPosition).cross(Properties.Force);
458  // inertia tensor
459  for (FSkeletonBone& Bone : Joint.Bones)
460  {
461  if (Bone.Length < 1)
462  continue;
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));
475  LocalV2.normalize();
476  Eigen::Vector3d LocalV3 = LocalV1.cross(LocalV2);
477  Eigen::Matrix3d LocalToJointMatrix;
478  LocalToJointMatrix << LocalV1, LocalV2, LocalV3;
479  Eigen::Matrix3d JointSpaceInertia = LocalToJointMatrix*LocalCylinderInertia*(LocalToJointMatrix.transpose());
480  Properties.InertiaTensor += Properties.JointToGlobalMatrix*JointSpaceInertia*Properties.JointToGlobalMatrix.transpose();
481  Eigen::Vector3d BoneCenterWorldSpace = ToEigenVector(Joint.GlobalTransform.TransformPositionNoScale(Bone.CenterOfMass))/100.0;
482  Properties.InertiaTensor += Properties.Mass*OuterProduct(Properties.CenterOfMass - BoneCenterWorldSpace, Properties.CenterOfMass - BoneCenterWorldSpace);
483  }
484  ACC_LOG(Log, "Local Joint: %s \n Inertia \n %s \n Force \n %s \n Torque \n %s \n COM: \n %s \n Mass %f", *Joint.JointName, *EigenToFString(Properties.InertiaTensor), *EigenToFString(Properties.Force), *EigenToFString(Properties.Torque), *EigenToFString(Properties.CenterOfMass), Properties.Mass);
485  }
486 }
487 
488 // Compute accumulated properties (Center of Mass, Inertia, Forces and Torque)
489 void USpringBasedVegetationComponent::ComputeCompositeBodyContribution(
490  std::vector<FJointProperties>& JointLocalPropertiesList,
491  std::vector<FJointProperties>& JointPropertiesList)
492 {
493  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeCompositeBodyContribution);
494  for (int Id : Skeleton.EndToRootOrder)
495  {
496  FSkeletonJoint& Joint = Skeleton.Joints[Id];
497  FJointProperties& JointProperties = JointPropertiesList[Joint.JointId];
498  FJointProperties& JointLocalProperties = JointLocalPropertiesList[Joint.JointId];
499 
500  if (!Joint.ChildrenIds.Num())
501  {
502  continue;
503  }
504 
505  Eigen::Vector3d CenterOfMass = JointLocalProperties.Mass*JointLocalProperties.CenterOfMass;
506  // compute COM and accumulate mass
507  JointProperties.Mass = JointLocalProperties.Mass;
508  for(int ChildrenId : Joint.ChildrenIds)
509  {
510  FSkeletonJoint& ChildrenJoint = Skeleton.Joints[ChildrenId];
511  FJointProperties& ChildrenProperties = JointPropertiesList[ChildrenId];
512  CenterOfMass += ChildrenProperties.Mass*ChildrenProperties.CenterOfMass;
513  JointProperties.Mass += ChildrenProperties.Mass;
514  }
515  JointProperties.CenterOfMass = CenterOfMass / JointProperties.Mass;
516 
517  // compute forces
518  JointProperties.Force = JointLocalProperties.Force;
519  for(int ChildrenId : Joint.ChildrenIds)
520  {
521  FSkeletonJoint& ChildrenJoint = Skeleton.Joints[ChildrenId];
522  FJointProperties& ChildrenProperties = JointPropertiesList[ChildrenId];
523  JointProperties.Force += ChildrenProperties.Force;
524  }
525 
526  // compute torque
527  JointProperties.Torque = JointLocalProperties.Torque;
528  for(int ChildrenId : Joint.ChildrenIds)
529  {
530  FSkeletonJoint& ChildrenJoint = Skeleton.Joints[ChildrenId];
531  FJointProperties& ChildrenProperties = JointPropertiesList[ChildrenId];
532  Eigen::Vector3d ChildrenJointGlobalPosition = ToEigenVector(ChildrenJoint.GlobalTransform.GetLocation())/100.f;
533  Eigen::Vector3d ParentJointGlobalPosition = ToEigenVector(Joint.GlobalTransform.GetLocation())/100.f;
534  JointProperties.Torque += ChildrenProperties.Torque + (ChildrenJointGlobalPosition - ParentJointGlobalPosition).cross(ChildrenProperties.Force);
535  }
536 
537  // compute inertia tensor
538  JointProperties.InertiaTensor = JointLocalProperties.InertiaTensor + JointLocalProperties.Mass*OuterProduct(JointProperties.CenterOfMass - JointLocalProperties.CenterOfMass, JointProperties.CenterOfMass - JointLocalProperties.CenterOfMass);
539  for(int ChildrenId : Joint.ChildrenIds)
540  {
541  FSkeletonJoint& ChildrenJoint = Skeleton.Joints[ChildrenId];
542  FJointProperties& ChildrenProperties = JointPropertiesList[ChildrenId];
543  Eigen::Vector3d ChildrenToCenterOfMass = JointProperties.CenterOfMass - ChildrenProperties.CenterOfMass;
544 
545  JointProperties.InertiaTensor += ChildrenProperties.InertiaTensor + ChildrenProperties.Mass*OuterProduct(ChildrenToCenterOfMass, ChildrenToCenterOfMass);
546  }
547  ACC_LOG(Log, "Accumulated Joint: %s \n Inertia \n %s \n Force \n %s \n Torque \n %s \n COM: \n %s \n Mass %f", *Joint.JointName, *EigenToFString(JointProperties.InertiaTensor), *EigenToFString(JointProperties.Force), *EigenToFString(JointProperties.Torque), *EigenToFString(JointProperties.CenterOfMass), JointProperties.Mass);
548  }
549 }
550 
551 void USpringBasedVegetationComponent::ComputeFictitiousForces(
552  std::vector<FJointProperties>& JointLocalPropertiesList,
553  std::vector<FJointProperties>& JointPropertiesList)
554 {
555  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeFictitiousForces);
556  // fictitious forces
557  FSkeletonJoint& RootJoint = Skeleton.Joints[0];
558  FJointProperties& RootProperties = JointPropertiesList[0];
559  for (int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
560  {
561  int Id = Skeleton.RootToEndOrder[i];
562  FSkeletonJoint& Joint = Skeleton.Joints[Id];
563  FJointProperties& JointProperties = JointPropertiesList[Joint.JointId];
564  FSkeletonJoint& ParentJoint = Skeleton.Joints[Joint.ParentId];
565  FJointProperties& ParentProperties = JointPropertiesList[Joint.ParentId];
566 
567  Eigen::Matrix3d JointToGlobalMatrix = JointProperties.JointToGlobalMatrix;
568  Eigen::Matrix3d GlobalToJointMatrix = JointToGlobalMatrix.transpose();
569  Eigen::Matrix3d ParentJointToGlobalMatrix = ParentProperties.JointToGlobalMatrix;
570  Eigen::Matrix3d GlobalToParentJointMatrix = ParentJointToGlobalMatrix.transpose();
571  Eigen::Vector3d JointGlobalPosition = ToEigenVector(Joint.GlobalTransform.GetLocation())/100.f;
572  Eigen::Vector3d ParentJointGlobalPosition = ToEigenVector(ParentJoint.GlobalTransform.GetLocation())/100.f;
573 
574  JointProperties.AngularVelocity = ParentProperties.AngularVelocity
575  + ParentJointToGlobalMatrix*RotatorToEigenVector(ParentJoint.AngularVelocity);
576  JointProperties.LinearVelocity = ParentProperties.LinearVelocity
577  + ParentProperties.AngularVelocity.cross(JointGlobalPosition - ParentJointGlobalPosition);
578  JointProperties.AngularAcceleration = ParentProperties.AngularAcceleration
579  + ParentJointToGlobalMatrix*RotatorToEigenVector(ParentJoint.AngularAcceleration)
580  + ParentProperties.AngularVelocity.cross(JointProperties.AngularVelocity);
581  JointProperties.LinearAcceleration = ParentProperties.LinearAcceleration
582  + ParentProperties.LinearAcceleration.cross(JointGlobalPosition - ParentJointGlobalPosition)
583  + ParentProperties.AngularVelocity.cross(JointProperties.AngularVelocity - ParentProperties.AngularVelocity);
584 
585  Eigen::Vector3d CompositeCenterOfMass = JointProperties.CenterOfMass;
586  Eigen::Vector3d CompositeLinearVelocity = JointProperties.LinearVelocity
587  + JointProperties.AngularVelocity.cross(CompositeCenterOfMass - JointGlobalPosition);
588  Eigen::Vector3d CompositeLinearAcceleration = JointProperties.LinearAcceleration
589  + JointProperties.AngularAcceleration.cross(CompositeCenterOfMass - JointGlobalPosition)
590  + JointProperties.AngularVelocity.cross(CompositeLinearVelocity - JointProperties.LinearVelocity);
591 
592  Eigen::Vector3d TotalForces = -Joint.Mass * JointProperties.LinearAcceleration;
593  Eigen::Vector3d TotalTorque = (CompositeCenterOfMass - JointGlobalPosition).cross(TotalForces);
594  JointProperties.FictitiousTorque = GlobalToJointMatrix*TotalTorque;
595 
596  FICT_LOG(Log, "Joint: %s \n Position \n %s \n Velocity \n %s \n Acceleration \n %s \n Fictitious forces: \n %s", *Joint.JointName, *EigenToFString(CompositeCenterOfMass), *EigenToFString(CompositeLinearVelocity), *EigenToFString(CompositeLinearAcceleration), *EigenToFString(JointProperties.FictitiousTorque));
597  }
598 }
599 
600 void USpringBasedVegetationComponent::ResolveContactsAndCollisions(
601  std::vector<FJointProperties>& JointLocalPropertiesList,
602  std::vector<FJointProperties>& JointPropertiesList)
603 {
604  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ResolveContactsAndCollisions);
605 
606  // set all joints that can rest
607  for (auto &Joint : JointCollisionList)
608  {
609  Joint.CanRest = true;
610  }
611 
612  for (auto& ActorCapsules : OverlappingActors)
613  {
614  TRACE_CPUPROFILER_EVENT_SCOPE(ActorLoop);
615  AActor* CollidingActor = ActorCapsules.Key;
616  if (!IsValid(CollidingActor))
617  continue;
618  UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(CollidingActor->GetRootComponent());
619  if (!IsValid(Primitive))
620  continue;
621  // force transferring momentum (for the initial collision frame)
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;
627  for (UPrimitiveComponent* Capsule : CollidingCapsules)
628  {
629  TRACE_CPUPROFILER_EVENT_SCOPE(CapsuleLoop);
630  if (!IsValid(Capsule))
631  continue;
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;
638  bool HitFound;
639  FVector ClosestPointOnCollider;
640  float DistanceToCollider;
641  {
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)
652  {
653  DrawDebugLine(GetWorld(), LineTraceStart, LineTraceEnd, FColor::Orange, false, 0.1f, 0.0f, 1.f);
654  }
655  }
656  if(!HitFound)
657  {
658  continue;
659  }
660 
661  if (DebugEnableVisualization)
662  {
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));
670  }
671 
672  const int JointId = CapsuleToJointId[Capsule];
673  const FSkeletonJoint& Joint = Skeleton.Joints[JointId];
674  FJointProperties& JointProperties = JointLocalPropertiesList[Joint.JointId];
675  FJointCollision& JointCollision = JointCollisionList[Joint.JointId];
676  const Eigen::Vector3d JointGlobalPosition = ToEigenVector(Joint.GlobalTransform.GetLocation()) / 100.f;
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();
681 
682  // Contact forces due to spring strength
683  const FRotator CurrRotator = Joint.Transform.Rotator();
684  const FRotator RestRotator = Joint.RestingAngles;
685  const FRotator DeltaRotator =
686  GetDeltaRotator(CurrRotator, RestRotator);
687  const Eigen::Vector3d SpringTorque = Joint.SpringStrength * RotatorToEigenVector(DeltaRotator);
688  const Eigen::Vector3d JointCapsuleVector = JointGlobalPosition - CapsulePosition;
689  const Eigen::Vector3d RepulsionForce = SpringTorque.cross(JointCapsuleVector) * JointCapsuleVector.squaredNorm();
690 
691  FVector RepulsionForceUE = -ToUnrealVector(RepulsionForce) * 100.f;
692  Primitive->AddForceAtLocation(RepulsionForceUE, ClosestPointOnCollider);
693 
694  // force to repel geometry overlapping
695  float ForceFactor = 1.f;
696  // from eq f = 1 - a*d^p, f: ForceFactor, a: ProportionalConstant, p: ForceDistanceFalloffExponent, d: DistanceOnCapsule
697  // float ProportionalConstant = 1.f/(FMath::Pow(ForceMaxDistance, ForceDistanceFalloffExponent));
698  // ForceFactor = 1.f - ProportionalConstant * FMath::Pow(DistanceOnCapsule, ForceDistanceFalloffExponent);
699  // from eq f = a*d^p, f: ForceFactor, a: ProportionalConstant, p: ForceDistanceFalloffExponent, d: DistanceToCollider
700  float ProportionalConstant = 1.f/(FMath::Pow(ForceMaxDistance, ForceDistanceFalloffExponent));
701  ForceFactor = ProportionalConstant * FMath::Pow(DistanceToCollider, ForceDistanceFalloffExponent);
702  ForceFactor = FMath::Clamp(ForceFactor, MinForceFactor, 1.f);
703 
704  // const Eigen::Vector3d OverlappingForces = (ColliderPosition - CapsulePosition).normalized() * CollisionForceParameter * ForceFactor;
705  // const Eigen::Vector3d OverlappingForces = (ColliderPosition - PointOnCapsulePosition).normalized() * CollisionForceParameter * ForceFactor;
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;
711  // COLLISION_LOG(Log, "Joint: %s \n ProjectedSpeed %f, ProportionalFactor %f \n RepulsionForce %s \n", *Joint.JointName,ProjectedSpeed,ProportionalFactor,*EigenToFString(RepulsionForce),*EigenToFString(CollisionTorque));
712  //UE_LOG(LogCarla, Display, TEXT("DistanceToCollider: %f, ForceFactor: %f"), DistanceToCollider, ForceFactor);
713 
714  // block forces to go to rest angles
715  int TempId = JointId;
716  do
717  {
718  FJointCollision& TempJointCollision = JointCollisionList[TempId];
719  TempJointCollision.CanRest = false;
720 
721  FSkeletonJoint &TempJoint = Skeleton.Joints[TempId];
722  TempId = TempJoint.ParentId;
723  }
724  while (TempId != -1);
725 
726  if (DebugEnableVisualization)
727  {
728  static constexpr float DEBUG_SPHERE_SIZE = 5.0f;
729  // drawing
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);
737  FVector UECOM = ToUnrealVector(JointProperties.CenterOfMass )*100.f;
738  DrawDebugSphere(GetWorld(), UECOM, DEBUG_SPHERE_SIZE, 64, FColor::Emerald);
739  FVector UEJointPos = ToUnrealVector(JointGlobalPosition )*100.f;
740  DrawDebugSphere(GetWorld(), UEJointPos, DEBUG_SPHERE_SIZE, 64, FColor::Purple);
741  DrawDebugLine(GetWorld(), ClosestPointOnCapsule, UEJointPos, FColor::Cyan, false, 0.1f, 0.0f, 1.f);
742  }
743  }
744  }
745 
746  // reset iteration
747  for (auto &Joint : JointCollisionList)
748  {
749  if (Joint.CanRest)
750  {
751  if (Joint.Iteration > 1)
752  {
753  --Joint.Iteration;
754  }
755  }
756  else
757  {
758  if (Joint.Iteration <= 95)
759  {
760  Joint.Iteration += 5;
761  }
762  }
763  }
764 }
765 
766 void USpringBasedVegetationComponent::SolveEquationOfMotion(
767  std::vector<FJointProperties>& JointPropertiesList,
768  float DeltaTime)
769 {
770  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::SolveEquationOfMotion);
771  // solver
772  for (FSkeletonJoint& Joint : Skeleton.Joints)
773  {
774  if (!Joint.Bones.Num() || Joint.bIsStatic)
775  {
776  continue;
777  }
778  FJointProperties& JointProperties = JointPropertiesList[Joint.JointId];
779  FJointCollision& JointCollision = JointCollisionList[Joint.JointId];
780 
781  // debug drawing
782  if (DebugEnableVisualization)
783  {
784  if (Joint.ParentId != -1)
785  {
786  FVector Start = Joint.GlobalTransform.GetLocation();
787  FVector End = Skeleton.Joints[Joint.ParentId].GlobalTransform.GetLocation();
788  if (!JointCollision.CanRest)
789  {
790  DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 0.3f, 0.0f, 1.f);
791  }
792  else
793  {
794  DrawDebugLine(GetWorld(), Start, End, FColor::Blue, false, 0.1f, 0.0f, 1.f);
795  }
796  }
797  }
798 
799  float Mass = JointProperties.Mass;
800  Eigen::Vector3d CenterToJoint = JointProperties.CenterOfMass - ToEigenVector(Joint.GlobalTransform.GetLocation())/100.f;
801  Eigen::Matrix3d GlobalToJointMatrix = JointProperties.JointToGlobalMatrix.transpose();
802  Eigen::Matrix3d JointSpaceIntertiaTensor = JointProperties.Mass*OuterProduct(CenterToJoint, CenterToJoint)
803  + GlobalToJointMatrix*JointProperties.InertiaTensor*GlobalToJointMatrix.transpose();
804  Eigen::Vector3d Torque = GlobalToJointMatrix*JointProperties.Torque + JointProperties.FictitiousTorque;
805  // build differential equation
806  Eigen::Matrix3d I = JointSpaceIntertiaTensor;
807  float SpringStrength = Joint.SpringStrength;
808  float beta = Beta;
809  float alpha = Alpha;
810  Eigen::Matrix3d K;
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();
820  // Eigen::Matrix3d Lambda = U.transpose()*K*U;
821  Eigen::Matrix3d U = Linv.transpose()*X;
822  // Eigen::Matrix3d Uinv = U.inverse();
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;
827  FString StringI = EigenToFString(I);
828  FString StringTorque = EigenToFString(Torque);
829  FString StringU = EigenToFString(U);
830  FString StringLambda = EigenToFString(Lambda);
831  FString StringGlobalToJoint = EigenToFString(GlobalToJointMatrix);
832  FString StringCenterToJoint = EigenToFString(CenterToJoint);
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);
834 
835  FRotator CurrRotator = Joint.Transform.Rotator();
836  FRotator RestRotator = Joint.RestingAngles;
837  FRotator AngularVelocity = Joint.AngularVelocity;
838  FRotator DeltaRotator =
839  GetDeltaRotator(CurrRotator, RestRotator);
840  if (!JointCollision.CanRest)
841  {
842  float Factor = 1.0f - ((JointCollision.Iteration / 100.0f) * RestFactor);
843  // DeltaRotator *= Factor;
844  AngularVelocity *= Factor;
845  }
846  Eigen::Vector3d InitialTheta = Uinv*RotatorToEigenVector(DeltaRotator);
847  Eigen::Vector3d InitialThetaVelocity = Uinv*RotatorToEigenVector(AngularVelocity);
848  SOLVER_LOG(Log, "Old angle for joint %s, %s", *Joint.JointName, *CurrRotator.ToString());
849  SOLVER_LOG(Log, "InitialTheta \n %s", *EigenToFString(InitialTheta));
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;
854  // 1st row
855  for (int i = 0; i < 3; i++)
856  {
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);
862 
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);
864  if(k == 0)
865  {
866  NewTheta(i) = theta0;
867  SOLVER_LOG(Log, "In Bone %s, K is 0 in component %d", *Joint.JointName, i);
868  }
869  double dtheta0 = InitialThetaVelocity(i); // compute
870  double deltatheta = 0;
871  double angularvelocity = 0;
872  float angularaccel = 0;
873  if (discriminant > 0)
874  {
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);
882  }
883  else if (discriminant == 0)
884  {
885  double r = -b/2.f;
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);
891  }
892  else
893  {
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;
898  deltatheta =
899  c1*std::exp(gamma*DeltaTime)*std::cos(mu*DeltaTime) +
900  c2*std::exp(gamma*DeltaTime)*std::sin(mu*DeltaTime) + f/k;
901  angularvelocity =
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);
905  }
906  angularaccel = f - b*angularvelocity - k*deltatheta;
907  if (!FMath::IsNaN(deltatheta))
908  {
909  NewTheta(i) = deltatheta;
910  if (angularvelocity > 1e-4)
911  {
912  NewThetaVelocity(i) = angularvelocity;
913  }
914  if (angularaccel > 1e-2)
915  {
916  NewThetaAccel(i) = angularaccel;
917  }
918  }
919  }
920  Eigen::Vector3d FinalNewTheta = U*NewTheta;
921  Eigen::Vector3d FinalNewThetaVelocity = U*NewThetaVelocity;
922  Eigen::Vector3d FinalNewThetaAccel = U*NewThetaAccel;
923 
924  auto NewPitch = FMath::RadiansToDegrees(FinalNewTheta(1));
925  auto NewYaw = FMath::RadiansToDegrees(FinalNewTheta(2));
926  auto NewRoll = FMath::RadiansToDegrees(FinalNewTheta(0));
927 
928  FRotator NewAngularVelocity = EigenVectorToRotator(FinalNewThetaVelocity);
929  FRotator NewAngularAccel = EigenVectorToRotator(FinalNewThetaAccel);
930 
931  FRotator NewAngle(
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",
936  *EigenToFString(FinalNewTheta), *EigenToFString(FinalNewThetaVelocity), *EigenToFString(FinalNewThetaAccel));
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());
940  Joint.AngularVelocity = NewAngularVelocity;
941  Joint.AngularAcceleration = NewAngularAccel;
942  }
943 }
944 
945 void USpringBasedVegetationComponent::TickComponent(
946  float DeltaTime,
947  enum ELevelTick TickType,
948  FActorComponentTickFunction * ThisTickFunction)
949 {
950  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::TickComponent);
951  Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
952 
953  float DeltaTimeFinal = DeltaTime;
954  if (DeltaTimeOverride > 0)
955  DeltaTimeFinal = DeltaTimeOverride;
956 
957  std::vector<FJointProperties> JointPropertiesList;
958  JointPropertiesList.resize(Skeleton.Joints.Num());
959  std::vector<FJointProperties> JointLocalPropertiesList;
960  JointLocalPropertiesList.resize(Skeleton.Joints.Num());
961 
962  ComputePerJointProperties(JointLocalPropertiesList, JointPropertiesList);
963 
964  ResolveContactsAndCollisions(JointLocalPropertiesList, JointPropertiesList);
965 
966  ComputeCompositeBodyContribution(JointLocalPropertiesList, JointPropertiesList);
967 
968  ComputeFictitiousForces(JointLocalPropertiesList, JointPropertiesList);
969 
970  SolveEquationOfMotion(JointPropertiesList, DeltaTimeFinal);
971 
972  UpdateSkeletalMesh();
973  UpdateGlobalTransform();
974  Skeleton.ClearExternalForces();
975 }
976 
977 void USpringBasedVegetationComponent::OnCollisionEvent(
978  UPrimitiveComponent* HitComponent,
979  AActor* OtherActor,
980  UPrimitiveComponent* OtherComponent,
981  FVector NormalImpulse,
982  const FHitResult& Hit)
983 {
984  // prevent self collision
985  if (OtherActor == GetOwner())
986  return;
987  // prevent collision with other tree actors
988  if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) != nullptr)
989  return;
990  ACarlaWheeledVehicle* Vehicle = nullptr;
991  if (DebugEnableAllCollisions)
992  {
993  if (!IsValid(OtherActor))
994  return;
995  }
996  else
997  {
998  Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
999  if (!IsValid(Vehicle))
1000  return;
1001  }
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);
1004 }
1005 
1006 void USpringBasedVegetationComponent::OnBeginOverlapEvent(
1007  UPrimitiveComponent* OverlapComponent,
1008  AActor* OtherActor,
1009  UPrimitiveComponent* OtherComponent,
1010  int32 OtherBodyIndex,
1011  bool bFromSweep,
1012  const FHitResult& SweepResult)
1013 {
1014  // prevent self collision
1015  if (OtherActor == GetOwner())
1016  return;
1017  // prevent collision with other tree actors
1018  if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) != nullptr)
1019  return;
1020  ACarlaWheeledVehicle* Vehicle = nullptr;
1021  if (DebugEnableAllCollisions)
1022  {
1023  if (!IsValid(OtherActor))
1024  return;
1025  }
1026  else
1027  {
1028  Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1029  if (!IsValid(Vehicle))
1030  return;
1031  }
1032 
1033  if (!OverlappingActors.Contains(OtherActor))
1034  {
1035  OverlappingActors.Add(OtherActor);
1036  }
1037  TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1038  if (!OverlappingCapsules.Contains(OverlapComponent))
1039  {
1040  OverlappingCapsules.Add(OverlapComponent);
1041  }
1042 }
1043 
1044 void USpringBasedVegetationComponent::OnEndOverlapEvent(
1045  UPrimitiveComponent* OverlapComponent,
1046  AActor* OtherActor,
1047  UPrimitiveComponent* OtherComponent,
1048  int32 OtherBodyIndex)
1049 {
1050  // prevent self collision
1051  if (OtherActor == GetOwner())
1052  return;
1053  // prevent collision with other tree actors
1054  if(OtherActor->GetComponentByClass(USpringBasedVegetationComponent::StaticClass()) != nullptr)
1055  return;
1056  ACarlaWheeledVehicle* Vehicle = nullptr;
1057  if (DebugEnableAllCollisions)
1058  {
1059  if (!IsValid(OtherActor))
1060  return;
1061  }
1062  else
1063  {
1064  Vehicle = Cast<ACarlaWheeledVehicle>(OtherActor);
1065  if (!IsValid(Vehicle))
1066  return;
1067  }
1068 
1069  if (!OverlappingActors.Contains(OtherActor))
1070  return;
1071  TArray<UPrimitiveComponent*>& OverlappingCapsules = OverlappingActors.FindOrAdd(OtherActor);
1072  if (OverlappingCapsules.Contains(OverlapComponent))
1073  {
1074  OverlappingCapsules.RemoveSingle(OverlapComponent);
1075  if (OverlappingCapsules.Num() == 0)
1076  {
1077  OverlappingActors.Remove(OtherActor);
1078  }
1079  }
1080 }
1081 
1082 void USpringBasedVegetationComponent::UpdateSkeletalMesh()
1083 {
1084  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateSkeletalMesh);
1085  // get the walker animation class
1086  auto *AnimInst = SkeletalMesh->GetAnimInstance();
1087  if (!AnimInst) return;
1088  UWalkerAnim *WalkerAnim = Cast<UWalkerAnim>(AnimInst);
1089  if (!WalkerAnim) return;
1090 
1091  // if pose is empty, then get a first version
1092  if (WalkerAnim->Snap.BoneNames.Num() == 0)
1093  {
1094  // get current pose
1095  SkeletalMesh->SnapshotPose(WalkerAnim->Snap);
1096  }
1097 
1098  TMap<FName, FTransform> JointsMap;
1099  for (int i=0; i<Skeleton.Joints.Num(); ++i)
1100  {
1101  FSkeletonJoint& Joint = Skeleton.Joints[i];
1102  FName JointName = FName(*Joint.JointName);
1103  JointsMap.Add(JointName, Joint.Transform);
1104  }
1105 
1106  // assign common bones
1107  for (int i=0; i<WalkerAnim->Snap.BoneNames.Num(); ++i)
1108  {
1109  FTransform *Trans = JointsMap.Find(WalkerAnim->Snap.BoneNames[i]);
1110  if (Trans)
1111  {
1112  WalkerAnim->Snap.LocalTransforms[i] = *Trans;
1113  }
1114  }
1115 }
1116 
1117 void USpringBasedVegetationComponent::UpdateGlobalTransform()
1118 {
1119  TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateGlobalTransform);
1120  FTransform InitialTransform = SkeletalMesh->GetOwner()->GetActorTransform();
1121  FSkeletonJoint& RootJoint = Skeleton.Joints[0];
1122  RootJoint.GlobalTransform = RootJoint.Transform * InitialTransform;
1123  RootJoint.GolbalInverseTransform = RootJoint.GlobalTransform.Inverse();
1124  for (int i = 1; i < Skeleton.RootToEndOrder.Num(); i++)
1125  {
1126  int Id = Skeleton.RootToEndOrder[i];
1127  FSkeletonJoint& Joint = Skeleton.Joints[Id];
1128  FSkeletonJoint& ParentJoint = Skeleton.Joints[Joint.ParentId];
1129  FTransform Transform = Joint.Transform * ParentJoint.GlobalTransform;
1130  Joint.GlobalTransform = Transform;
1131  Joint.GolbalInverseTransform = Transform.Inverse();
1132  }
1133 }
#define FICT_LOG(...)
FPoseSnapshot Snap
Definition: WalkerAnim.h:25
static Eigen::Matrix3d OuterProduct(const Eigen::Vector3d &V1, const Eigen::Vector3d &V2)
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
#define COLLISION_LOG(...)
#define OTHER_LOG(...)
TArray< FSkeletonBone > Bones
#define ACC_LOG(...)
#define SOLVER_LOG(...)
void AddForce(const FString &BoneName, const FVector &Force)
FRotator GetDeltaRotator(const FRotator &Rotator1, const FRotator &Rotator2)
static FVector ToUnrealVector(const Eigen::Vector3d &V1)
static Eigen::Vector3d ToEigenVector(const FVector &V1)
static T GetSign(T n)
static FString EigenToFString(T &t)
Base class for CARLA wheeled vehicles.
TArray< FSkeletonJoint > Joints
static Eigen::Vector3d RotatorToEigenVector(const FRotator &Rotator)
static FRotator EigenVectorToRotator(const Eigen::Vector3d &Vector)
geom::Transform Transform
Definition: rpc/Transform.h:16
static Eigen::Matrix3d ToEigenMatrix(const FMatrix &Matrix)