959 lines
31 KiB
C++
959 lines
31 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Physics/PhysicsInterfaceCore.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "ChaosVehicleMovementComponent.h"
|
|
#include "Curves/CurveFloat.h"
|
|
#include "VehicleUtility.h"
|
|
#include "Chaos/PBDSuspensionConstraints.h"
|
|
#include "PhysicsProxy/SingleParticlePhysicsProxyFwd.h"
|
|
#include "ChaosWheeledVehicleMovementComponent.generated.h"
|
|
|
|
#if VEHICLE_DEBUGGING_ENABLED
|
|
UE_DISABLE_OPTIMIZATION
|
|
#endif
|
|
|
|
struct FWheeledVehicleDebugParams
|
|
{
|
|
bool ShowWheelCollisionNormal = false;
|
|
bool ShowSuspensionRaycasts = false;
|
|
bool ShowSuspensionLimits = false;
|
|
bool ShowWheelForces = false;
|
|
bool ShowSuspensionForces = false;
|
|
bool ShowBatchQueryExtents = false;
|
|
bool ShowRaycastComponent = false;
|
|
bool ShowRaycastMaterial = false;
|
|
bool CacheSuspensionOffset = true;
|
|
int TraceTypeOverride = 0;
|
|
|
|
bool DisableSuspensionForces = false;
|
|
bool DisableFrictionForces = false;
|
|
bool DisableRollbarForces = false;
|
|
bool DisableConstraintSuspension = false;
|
|
|
|
float ThrottleOverride = 0.f;
|
|
float SteeringOverride = 0.f;
|
|
|
|
bool ResetPerformanceMeasurements = false;
|
|
|
|
float OverlapTestExpansionXY = 100.f;
|
|
float OverlapTestExpansionZ = 50.f;
|
|
};
|
|
|
|
/**
|
|
* There is too much information for one screen full of debug data, so sub-pages of information are available
|
|
* Advance through pages using p.Vehicles.NextDebugPage | p.Vehicles.PrevDebugPage which can be hooked
|
|
* up to the keyboard or a controller in blueprint using execCommand
|
|
*/
|
|
enum EDebugPages : uint8
|
|
{
|
|
BasicPage = 0,
|
|
PerformancePage,
|
|
SteeringPage,
|
|
FrictionPage,
|
|
SuspensionPage,
|
|
TransmissionPage,
|
|
|
|
MaxDebugPages // keep as last value
|
|
};
|
|
|
|
UENUM()
|
|
enum class EVehicleDifferential : uint8
|
|
{
|
|
Undefined,
|
|
AllWheelDrive,
|
|
FrontWheelDrive,
|
|
RearWheelDrive,
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Structure containing information about the status of a single wheel of the vehicle.
|
|
*/
|
|
USTRUCT(BlueprintType)
|
|
struct CHAOSVEHICLES_API FWheelStatus
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
/** This wheel is in contact with the ground */
|
|
UPROPERTY()
|
|
bool bInContact;
|
|
|
|
/** Wheel contact point */
|
|
UPROPERTY()
|
|
FVector ContactPoint;
|
|
|
|
/** Wheel contact location */
|
|
UPROPERTY()
|
|
FVector HitLocation;
|
|
|
|
/** Material that wheel is in contact with */
|
|
UPROPERTY()
|
|
TWeakObjectPtr<class UPhysicalMaterial> PhysMaterial;
|
|
|
|
/** Normalized suspension length at this wheel */
|
|
UPROPERTY()
|
|
float NormalizedSuspensionLength;
|
|
|
|
/** Spring Force that is occurring at wheel suspension */
|
|
UPROPERTY()
|
|
float SpringForce;
|
|
|
|
/** Slip angle at the wheel - difference between wheel local direction and velocity at wheel */
|
|
UPROPERTY()
|
|
float SlipAngle;
|
|
|
|
/** Is the wheel slipping */
|
|
UPROPERTY()
|
|
bool bIsSlipping;
|
|
|
|
/** Magnitude of slippage of wheel, difference between wheel speed and ground speed */
|
|
UPROPERTY()
|
|
float SlipMagnitude;
|
|
|
|
|
|
/** Is the wheel skidding */
|
|
UPROPERTY()
|
|
bool bIsSkidding;
|
|
|
|
/** Magnitude of skid */
|
|
UPROPERTY()
|
|
float SkidMagnitude;
|
|
|
|
/** Direction of skid, i.e. normalized direction */
|
|
UPROPERTY()
|
|
FVector SkidNormal;
|
|
|
|
/** Drive torque currently applied at wheel */
|
|
UPROPERTY()
|
|
float DriveTorque;
|
|
|
|
/** Brake torque currently applied at wheel */
|
|
UPROPERTY()
|
|
float BrakeTorque;
|
|
|
|
/** Is the ABS currently engaged - useful for audio Q's */
|
|
UPROPERTY()
|
|
bool bABSActivated;
|
|
|
|
FWheelStatus()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
explicit FWheelStatus(EForceInit InInit)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
explicit FWheelStatus(ENoInit NoInit)
|
|
{
|
|
bIsValid = false;
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
SlipAngle = 0.0f;
|
|
bInContact = false;
|
|
bIsSlipping = false;
|
|
bIsSkidding = false;
|
|
SlipMagnitude = 0.f;
|
|
SkidMagnitude = 0.f;
|
|
NormalizedSuspensionLength = 1.f;
|
|
SpringForce = 0.f;
|
|
SkidNormal = FVector::ZeroVector;
|
|
ContactPoint = FVector::ZeroVector;
|
|
HitLocation = FVector::ZeroVector;
|
|
bIsValid = false;
|
|
bABSActivated = false;
|
|
DriveTorque = 0.0f;
|
|
BrakeTorque = 0.0f;
|
|
}
|
|
|
|
FString ToString() const;
|
|
|
|
bool bIsValid;
|
|
};
|
|
|
|
USTRUCT()
|
|
struct CHAOSVEHICLES_API FVehicleDifferentialConfig
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FVehicleDifferentialConfig()
|
|
{
|
|
InitDefaults();
|
|
}
|
|
|
|
/** Type of differential */
|
|
UPROPERTY(EditAnywhere, Category=Setup)
|
|
EVehicleDifferential DifferentialType;
|
|
|
|
/** Ratio of torque split between front and rear (<0.5 means more to front, >0.5 means more to rear, works only with 4W type) */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.0", UIMin = "0.0", ClampMax = "1.0", UIMax = "1.0"))
|
|
float FrontRearSplit;
|
|
|
|
const Chaos::FSimpleDifferentialConfig& GetPhysicsDifferentialConfig()
|
|
{
|
|
FillDifferentialSetup();
|
|
return PDifferentialConfig;
|
|
}
|
|
|
|
void InitDefaults()
|
|
{
|
|
DifferentialType = EVehicleDifferential::RearWheelDrive;
|
|
FrontRearSplit = 0.5f;
|
|
}
|
|
|
|
void FillDifferentialSetup()
|
|
{
|
|
PDifferentialConfig.DifferentialType = static_cast<Chaos::EDifferentialType>(this->DifferentialType);
|
|
PDifferentialConfig.FrontRearSplit = this->FrontRearSplit;
|
|
}
|
|
|
|
Chaos::FSimpleDifferentialConfig PDifferentialConfig;
|
|
|
|
};
|
|
|
|
USTRUCT()
|
|
struct CHAOSVEHICLES_API FVehicleEngineConfig
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FVehicleEngineConfig()
|
|
{
|
|
InitDefaults();
|
|
}
|
|
|
|
/** Torque [Normalized 0..1] for a given RPM */
|
|
UPROPERTY(EditAnywhere, Category = Setup)
|
|
FRuntimeFloatCurve TorqueCurve;
|
|
|
|
/** Max Engine Torque (Nm) is multiplied by TorqueCurve */
|
|
UPROPERTY(EditAnywhere, Category = Setup)
|
|
float MaxTorque;
|
|
|
|
/** Maximum revolutions per minute of the engine */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.01", UIMin = "0.01"))
|
|
float MaxRPM;
|
|
|
|
/** Idle RMP of engine then in neutral/stationary */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.01", UIMin = "0.01"))
|
|
float EngineIdleRPM;
|
|
|
|
/** Braking effect from engine, when throttle released */
|
|
UPROPERTY(EditAnywhere, Category = Setup)
|
|
float EngineBrakeEffect;
|
|
|
|
/** Affects how fast the engine RPM speed up */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.01", UIMin = "0.01"))
|
|
float EngineRevUpMOI;
|
|
|
|
/** Affects how fast the engine RPM slows down */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.01", UIMin = "0.01"))
|
|
float EngineRevDownRate;
|
|
|
|
const Chaos::FSimpleEngineConfig& GetPhysicsEngineConfig()
|
|
{
|
|
FillEngineSetup();
|
|
return PEngineConfig;
|
|
}
|
|
|
|
void InitDefaults()
|
|
{
|
|
MaxTorque = 300.0f;
|
|
MaxRPM = 4500.0f;
|
|
EngineIdleRPM = 1200.0f;
|
|
EngineBrakeEffect = 0.05f;
|
|
EngineRevUpMOI = 5.0f;
|
|
EngineRevDownRate = 600.0f;
|
|
}
|
|
|
|
float GetTorqueFromRPM(float EngineRPM)
|
|
{
|
|
// The source curve does not need to be normalized, however we are normalizing it when it is passed on,
|
|
// since it's the MaxRPM and MaxTorque values that determine the range of RPM and Torque
|
|
float MinVal = 0.f, MaxVal = 0.f;
|
|
this->TorqueCurve.GetRichCurveConst()->GetValueRange(MinVal, MaxVal);
|
|
return TorqueCurve.GetRichCurve()->Eval(EngineRPM) / MaxVal * MaxTorque;
|
|
}
|
|
private:
|
|
|
|
void FillEngineSetup()
|
|
{
|
|
// The source curve does not need to be normalized, however we are normalizing it when it is passed on,
|
|
// since it's the MaxRPM and MaxTorque values that determine the range of RPM and Torque
|
|
PEngineConfig.TorqueCurve.Empty();
|
|
float NumSamples = 20;
|
|
for (float X = 0; X <= this->MaxRPM; X+= (this->MaxRPM / NumSamples))
|
|
{
|
|
float MinVal = 0.f, MaxVal = 0.f;
|
|
this->TorqueCurve.GetRichCurveConst()->GetValueRange(MinVal, MaxVal);
|
|
float Y = this->TorqueCurve.GetRichCurveConst()->Eval(X) / MaxVal;
|
|
PEngineConfig.TorqueCurve.AddNormalized(Y);
|
|
}
|
|
PEngineConfig.MaxTorque = this->MaxTorque;
|
|
PEngineConfig.MaxRPM = this->MaxRPM;
|
|
PEngineConfig.EngineIdleRPM = this->EngineIdleRPM;
|
|
PEngineConfig.EngineBrakeEffect = this->EngineBrakeEffect;
|
|
PEngineConfig.EngineRevUpMOI = this->EngineRevUpMOI;
|
|
PEngineConfig.EngineRevDownRate = this->EngineRevDownRate;
|
|
}
|
|
|
|
Chaos::FSimpleEngineConfig PEngineConfig;
|
|
|
|
};
|
|
|
|
USTRUCT()
|
|
struct CHAOSVEHICLES_API FVehicleTransmissionConfig
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FVehicleTransmissionConfig()
|
|
{
|
|
InitDefaults();
|
|
}
|
|
|
|
friend class UChaosVehicleWheel;
|
|
|
|
/** Whether to use automatic transmission */
|
|
UPROPERTY(EditAnywhere, Category = VehicleSetup, meta=(DisplayName = "Automatic Transmission"))
|
|
bool bUseAutomaticGears;
|
|
|
|
UPROPERTY(EditAnywhere, Category = VehicleSetup, meta = (DisplayName = "Automatic Reverse"))
|
|
bool bUseAutoReverse;
|
|
|
|
/** The final gear ratio multiplies the transmission gear ratios.*/
|
|
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Setup)
|
|
float FinalRatio;
|
|
|
|
/** Forward gear ratios */
|
|
UPROPERTY(EditAnywhere, Category = Setup, AdvancedDisplay)
|
|
TArray<float> ForwardGearRatios;
|
|
|
|
/** Reverse gear ratio(s) */
|
|
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Setup)
|
|
TArray<float> ReverseGearRatios;
|
|
|
|
/** Engine Revs at which gear up change ocurrs */
|
|
UPROPERTY(EditAnywhere, meta = (ClampMin = "0.0", UIMin = "0.0", ClampMax = "50000.0", UIMax = "50000.0"), Category = Setup)
|
|
float ChangeUpRPM;
|
|
|
|
/** Engine Revs at which gear down change ocurrs */
|
|
UPROPERTY(EditAnywhere, meta = (ClampMin = "0.0", UIMin = "0.0", ClampMax = "50000.0", UIMax = "50000.0"), Category = Setup)
|
|
float ChangeDownRPM;
|
|
|
|
/** Time it takes to switch gears (seconds) */
|
|
UPROPERTY(EditAnywhere, Category = Setup, meta = (ClampMin = "0.0", UIMin = "0.0"))
|
|
float GearChangeTime;
|
|
|
|
/** Mechanical frictional losses mean transmission might operate at 0.94 (94% efficiency) */
|
|
UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Setup)
|
|
float TransmissionEfficiency;
|
|
|
|
const Chaos::FSimpleTransmissionConfig& GetPhysicsTransmissionConfig()
|
|
{
|
|
FillTransmissionSetup();
|
|
return PTransmissionConfig;
|
|
}
|
|
|
|
void InitDefaults()
|
|
{
|
|
bUseAutomaticGears = true;
|
|
bUseAutoReverse = true;
|
|
FinalRatio = 3.08f;
|
|
|
|
ForwardGearRatios.Add(2.85f);
|
|
ForwardGearRatios.Add(2.02f);
|
|
ForwardGearRatios.Add(1.35f);
|
|
ForwardGearRatios.Add(1.0f);
|
|
|
|
ReverseGearRatios.Add(2.86f);
|
|
|
|
ChangeUpRPM = 4500.0f;
|
|
ChangeDownRPM = 2000.0f;
|
|
GearChangeTime = 0.4f;
|
|
|
|
TransmissionEfficiency = 0.9f;
|
|
}
|
|
|
|
float GetGearRatio(int32 InGear)
|
|
{
|
|
if (InGear > 0) // a forwards gear
|
|
{
|
|
return ForwardGearRatios[InGear - 1] * FinalRatio;
|
|
}
|
|
else if (InGear < 0) // a reverse gear
|
|
{
|
|
return -ReverseGearRatios[FMath::Abs(InGear) - 1] * FinalRatio;
|
|
}
|
|
else
|
|
{
|
|
return 0.f; // neutral has no ratio
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
void FillTransmissionSetup()
|
|
{
|
|
PTransmissionConfig.TransmissionType = this->bUseAutomaticGears ? Chaos::ETransmissionType::Automatic : Chaos::ETransmissionType::Manual;
|
|
PTransmissionConfig.AutoReverse = this->bUseAutoReverse;
|
|
PTransmissionConfig.ChangeUpRPM = this->ChangeUpRPM;
|
|
PTransmissionConfig.ChangeDownRPM = this->ChangeDownRPM;
|
|
PTransmissionConfig.GearChangeTime = this->GearChangeTime;
|
|
PTransmissionConfig.FinalDriveRatio = this->FinalRatio;
|
|
PTransmissionConfig.ForwardRatios.Reset();
|
|
PTransmissionConfig.TransmissionEfficiency = this->TransmissionEfficiency;
|
|
for (float Ratio : this->ForwardGearRatios)
|
|
{
|
|
PTransmissionConfig.ForwardRatios.Add(Ratio);
|
|
}
|
|
|
|
PTransmissionConfig.ReverseRatios.Reset();
|
|
for (float Ratio : this->ReverseGearRatios)
|
|
{
|
|
PTransmissionConfig.ReverseRatios.Add(Ratio);
|
|
}
|
|
}
|
|
|
|
Chaos::FSimpleTransmissionConfig PTransmissionConfig;
|
|
|
|
};
|
|
|
|
/** Single angle : both wheels steer by the same amount
|
|
* AngleRatio : outer wheels on corner steer less than the inner ones by set ratio
|
|
* Ackermann : Ackermann steering principle is applied */
|
|
UENUM()
|
|
enum class ESteeringType : uint8
|
|
{
|
|
SingleAngle,
|
|
AngleRatio,
|
|
Ackermann,
|
|
};
|
|
|
|
|
|
USTRUCT()
|
|
struct CHAOSVEHICLES_API FVehicleSteeringConfig
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
FVehicleSteeringConfig()
|
|
{
|
|
InitDefaults();
|
|
}
|
|
|
|
/** Single angle : both wheels steer by the same amount
|
|
* AngleRatio : outer wheels on corner steer less than the inner ones by set ratio
|
|
* Ackermann : Ackermann steering principle is applied */
|
|
UPROPERTY(EditAnywhere, Category = SteeringSetup)
|
|
ESteeringType SteeringType;
|
|
|
|
/** Only applies when AngleRatio is selected */
|
|
UPROPERTY(EditAnywhere, Category = SteeringSetup)
|
|
float AngleRatio;
|
|
|
|
/** Maximum steering versus forward speed (MPH) */
|
|
UPROPERTY(EditAnywhere, Category = SteeringSetup)
|
|
FRuntimeFloatCurve SteeringCurve;
|
|
|
|
|
|
const Chaos::FSimpleSteeringConfig& GetPhysicsSteeringConfig(FVector2D WheelTrackDimensions)
|
|
{
|
|
FillSteeringSetup(WheelTrackDimensions);
|
|
return PSteeringConfig;
|
|
}
|
|
|
|
void InitDefaults()
|
|
{
|
|
SteeringType = ESteeringType::AngleRatio;
|
|
AngleRatio = 0.7f;
|
|
|
|
// Init steering speed curve
|
|
FRichCurve* SteeringCurveData = SteeringCurve.GetRichCurve();
|
|
SteeringCurveData->AddKey(0.f, 1.f);
|
|
SteeringCurveData->AddKey(20.f, 0.8f);
|
|
SteeringCurveData->AddKey(60.f, 0.4f);
|
|
SteeringCurveData->AddKey(120.f, 0.3f);
|
|
}
|
|
|
|
private:
|
|
|
|
void FillSteeringSetup(FVector2D WheelTrackDimensions)
|
|
{
|
|
|
|
PSteeringConfig.SteeringType = (Chaos::ESteerType)this->SteeringType;
|
|
PSteeringConfig.AngleRatio = AngleRatio;
|
|
|
|
float MinValue = 0.f, MaxValue = 1.f;
|
|
this->SteeringCurve.GetRichCurveConst()->GetValueRange(MinValue, MaxValue);
|
|
float MaxX = this->SteeringCurve.GetRichCurveConst()->GetLastKey().Time;
|
|
PSteeringConfig.SpeedVsSteeringCurve.Empty();
|
|
float NumSamples = 20;
|
|
for (float X = 0; X <= MaxX; X += (MaxX / NumSamples))
|
|
{
|
|
float Y = this->SteeringCurve.GetRichCurveConst()->Eval(X) / MaxValue;
|
|
PSteeringConfig.SpeedVsSteeringCurve.Add(FVector2D(X, Y));
|
|
}
|
|
|
|
PSteeringConfig.TrackWidth = WheelTrackDimensions.Y;
|
|
PSteeringConfig.WheelBase = WheelTrackDimensions.X;
|
|
}
|
|
|
|
Chaos::FSimpleSteeringConfig PSteeringConfig;
|
|
|
|
};
|
|
|
|
|
|
USTRUCT()
|
|
struct CHAOSVEHICLES_API FChaosWheelSetup
|
|
{
|
|
GENERATED_USTRUCT_BODY()
|
|
|
|
// The wheel class to use
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
TSubclassOf<UChaosVehicleWheel> WheelClass;
|
|
|
|
// Bone name on mesh to create wheel at
|
|
//UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
//FName SteeringBoneName;
|
|
|
|
// Bone name on mesh to create wheel at
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
FName BoneName;
|
|
|
|
// Additional offset to give the wheels for this axle.
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
FVector AdditionalOffset;
|
|
|
|
FChaosWheelSetup();
|
|
};
|
|
|
|
/** Commonly used Wheel state - evaluated once used wherever required for that frame */
|
|
struct CHAOSVEHICLES_API FWheelState
|
|
{
|
|
FWheelState();
|
|
~FWheelState();
|
|
|
|
FWheelState(const FWheelState& Other);
|
|
FWheelState& operator=(const FWheelState& Other);
|
|
|
|
void Init(int NumWheels);
|
|
|
|
/** Commonly used Wheel state - evaluated once used wherever required for that frame */
|
|
void CaptureState(int WheelIdx, const FVector& WheelOffset, const FBodyInstance* TargetInstance);
|
|
void CaptureState(int WheelIdx, const FVector& WheelOffset, const Chaos::FRigidBodyHandle_Internal* Handle);
|
|
void CaptureState(int WheelIdx, const FVector& WheelOffset, const Chaos::FRigidBodyHandle_Internal* VehicleHandle, const FVector& ContactPoint, const Chaos::FRigidBodyHandle_Internal* SurfaceHandle);
|
|
static FVector GetVelocityAtPoint(const Chaos::FRigidBodyHandle_Internal* Rigid, const FVector& InPoint);
|
|
|
|
TArray<FVector> WheelLocalLocation; /** Current Location Of Wheels In Local Coordinates */
|
|
TArray<FVector> WheelWorldLocation; /** Current Location Of Wheels In World Coordinates */
|
|
TArray<FVector> WorldWheelVelocity; /** Current velocity at wheel location In World Coordinates - combined linear and angular */
|
|
TArray<FVector> LocalWheelVelocity; /** Local velocity of Wheel */
|
|
TArray<Chaos::FSuspensionTrace> Trace;
|
|
TArray<FHitResult> TraceResult;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class CHAOSVEHICLES_API UChaosWheeledVehicleSimulation : public UChaosVehicleSimulation
|
|
{
|
|
public:
|
|
|
|
UChaosWheeledVehicleSimulation();
|
|
|
|
virtual ~UChaosWheeledVehicleSimulation();
|
|
|
|
virtual void Init(TUniquePtr<Chaos::FSimpleWheeledVehicle>& PVehicleIn) override
|
|
{
|
|
UChaosVehicleSimulation::Init(PVehicleIn);
|
|
|
|
WheelState.Init(PVehicle->Wheels.Num());
|
|
}
|
|
|
|
virtual void UpdateConstraintHandles(TArray<FPhysicsConstraintHandle>& ConstraintHandlesIn) override;
|
|
|
|
virtual void TickVehicle(UWorld* WorldIn, float DeltaTime, const FChaosVehicleAsyncInput& InputData, FChaosVehicleAsyncOutput& OutputData, Chaos::FRigidBodyHandle_Internal* Handle) override;
|
|
|
|
/** Advance the vehicle simulation */
|
|
virtual void UpdateSimulation(float DeltaTime, const FChaosVehicleAsyncInput& InputData, Chaos::FRigidBodyHandle_Internal* Handle) override;
|
|
|
|
/** Update the vehicle state */
|
|
virtual void UpdateState(float DeltaTime, const FChaosVehicleAsyncInput& InputData, Chaos::FRigidBodyHandle_Internal* Handle) override;
|
|
|
|
virtual void FillOutputState(FChaosVehicleAsyncOutput& Output) override;
|
|
|
|
/** Are enough vehicle systems specified such that physics vehicle simulation is possible */
|
|
virtual bool CanSimulate() const override;
|
|
|
|
/** Pass control Input to the vehicle systems */
|
|
virtual void ApplyInput(const FControlInputs& ControlInputs, float DeltaTime) override;
|
|
|
|
/** Perform suspension ray/shape traces */
|
|
virtual void PerformSuspensionTraces(const TArray<Chaos::FSuspensionTrace>& SuspensionTrace, FCollisionQueryParams& TraceParams, FCollisionResponseContainer& CollisionResponse, TArray<FWheelTraceParams>& WheelTraceParams);
|
|
|
|
|
|
/** Update the engine/transmission simulation */
|
|
virtual void ProcessMechanicalSimulation(float DeltaTime);
|
|
|
|
/** Process steering mechanism */
|
|
virtual void ProcessSteering(const FControlInputs& ControlInputs);
|
|
|
|
/** calculate and apply lateral and longitudinal friction forces from wheels */
|
|
virtual void ApplyWheelFrictionForces(float DeltaTime);
|
|
|
|
/** calculate and apply chassis suspension forces */
|
|
virtual void ApplySuspensionForces(float DeltaTime, TArray<FWheelTraceParams>& WheelTraceParams);
|
|
|
|
bool IsWheelSpinning() const;
|
|
bool ContainsTraces(const FBox& Box, const TArray<struct Chaos::FSuspensionTrace>& SuspensionTrace);
|
|
|
|
|
|
/** Draw 3D debug lines and things along side the 3D model */
|
|
virtual void DrawDebug3D() override;
|
|
|
|
FWheelState WheelState; /** Cached state that holds wheel data for this frame */
|
|
|
|
TArray<FPhysicsConstraintHandle> ConstraintHandles;
|
|
|
|
// cache trace overlap query
|
|
TArray<FOverlapResult> OverlapResults;
|
|
bool bOverlapHit;
|
|
FBox QueryBox;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
UCLASS(ClassGroup = (Physics), meta = (BlueprintSpawnableComponent), hidecategories = (PlanarMovement, "Components|Movement|Planar", Activation, "Components|Activation"))
|
|
class CHAOSVEHICLES_API UChaosWheeledVehicleMovementComponent : public UChaosVehicleMovementComponent
|
|
{
|
|
GENERATED_UCLASS_BODY()
|
|
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
bool bSuspensionEnabled;
|
|
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
bool bWheelFrictionEnabled;
|
|
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
bool bLegacyWheelFrictionPosition;
|
|
|
|
/** Wheels to create */
|
|
UPROPERTY(EditAnywhere, Category = WheelSetup)
|
|
TArray<FChaosWheelSetup> WheelSetups;
|
|
|
|
UPROPERTY(EditAnywhere, Category = Custom)
|
|
struct FCollisionResponseContainer WheelTraceCollisionResponses;
|
|
|
|
UPROPERTY(EditAnywhere, Category = MechanicalSetup)
|
|
bool bMechanicalSimEnabled;
|
|
|
|
/** Engine */
|
|
UPROPERTY(EditAnywhere, Category = MechanicalSetup, meta = (EditCondition = "bMechanicalSimEnabled"))
|
|
FVehicleEngineConfig EngineSetup;
|
|
|
|
/** Differential */
|
|
UPROPERTY(EditAnywhere, Category = MechanicalSetup, meta = (EditCondition = "bMechanicalSimEnabled"))
|
|
FVehicleDifferentialConfig DifferentialSetup;
|
|
|
|
/** Transmission data */
|
|
UPROPERTY(EditAnywhere, Category = MechanicalSetup, meta = (EditCondition = "bMechanicalSimEnabled"))
|
|
FVehicleTransmissionConfig TransmissionSetup;
|
|
|
|
/** Transmission data */
|
|
UPROPERTY(EditAnywhere, Category = SteeringSetup)
|
|
FVehicleSteeringConfig SteeringSetup;
|
|
|
|
// Our instanced wheels
|
|
UPROPERTY(transient, duplicatetransient, BlueprintReadOnly, Category = Vehicle)
|
|
TArray<TObjectPtr<class UChaosVehicleWheel>> Wheels;
|
|
|
|
/** Get current engine's rotation speed */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
float GetEngineRotationSpeed() const;
|
|
|
|
/** Get current engine's max rotation speed */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
float GetEngineMaxRotationSpeed() const;
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
int GetNumWheels() const
|
|
{
|
|
return WheelStatus.Num();
|
|
}
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static void BreakWheelStatus(const struct FWheelStatus& Status, bool& bInContact, FVector& ContactPoint, UPhysicalMaterial*& PhysMaterial
|
|
, float& NormalizedSuspensionLength, float& SpringForce, float& SlipAngle, bool& bIsSlipping, float& SlipMagnitude, bool& bIsSkidding, float& SkidMagnitude, FVector& SkidNormal, float& DriveTorque, float& BrakeTorque, bool& bABSActivated);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static FWheelStatus MakeWheelStatus(bool bInContact, FVector& ContactPoint, UPhysicalMaterial* PhysMaterial
|
|
, float NormalizedSuspensionLength, float SpringForce, float SlipAngle, bool bIsSlipping, float SlipMagnitude, bool bIsSkidding, float SkidMagnitude, FVector& SkidNormal, float DriveTorque, float BrakeTorque, bool bABSActivated);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static void BreakWheeledSnapshot(const struct FWheeledSnaphotData& Snapshot, FTransform& Transform, FVector& LinearVelocity
|
|
, FVector& AngularVelocity, int& SelectedGear, float& EngineRPM, TArray<FWheelSnapshot>& WheelSnapshots);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static FWheeledSnaphotData MakeWheeledSnapshot(FTransform Transform, FVector LinearVelocity, FVector AngularVelocity
|
|
, int SelectedGear, float EngineRPM, const TArray<FWheelSnapshot>& WheelSnapshots);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static void BreakWheelSnapshot(const struct FWheelSnapshot& Snapshot, float& SuspensionOffset
|
|
, float& WheelRotationAngle, float& SteeringAngle, float& WheelRadius, float& WheelAngularVelocity);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Vehicles")
|
|
static FWheelSnapshot MakeWheelSnapshot(float SuspensionOffset, float WheelRotationAngle
|
|
, float SteeringAngle, float WheelRadius, float WheelAngularVelocity);
|
|
|
|
/** Get a wheels current simulation state */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
const FWheelStatus& GetWheelState(int WheelIndex) const
|
|
{
|
|
return WheelStatus[WheelIndex];
|
|
}
|
|
|
|
virtual float GetSuspensionOffset(int WheelIndex) override;
|
|
UPhysicalMaterial* GetPhysMaterial(int WheelIndex);
|
|
|
|
/** Set all channels to the specified response - for wheel raycasts */
|
|
void SetWheelTraceAllChannels(ECollisionResponse NewResponse)
|
|
{
|
|
WheelTraceCollisionResponses.SetAllChannels(NewResponse);
|
|
}
|
|
|
|
/** Set the response of this body to the supplied settings - for wheel raycasts */
|
|
void SetWheelTraceResponseToChannel(ECollisionChannel Channel, ECollisionResponse NewResponse)
|
|
{
|
|
WheelTraceCollisionResponses.SetResponse(Channel, NewResponse);
|
|
}
|
|
|
|
/** Get Collision ResponseToChannels container for this component **/
|
|
// FORCEINLINE_DEBUGGABLE const FCollisionResponseContainer& GetTraceResponseToChannels() const { return WheelTraceCollisionResponses.GetResponseContainer(); }
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Public
|
|
|
|
virtual void Serialize(FArchive& Ar) override;
|
|
virtual void PostLoad() override;
|
|
|
|
// Get output data from Physics Thread
|
|
virtual void ParallelUpdate(float DeltaSeconds);
|
|
|
|
#if WITH_EDITOR
|
|
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
|
#endif
|
|
/** Are the configuration references configured sufficiently that the vehicle can be created */
|
|
virtual bool CanCreateVehicle() const override;
|
|
|
|
/** Used to create any physics engine information for this component */
|
|
virtual void OnCreatePhysicsState() override;
|
|
|
|
/** Used to shut down and pysics engine structure for this component */
|
|
virtual void OnDestroyPhysicsState() override;
|
|
|
|
/** display next debug page */
|
|
static void NextDebugPage();
|
|
|
|
/** display previous debug page */
|
|
static void PrevDebugPage();
|
|
|
|
/** Enable or completely bypass the ProcessMechanicalSimulation call */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void EnableMechanicalSim(bool InState)
|
|
{
|
|
bMechanicalSimEnabled = InState;
|
|
}
|
|
|
|
/** Enable or completely bypass the ApplySuspensionForces call */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void EnableSuspension(bool InState)
|
|
{
|
|
bSuspensionEnabled = InState;
|
|
}
|
|
|
|
/** Enable or completely bypass the ApplyWheelFrictionForces call */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void EnableWheelFriction(bool InState)
|
|
{
|
|
bWheelFrictionEnabled = InState;
|
|
}
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelClass(int WheelIndex, TSubclassOf<UChaosVehicleWheel> InWheelClass);
|
|
|
|
/** Grab a snapshot of the vehicle instance dynamic state */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
virtual FWheeledSnaphotData GetSnapshot() const;
|
|
|
|
/** Set snapshot of vehicle instance dynamic state */
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
virtual void SetSnapshot(const FWheeledSnaphotData& SnapshotIn);
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// change handling via blueprint at runtime
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetMaxEngineTorque(float Torque);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetDragCoefficient(float DragCoeff);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetDownforceCoefficient(float DownforceCoeff);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetDifferentialFrontRearSplit(float FrontRearSlpit);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetTractionControlEnabled(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetABSEnabled(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetAffectedByBrake(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetAffectedByHandbrake(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetAffectedBySteering(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetAffectedByEngine(int WheelIndex, bool Enabled);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelRadius(int WheelIndex, float Radius);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelFrictionMultiplier(int WheelIndex, float Friction);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelSlipGraphMultiplier(int WheelIndex, float Multiplier);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelMaxBrakeTorque(int WheelIndex, float Torque);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelHandbrakeTorque(int WheelIndex, float Torque);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetWheelMaxSteerAngle(int WheelIndex, float AngleDegrees);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetTorqueCombineMethod(ETorqueCombineMethod InCombineMethod, int32 WheelIndex);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetDriveTorque(float DriveTorque, int32 WheelIndex);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetBrakeTorque(float BrakeTorque, int32 WheelIndex);
|
|
|
|
UFUNCTION(BlueprintCallable, Category = "Game|Components|ChaosWheeledVehicleMovement")
|
|
void SetSuspensionParams(float Rate, float Damping, float Preload, float MaxRaise, float MaxDrop, int32 WheelIndex);
|
|
|
|
/** */
|
|
virtual TUniquePtr<Chaos::FSimpleWheeledVehicle> CreatePhysicsVehicle() override
|
|
{
|
|
// Make the Vehicle Simulation class that will be updated from the physics thread async callback
|
|
VehicleSimulationPT = MakeUnique<UChaosWheeledVehicleSimulation>();
|
|
|
|
return UChaosVehicleMovementComponent::CreatePhysicsVehicle();
|
|
}
|
|
|
|
/** Allocate and setup the Chaos vehicle */
|
|
virtual void SetupVehicle(TUniquePtr<Chaos::FSimpleWheeledVehicle>& PVehicle) override;
|
|
|
|
virtual void ResetVehicleState() override;
|
|
|
|
protected:
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Setup
|
|
|
|
/** Re-Compute any runtime constants values that rely on setup data */
|
|
virtual void ComputeConstants() override;
|
|
|
|
/** Skeletal mesh needs some special handling in the vehicle case */
|
|
virtual void FixupSkeletalMesh();
|
|
|
|
/** Create and setup the Chaos vehicle */
|
|
virtual void CreateVehicle();
|
|
|
|
/** Instantiate and setup our wheel objects */
|
|
virtual void CreateWheels();
|
|
|
|
/** Release our wheel objects */
|
|
virtual void DestroyWheels();
|
|
|
|
/** Set up the chassis and wheel shapes */
|
|
virtual void SetupVehicleShapes();
|
|
|
|
/** Setup calculated suspension parameters */
|
|
void SetupSuspension(TUniquePtr<Chaos::FSimpleWheeledVehicle>& PVehicle);
|
|
|
|
/** Maps UChaosVehicleWheel Axle to a wheel index */
|
|
void RecalculateAxles();
|
|
|
|
/** Get the local position of the wheel at rest */
|
|
virtual FVector GetWheelRestingPosition(const FChaosWheelSetup& WheelSetup);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Update
|
|
void FillWheelOutputState();
|
|
|
|
/* Fill Async input state */
|
|
virtual void Update(float DeltaTime) override;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Debug
|
|
|
|
/** Draw 2D debug text graphs on UI for the wheels, suspension and other systems */
|
|
virtual void DrawDebug(UCanvas* Canvas, float& YL, float& YPos);
|
|
|
|
/** Get distances between wheels - primarily a debug display helper */
|
|
const FVector2D& GetWheelLayoutDimensions() const
|
|
{
|
|
return WheelTrackDimensions;
|
|
}
|
|
|
|
private:
|
|
|
|
|
|
/** Get distances between wheels - primarily a debug display helper */
|
|
FVector2D CalculateWheelLayoutDimensions();
|
|
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
float CalcDialAngle(float CurrentValue, float MaxValue);
|
|
void DrawDial(UCanvas* Canvas, FVector2D Pos, float Radius, float CurrentValue, float MaxValue);
|
|
#endif
|
|
|
|
struct FCachedState
|
|
{
|
|
FCachedState() : WheelOffset(0.f), bIsValid(false)
|
|
{ }
|
|
|
|
float WheelOffset;
|
|
bool bIsValid;
|
|
};
|
|
|
|
static EDebugPages DebugPage;
|
|
uint32 NumDrivenWheels; /** The number of wheels that have engine enabled checked */
|
|
FVector2D WheelTrackDimensions; // Wheelbase (X) and track (Y) dimensions
|
|
TMap<UChaosVehicleWheel*, TArray<int>> AxleToWheelMap;
|
|
TArray<FPhysicsConstraintHandle> ConstraintHandles;
|
|
TArray<FWheelStatus> WheelStatus; /** Wheel output status */
|
|
TArray<FCachedState> CachedState;
|
|
Chaos::FPerformanceMeasure PerformanceMeasure;
|
|
};
|
|
|
|
#if VEHICLE_DEBUGGING_ENABLED
|
|
UE_ENABLE_OPTIMIZATION
|
|
#endif
|