// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "PhysicsPublic.h" #include "Chaos/SimCallbackInput.h" #include "Chaos/SimCallbackObject.h" #include "Chaos/GeometryParticlesfwd.h" #include "TransmissionSystem.h" #include "PhysicsProxy/SingleParticlePhysicsProxyFwd.h" #include "Physics/NetworkPhysicsComponent.h" #include "ChaosVehicleWheel.h" #include "ChaosVehicleManagerAsyncCallback.generated.h" class UChaosVehicleMovementComponent; DECLARE_STATS_GROUP(TEXT("ChaosVehicleManager"), STATGROUP_ChaosVehicleManager, STATGROUP_Advanced); enum EChaosAsyncVehicleDataType : int8 { AsyncInvalid, AsyncDefault, }; /** Vehicle inputs from the player controller */ USTRUCT() struct CHAOSVEHICLES_API FVehicleInputs { GENERATED_USTRUCT_BODY() FVehicleInputs() : SteeringInput(0.f) , ThrottleInput(0.f) , BrakeInput(0.f) , PitchInput(0.f) , RollInput(0.f) , YawInput(0.f) , HandbrakeInput(0.f) {} // Steering output to physics system. Range -1...1 UPROPERTY() float SteeringInput; // Accelerator output to physics system. Range 0...1 UPROPERTY() float ThrottleInput; // Brake output to physics system. Range 0...1 UPROPERTY() float BrakeInput; // Body Pitch output to physics system. Range -1...1 UPROPERTY() float PitchInput; // Body Roll output to physics system. Range -1...1 UPROPERTY() float RollInput; // Body Yaw output to physics system. Range -1...1 UPROPERTY() float YawInput; // Handbrake output to physics system. Range 0...1 UPROPERTY() float HandbrakeInput; }; /** Control inputs holding the vehicle ones + persisten ones */ USTRUCT() struct CHAOSVEHICLES_API FControlInputs : public FVehicleInputs { GENERATED_USTRUCT_BODY() FControlInputs() : FVehicleInputs() , ParkingEnabled(false) , TransmissionType(Chaos::ETransmissionType::Automatic) , GearUpInput(false) , GearDownInput(false) {} /** Check if the parking is enabled */ UPROPERTY() bool ParkingEnabled; /** Type of transmission (manual/auto) */ UPROPERTY() uint8 TransmissionType; /** Check if the user has risen the gear */ UPROPERTY() bool GearUpInput; /** Check if the user has descreased the gear */ UPROPERTY() bool GearDownInput; }; /** Vehicle Inputs data that will be used in the inputs history to be applied while simulating */ USTRUCT() struct CHAOSVEHICLES_API FNetworkVehicleInputs : public FNetworkPhysicsData { GENERATED_USTRUCT_BODY() /** List of incoming control inputs coming from the local client */ UPROPERTY() FControlInputs VehicleInputs; /** Transmission change time that could be set/changed from GT */ UPROPERTY() float TransmissionChangeTime = 0.0; /** Transmission current gear that could be set/changed from GT */ UPROPERTY() int32 TransmissionCurrentGear = 0; /** Transmission target gear that could be set/changed from GT */ UPROPERTY() int32 TransmissionTargetGear = 0; /** Apply the data onto the network physics component */ virtual void ApplyData(UActorComponent* NetworkComponent) const override; /** Build the data from the network physics component */ virtual void BuildData(const UActorComponent* NetworkComponent) override; /** Decay input during resimulation */ virtual void DecayData(float DecayAmount) override; /** Serialize data function that will be used to transfer the struct across the network */ bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess); /** Interpolate the data in between two inputs data */ virtual void InterpolateData(const FNetworkPhysicsData& MinData, const FNetworkPhysicsData& MaxData) override; /** Merge data into this input */ virtual void MergeData(const FNetworkPhysicsData& FromData) override; /** Return string with input debug data */ virtual const FString DebugData() override; }; template<> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithNetSerializer = true, }; }; /** Vehicle states data that will be used in the states history to rewind the simulation at some point inn time */ USTRUCT() struct CHAOSVEHICLES_API FNetworkVehicleStates : public FNetworkPhysicsData { GENERATED_USTRUCT_BODY() /** Vehicle state last velocity */ UPROPERTY() FVector StateLastVelocity = FVector(0,0,0); /** Angular velocity for each wheels */ UPROPERTY() TArray WheelsOmega; /** Angular position for each wheels */ UPROPERTY() TArray WheelsAngularPosition; /** Suspension latest displacement to be used while simulating */ UPROPERTY() TArray SuspensionLastDisplacement; /** Suspension latest spring length to be used while simulating */ UPROPERTY() TArray SuspensionLastSpringLength; /** Suspension averaged length for smoothing */ UPROPERTY() TArray SuspensionAveragedLength; /** Suspension averaged count for smoothing */ UPROPERTY() TArray SuspensionAveragedCount; /** Suspension averaged number for smoothing */ UPROPERTY() TArray SuspensionAveragedNum; /** Engine angular velocity */ UPROPERTY() float EngineOmega = 0.0; /** Apply the data onto the network physics component */ virtual void ApplyData(UActorComponent* NetworkComponent) const override; /** Build the data from the network physics component */ virtual void BuildData(const UActorComponent* NetworkComponent) override; /** Serialize data function that will be used to transfer the struct across the network */ bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess); /** Interpolate the data in between two inputs data */ virtual void InterpolateData(const FNetworkPhysicsData& MinData, const FNetworkPhysicsData& MaxData) override; /** Return string with state debug data */ virtual const FString DebugData() override; }; template<> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithNetSerializer = true, }; }; /** Local physics wheels outputs not replicated across the network */ struct CHAOSVEHICLES_API FWheelsOutput { FWheelsOutput() : InContact(false) , SteeringAngle(0.f) , AngularPosition(0.f) , AngularVelocity(0.f) , WheelRadius(0.f) , LateralAdhesiveLimit(0.f) , LongitudinalAdhesiveLimit(0.f) , SlipAngle(0.f) , bIsSlipping(false) , SlipMagnitude(0.f) , bIsSkidding(false) , SkidMagnitude(0.f) , SkidNormal(FVector(1,0,0)) , SuspensionOffset(0.f) , SpringForce(0.f) , NormalizedSuspensionLength(0.f) , DriveTorque(0.f) , BrakeTorque(0.f) , bABSActivated(false) , ImpactPoint(FVector::ZeroVector) , HitLocation(FVector::ZeroVector) , PhysMaterial(nullptr) { } // wheels bool InContact; float SteeringAngle; float AngularPosition; float AngularVelocity; float WheelRadius; float LateralAdhesiveLimit; float LongitudinalAdhesiveLimit; float SlipAngle; bool bIsSlipping; float SlipMagnitude; bool bIsSkidding; float SkidMagnitude; FVector SkidNormal; // suspension related float SuspensionOffset; float SpringForce; float NormalizedSuspensionLength; float DriveTorque; float BrakeTorque; bool bABSActivated; bool bBlockingHit; FVector ImpactPoint; FVector HitLocation; TWeakObjectPtr PhysMaterial; }; /** * Per Vehicle Output State from Physics Thread to Game Thread */ struct CHAOSVEHICLES_API FPhysicsVehicleOutput { FPhysicsVehicleOutput() : CurrentGear(0) , TargetGear(0) , EngineRPM(0.f) , EngineTorque(0.f) , TransmissionRPM(0.f) , TransmissionTorque(0.f) { } TArray Wheels; int32 CurrentGear; int32 TargetGear; float EngineRPM; float EngineTorque; float TransmissionRPM; float TransmissionTorque; }; struct CHAOSVEHICLES_API FWheelTraceParams { ESweepType SweepType; ESweepShape SweepShape; }; /** * Per Vehicle input State from Game Thread to Physics Thread */ struct CHAOSVEHICLES_API FPhysicsVehicleInputs { FPhysicsVehicleInputs() : GravityZ(0.0f) , TraceParams() , TraceCollisionResponse() , WheelTraceParams() { } float GravityZ; mutable FNetworkVehicleInputs NetworkInputs; mutable FCollisionQueryParams TraceParams; mutable FCollisionResponseContainer TraceCollisionResponse; mutable TArray WheelTraceParams; }; struct CHAOSVEHICLES_API FPhysicsVehicleTraits { using InputsType = FNetworkVehicleInputs; using StatesType = FNetworkVehicleStates; }; /** * Per Vehicle Input State from Game Thread to Physics Thread */ struct CHAOSVEHICLES_API FChaosVehicleAsyncInput { const EChaosAsyncVehicleDataType Type; UChaosVehicleMovementComponent* Vehicle; Chaos::FSingleParticlePhysicsProxy* Proxy; FPhysicsVehicleInputs PhysicsInputs; /** * Vehicle simulation running on the Physics Thread */ virtual TUniquePtr Simulate(UWorld* World, const float DeltaSeconds, const float TotalSeconds, bool& bWakeOut) const; virtual void ApplyDeferredForces(Chaos::FRigidBodyHandle_Internal* RigidHandle) const; FChaosVehicleAsyncInput(EChaosAsyncVehicleDataType InType = EChaosAsyncVehicleDataType::AsyncInvalid) : Type(InType) , Vehicle(nullptr) { Proxy = nullptr; //indicates async/sync task not needed } virtual ~FChaosVehicleAsyncInput() = default; }; struct FChaosVehicleManagerAsyncInput : public Chaos::FSimCallbackInput { TArray> VehicleInputs; TWeakObjectPtr World; int32 Timestamp = INDEX_NONE; void Reset() { VehicleInputs.Reset(); World.Reset(); } }; /** * Async Output Data */ struct CHAOSVEHICLES_API FChaosVehicleAsyncOutput { const EChaosAsyncVehicleDataType Type; bool bValid; // indicates no work was done FPhysicsVehicleOutput VehicleSimOutput; FChaosVehicleAsyncOutput(EChaosAsyncVehicleDataType InType = EChaosAsyncVehicleDataType::AsyncInvalid) : Type(InType) , bValid(false) { } virtual ~FChaosVehicleAsyncOutput() = default; }; /** * Async Output for all of the vehicles handled by this Vehicle Manager */ struct FChaosVehicleManagerAsyncOutput : public Chaos::FSimCallbackOutput { TArray> VehicleOutputs; int32 Timestamp = INDEX_NONE; void Reset() { VehicleOutputs.Reset(); } }; /** * Async callback from the Physics Engine where we can perform our vehicle simulation */ class CHAOSVEHICLES_API FChaosVehicleManagerAsyncCallback : public Chaos::TSimCallbackObject { public: virtual FName GetFNameForStatId() const override; private: virtual void ProcessInputs_Internal(int32 PhysicsStep) override; virtual void OnPreSimulate_Internal() override; };