CARLA
WheeledVehicleMovementComponentNW.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 
8 #include "PhysicsPublic.h"
9 #include "PhysXPublic.h"
10 #include "PhysXVehicleManager.h"
11 #include "Components/PrimitiveComponent.h"
12 #include "Logging/MessageLog.h"
13 
14 UWheeledVehicleMovementComponentNW::UWheeledVehicleMovementComponentNW(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
15 {
16  // grab default values from physx
17  PxVehicleEngineData DefEngineData;
18  EngineSetup.MOI = DefEngineData.mMOI;
19  EngineSetup.MaxRPM = OmegaToRPM(DefEngineData.mMaxOmega);
20  EngineSetup.DampingRateFullThrottle = DefEngineData.mDampingRateFullThrottle;
21  EngineSetup.DampingRateZeroThrottleClutchEngaged = DefEngineData.mDampingRateZeroThrottleClutchEngaged;
22  EngineSetup.DampingRateZeroThrottleClutchDisengaged = DefEngineData.mDampingRateZeroThrottleClutchDisengaged;
23 
24  // Convert from PhysX curve to ours
25  FRichCurve* TorqueCurveData = EngineSetup.TorqueCurve.GetRichCurve();
26  for (PxU32 KeyIdx = 0; KeyIdx < DefEngineData.mTorqueCurve.getNbDataPairs(); ++KeyIdx)
27  {
28  float Input = DefEngineData.mTorqueCurve.getX(KeyIdx) * EngineSetup.MaxRPM;
29  float Output = DefEngineData.mTorqueCurve.getY(KeyIdx) * DefEngineData.mPeakTorque;
30  TorqueCurveData->AddKey(Input, Output);
31  }
32 
33  PxVehicleClutchData DefClutchData;
34  TransmissionSetup.ClutchStrength = DefClutchData.mStrength;
35 
36  PxVehicleGearsData DefGearSetup;
37  TransmissionSetup.GearSwitchTime = DefGearSetup.mSwitchTime;
38  TransmissionSetup.ReverseGearRatio = DefGearSetup.mRatios[PxVehicleGearsData::eREVERSE];
39  TransmissionSetup.FinalRatio = DefGearSetup.mFinalRatio;
40 
41  PxVehicleAutoBoxData DefAutoBoxSetup;
42  TransmissionSetup.NeutralGearUpRatio = DefAutoBoxSetup.mUpRatios[PxVehicleGearsData::eNEUTRAL];
43  TransmissionSetup.GearAutoBoxLatency = DefAutoBoxSetup.getLatency();
44  TransmissionSetup.bUseGearAutoBox = true;
45 
46  for (uint32 i = PxVehicleGearsData::eFIRST; i < DefGearSetup.mNbRatios; ++i)
47  {
48  FVehicleNWGearData GearData;
49  GearData.DownRatio = DefAutoBoxSetup.mDownRatios[i];
50  GearData.UpRatio = DefAutoBoxSetup.mUpRatios[i];
51  GearData.Ratio = DefGearSetup.mRatios[i];
52  TransmissionSetup.ForwardGears.Add(GearData);
53  }
54 
55  // Init steering speed curve
56  FRichCurve* SteeringCurveData = SteeringCurve.GetRichCurve();
57  SteeringCurveData->AddKey(0.0f, 1.0f);
58  SteeringCurveData->AddKey(20.0f, 0.9f);
59  SteeringCurveData->AddKey(60.0f, 0.8f);
60  SteeringCurveData->AddKey(120.0f, 0.7f);
61 
62  // Initialize WheelSetups array with 4 wheels, this can be modified via editor later
63  const int32 NbrWheels = 4;
64  WheelSetups.SetNum(NbrWheels);
65  DifferentialSetup.SetNum(NbrWheels);
66 
67  IdleBrakeInput = 10;
68 }
69 
70 #if WITH_EDITOR
71 void UWheeledVehicleMovementComponentNW::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
72 {
73  Super::PostEditChangeProperty(PropertyChangedEvent);
74  const FName PropertyName = PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None;
75 
76  if (PropertyName == TEXT("DownRatio"))
77  {
78  for (int32 GearIdx = 0; GearIdx < TransmissionSetup.ForwardGears.Num(); ++GearIdx)
79  {
80  FVehicleNWGearData& GearData = TransmissionSetup.ForwardGears[GearIdx];
81  GearData.DownRatio = FMath::Min(GearData.DownRatio, GearData.UpRatio);
82  }
83  }
84  else if (PropertyName == TEXT("UpRatio"))
85  {
86  for (int32 GearIdx = 0; GearIdx < TransmissionSetup.ForwardGears.Num(); ++GearIdx)
87  {
88  FVehicleNWGearData& GearData = TransmissionSetup.ForwardGears[GearIdx];
89  GearData.UpRatio = FMath::Max(GearData.DownRatio, GearData.UpRatio);
90  }
91  }
92  else if (PropertyName == TEXT("SteeringCurve"))
93  {
94  //make sure values are capped between 0 and 1
95  TArray<FRichCurveKey> SteerKeys = SteeringCurve.GetRichCurve()->GetCopyOfKeys();
96  for (int32 KeyIdx = 0; KeyIdx < SteerKeys.Num(); ++KeyIdx)
97  {
98  float NewValue = FMath::Clamp(SteerKeys[KeyIdx].Value, 0.0f, 1.0f);
99  SteeringCurve.GetRichCurve()->UpdateOrAddKey(SteerKeys[KeyIdx].Time, NewValue);
100  }
101  }
102 }
103 #endif
104 
105 static void GetVehicleDifferentialNWSetup(const TArray<FVehicleNWWheelDifferentialData>& Setup, PxVehicleDifferentialNWData& PxSetup)
106 {
107  for (int32 i = 0; i < Setup.Num(); ++i)
108  {
109  PxSetup.setDrivenWheel(i, Setup[i].bDriven);
110  }
111 }
112 
114 {
115  // Find max torque
116  float PeakTorque = 0.0f;
117  TArray<FRichCurveKey> TorqueKeys = TorqueCurve.GetRichCurveConst()->GetCopyOfKeys();
118  for (int32 KeyIdx = 0; KeyIdx < TorqueKeys.Num(); ++KeyIdx)
119  {
120  FRichCurveKey& Key = TorqueKeys[KeyIdx];
121  PeakTorque = FMath::Max(PeakTorque, Key.Value);
122  }
123  return PeakTorque;
124 }
125 
126 static void GetVehicleEngineSetup(const FVehicleNWEngineData& Setup, PxVehicleEngineData& PxSetup)
127 {
128  PxSetup.mMOI = M2ToCm2(Setup.MOI);
129  PxSetup.mMaxOmega = RPMToOmega(Setup.MaxRPM);
130  PxSetup.mDampingRateFullThrottle = M2ToCm2(Setup.DampingRateFullThrottle);
131  PxSetup.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(Setup.DampingRateZeroThrottleClutchEngaged);
132  PxSetup.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(Setup.DampingRateZeroThrottleClutchDisengaged);
133 
134  float PeakTorque = Setup.FindPeakTorque(); // In Nm
135  PxSetup.mPeakTorque = M2ToCm2(PeakTorque); // convert Nm to (kg cm^2/s^2)
136 
137  // Convert from our curve to PhysX
138  PxSetup.mTorqueCurve.clear();
139  TArray<FRichCurveKey> TorqueKeys = Setup.TorqueCurve.GetRichCurveConst()->GetCopyOfKeys();
140  int32 NumTorqueCurveKeys = FMath::Min<int32>(TorqueKeys.Num(), PxVehicleEngineData::eMAX_NB_ENGINE_TORQUE_CURVE_ENTRIES);
141  for (int32 KeyIdx = 0; KeyIdx < NumTorqueCurveKeys; ++KeyIdx)
142  {
143  FRichCurveKey& Key = TorqueKeys[KeyIdx];
144  PxSetup.mTorqueCurve.addPair(FMath::Clamp(Key.Time / Setup.MaxRPM, 0.0f, 1.0f), Key.Value / PeakTorque); // Normalize torque to 0-1 range
145  }
146 }
147 
148 static void GetVehicleGearSetup(const FVehicleNWTransmissionData& Setup, PxVehicleGearsData& PxSetup)
149 {
150  PxSetup.mSwitchTime = Setup.GearSwitchTime;
151  PxSetup.mRatios[PxVehicleGearsData::eREVERSE] = Setup.ReverseGearRatio;
152  for (int32 i = 0; i < Setup.ForwardGears.Num(); i++)
153  {
154  PxSetup.mRatios[i + PxVehicleGearsData::eFIRST] = Setup.ForwardGears[i].Ratio;
155  }
156  PxSetup.mFinalRatio = Setup.FinalRatio;
157  PxSetup.mNbRatios = Setup.ForwardGears.Num() + PxVehicleGearsData::eFIRST;
158 }
159 
160 static void GetVehicleAutoBoxSetup(const FVehicleNWTransmissionData& Setup, PxVehicleAutoBoxData& PxSetup)
161 {
162  for (int32 i = 0; i < Setup.ForwardGears.Num(); ++i)
163  {
164  const FVehicleNWGearData& GearData = Setup.ForwardGears[i];
165  PxSetup.mUpRatios[i + PxVehicleGearsData::eFIRST] = GearData.UpRatio;
166  PxSetup.mDownRatios[i + PxVehicleGearsData::eFIRST] = GearData.DownRatio;
167  }
168  PxSetup.mUpRatios[PxVehicleGearsData::eNEUTRAL] = Setup.NeutralGearUpRatio;
169  PxSetup.setLatency(Setup.GearAutoBoxLatency);
170 
171 }
172 
173 int32 UWheeledVehicleMovementComponentNW::GetCustomGearBoxNumForwardGears() const
174 {
175  return TransmissionSetup.ForwardGears.Num();
176 }
177 
178 void SetupDriveHelper(const UWheeledVehicleMovementComponentNW* VehicleData, const PxVehicleWheelsSimData* PWheelsSimData, PxVehicleDriveSimDataNW& DriveData)
179 {
180  PxVehicleDifferentialNWData DifferentialSetup;
181  GetVehicleDifferentialNWSetup(VehicleData->DifferentialSetup, DifferentialSetup);
182 
183  DriveData.setDiffData(DifferentialSetup);
184 
185  PxVehicleEngineData EngineSetup;
186  GetVehicleEngineSetup(VehicleData->EngineSetup, EngineSetup);
187  DriveData.setEngineData(EngineSetup);
188 
189  PxVehicleClutchData ClutchSetup;
190  ClutchSetup.mStrength = M2ToCm2(VehicleData->TransmissionSetup.ClutchStrength);
191  DriveData.setClutchData(ClutchSetup);
192 
193  PxVehicleGearsData GearSetup;
194  GetVehicleGearSetup(VehicleData->TransmissionSetup, GearSetup);
195  DriveData.setGearsData(GearSetup);
196 
197  PxVehicleAutoBoxData AutoBoxSetup;
198  GetVehicleAutoBoxSetup(VehicleData->TransmissionSetup, AutoBoxSetup);
199  DriveData.setAutoBoxData(AutoBoxSetup);
200 }
201 
202 void UWheeledVehicleMovementComponentNW::SetupVehicle()
203 {
204  if (!UpdatedPrimitive)
205  {
206  return;
207  }
208 
209  if (WheelSetups.Num() < 2)
210  {
211  PVehicle = nullptr;
212  PVehicleDrive = nullptr;
213  return;
214  }
215 
216  for (int32 WheelIdx = 0; WheelIdx < WheelSetups.Num(); ++WheelIdx)
217  {
218  const FWheelSetup& WheelSetup = WheelSetups[WheelIdx];
219  if (WheelSetup.BoneName == NAME_None)
220  {
221  return;
222  }
223  }
224 
225  // Setup the chassis and wheel shapes
226  SetupVehicleShapes();
227 
228  // Setup mass properties
229  SetupVehicleMass();
230 
231  // Setup the wheels
232  PxVehicleWheelsSimData* PWheelsSimData = PxVehicleWheelsSimData::allocate(WheelSetups.Num());
233  SetupWheels(PWheelsSimData);
234 
235  // Setup drive data
236  PxVehicleDriveSimDataNW DriveData;
237  SetupDriveHelper(this, PWheelsSimData, DriveData);
238 
239  // Create the vehicle
240  PxVehicleDriveNW* PVehicleDriveNW = PxVehicleDriveNW::allocate(WheelSetups.Num());
241  check(PVehicleDriveNW);
242 
243  FBodyInstance* TargetInstance = UpdatedPrimitive->GetBodyInstance();
244 
245  FPhysicsCommand::ExecuteWrite(TargetInstance->ActorHandle, [&](const FPhysicsActorHandle& Actor)
246  {
247  PxRigidActor* PActor = FPhysicsInterface::GetPxRigidActor_AssumesLocked(Actor);
248  if (!PActor)
249  {
250  return;
251  }
252 
253  if (PxRigidDynamic* PVehicleActor = PActor->is<PxRigidDynamic>())
254  {
255  PVehicleDriveNW->setup(GPhysXSDK, PVehicleActor, *PWheelsSimData, DriveData, 0);
256  PVehicleDriveNW->setToRestState();
257 
258  // cleanup
259  PWheelsSimData->free();
260  }
261  });
262 
263  PWheelsSimData = nullptr;
264 
265  // cache values
266  PVehicle = PVehicleDriveNW;
267  PVehicleDrive = PVehicleDriveNW;
268 
269  SetUseAutoGears(TransmissionSetup.bUseGearAutoBox);
270 
271 }
272 
273 void UWheeledVehicleMovementComponentNW::UpdateSimulation(float DeltaTime)
274 {
275  if (PVehicleDrive == nullptr)
276  return;
277 
278  FBodyInstance* TargetInstance = UpdatedPrimitive->GetBodyInstance();
279 
280  FPhysicsCommand::ExecuteWrite(TargetInstance->ActorHandle, [&](const FPhysicsActorHandle& Actor)
281  {
282  PxVehicleDriveNWRawInputData RawInputData;
283  RawInputData.setAnalogAccel(ThrottleInput);
284  RawInputData.setAnalogSteer(SteeringInput);
285  RawInputData.setAnalogBrake(BrakeInput);
286  RawInputData.setAnalogHandbrake(HandbrakeInput);
287 
288  if (!PVehicleDrive->mDriveDynData.getUseAutoGears())
289  {
290  RawInputData.setGearUp(bRawGearUpInput);
291  RawInputData.setGearDown(bRawGearDownInput);
292  }
293 
294  // Convert from our curve to PxFixedSizeLookupTable
295  PxFixedSizeLookupTable<8> SpeedSteerLookup;
296  TArray<FRichCurveKey> SteerKeys = SteeringCurve.GetRichCurve()->GetCopyOfKeys();
297  const int32 MaxSteeringSamples = FMath::Min(8, SteerKeys.Num());
298  for (int32 KeyIdx = 0; KeyIdx < MaxSteeringSamples; KeyIdx++)
299  {
300  FRichCurveKey& Key = SteerKeys[KeyIdx];
301  SpeedSteerLookup.addPair(KmHToCmS(Key.Time), FMath::Clamp(Key.Value, 0.0f, 1.0f));
302  }
303 
304  PxVehiclePadSmoothingData SmoothData = {
305  { ThrottleInputRate.RiseRate, BrakeInputRate.RiseRate, HandbrakeInputRate.RiseRate, SteeringInputRate.RiseRate, SteeringInputRate.RiseRate },
306  { ThrottleInputRate.FallRate, BrakeInputRate.FallRate, HandbrakeInputRate.FallRate, SteeringInputRate.FallRate, SteeringInputRate.FallRate }
307  };
308 
309  PxVehicleDriveNW* PVehicleDriveNW = (PxVehicleDriveNW*)PVehicleDrive;
310  PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(SmoothData, SpeedSteerLookup, RawInputData, DeltaTime, false, *PVehicleDriveNW);
311  });
312 }
313 
314 void UWheeledVehicleMovementComponentNW::UpdateEngineSetup(const FVehicleNWEngineData& NewEngineSetup)
315 {
316  if (PVehicleDrive)
317  {
318  PxVehicleEngineData EngineData;
319  GetVehicleEngineSetup(NewEngineSetup, EngineData);
320 
321  PxVehicleDriveNW* PVehicleDriveNW = (PxVehicleDriveNW*)PVehicleDrive;
322  PVehicleDriveNW->mDriveSimData.setEngineData(EngineData);
323  }
324 }
325 
326 void UWheeledVehicleMovementComponentNW::UpdateDifferentialSetup(const TArray<FVehicleNWWheelDifferentialData>& NewDifferentialSetup)
327 {
328  if (PVehicleDrive)
329  {
330  PxVehicleDifferentialNWData DifferentialData;
331  GetVehicleDifferentialNWSetup(NewDifferentialSetup, DifferentialData);
332 
333  PxVehicleDriveNW* PVehicleDriveNW = (PxVehicleDriveNW*)PVehicleDrive;
334  PVehicleDriveNW->mDriveSimData.setDiffData(DifferentialData);
335  }
336 }
337 
338 void UWheeledVehicleMovementComponentNW::UpdateTransmissionSetup(const FVehicleNWTransmissionData& NewTransmissionSetup)
339 {
340  if (PVehicleDrive)
341  {
342  PxVehicleGearsData GearData;
343  GetVehicleGearSetup(NewTransmissionSetup, GearData);
344 
345  PxVehicleAutoBoxData AutoBoxData;
346  GetVehicleAutoBoxSetup(NewTransmissionSetup, AutoBoxData);
347 
348  PxVehicleDriveNW* PVehicleDriveNW = (PxVehicleDriveNW*)PVehicleDrive;
349  PVehicleDriveNW->mDriveSimData.setGearsData(GearData);
350  PVehicleDriveNW->mDriveSimData.setAutoBoxData(AutoBoxData);
351  }
352 }
353 
354 void BackwardsConvertCm2ToM2NW(float& val, float defaultValue)
355 {
356  if (val != defaultValue)
357  {
358  val = Cm2ToM2(val);
359  }
360 }
361 
362 void UWheeledVehicleMovementComponentNW::Serialize(FArchive& Ar)
363 {
364  Super::Serialize(Ar);
365  if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_VEHICLES_UNIT_CHANGE)
366  {
367  PxVehicleEngineData DefEngineData;
368  const float DefaultRPM = OmegaToRPM(DefEngineData.mMaxOmega);
369 
370  // We need to convert from old units to new. This backwards compatible code fails in the rare case that they were using very strange values that are the new defaults in the correct units.
371  EngineSetup.MaxRPM = EngineSetup.MaxRPM != DefaultRPM ? OmegaToRPM(EngineSetup.MaxRPM) : DefaultRPM; //need to convert from rad/s to RPM
372  }
373 
374  if (Ar.IsLoading() && Ar.UE4Ver() < VER_UE4_VEHICLES_UNIT_CHANGE2)
375  {
376  PxVehicleEngineData DefEngineData;
377  PxVehicleClutchData DefClutchData;
378 
379  // We need to convert from old units to new. This backwards compatable code fails in the rare case that they were using very strange values that are the new defaults in the correct units.
380  BackwardsConvertCm2ToM2NW(EngineSetup.DampingRateFullThrottle, DefEngineData.mDampingRateFullThrottle);
381  BackwardsConvertCm2ToM2NW(EngineSetup.DampingRateZeroThrottleClutchDisengaged, DefEngineData.mDampingRateZeroThrottleClutchDisengaged);
382  BackwardsConvertCm2ToM2NW(EngineSetup.DampingRateZeroThrottleClutchEngaged, DefEngineData.mDampingRateZeroThrottleClutchEngaged);
383  BackwardsConvertCm2ToM2NW(EngineSetup.MOI, DefEngineData.mMOI);
384  BackwardsConvertCm2ToM2NW(TransmissionSetup.ClutchStrength, DefClutchData.mStrength);
385  }
386 }
387 
388 void UWheeledVehicleMovementComponentNW::ComputeConstants()
389 {
390  Super::ComputeConstants();
391  MaxEngineRPM = EngineSetup.MaxRPM;
392 }
393 
394 const void* UWheeledVehicleMovementComponentNW::GetTireData(physx::PxVehicleWheels* InWheels, UVehicleWheel* InWheel)
395 {
396  const void* realShaderData = &InWheels->mWheelsSimData.getTireData((PxU32)InWheel->WheelIndex);
397  return realShaderData;
398 }
399 
400 const int32 UWheeledVehicleMovementComponentNW::GetWheelShapeMapping(physx::PxVehicleWheels* InWheels, uint32 InWheel)
401 {
402  const physx::PxI32 ShapeIndex = InWheels->mWheelsSimData.getWheelShapeMapping((PxU32)InWheel);
403  return ShapeIndex;
404 }
405 
406 const physx::PxVehicleWheelData UWheeledVehicleMovementComponentNW::GetWheelData(physx::PxVehicleWheels* InWheels, uint32 InWheel)
407 {
408  const physx::PxVehicleWheelData WheelData = InWheels->mWheelsSimData.getWheelData((physx::PxU32)InWheel);
409  return WheelData;
410 }
void BackwardsConvertCm2ToM2NW(float &val, float defaultValue)
float Ratio
Determines the amount of torque multiplication.
float NeutralGearUpRatio
Value of engineRevs/maxEngineRevs that is high enough to increment gear.
float GearAutoBoxLatency
Minimum time it takes the automatic transmission to initiate a gear change (seconds) ...
float GearSwitchTime
Time it takes to switch gears (seconds)
float MOI
Moment of inertia of the engine around the axis of rotation (Kgm^2).
float UpRatio
Value of engineRevs/maxEngineRevs that is high enough to gear up.
static void GetVehicleAutoBoxSetup(const FVehicleNWTransmissionData &Setup, PxVehicleAutoBoxData &PxSetup)
void SetupDriveHelper(const UWheeledVehicleMovementComponentNW *VehicleData, const PxVehicleWheelsSimData *PWheelsSimData, PxVehicleDriveSimDataNW &DriveData)
float DampingRateZeroThrottleClutchEngaged
Damping rate of engine in at zero throttle when the clutch is engaged (Kgm^2/s)
float MaxRPM
Maximum revolutions per minute of the engine.
carla::SharedPtr< cc::Actor > Actor
static void GetVehicleDifferentialNWSetup(const TArray< FVehicleNWWheelDifferentialData > &Setup, PxVehicleDifferentialNWData &PxSetup)
static void GetVehicleEngineSetup(const FVehicleNWEngineData &Setup, PxVehicleEngineData &PxSetup)
float DampingRateZeroThrottleClutchDisengaged
Damping rate of engine in at zero throttle when the clutch is disengaged (in neutral gear) (Kgm^2/s) ...
TArray< FVehicleNWGearData > ForwardGears
Forward gear ratios (up to 30)
float FindPeakTorque() const
Find the peak torque produced by the TorqueCurve.
float DampingRateFullThrottle
Damping rate of engine when full throttle is applied (Kgm^2/s)
float DownRatio
Value of engineRevs/maxEngineRevs that is low enough to gear down.
static void GetVehicleGearSetup(const FVehicleNWTransmissionData &Setup, PxVehicleGearsData &PxSetup)
FRuntimeFloatCurve TorqueCurve
Torque (Nm) at a given RPM.
float FinalRatio
The final gear ratio multiplies the transmission gear ratios.